Skip to content

ui-next: page registration#1159

Open
renbaoshuo wants to merge 7 commits into
hydro-dev:masterfrom
renbaoshuo:ui-next
Open

ui-next: page registration#1159
renbaoshuo wants to merge 7 commits into
hydro-dev:masterfrom
renbaoshuo:ui-next

Conversation

@renbaoshuo
Copy link
Copy Markdown
Contributor

@renbaoshuo renbaoshuo commented May 10, 2026

Summary by CodeRabbit

  • New Features

    • App now selects and renders pages by name with lazy loading, loading/error fallbacks, and a root app slot.
    • Plugins can register pages via the plugin API; new layout slot support.
    • Link building uses a new URL builder that respects UI/domain context.
    • New hooks to read UiContext and UserContext; runtime injection flags and plugin URL exposed.
  • Chores

    • Sample plugin updated to the new UI registration style.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 10, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 45704338-9b5c-4843-abe4-0f633d702149

📥 Commits

Reviewing files that changed from the base of the PR and between e6004ec and 15fa88f.

📒 Files selected for processing (1)
  • packages/ui-next/src/globals.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ui-next/src/globals.ts

Walkthrough

The PR replaces the page:app slot with a dynamic app:root slot that selects and renders pages by name, adds registerPage for lazy page registration with optional per-page Layouts, and updates the registry store to bump slot versions when defaults change. It introduces a simple app:layout slot, new pages, and exposes isInjected, hydroDomains, and pluginsUrl globals. Routing helpers were changed from useUrl to useBuildUrl (Link updated), Vite/tsconfig aliases were added, and the hydro plugin virtual module/build now discovers addon UI entries from global.addons and emits plugin objects including addon names; the ui-next plugin sample was adjusted accordingly.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • hydro-dev/Hydro#1138: Modifies the same ui-next registry/slot and plugin generation areas touched here (registerPage, use-build-url, slot changes).
  • hydro-dev/Hydro#951: Aligns with changes that convert global.addons shape and addon discovery, which this PR consumes when building hydro plugins.

Suggested reviewers

  • pandadtdyy
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'ui-next: page registration' directly and accurately describes the main changes: introducing a new page registration system via registerPage(), page component routing, and related infrastructure updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/ui-next/src/globals.ts

Oops! Something went wrong! :(

ESLint: 9.39.4

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'eslint-plugin-react-hooks' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:301:9)
at packageResolve (node:internal/modules/esm/resolve:764:81)
at moduleResolve (node:internal/modules/esm/resolve:855:18)
at defaultResolve (node:internal/modules/esm/resolve:988:11)
at #cachedDefaultResolve (node:internal/modules/esm/loader:697:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:714:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:746:52)
at #resolve (node:internal/modules/esm/loader:679:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:599:35)
at ModuleJob.syncLink (node:internal/modules/esm/module_job:162:33)

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/ui-next/src/registry/page.tsx (1)

18-21: ⚡ Quick win

Reconsider the props type signature.

The wrapper component is typed as accepting React.PropsWithChildren<P>, but:

  1. It's called without children in app.tsx (line 31: <Comp />)
  2. Spreading {...props} to PageComp includes a children prop that PageComp doesn't expect

The wrapper generates its own children (<PageComp />), so it shouldn't accept children from its caller.

