-
- Not Found
-
-
- That route does not exist.
-
-
- ),
- }}
+ routes={clientRoutes}
+ fallback={
+
+
+ Not Found
+
+
+ That route does not exist.
+
+
+ }
/>
)
diff --git a/client/client-router.tsx b/client/client-router.tsx
index 813fdf4..da18750 100644
--- a/client/client-router.tsx
+++ b/client/client-router.tsx
@@ -1,4 +1,4 @@
-import { type Handle } from 'remix/component'
+import { type Handle } from 'remix/ui'
type RouterSetup = {
routes: Record
@@ -249,11 +249,18 @@ function ensureRouter() {
document.addEventListener('submit', handleDocumentSubmit)
}
-export function listenToRouterNavigation(handle: Handle, listener: () => void) {
+export function listenToRouterNavigation(
+ handle: Pick,
+ listener: () => void,
+) {
ensureRouter()
- handle.on(routerEvents, {
- navigate: () => listener(),
- })
+ const onNavigate = () => listener()
+ routerEvents.addEventListener('navigate', onNavigate)
+ handle.signal.addEventListener(
+ 'abort',
+ () => routerEvents.removeEventListener('navigate', onNavigate),
+ { once: true },
+ )
}
export function getPathname() {
@@ -281,15 +288,15 @@ export function navigate(to: string) {
notify()
}
-export function Router(handle: Handle, setup: RouterSetup) {
+export function Router(handle: Handle) {
listenToRouterNavigation(handle, () => {
void handle.update()
})
return () => {
const path = getPathname()
- const routeElement = matchRoute(path, setup.routes)
+ const routeElement = matchRoute(path, handle.props.routes)
if (routeElement) return routeElement
- return setup.fallback ?? null
+ return handle.props.fallback ?? null
}
}
diff --git a/client/counter.tsx b/client/counter.tsx
index f7fcea9..17bd17a 100644
--- a/client/counter.tsx
+++ b/client/counter.tsx
@@ -1,4 +1,4 @@
-import { type Handle } from 'remix/component'
+import { type Handle } from 'remix/ui'
import {
colors,
radius,
@@ -11,8 +11,8 @@ type CounterSetup = {
initial?: number
}
-export function Counter(handle: Handle, setup: CounterSetup = {}) {
- let count = setup.initial ?? 0
+export function Counter(handle: Handle) {
+ let count = handle.props.initial ?? 0
function increment() {
count += 1
diff --git a/client/double-check.ts b/client/double-check.ts
index e4bf7c0..f3bb626 100644
--- a/client/double-check.ts
+++ b/client/double-check.ts
@@ -1,4 +1,4 @@
-import { type Handle } from 'remix/component'
+import { type Handle } from 'remix/ui'
type BlurHandler = (event: FocusEvent) => void
type ClickHandler = (event: MouseEvent) => void
diff --git a/client/editable-text.tsx b/client/editable-text.tsx
index 468baf3..d0852ef 100644
--- a/client/editable-text.tsx
+++ b/client/editable-text.tsx
@@ -1,4 +1,4 @@
-import { type Handle } from 'remix/component'
+import { type Handle } from 'remix/ui'
type EditableTextProps = {
id: string
@@ -20,7 +20,7 @@ const inheritTextStyles = {
color: 'inherit',
} as const
-export function EditableText(handle: Handle) {
+export function EditableText(handle: Handle) {
let isEditing = false
let draftValue = ''
let isSaving = false
@@ -42,7 +42,8 @@ export function EditableText(handle: Handle) {
})
}
- return (props: EditableTextProps) => {
+ return () => {
+ const props = handle.props
const buttonId = `${props.id}-button`
function startEditing() {
diff --git a/client/entry.tsx b/client/entry.tsx
index 4b0ed1a..3cd4ede 100644
--- a/client/entry.tsx
+++ b/client/entry.tsx
@@ -1,4 +1,4 @@
-import { createRoot } from 'remix/component'
+import { createRoot } from 'remix/ui'
import { App } from './app.tsx'
const rootElement = document.getElementById('root') ?? document.body
diff --git a/client/notifications.tsx b/client/notifications.tsx
index 0bd6243..92fbca6 100644
--- a/client/notifications.tsx
+++ b/client/notifications.tsx
@@ -1,4 +1,4 @@
-import { type Handle } from 'remix/component'
+import { type Handle } from 'remix/ui'
import {
colors,
radius,
diff --git a/client/remix-ui-compat/jsx-runtime.ts b/client/remix-ui-compat/jsx-runtime.ts
new file mode 100644
index 0000000..eb51d18
--- /dev/null
+++ b/client/remix-ui-compat/jsx-runtime.ts
@@ -0,0 +1,111 @@
+import {
+ css as cssMixin,
+ Fragment,
+ on as onMixin,
+ type Handle,
+ type Props as RemixProps,
+ type RemixElement,
+ type RemixNode,
+} from 'remix/ui'
+import {
+ jsx as remixJsx,
+ jsxDEV as remixJsxDev,
+ jsxs as remixJsxs,
+} from 'remix/ui/jsx-runtime'
+
+type LegacyEventHandler = {
+ bivarianceHack(event: any, signal: AbortSignal): void
+}['bivarianceHack']
+type LegacyEventMap = Record
+type RenderFn = () => RemixNode
+
+type LegacyProps = Record & {
+ children?: RemixNode
+ css?: Record
+ key?: unknown
+ mix?: unknown
+ on?: LegacyEventMap
+}
+type KnownElementName =
+ | keyof HTMLElementTagNameMap
+ | keyof SVGElementTagNameMap
+ | keyof MathMLElementTagNameMap
+type CompatIntrinsicElements = {
+ [elementName in KnownElementName]: RemixProps<
+ elementName & keyof globalThis.JSX.IntrinsicElements
+ > &
+ LegacyProps
+}
+
+type ElementType = string | ((handle: Handle) => RenderFn)
+
+function normalizeProps(props: Record | null | undefined) {
+ if (!props) return {}
+ const { css, mix, on, ...rest } = props
+ const nextMix = []
+ if (css && typeof css === 'object') {
+ nextMix.push(cssMixin(css as any))
+ }
+ if (on && typeof on === 'object') {
+ for (const [eventName, handler] of Object.entries(on as LegacyEventMap)) {
+ nextMix.push(onMixin(eventName as never, handler as never))
+ }
+ }
+ if (Array.isArray(mix)) {
+ nextMix.push(...mix)
+ } else if (mix) {
+ nextMix.push(mix)
+ }
+ if (nextMix.length === 0) {
+ return props
+ }
+ return {
+ ...rest,
+ mix: nextMix,
+ }
+}
+
+export function jsx(
+ type: ElementType,
+ props: Record | null | undefined,
+ key?: string,
+) {
+ return remixJsx(type, normalizeProps(props), key)
+}
+
+export function jsxs(
+ type: ElementType,
+ props: Record | null | undefined,
+ key?: string,
+) {
+ return remixJsxs(type, normalizeProps(props), key)
+}
+
+export function jsxDEV(
+ type: ElementType,
+ props: Record | null | undefined,
+ key?: string,
+) {
+ return remixJsxDev(type, normalizeProps(props), key)
+}
+
+export { Fragment }
+
+export namespace JSX {
+ export type Element = RemixElement
+ export type ElementType = string | ((handle: Handle) => RenderFn)
+ export type ElementChildrenAttribute = {
+ children: unknown
+ }
+ export interface IntrinsicAttributes {
+ key?: unknown
+ }
+ export type LibraryManagedAttributes = component extends (
+ handle: Handle,
+ ) => RenderFn
+ ? componentProps extends Record
+ ? { key?: unknown }
+ : componentProps & { key?: unknown }
+ : props
+ export type IntrinsicElements = CompatIntrinsicElements
+}
diff --git a/client/routes/account.tsx b/client/routes/account.tsx
index 28418c7..bbb5b5b 100644
--- a/client/routes/account.tsx
+++ b/client/routes/account.tsx
@@ -1,4 +1,4 @@
-import { type Handle } from 'remix/component'
+import { type Handle } from 'remix/ui'
import { isAdminEmail } from '#shared/admin.ts'
import { colors, spacing, typography } from '#client/styles/tokens.ts'
diff --git a/client/routes/admin-agents.tsx b/client/routes/admin-agents.tsx
index b35a4ba..1b4d8ea 100644
--- a/client/routes/admin-agents.tsx
+++ b/client/routes/admin-agents.tsx
@@ -1,4 +1,4 @@
-import { type Handle } from 'remix/component'
+import { type Handle } from 'remix/ui'
import { createNotifications } from '#client/notifications.tsx'
import {
colors,
diff --git a/client/routes/chat.tsx b/client/routes/chat.tsx
index 7f2ca7c..aeaf261 100644
--- a/client/routes/chat.tsx
+++ b/client/routes/chat.tsx
@@ -1,4 +1,4 @@
-import { type Handle } from 'remix/component'
+import { type Handle } from 'remix/ui'
import { AgentMultiSelectCombobox } from '#client/agent-multi-select-combobox.tsx'
import { ChatClient, type ChatClientSnapshot } from '#client/chat-client.ts'
import { navigate, routerEvents } from '#client/client-router.tsx'
@@ -1094,11 +1094,15 @@ export function ChatRoute(handle: Handle) {
}
}
- handle.on(routerEvents, {
- navigate: () => {
- void syncActiveThreadFromLocation()
- },
- })
+ const handleRouterNavigate = () => {
+ void syncActiveThreadFromLocation()
+ }
+ routerEvents.addEventListener('navigate', handleRouterNavigate)
+ handle.signal.addEventListener(
+ 'abort',
+ () => routerEvents.removeEventListener('navigate', handleRouterNavigate),
+ { once: true },
+ )
handle.queueTask(() => {
if (typeof window === 'undefined') return
@@ -1464,11 +1468,7 @@ export function ChatRoute(handle: Handle) {
key={thread.id}
css={{
position: 'relative',
- '&:hover [data-thread-delete-button="true"], &:focus-within [data-thread-delete-button="true"]':
- {
- opacity: 1,
- pointerEvents: 'auto',
- },
+ isolation: 'isolate',
}}
>