npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dayos/next

v0.2.2

Published

DayOS adapter for the Next App Router: windows with URLs of their own.

Readme

@dayos/next

The DayOS adapter for the Next App Router: every window gets a URL of its own, and the server render puts each route's content inside the window it belongs to.

npm install @dayos/core @dayos/next

Why it exists

Without it, a desktop is a single page. With it:

  • Requesting /docs returns HTML that already has the docs window open with its content inside — indexable, and no flash of an empty desktop.
  • Focusing a window updates the URL (via replace, so the back button still walks pages and not focus changes), which makes windows shareable by link.
  • A window that loses focus keeps showing its own content instead of the current route's.

Usage

Two routes, / and /about. The layout hands the current page's content to the provider:

// app/layout.tsx
import { Shell } from "./shell";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <Shell>{children}</Shell>
      </body>
    </html>
  );
}
// app/shell.tsx
"use client";

import { DesktopApp, DesktopIcon, Window, WindowContent } from "@dayos/core";
import { RoutedDesktop, useWindowRoute, WindowRouteProvider } from "@dayos/next";

const ROUTES = ["/", "/about"] as const;

function App({ href }: { href: string }) {
  return (
    <DesktopApp id={href}>
      <AppShell href={href} />
    </DesktopApp>
  );
}

function AppShell({ href }: { href: string }) {
  // The content of this window's route, frozen when focus moves elsewhere.
  const content = useWindowRoute();

  return (
    <>
      <DesktopIcon>{href}</DesktopIcon>
      <Window>
        <WindowContent>{content}</WindowContent>
      </Window>
    </>
  );
}

export function Shell({ children }: { children: React.ReactNode }) {
  return (
    <WindowRouteProvider content={children} routes={ROUTES}>
      <RoutedDesktop>
        {ROUTES.map((href) => (
          <App href={href} key={href} />
        ))}
      </RoutedDesktop>
    </WindowRouteProvider>
  );
}

A window's href is its DesktopApp id — one identity, declared once.

API

| | | | --- | --- | | WindowRouteProvider | Takes the layout's children as content and every route up front. Seeds the open window from the URL during render, which is what gets it into the server HTML. | | RoutedDesktop | Desktop with its window state delegated to the provider. Also the sole owner of the window → URL sync. | | useWindowRoute() | Returns the content of the containing app's route. Live while it's the current route, frozen once focus moves away. | | useDynamicWindows(pattern) | The windows a dynamic route has open right now, as { href, params }. |

RoutedDesktop accepts everything Desktop does except the window-state props, plus exitHref (default "/") — where the URL goes when the last window closes.

Dynamic routes

A route segment written :like-this matches anything, and the window is identified by the URL it matched — so one declaration stands for a window per document:

const ROUTES = ["/", "/documents", "/documents/:file"];

The app can't spell those windows out the way it does the static ones, so it asks the desktop which are open:

function DocumentApps() {
  return useDynamicWindows("/documents/:file").map(({ href, params }) => (
    <DesktopApp id={href} key={href}>
      <DocumentShell file={params.file} />
    </DesktopApp>
  ));
}

Nothing else is needed: an ordinary <Link href="/documents/violin.avif"> opens the document's window beside the list, and a visitor landing on that URL gets it in the server HTML.

params is what the pattern filled in, decoded the way a Next page's own params are, so a window gets sheet music.avif and not sheet%20music.avif. The href keeps the URL's spelling — it's the window's identity.

Declaring the pattern is what asks for a window per document. Leaving it out isn't a missing feature but the other reasonable behavior — /documents covers its subroutes, so the document opens inside the list's window and navigating there is navigating within it.

Notes

Routes are matched a segment at a time, most specific first: more segments win, and at the same depth a static segment beats a param. With /docs, /docs/api and /docs/:page declared, /docs/api opens the specific window, /docs/setup opens the param's, /docs/a/b opens /docs/a's, and /docsy opens none. / is the exception to covering subroutes: it matches only itself, or every URL on the site would belong to the home window.

Not every app has to be routed. One with a generated id simply has no URL, and the desktop treats it the same.

The adapter imports LayoutRouterContext from Next's internals — the price of keeping per-window content without parallel routes. A Next update that moves that module breaks the build loudly rather than silently.

License

MIT