mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
Every import, the catalog pin and the docs name @hanzo/app and github.com/hanzoai/app. lib/rooms.ts is lib/app.ts and components/rooms.tsx is components/app.tsx.
91 lines
3.6 KiB
TypeScript
91 lines
3.6 KiB
TypeScript
'use client'
|
||
|
||
// /dev, and by the edge every path under it (lib/edge.ts `APP`): Dev. Its
|
||
// address is the path — `/dev/run/<id>` a run, `/dev/projects/<org>/<repo>` a
|
||
// project, `/dev/<place>` every other place (@hanzo/app lib/host `app`,
|
||
// `place`).
|
||
//
|
||
// signed in the builder: the app (app/_web.tsx) in Dev, in the rooms' frame
|
||
// (components/rooms `Room`). A retired address is replaced by
|
||
// its path first, once, so Back does not land on it (`moved`);
|
||
// one whose page is not the app's (a builder Settings section is
|
||
// the site's /settings, @hanzo/app `settings`) is loaded instead.
|
||
// signed out bare `/dev` is Dev's page, the landing the export holds for
|
||
// crawlers and strangers; its Open Dev signs in and comes back.
|
||
// Any other address (./_away `away`) signs in first
|
||
// (destination.ts `enter`): the address is written down on the
|
||
// way out, so the sign-in returns to it and the first case takes
|
||
// over.
|
||
//
|
||
// The deciding is `Apex`'s (app/_apex.tsx), the door `/` is: the landing until
|
||
// hydration, then IAM's answer, the marks before paint both ways.
|
||
|
||
import { useEffect, useState, type ReactNode } from 'react'
|
||
import dynamic from 'next/dynamic'
|
||
import { useIam } from '@hanzo/iam/react'
|
||
import { path, route } from '@hanzo/build'
|
||
import { app, DEV, place } from '@hanzo/app/lib/host'
|
||
import { Room } from '@/components/app'
|
||
import { Apex } from '../_apex'
|
||
import { away } from './_away'
|
||
|
||
/** The app, fetched only for a signed-in reader. */
|
||
const App = dynamic(() => import('../_web'), { ssr: false })
|
||
|
||
/**
|
||
* The path a retired Dev address names, canonical; null when it is current.
|
||
*
|
||
* Retired: a query from before Dev had paths — `?at=` in the builder's grammar
|
||
* (what hanzo.build's addresses arrive as), or `?run=`, `?project=` or `?view=`
|
||
* (the console's and shared links') — and the builder's own grammar that
|
||
* @hanzo/app 0.1.35–0.1.38 wrote under /dev: `/dev/sess_<id>`,
|
||
* `/dev/-/<place>`, `/dev/<org>/<repo>`, `/dev/<slug>`. /dev/settings is not
|
||
* one: the edge answers it with a 301 to /settings (lib/publish.ts `MOVED`).
|
||
*/
|
||
export function moved(pathname: string, q: URLSearchParams): string | null {
|
||
const at = q.get('at')
|
||
if (at !== null) return app(path(route(at)))
|
||
const view = q.get('view')
|
||
const ref = q.get('run') ?? q.get('project') ?? (view ? `-/${view}` : null)
|
||
if (ref !== null) return app(path(route(ref)))
|
||
const rest = pathname.slice(DEV.length + 1)
|
||
const now = place(pathname)
|
||
if (!rest || (now?.mode === 'dev' && app(now.at) === pathname)) return null
|
||
return route(rest).kind === 'new' ? null : app(rest)
|
||
}
|
||
|
||
/** Signed in: the builder once IAM has settled, an old query replaced by its path first. */
|
||
function Builder() {
|
||
const { isLoading } = useIam()
|
||
const [ready, setReady] = useState(false)
|
||
useEffect(() => {
|
||
if (isLoading) return
|
||
const to = moved(window.location.pathname, new URLSearchParams(window.location.search))
|
||
if (to && !place(to)) {
|
||
window.location.replace(to)
|
||
return
|
||
}
|
||
if (to) window.history.replaceState(null, '', to)
|
||
setReady(true)
|
||
}, [isLoading])
|
||
return ready ? <App /> : null
|
||
}
|
||
|
||
/** Whether this address is a place in the builder, read once the browser has one. */
|
||
const placed = (): boolean => typeof window !== 'undefined' && away(window.location)
|
||
|
||
export function Door({ children }: { children: ReactNode }) {
|
||
return (
|
||
<Apex
|
||
placed={placed}
|
||
app={
|
||
<Room>
|
||
<Builder />
|
||
</Room>
|
||
}
|
||
>
|
||
{children}
|
||
</Apex>
|
||
)
|
||
}
|