diff --git a/biome.json b/biome.json
index 3b18c66041..81563a2bc5 100644
--- a/biome.json
+++ b/biome.json
@@ -5,6 +5,7 @@
"**/*.js",
"**/*.json",
"**/*.ts",
+ "rivetkit-typescript/packages/inspect/**/*",
"!engine/artifacts",
"!engine/sdks/typescript/api-full",
"!engine/sdks/typescript/runner-protocol",
diff --git a/examples/chat-room/package.json b/examples/chat-room/package.json
index 537ab51547..440b2032df 100644
--- a/examples/chat-room/package.json
+++ b/examples/chat-room/package.json
@@ -25,7 +25,8 @@
"vitest": "^3.1.1",
"@rivetkit/react": "workspace:*",
"react": "^18.2.0",
- "react-dom": "^18.2.0"
+ "react-dom": "^18.2.0",
+ "@rivetkit/inspector": "workspace:*"
},
"dependencies": {
"rivetkit": "workspace:*"
diff --git a/examples/chat-room/src/frontend/main.tsx b/examples/chat-room/src/frontend/main.tsx
index bd39f29eec..189dae5204 100644
--- a/examples/chat-room/src/frontend/main.tsx
+++ b/examples/chat-room/src/frontend/main.tsx
@@ -1,6 +1,7 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { App } from "./App";
+import "@rivetkit/inspector";
const root = document.getElementById("root");
if (!root) throw new Error("Root element not found");
diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx
index 6c586171f3..9158387842 100644
--- a/frontend/src/app/layout.tsx
+++ b/frontend/src/app/layout.tsx
@@ -465,7 +465,7 @@ function HeaderButton({ children, className, ...props }: ButtonProps) {
);
}
-function ConnectionStatus(): ReactNode {
+export function ConnectionStatus({ className }: { className?: string }) {
const endpoint = useSearch({
from: "/_context",
select: (s) => s.u,
@@ -478,7 +478,12 @@ function ConnectionStatus(): ReactNode {
if (isLoading) {
return (
-
+
Connecting
{endpoint}
@@ -490,7 +495,12 @@ function ConnectionStatus(): ReactNode {
if (isError) {
return (
-
+
Disconnected
@@ -515,7 +525,12 @@ function ConnectionStatus(): ReactNode {
if (isSuccess) {
return (
-
+
Connected
{endpoint}
diff --git a/frontend/src/app/route-layout.tsx b/frontend/src/app/route-layout.tsx
index 46522bf767..3e07e1c200 100644
--- a/frontend/src/app/route-layout.tsx
+++ b/frontend/src/app/route-layout.tsx
@@ -1,5 +1,5 @@
import { Outlet } from "@tanstack/react-router";
-import { useRef, useState } from "react";
+import { useEffect, useRef, useState } from "react";
import type { ImperativePanelHandle } from "react-resizable-panels";
import { H2, Skeleton } from "@/components";
import { RootLayoutContextProvider } from "@/components/actors/root-layout-context";
@@ -7,11 +7,21 @@ import * as Layout from "./layout";
export function RouteLayout({
children =
,
+ defaultCollapsed = false,
}: {
children?: React.ReactNode;
+ defaultCollapsed?: boolean;
}) {
const sidebarRef = useRef
(null);
- const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
+ const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(
+ () => defaultCollapsed,
+ );
+
+ useEffect(() => {
+ if (isSidebarCollapsed) {
+ sidebarRef.current?.collapse();
+ }
+ }, []);
return (
diff --git a/frontend/src/components/actors/actors-actor-details.tsx b/frontend/src/components/actors/actors-actor-details.tsx
index 4b782e3dd5..c860d58b46 100644
--- a/frontend/src/components/actors/actors-actor-details.tsx
+++ b/frontend/src/components/actors/actors-actor-details.tsx
@@ -136,13 +136,84 @@ export function ActorTabs({
className={cn(className, "flex-1 min-h-0 min-w-0 flex flex-col ")}
>
-
-
- {supportsState ? (
- div]:h-full",
+ }}
+ >
+
+
+ {supportsState ? (
+
+ State
+
+ ) : null}
+ {supportsConnections ? (
+
+ Connections
+
+ ) : null}
+ {supportsEvents ? (
+
+ Events
+
+ ) : null}
+ {supportsDatabase ? (
+
+ Database
+
+ ) : null}
+ {supportsLogs ? (
+
+ Logs
+
+ ) : null}
+ {supportsMetadata ? (
+
+ Metadata
+
+ ) : null}
+ {supportsMetrics ? (
+
+ Metrics
+
+ ) : null}
+
+ {actorId ? (
+
State
diff --git a/frontend/src/components/actors/worker/actor-worker-context.tsx b/frontend/src/components/actors/worker/actor-worker-context.tsx
index 4a51e7e651..470a21dd07 100644
--- a/frontend/src/components/actors/worker/actor-worker-context.tsx
+++ b/frontend/src/components/actors/worker/actor-worker-context.tsx
@@ -20,6 +20,20 @@ export const ActorWorkerContext = createContext(
null,
);
+const useConnectionDetails = () => {
+ return match(__APP_TYPE__)
+ .with("inspector", () => {
+ return { namespace: "", engineToken: "" };
+ })
+ .otherwise(() => {
+ const provider = useEngineCompatDataProvider();
+ return {
+ namespace: provider.engineNamespace,
+ engineToken: provider.engineToken,
+ };
+ });
+};
+
export const useActorWorker = () => {
const value = useContext(ActorWorkerContext);
assertNonNullable(value);
diff --git a/frontend/src/components/theme.css b/frontend/src/components/theme.css
index 6faea737e8..e44026ad7c 100644
--- a/frontend/src/components/theme.css
+++ b/frontend/src/components/theme.css
@@ -1,27 +1,4 @@
-:root {
- --background: 0 0% 100%;
- --foreground: 20 14.3% 4.1%;
- --card: 0 0% 100%;
- --card-foreground: 20 14.3% 4.1%;
- --popover: 0 0% 100%;
- --popover-foreground: 20 14.3% 4.1%;
- --primary: 24.6 95% 53.1%;
- --primary-foreground: 60 9.1% 97.8%;
- --secondary: 60 4.8% 95.9%;
- --secondary-foreground: 24 9.8% 10%;
- --muted: 60 4.8% 95.9%;
- --muted-foreground: 25 5.3% 44.7%;
- --muted-destructive: 0 72% 30%;
- --accent: 60 4.8% 95.9%;
- --accent-foreground: 24 9.8% 10%;
- --destructive: 0 84.2% 60.2%;
- --destructive-foreground: 60 9.1% 97.8%;
- --border: 20 5.9% 90%;
- --input: 20 5.9% 90%;
- --ring: 24.6 95% 53.1%;
- --radius: 0.5rem;
-}
-:root[class~="dark"] {
+:root[class~="dark"], :root, :host {
--background: 20 14.3% 4.1%;
--foreground: 60 9.1% 97.8%;
--card: 0 9.09% 6.47%;
@@ -43,6 +20,7 @@
--input: 12 6.5% 15.1%;
--ring: 18.59deg 100% 50%;
--background-main: 0 7.14% 5.49%;
+ --radius: 0.5rem;
}
:root {
diff --git a/rivetkit-typescript/packages/inspect/.gitignore b/rivetkit-typescript/packages/inspect/.gitignore
new file mode 100644
index 0000000000..a547bf36d8
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/rivetkit-typescript/packages/inspect/README.md b/rivetkit-typescript/packages/inspect/README.md
new file mode 100644
index 0000000000..d2e77611fd
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/README.md
@@ -0,0 +1,73 @@
+# React + TypeScript + Vite
+
+This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
+
+Currently, two official plugins are available:
+
+- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
+- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
+
+## React Compiler
+
+The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
+
+## Expanding the ESLint configuration
+
+If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
+
+```js
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ // Other configs...
+
+ // Remove tseslint.configs.recommended and replace with this
+ tseslint.configs.recommendedTypeChecked,
+ // Alternatively, use this for stricter rules
+ tseslint.configs.strictTypeChecked,
+ // Optionally, add this for stylistic rules
+ tseslint.configs.stylisticTypeChecked,
+
+ // Other configs...
+ ],
+ languageOptions: {
+ parserOptions: {
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
+ tsconfigRootDir: import.meta.dirname,
+ },
+ // other options...
+ },
+ },
+])
+```
+
+You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
+
+```js
+// eslint.config.js
+import reactX from 'eslint-plugin-react-x'
+import reactDom from 'eslint-plugin-react-dom'
+
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ // Other configs...
+ // Enable lint rules for React
+ reactX.configs['recommended-typescript'],
+ // Enable lint rules for React DOM
+ reactDom.configs.recommended,
+ ],
+ languageOptions: {
+ parserOptions: {
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
+ tsconfigRootDir: import.meta.dirname,
+ },
+ // other options...
+ },
+ },
+])
+```
diff --git a/rivetkit-typescript/packages/inspect/index.html b/rivetkit-typescript/packages/inspect/index.html
new file mode 100644
index 0000000000..156bc24f5d
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ inspect
+
+
+
+
+
+
diff --git a/rivetkit-typescript/packages/inspect/package.json b/rivetkit-typescript/packages/inspect/package.json
new file mode 100644
index 0000000000..3d4e09f673
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "@rivetkit/inspector",
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "exports": "./dist/index.js",
+ "dependencies": {
+ "@rivet-gg/icons": "workspace:*",
+ "@tanstack/react-query": "^5.87.1",
+ "@tanstack/react-router": "^1.131.36",
+ "@tanstack/router-plugin": "^1.131.36",
+ "autoprefixer": "^10.4.21",
+ "postcss": "^8.5.6",
+ "react": "^19.1.1",
+ "react-dom": "^19.1.1",
+ "tailwindcss": "^3.4.17",
+ "vite": "^7.2.2"
+ },
+ "devDependencies": {
+ "@types/node": "^24.6.0",
+ "@types/react": "^19.1.16",
+ "@types/react-dom": "^19.1.9",
+ "@vitejs/plugin-react": "^5.0.4",
+ "globals": "^16.4.0",
+ "typescript": "~5.9.3"
+ }
+}
diff --git a/rivetkit-typescript/packages/inspect/postcss.config.js b/rivetkit-typescript/packages/inspect/postcss.config.js
new file mode 100644
index 0000000000..7b75c83aff
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+};
diff --git a/rivetkit-typescript/packages/inspect/src/App.tsx b/rivetkit-typescript/packages/inspect/src/App.tsx
new file mode 100644
index 0000000000..0a26c0f7a3
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/src/App.tsx
@@ -0,0 +1,84 @@
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import {
+ createMemoryHistory,
+ createRouter,
+ RouterProvider,
+} from "@tanstack/react-router";
+import { useLayoutEffect, useState } from "react";
+import rivetLogo from "./assets/rivet.svg";
+import { TooltipProvider } from "./components";
+import { routeTree } from "./routeTree.gen";
+
+declare module "@tanstack/react-router" {
+ interface Register {
+ router: typeof router;
+ }
+}
+
+const memoryHistory = createMemoryHistory({
+ initialEntries: ["/"], // Pass your initial url
+});
+const getRoot = () => document.getElementById("rivetkit-inspector");
+const getBody = () => document.body;
+const queryClient = new QueryClient();
+const router = createRouter({
+ basepath: import.meta.env.BASE_URL,
+ routeTree,
+ context: {
+ queryClient: queryClient,
+ },
+ defaultPreloadStaleTime: 0,
+ defaultGcTime: 0,
+ defaultPreloadGcTime: 0,
+ defaultStaleTime: Infinity,
+ scrollRestoration: true,
+ defaultPendingMinMs: 300,
+ history: memoryHistory,
+ defaultOnCatch: (error) => {
+ console.error("Router caught an error:", error);
+ },
+});
+
+function App() {
+ const [isOpen, setIsOpen] = useState(true);
+
+ useLayoutEffect(() => {
+ getRoot()?.style.setProperty("position", "fixed");
+ getRoot()?.style.setProperty("z-index", "2147483647");
+ if (isOpen) {
+ getRoot()?.style.setProperty("bottom", "0");
+ getRoot()?.style.setProperty("right", "0");
+ getRoot()?.style.setProperty("left", "0");
+ getBody()?.style.setProperty("padding-bottom", "24rem");
+ } else {
+ getRoot()?.style.setProperty("bottom", "16px");
+ getRoot()?.style.setProperty("right", "16px");
+ getRoot()?.style.removeProperty("left");
+ getBody()?.style.removeProperty("padding-bottom");
+ }
+ }, [isOpen]);
+
+ if (!isOpen) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+ );
+}
+
+export default App;
diff --git a/rivetkit-typescript/packages/inspect/src/app b/rivetkit-typescript/packages/inspect/src/app
new file mode 120000
index 0000000000..b4bfee75ea
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/src/app
@@ -0,0 +1 @@
+../../../../frontend/src/app
\ No newline at end of file
diff --git a/rivetkit-typescript/packages/inspect/src/assets/rivet.svg b/rivetkit-typescript/packages/inspect/src/assets/rivet.svg
new file mode 100644
index 0000000000..287c425791
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/src/assets/rivet.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/rivetkit-typescript/packages/inspect/src/components b/rivetkit-typescript/packages/inspect/src/components
new file mode 120000
index 0000000000..5d2def2e89
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/src/components
@@ -0,0 +1 @@
+../../../../frontend/src/components
\ No newline at end of file
diff --git a/rivetkit-typescript/packages/inspect/src/content b/rivetkit-typescript/packages/inspect/src/content
new file mode 120000
index 0000000000..9492562148
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/src/content
@@ -0,0 +1 @@
+../../../../frontend/src/content
\ No newline at end of file
diff --git a/rivetkit-typescript/packages/inspect/src/index.css b/rivetkit-typescript/packages/inspect/src/index.css
new file mode 100644
index 0000000000..8db7c73b6e
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/src/index.css
@@ -0,0 +1,17 @@
+@import "./components/theme.css";
+
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+.bg-stripes {
+ background: repeating-linear-gradient(
+ 45deg,
+ /* biome-ignore lint/correctness/noUnknownFunction: tailwind functions */
+ theme("colors.primary.DEFAULT" / 5%),
+ /* biome-ignore lint/correctness/noUnknownFunction: tailwind functions */
+ theme("colors.primary.DEFAULT" / 5%) 20px,
+ transparent 20px,
+ transparent 40px
+ );
+}
diff --git a/rivetkit-typescript/packages/inspect/src/lib b/rivetkit-typescript/packages/inspect/src/lib
new file mode 120000
index 0000000000..53744a9fed
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/src/lib
@@ -0,0 +1 @@
+../../../../frontend/src/lib
\ No newline at end of file
diff --git a/rivetkit-typescript/packages/inspect/src/main.tsx b/rivetkit-typescript/packages/inspect/src/main.tsx
new file mode 100644
index 0000000000..0613891145
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/src/main.tsx
@@ -0,0 +1,17 @@
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+import App from "./App.tsx";
+import styles from "./index.css?inline";
+
+const root = document.createElement("div");
+root.id = "rivetkit-inspector";
+
+const shadow = root.attachShadow({ mode: "open" });
+document.body.appendChild(root);
+
+createRoot(shadow).render(
+
+
+
+ ,
+);
diff --git a/rivetkit-typescript/packages/inspect/src/queries b/rivetkit-typescript/packages/inspect/src/queries
new file mode 120000
index 0000000000..71b4bf2094
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/src/queries
@@ -0,0 +1 @@
+../../../../frontend/src/queries
\ No newline at end of file
diff --git a/rivetkit-typescript/packages/inspect/src/routeTree.gen.ts b/rivetkit-typescript/packages/inspect/src/routeTree.gen.ts
new file mode 100644
index 0000000000..57d51a3b6c
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/src/routeTree.gen.ts
@@ -0,0 +1,83 @@
+/* eslint-disable */
+
+// @ts-nocheck
+
+// noinspection JSUnusedGlobalSymbols
+
+// This file was automatically generated by TanStack Router.
+// You should NOT make any changes in this file as it will be overwritten.
+// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
+
+import { Route as rootRouteImport } from "./routes/__root";
+import { Route as ContextRouteImport } from "./routes/_context";
+import { Route as ContextIndexRouteImport } from "./routes/_context/index";
+
+const ContextRoute = ContextRouteImport.update({
+ id: "/_context",
+ getParentRoute: () => rootRouteImport,
+} as any);
+const ContextIndexRoute = ContextIndexRouteImport.update({
+ id: "/",
+ path: "/",
+ getParentRoute: () => ContextRoute,
+} as any);
+
+export interface FileRoutesByFullPath {
+ "/": typeof ContextIndexRoute;
+}
+export interface FileRoutesByTo {
+ "/": typeof ContextIndexRoute;
+}
+export interface FileRoutesById {
+ __root__: typeof rootRouteImport;
+ "/_context": typeof ContextRouteWithChildren;
+ "/_context/": typeof ContextIndexRoute;
+}
+export interface FileRouteTypes {
+ fileRoutesByFullPath: FileRoutesByFullPath;
+ fullPaths: "/";
+ fileRoutesByTo: FileRoutesByTo;
+ to: "/";
+ id: "__root__" | "/_context" | "/_context/";
+ fileRoutesById: FileRoutesById;
+}
+export interface RootRouteChildren {
+ ContextRoute: typeof ContextRouteWithChildren;
+}
+
+declare module "@tanstack/react-router" {
+ interface FileRoutesByPath {
+ "/_context": {
+ id: "/_context";
+ path: "";
+ fullPath: "";
+ preLoaderRoute: typeof ContextRouteImport;
+ parentRoute: typeof rootRouteImport;
+ };
+ "/_context/": {
+ id: "/_context/";
+ path: "/";
+ fullPath: "/";
+ preLoaderRoute: typeof ContextIndexRouteImport;
+ parentRoute: typeof ContextRoute;
+ };
+ }
+}
+
+interface ContextRouteChildren {
+ ContextIndexRoute: typeof ContextIndexRoute;
+}
+
+const ContextRouteChildren: ContextRouteChildren = {
+ ContextIndexRoute: ContextIndexRoute,
+};
+
+const ContextRouteWithChildren =
+ ContextRoute._addFileChildren(ContextRouteChildren);
+
+const rootRouteChildren: RootRouteChildren = {
+ ContextRoute: ContextRouteWithChildren,
+};
+export const routeTree = rootRouteImport
+ ._addFileChildren(rootRouteChildren)
+ ._addFileTypes();
diff --git a/rivetkit-typescript/packages/inspect/src/routes/__root.tsx b/rivetkit-typescript/packages/inspect/src/routes/__root.tsx
new file mode 100644
index 0000000000..56d08f76f1
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/src/routes/__root.tsx
@@ -0,0 +1,10 @@
+import type { QueryClient } from "@tanstack/react-query";
+import { createRootRouteWithContext, Outlet } from "@tanstack/react-router";
+
+interface RootRouteContext {
+ queryClient: QueryClient;
+}
+
+export const Route = createRootRouteWithContext()({
+ component: () => ,
+});
diff --git a/rivetkit-typescript/packages/inspect/src/routes/_context.tsx b/rivetkit-typescript/packages/inspect/src/routes/_context.tsx
new file mode 100644
index 0000000000..db62137715
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/src/routes/_context.tsx
@@ -0,0 +1,19 @@
+import { createFileRoute, Outlet } from "@tanstack/react-router";
+import { createGlobalContext } from "../app/data-providers/inspector-data-provider";
+
+export const Route = createFileRoute("/_context")({
+ component: RouteComponent,
+ context: ({ location: { search }, context }) => {
+ return {
+ dataProvider: createGlobalContext({
+ url: "http://127.0.0.1:6420",
+ token: "lNtOy9TZxmt2yGukMJREcJcEp78WDzuj",
+ }),
+ __type: "inspector" as const,
+ };
+ },
+});
+
+function RouteComponent() {
+ return ;
+}
diff --git a/rivetkit-typescript/packages/inspect/src/routes/_context/index.tsx b/rivetkit-typescript/packages/inspect/src/routes/_context/index.tsx
new file mode 100644
index 0000000000..6484ec3dbb
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/src/routes/_context/index.tsx
@@ -0,0 +1,59 @@
+import { faAngleDown, Icon } from "@rivet-gg/icons";
+import {
+ CatchBoundary,
+ createFileRoute,
+ useSearch,
+} from "@tanstack/react-router";
+import { useState } from "react";
+import { Actors } from "@/app/actors";
+import { BuildPrefiller } from "@/app/build-prefiller";
+import { InspectorCredentialsProvider } from "@/app/credentials-context";
+import { ConnectionStatus } from "@/app/layout";
+import { RouteLayout } from "@/app/route-layout";
+import rivetLogo from "../../assets/rivet.svg";
+
+export const Route = createFileRoute("/_context/")({
+ component: RouteComponent,
+});
+
+function RouteComponent() {
+ const search = useSearch({ from: "/_context" });
+ const [credentials, setCredentials] = useState({
+ url: "http://127.0.0.1:6420",
+ token: "lNtOy9TZxmt2yGukMJREcJcEp78WDzuj",
+ });
+
+ return (
+
+
+
+

+
Rivet Inspector
+
+
+
+
+
+
+
+
+
+
+ search.n?.join(",") ?? "no-build-name"
+ }
+ errorComponent={() => null}
+ >
+ {!search.n ? : null}
+
+
+
+
+ );
+}
diff --git a/rivetkit-typescript/packages/inspect/src/stores b/rivetkit-typescript/packages/inspect/src/stores
new file mode 120000
index 0000000000..d65630c2a3
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/src/stores
@@ -0,0 +1 @@
+../../../../frontend/src/stores
\ No newline at end of file
diff --git a/rivetkit-typescript/packages/inspect/src/utils b/rivetkit-typescript/packages/inspect/src/utils
new file mode 120000
index 0000000000..40cfb9f838
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/src/utils
@@ -0,0 +1 @@
+../../../../frontend/src/utils
\ No newline at end of file
diff --git a/rivetkit-typescript/packages/inspect/tailwind.config.js b/rivetkit-typescript/packages/inspect/tailwind.config.js
new file mode 100644
index 0000000000..5938dd2b6f
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/tailwind.config.js
@@ -0,0 +1,5 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: ["./src/**/*.{ts,tsx}"],
+ presets: [require("./src/components/tailwind-base")],
+};
diff --git a/rivetkit-typescript/packages/inspect/tsconfig.app.json b/rivetkit-typescript/packages/inspect/tsconfig.app.json
new file mode 100644
index 0000000000..25d77c1d75
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/tsconfig.app.json
@@ -0,0 +1,32 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "types": ["vite/client"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["src"]
+}
diff --git a/rivetkit-typescript/packages/inspect/tsconfig.json b/rivetkit-typescript/packages/inspect/tsconfig.json
new file mode 100644
index 0000000000..fb1241881d
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/rivetkit-typescript/packages/inspect/tsconfig.node.json b/rivetkit-typescript/packages/inspect/tsconfig.node.json
new file mode 100644
index 0000000000..e75109e0c5
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/tsconfig.node.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "ES2023",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "types": ["node"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/rivetkit-typescript/packages/inspect/vite.config.ts b/rivetkit-typescript/packages/inspect/vite.config.ts
new file mode 100644
index 0000000000..22a36155e1
--- /dev/null
+++ b/rivetkit-typescript/packages/inspect/vite.config.ts
@@ -0,0 +1,35 @@
+import path from "node:path";
+import { tanstackRouter } from "@tanstack/router-plugin/vite";
+import react from "@vitejs/plugin-react";
+import { defineConfig } from "vite";
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [
+ tanstackRouter({ target: "react", autoCodeSplitting: true }),
+ react(),
+ ],
+ optimizeDeps: {
+ include: ["@fortawesome/*", "@rivet-gg/icons"],
+ },
+ define: {
+ __APP_TYPE__: JSON.stringify("inspector"),
+ __APP_BUILD_ID__: JSON.stringify(Date.now().toString()),
+ },
+ build: {
+ lib: {
+ entry: path.resolve(__dirname, "src/main.tsx"),
+ name: "Inspector",
+ // the proper extensions will be added
+ fileName: "index",
+ },
+ },
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "src"),
+ },
+ },
+ worker: {
+ format: "es",
+ },
+});