♻️ Proposed fix: Remove children from props type
-      default(props: React.PropsWithChildren<P>) {
+      default(props: P) {
         return (
           <LayoutComp>
             <PageComp {...props} />
           </LayoutComp>
         );
       },

Alternatively, if pages should support receiving children, destructure them:

-      default(props: React.PropsWithChildren<P>) {
+      default({ children, ...pageProps }: React.PropsWithChildren<P>) {
         return (
           <LayoutComp>
-            <PageComp {...props} />
+            <PageComp {...pageProps} />
           </LayoutComp>
         );
       },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui-next/src/registry/page.tsx` around lines 18 - 21, The wrapper
component currently types its parameter as React.PropsWithChildren<P>, causing
an unexpected children prop to be forwarded to PageComp; change the component
signature from default(props: React.PropsWithChildren<P>) to default(props: P)
so the wrapper does not accept or forward children, and keep using
<LayoutComp><PageComp {...props} /></LayoutComp> (or alternatively explicitly
destructure and drop children: const { children, ...rest } = props and pass rest
to PageComp) to ensure PageComp only receives the props it expects.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ui-next/index.ts`:
- Around line 52-55: The generated plugin entries use raw interpolation and
place name before the plugin spread so plugin${i}.name can overwrite it and
unescaped addon names can break JS; change the construction to ensure the
addon-derived name is safely JSON-escaped (e.g. use JSON.stringify(addonName)
when inserting the literal) and make the name property come after the plugin
spread so it cannot be overridden (e.g. produce an entry like `{ ...plugin${i},
name: <escaped-addonName> }` or use Object.assign({}, plugin${i}, { name:
<escaped-addonName> }) ); apply the same fix to the other occurrence around
lines 128-131.

---

Nitpick comments:
In `@packages/ui-next/src/registry/page.tsx`:
- Around line 18-21: The wrapper component currently types its parameter as
React.PropsWithChildren<P>, causing an unexpected children prop to be forwarded
to PageComp; change the component signature from default(props:
React.PropsWithChildren<P>) to default(props: P) so the wrapper does not accept
or forward children, and keep using <LayoutComp><PageComp {...props}
/></LayoutComp> (or alternatively explicitly destructure and drop children:
const { children, ...rest } = props and pass rest to PageComp) to ensure
PageComp only receives the props it expects.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6818855e-0d95-4d73-a525-6a5c46c9398f

📥 Commits

Reviewing files that changed from the base of the PR and between 8dc7a73 and ff02037.

📒 Files selected for processing (13)
  • packages/ui-next/index.ts
  • packages/ui-next/src/app.tsx
  • packages/ui-next/src/components/layout.tsx
  • packages/ui-next/src/globals.ts
  • packages/ui-next/src/main.tsx
  • packages/ui-next/src/pages/homepage.tsx
  • packages/ui-next/src/pages/index.ts
  • packages/ui-next/src/pages/problem_main.tsx
  • packages/ui-next/src/registry/index.ts
  • packages/ui-next/src/registry/page.tsx
  • packages/ui-next/src/registry/plugin.ts
  • packages/ui-next/src/registry/store.ts
  • packages/ui-next/src/registry/types.ts
💤 Files with no reviewable changes (1)
  • packages/ui-next/src/globals.ts

Comment thread packages/ui-next/index.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui-next/index.ts (1)

152-152: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the entries count in the log message.

The entries variable is a Record<string, string> object, which does not have a .length property. This will log undefined for the entry count.

📊 Proposed fix
-        logger.success('Plugins built in %dms (%d entries, %s)', Date.now() - start, entries.length, size(totalSize));
+        logger.success('Plugins built in %dms (%d entries, %s)', Date.now() - start, Object.keys(entries).length, size(totalSize));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui-next/index.ts` at line 152, The log uses entries.length but
entries is a Record<string,string>, so replace the length access with a proper
key count (e.g., compute Object.keys(entries).length or
Object.entries(entries).length) before calling logger.success in the same
location where entries and totalSize are used; update the logger.success call to
pass that computed count (refer to the variables entries, totalSize and the
logger.success invocation).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ui-next/index.ts`:
- Around line 194-198: The non-dev renderer is using context.handler.user while
the dev renderer and the RendererContext interface expose the user as
context.UserContext; update the non-dev renderer to pass the same property by
replacing usages of context.handler.user with context.UserContext (and keep
UiContext as context.handler.UiContext) so both renderers consistently use
context.UserContext for the UserContext argument.

---

Outside diff comments:
In `@packages/ui-next/index.ts`:
- Line 152: The log uses entries.length but entries is a Record<string,string>,
so replace the length access with a proper key count (e.g., compute
Object.keys(entries).length or Object.entries(entries).length) before calling
logger.success in the same location where entries and totalSize are used; update
the logger.success call to pass that computed count (refer to the variables
entries, totalSize and the logger.success invocation).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 06b4b7a9-1bf2-4186-a4ad-6175bd864f80

📥 Commits

Reviewing files that changed from the base of the PR and between ff02037 and 46a9868.

📒 Files selected for processing (8)
  • build/prepare.js
  • packages/ui-next-plugin-sample/package.json
  • packages/ui-next-plugin-sample/ui/before.tsx
  • packages/ui-next-plugin-sample/ui/index.ts
  • packages/ui-next-plugin-sample/ui/index.tsx
  • packages/ui-next/index.ts
  • packages/ui-next/src/context/page-data.tsx
  • packages/ui-next/vite.config.ts
💤 Files with no reviewable changes (3)
  • packages/ui-next-plugin-sample/ui/before.tsx
  • packages/ui-next-plugin-sample/ui/index.ts
  • build/prepare.js
✅ Files skipped from review due to trivial changes (2)
  • packages/ui-next-plugin-sample/package.json
  • packages/ui-next-plugin-sample/ui/index.tsx

Comment thread packages/ui-next/index.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ui-next/src/hooks/use-build-url.ts`:
- Around line 14-19: The getPrefix callback in use-build-url assumes domain.host
and window are always present and may produce "/d/undefined"; fix it by guarding
access: treat domain.host as optional (use empty array or fallback) and ensure
window is available (typeof window !== 'undefined') before reading
window.location.host; also normalize the id at the top (if id is falsy use
domainId, and if still falsy return '' immediately) so you never return
`/d/undefined`; update the logic inside getPrefix (references: getPrefix,
domainId, domain.host, window.location.host) to use these guards and safe
fallbacks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 61a76aaa-79aa-4cab-b163-fbc69c3f2306

📥 Commits

Reviewing files that changed from the base of the PR and between 46a9868 and c63dbf3.

📒 Files selected for processing (5)
  • packages/ui-next/src/api.ts
  • packages/ui-next/src/components/link.tsx
  • packages/ui-next/src/context/page-data.tsx
  • packages/ui-next/src/hooks/use-build-url.ts
  • packages/ui-next/src/hooks/use-url.ts
💤 Files with no reviewable changes (1)
  • packages/ui-next/src/hooks/use-url.ts

Comment on lines +14 to +19
const getPrefix = useCallback((id?: string) => {
id ||= domainId;
const domainHost = Array.isArray(domain.host) ? domain.host : [domain.host];
const currentHost = window.location.host;
return id === (domainHost && domainHost.includes(currentHost) ? domainId : 'system') ? '' : `/d/${id}`;
}, [domainId, domain]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard prefix resolution for SSR and partial UiContext.

Line 16 assumes domain.host exists and Line 17 assumes window exists. This can crash URL building in SSR/injected contexts, and can generate malformed /d/undefined when ids are missing.

Proposed fix
   const getPrefix = useCallback((id?: string) => {
-    id ||= domainId;
-    const domainHost = Array.isArray(domain.host) ? domain.host : [domain.host];
-    const currentHost = window.location.host;
-    return id === (domainHost && domainHost.includes(currentHost) ? domainId : 'system') ? '' : `/d/${id}`;
-  }, [domainId, domain]);
+    const resolvedId = id ?? domainId ?? 'system';
+    const domainHost = Array.isArray(domain?.host)
+      ? domain.host
+      : (domain?.host ? [domain.host] : []);
+    const currentHost = typeof window !== 'undefined' ? window.location.host : '';
+    const defaultId = currentHost && domainHost.includes(currentHost) ? (domainId ?? 'system') : 'system';
+    return resolvedId === defaultId ? '' : `/d/${resolvedId}`;
+  }, [domainId, domain]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui-next/src/hooks/use-build-url.ts` around lines 14 - 19, The
getPrefix callback in use-build-url assumes domain.host and window are always
present and may produce "/d/undefined"; fix it by guarding access: treat
domain.host as optional (use empty array or fallback) and ensure window is
available (typeof window !== 'undefined') before reading window.location.host;
also normalize the id at the top (if id is falsy use domainId, and if still
falsy return '' immediately) so you never return `/d/undefined`; update the
logic inside getPrefix (references: getPrefix, domainId, domain.host,
window.location.host) to use these guards and safe fallbacks.

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.

1 participant