feat: basic directory navigation

This commit is contained in:
2025-09-17 00:04:12 +00:00
parent c7fb40e8eb
commit 44ce32fd84
17 changed files with 456 additions and 47 deletions

View File

@@ -6,10 +6,14 @@
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.2.4", "@biomejs/biome": "2.2.4",
"@types/bun": "latest", "@types/bun": "latest",
"convex": "^1.27.0",
}, },
}, },
"packages/convex": { "packages/convex": {
"name": "@fileone/convex", "name": "@fileone/convex",
"dependencies": {
"@fileone/path": "workspace:*",
},
"peerDependencies": { "peerDependencies": {
"convex": "^1.27.0", "convex": "^1.27.0",
"typescript": "^5", "typescript": "^5",

4
convex.json Normal file
View File

@@ -0,0 +1,4 @@
{
"$schema": "https://raw.githubusercontent.com/get-convex/convex-backend/refs/heads/main/npm-packages/convex/schemas/convex.schema.json",
"functions": "packages/convex"
}

View File

@@ -13,6 +13,7 @@
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.2.4", "@biomejs/biome": "2.2.4",
"@types/bun": "latest" "@types/bun": "latest",
"convex": "^1.27.0"
} }
} }

View File

@@ -28,9 +28,10 @@ export const fetchFiles = authenticatedQuery({
export const fetchDirectoryContent = authenticatedQuery({ export const fetchDirectoryContent = authenticatedQuery({
args: { args: {
directoryId: v.optional(v.id("directories")), directoryId: v.optional(v.id("directories")),
path: v.optional(v.string()),
}, },
handler: async (ctx, { directoryId }): Promise<DirectoryItem[]> => { handler: async (ctx, { directoryId, path }): Promise<DirectoryItem[]> => {
return await Directories.fetchContent(ctx, directoryId) return await Directories.fetchContent(ctx, { directoryId, path })
}, },
}) })

View File

@@ -21,15 +21,34 @@ export type DirectoryItemKind = DirectoryItem["kind"]
export async function fetchContent( export async function fetchContent(
ctx: AuthenticatedQueryCtx, ctx: AuthenticatedQueryCtx,
directoryId?: Id<"directories">, {
path,
directoryId,
}: { path?: string; directoryId?: Id<"directories"> } = {},
): Promise<DirectoryItem[]> { ): Promise<DirectoryItem[]> {
let dirId: Id<"directories"> | undefined
if (path) {
dirId = await ctx.db
.query("directories")
.withIndex("byPath", (q) =>
q
.eq("userId", ctx.user._id)
.eq("path", path)
.eq("deletedAt", undefined),
)
.first()
.then((dir) => dir?._id)
} else if (directoryId) {
dirId = directoryId
}
const [files, directories] = await Promise.all([ const [files, directories] = await Promise.all([
ctx.db ctx.db
.query("files") .query("files")
.withIndex("byDirectoryId", (q) => .withIndex("byDirectoryId", (q) =>
q q
.eq("userId", ctx.user._id) .eq("userId", ctx.user._id)
.eq("directoryId", directoryId) .eq("directoryId", dirId)
.eq("deletedAt", undefined), .eq("deletedAt", undefined),
) )
.collect(), .collect(),
@@ -38,7 +57,7 @@ export async function fetchContent(
.withIndex("byParentId", (q) => .withIndex("byParentId", (q) =>
q q
.eq("userId", ctx.user._id) .eq("userId", ctx.user._id)
.eq("parentId", directoryId) .eq("parentId", dirId)
.eq("deletedAt", undefined), .eq("deletedAt", undefined),
) )
.collect(), .collect(),
@@ -95,7 +114,7 @@ export async function create(
userId: ctx.user._id, userId: ctx.user._id,
createdAt: now, createdAt: now,
updatedAt: now, updatedAt: now,
path: parentDir ? joinPath(parentDir.path, name) : PATH_SEPARATOR, path: parentDir ? joinPath(parentDir.path, name) : joinPath("", name),
}) })
} }

View File

@@ -42,7 +42,7 @@ const schema = defineSchema({
"name", "name",
"deletedAt", "deletedAt",
]) ])
.index("byPath", ["path", "deletedAt"]), .index("byPath", ["userId", "path", "deletedAt"]),
}) })
export default schema export default schema

View File

@@ -4,6 +4,15 @@ export function baseName(path: string): string {
return path.split(PATH_SEPARATOR).pop() ?? "" return path.split(PATH_SEPARATOR).pop() ?? ""
} }
export function isPathAbsolute(path: string): boolean {
return path.startsWith(PATH_SEPARATOR)
}
export function joinPath(...paths: string[]): string { export function joinPath(...paths: string[]): string {
return paths.join(PATH_SEPARATOR) return paths.join(PATH_SEPARATOR)
} }
export function splitPath(path: string): string[] {
const parts = path.split(PATH_SEPARATOR)
return isPathAbsolute(path) ? parts.slice(1) : parts
}

33
packages/web/convex/_generated/api.d.ts vendored Normal file
View File

@@ -0,0 +1,33 @@
/* eslint-disable */
/**
* Generated `api` utility.
*
* THIS CODE IS AUTOMATICALLY GENERATED.
*
* To regenerate, run `npx convex dev`.
* @module
*/
import type {
ApiFromModules,
FilterApi,
FunctionReference,
} from "convex/server";
/**
* A utility for referencing Convex functions in your app's API.
*
* Usage:
* ```js
* const myFunctionReference = api.myModule.myFunction;
* ```
*/
declare const fullApi: ApiFromModules<{}>;
export declare const api: FilterApi<
typeof fullApi,
FunctionReference<any, "public">
>;
export declare const internal: FilterApi<
typeof fullApi,
FunctionReference<any, "internal">
>;

View File

@@ -0,0 +1,22 @@
/* eslint-disable */
/**
* Generated `api` utility.
*
* THIS CODE IS AUTOMATICALLY GENERATED.
*
* To regenerate, run `npx convex dev`.
* @module
*/
import { anyApi } from "convex/server";
/**
* A utility for referencing Convex functions in your app's API.
*
* Usage:
* ```js
* const myFunctionReference = api.myModule.myFunction;
* ```
*/
export const api = anyApi;
export const internal = anyApi;

View File

@@ -0,0 +1,58 @@
/* eslint-disable */
/**
* Generated data model types.
*
* THIS CODE IS AUTOMATICALLY GENERATED.
*
* To regenerate, run `npx convex dev`.
* @module
*/
import { AnyDataModel } from "convex/server";
import type { GenericId } from "convex/values";
/**
* No `schema.ts` file found!
*
* This generated code has permissive types like `Doc = any` because
* Convex doesn't know your schema. If you'd like more type safety, see
* https://docs.convex.dev/using/schemas for instructions on how to add a
* schema file.
*
* After you change a schema, rerun codegen with `npx convex dev`.
*/
/**
* The names of all of your Convex tables.
*/
export type TableNames = string;
/**
* The type of a document stored in Convex.
*/
export type Doc = any;
/**
* An identifier for a document in Convex.
*
* Convex documents are uniquely identified by their `Id`, which is accessible
* on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).
*
* Documents can be loaded using `db.get(id)` in query and mutation functions.
*
* IDs are just strings at runtime, but this type can be used to distinguish them from other
* strings when type checking.
*/
export type Id<TableName extends TableNames = TableNames> =
GenericId<TableName>;
/**
* A type describing your Convex data model.
*
* This type includes information about what tables you have, the type of
* documents stored in those tables, and the indexes defined on them.
*
* This type is used to parameterize methods like `queryGeneric` and
* `mutationGeneric` to make them type-safe.
*/
export type DataModel = AnyDataModel;

View File

@@ -0,0 +1,142 @@
/* eslint-disable */
/**
* Generated utilities for implementing server-side Convex query and mutation functions.
*
* THIS CODE IS AUTOMATICALLY GENERATED.
*
* To regenerate, run `npx convex dev`.
* @module
*/
import {
ActionBuilder,
HttpActionBuilder,
MutationBuilder,
QueryBuilder,
GenericActionCtx,
GenericMutationCtx,
GenericQueryCtx,
GenericDatabaseReader,
GenericDatabaseWriter,
} from "convex/server";
import type { DataModel } from "./dataModel.js";
/**
* Define a query in this Convex app's public API.
*
* This function will be allowed to read your Convex database and will be accessible from the client.
*
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
*/
export declare const query: QueryBuilder<DataModel, "public">;
/**
* Define a query that is only accessible from other Convex functions (but not from the client).
*
* This function will be allowed to read from your Convex database. It will not be accessible from the client.
*
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
*/
export declare const internalQuery: QueryBuilder<DataModel, "internal">;
/**
* Define a mutation in this Convex app's public API.
*
* This function will be allowed to modify your Convex database and will be accessible from the client.
*
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
*/
export declare const mutation: MutationBuilder<DataModel, "public">;
/**
* Define a mutation that is only accessible from other Convex functions (but not from the client).
*
* This function will be allowed to modify your Convex database. It will not be accessible from the client.
*
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
*/
export declare const internalMutation: MutationBuilder<DataModel, "internal">;
/**
* Define an action in this Convex app's public API.
*
* An action is a function which can execute any JavaScript code, including non-deterministic
* code and code with side-effects, like calling third-party services.
* They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
* They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
*
* @param func - The action. It receives an {@link ActionCtx} as its first argument.
* @returns The wrapped action. Include this as an `export` to name it and make it accessible.
*/
export declare const action: ActionBuilder<DataModel, "public">;
/**
* Define an action that is only accessible from other Convex functions (but not from the client).
*
* @param func - The function. It receives an {@link ActionCtx} as its first argument.
* @returns The wrapped function. Include this as an `export` to name it and make it accessible.
*/
export declare const internalAction: ActionBuilder<DataModel, "internal">;
/**
* Define an HTTP action.
*
* This function will be used to respond to HTTP requests received by a Convex
* deployment if the requests matches the path and method where this action
* is routed. Be sure to route your action in `convex/http.js`.
*
* @param func - The function. It receives an {@link ActionCtx} as its first argument.
* @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up.
*/
export declare const httpAction: HttpActionBuilder;
/**
* A set of services for use within Convex query functions.
*
* The query context is passed as the first argument to any Convex query
* function run on the server.
*
* This differs from the {@link MutationCtx} because all of the services are
* read-only.
*/
export type QueryCtx = GenericQueryCtx<DataModel>;
/**
* A set of services for use within Convex mutation functions.
*
* The mutation context is passed as the first argument to any Convex mutation
* function run on the server.
*/
export type MutationCtx = GenericMutationCtx<DataModel>;
/**
* A set of services for use within Convex action functions.
*
* The action context is passed as the first argument to any Convex action
* function run on the server.
*/
export type ActionCtx = GenericActionCtx<DataModel>;
/**
* An interface to read from the database within Convex query functions.
*
* The two entry points are {@link DatabaseReader.get}, which fetches a single
* document by its {@link Id}, or {@link DatabaseReader.query}, which starts
* building a query.
*/
export type DatabaseReader = GenericDatabaseReader<DataModel>;
/**
* An interface to read from and write to the database within Convex mutation
* functions.
*
* Convex guarantees that all writes within a single mutation are
* executed atomically, so you never have to worry about partial writes leaving
* your data in an inconsistent state. See [the Convex Guide](https://docs.convex.dev/understanding/convex-fundamentals/functions#atomicity-and-optimistic-concurrency-control)
* for the guarantees Convex provides your functions.
*/
export type DatabaseWriter = GenericDatabaseWriter<DataModel>;

View File

@@ -0,0 +1,89 @@
/* eslint-disable */
/**
* Generated utilities for implementing server-side Convex query and mutation functions.
*
* THIS CODE IS AUTOMATICALLY GENERATED.
*
* To regenerate, run `npx convex dev`.
* @module
*/
import {
actionGeneric,
httpActionGeneric,
queryGeneric,
mutationGeneric,
internalActionGeneric,
internalMutationGeneric,
internalQueryGeneric,
} from "convex/server";
/**
* Define a query in this Convex app's public API.
*
* This function will be allowed to read your Convex database and will be accessible from the client.
*
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
*/
export const query = queryGeneric;
/**
* Define a query that is only accessible from other Convex functions (but not from the client).
*
* This function will be allowed to read from your Convex database. It will not be accessible from the client.
*
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
*/
export const internalQuery = internalQueryGeneric;
/**
* Define a mutation in this Convex app's public API.
*
* This function will be allowed to modify your Convex database and will be accessible from the client.
*
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
*/
export const mutation = mutationGeneric;
/**
* Define a mutation that is only accessible from other Convex functions (but not from the client).
*
* This function will be allowed to modify your Convex database. It will not be accessible from the client.
*
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
*/
export const internalMutation = internalMutationGeneric;
/**
* Define an action in this Convex app's public API.
*
* An action is a function which can execute any JavaScript code, including non-deterministic
* code and code with side-effects, like calling third-party services.
* They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
* They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
*
* @param func - The action. It receives an {@link ActionCtx} as its first argument.
* @returns The wrapped action. Include this as an `export` to name it and make it accessible.
*/
export const action = actionGeneric;
/**
* Define an action that is only accessible from other Convex functions (but not from the client).
*
* @param func - The function. It receives an {@link ActionCtx} as its first argument.
* @returns The wrapped function. Include this as an `export` to name it and make it accessible.
*/
export const internalAction = internalActionGeneric;
/**
* Define a Convex HTTP action.
*
* @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object
* as its second.
* @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.
*/
export const httpAction = httpActionGeneric;

View File

@@ -1,6 +1,8 @@
import { api } from "@fileone/convex/_generated/api" import { api } from "@fileone/convex/_generated/api"
import type { Doc } from "@fileone/convex/_generated/dataModel"
import type { DirectoryItem } from "@fileone/convex/model/directories" import type { DirectoryItem } from "@fileone/convex/model/directories"
import { useMutation } from "@tanstack/react-query" import { useMutation } from "@tanstack/react-query"
import { Link } from "@tanstack/react-router"
import { import {
type ColumnDef, type ColumnDef,
flexRender, flexRender,
@@ -81,12 +83,7 @@ const columns: ColumnDef<DirectoryItem>[] = [
case "file": case "file":
return <FileNameCell initialName={row.original.doc.name} /> return <FileNameCell initialName={row.original.doc.name} />
case "directory": case "directory":
return ( return <DirectoryNameCell directory={row.original.doc} />
<div className="flex w-full items-center gap-2">
<DirectoryIcon className="size-4" />
{row.original.doc.name}
</div>
)
} }
}, },
size: 1000, size: 1000,
@@ -116,11 +113,11 @@ const columns: ColumnDef<DirectoryItem>[] = [
}, },
] ]
export function FileTable() { export function FileTable({ path }: { path: string }) {
return ( return (
<FileTableContextMenu> <FileTableContextMenu>
<div className="w-full"> <div className="w-full">
<FileTableContent /> <FileTableContent path={path} />
</div> </div>
</FileTableContextMenu> </FileTableContextMenu>
) )
@@ -184,8 +181,8 @@ export function FileTableContextMenu({
) )
} }
export function FileTableContent() { export function FileTableContent({ path }: { path: string }) {
const directory = useQuery(api.files.fetchDirectoryContent, {}) const directory = useQuery(api.files.fetchDirectoryContent, { path })
const optimisticDeletedItems = useAtomValue(optimisticDeletedItemsAtom) const optimisticDeletedItems = useAtomValue(optimisticDeletedItemsAtom)
const setContextMenuTargetItem = useSetAtom(contextMenuTargeItemAtom) const setContextMenuTargetItem = useSetAtom(contextMenuTargeItemAtom)
@@ -377,6 +374,17 @@ function NewItemRow() {
) )
} }
function DirectoryNameCell({ directory }: { directory: Doc<"directories"> }) {
return (
<div className="flex w-full items-center gap-2">
<DirectoryIcon className="size-4" />
<Link className="hover:underline" to={`/files/${directory.path}`}>
{directory.name}
</Link>
</div>
)
}
function FileNameCell({ initialName }: { initialName: string }) { function FileNameCell({ initialName }: { initialName: string }) {
return ( return (
<div className="flex w-full items-center gap-2"> <div className="flex w-full items-center gap-2">

View File

@@ -1,5 +1,7 @@
import { api } from "@fileone/convex/_generated/api" import { api } from "@fileone/convex/_generated/api"
import { splitPath } from "@fileone/path"
import { useMutation } from "@tanstack/react-query" import { useMutation } from "@tanstack/react-query"
import { useParams } from "@tanstack/react-router"
import { useMutation as useConvexMutation } from "convex/react" import { useMutation as useConvexMutation } from "convex/react"
import { useSetAtom } from "jotai" import { useSetAtom } from "jotai"
import { import {
@@ -8,7 +10,7 @@ import {
PlusIcon, PlusIcon,
UploadCloudIcon, UploadCloudIcon,
} from "lucide-react" } from "lucide-react"
import { type ChangeEvent, useRef } from "react" import { type ChangeEvent, Fragment, useRef } from "react"
import { toast } from "sonner" import { toast } from "sonner"
import { import {
DropdownMenu, DropdownMenu,
@@ -29,16 +31,23 @@ import { Button } from "../components/ui/button"
import { FileTable } from "./file-table" import { FileTable } from "./file-table"
import { newItemKindAtom } from "./state" import { newItemKindAtom } from "./state"
export function FilesPage() { export function FilesPage({ path }: { path: string }) {
return ( return (
<> <>
<header className="flex py-2 shrink-0 items-center gap-2 border-b px-4 w-full"> <header className="flex py-1 shrink-0 items-center gap-2 border-b px-4 w-full">
<Breadcrumb> <Breadcrumb>
<BreadcrumbList> <BreadcrumbList>
<BreadcrumbItem> <BreadcrumbItem>
<BreadcrumbPage>All Files</BreadcrumbPage> <BreadcrumbPage>All Files</BreadcrumbPage>
</BreadcrumbItem> </BreadcrumbItem>
<BreadcrumbSeparator /> {splitPath(path).map((p) => (
<Fragment key={p}>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>{p}</BreadcrumbPage>
</BreadcrumbItem>
</Fragment>
))}
</BreadcrumbList> </BreadcrumbList>
</Breadcrumb> </Breadcrumb>
<div className="ml-auto flex flex-row gap-2"> <div className="ml-auto flex flex-row gap-2">
@@ -47,7 +56,7 @@ export function FilesPage() {
</div> </div>
</header> </header>
<div className="w-full"> <div className="w-full">
<FileTable /> <FileTable path={path} />
</div> </div>
</> </>
) )

View File

@@ -14,7 +14,7 @@ import { Route as AuthenticatedRouteImport } from './routes/_authenticated'
import { Route as AuthenticatedIndexRouteImport } from './routes/_authenticated/index' import { Route as AuthenticatedIndexRouteImport } from './routes/_authenticated/index'
import { Route as LoginCallbackRouteImport } from './routes/login_.callback' import { Route as LoginCallbackRouteImport } from './routes/login_.callback'
import { Route as AuthenticatedSidebarLayoutRouteImport } from './routes/_authenticated/_sidebar-layout' import { Route as AuthenticatedSidebarLayoutRouteImport } from './routes/_authenticated/_sidebar-layout'
import { Route as AuthenticatedSidebarLayoutFilesRouteImport } from './routes/_authenticated/_sidebar-layout/files' import { Route as AuthenticatedSidebarLayoutFilesSplatRouteImport } from './routes/_authenticated/_sidebar-layout/files.$'
const LoginRoute = LoginRouteImport.update({ const LoginRoute = LoginRouteImport.update({
id: '/login', id: '/login',
@@ -40,10 +40,10 @@ const AuthenticatedSidebarLayoutRoute =
id: '/_sidebar-layout', id: '/_sidebar-layout',
getParentRoute: () => AuthenticatedRoute, getParentRoute: () => AuthenticatedRoute,
} as any) } as any)
const AuthenticatedSidebarLayoutFilesRoute = const AuthenticatedSidebarLayoutFilesSplatRoute =
AuthenticatedSidebarLayoutFilesRouteImport.update({ AuthenticatedSidebarLayoutFilesSplatRouteImport.update({
id: '/files', id: '/files/$',
path: '/files', path: '/files/$',
getParentRoute: () => AuthenticatedSidebarLayoutRoute, getParentRoute: () => AuthenticatedSidebarLayoutRoute,
} as any) } as any)
@@ -51,13 +51,13 @@ export interface FileRoutesByFullPath {
'/login': typeof LoginRoute '/login': typeof LoginRoute
'/login/callback': typeof LoginCallbackRoute '/login/callback': typeof LoginCallbackRoute
'/': typeof AuthenticatedIndexRoute '/': typeof AuthenticatedIndexRoute
'/files': typeof AuthenticatedSidebarLayoutFilesRoute '/files/$': typeof AuthenticatedSidebarLayoutFilesSplatRoute
} }
export interface FileRoutesByTo { export interface FileRoutesByTo {
'/login': typeof LoginRoute '/login': typeof LoginRoute
'/login/callback': typeof LoginCallbackRoute '/login/callback': typeof LoginCallbackRoute
'/': typeof AuthenticatedIndexRoute '/': typeof AuthenticatedIndexRoute
'/files': typeof AuthenticatedSidebarLayoutFilesRoute '/files/$': typeof AuthenticatedSidebarLayoutFilesSplatRoute
} }
export interface FileRoutesById { export interface FileRoutesById {
__root__: typeof rootRouteImport __root__: typeof rootRouteImport
@@ -66,13 +66,13 @@ export interface FileRoutesById {
'/_authenticated/_sidebar-layout': typeof AuthenticatedSidebarLayoutRouteWithChildren '/_authenticated/_sidebar-layout': typeof AuthenticatedSidebarLayoutRouteWithChildren
'/login_/callback': typeof LoginCallbackRoute '/login_/callback': typeof LoginCallbackRoute
'/_authenticated/': typeof AuthenticatedIndexRoute '/_authenticated/': typeof AuthenticatedIndexRoute
'/_authenticated/_sidebar-layout/files': typeof AuthenticatedSidebarLayoutFilesRoute '/_authenticated/_sidebar-layout/files/$': typeof AuthenticatedSidebarLayoutFilesSplatRoute
} }
export interface FileRouteTypes { export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/login' | '/login/callback' | '/' | '/files' fullPaths: '/login' | '/login/callback' | '/' | '/files/$'
fileRoutesByTo: FileRoutesByTo fileRoutesByTo: FileRoutesByTo
to: '/login' | '/login/callback' | '/' | '/files' to: '/login' | '/login/callback' | '/' | '/files/$'
id: id:
| '__root__' | '__root__'
| '/_authenticated' | '/_authenticated'
@@ -80,7 +80,7 @@ export interface FileRouteTypes {
| '/_authenticated/_sidebar-layout' | '/_authenticated/_sidebar-layout'
| '/login_/callback' | '/login_/callback'
| '/_authenticated/' | '/_authenticated/'
| '/_authenticated/_sidebar-layout/files' | '/_authenticated/_sidebar-layout/files/$'
fileRoutesById: FileRoutesById fileRoutesById: FileRoutesById
} }
export interface RootRouteChildren { export interface RootRouteChildren {
@@ -126,23 +126,24 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AuthenticatedSidebarLayoutRouteImport preLoaderRoute: typeof AuthenticatedSidebarLayoutRouteImport
parentRoute: typeof AuthenticatedRoute parentRoute: typeof AuthenticatedRoute
} }
'/_authenticated/_sidebar-layout/files': { '/_authenticated/_sidebar-layout/files/$': {
id: '/_authenticated/_sidebar-layout/files' id: '/_authenticated/_sidebar-layout/files/$'
path: '/files' path: '/files/$'
fullPath: '/files' fullPath: '/files/$'
preLoaderRoute: typeof AuthenticatedSidebarLayoutFilesRouteImport preLoaderRoute: typeof AuthenticatedSidebarLayoutFilesSplatRouteImport
parentRoute: typeof AuthenticatedSidebarLayoutRoute parentRoute: typeof AuthenticatedSidebarLayoutRoute
} }
} }
} }
interface AuthenticatedSidebarLayoutRouteChildren { interface AuthenticatedSidebarLayoutRouteChildren {
AuthenticatedSidebarLayoutFilesRoute: typeof AuthenticatedSidebarLayoutFilesRoute AuthenticatedSidebarLayoutFilesSplatRoute: typeof AuthenticatedSidebarLayoutFilesSplatRoute
} }
const AuthenticatedSidebarLayoutRouteChildren: AuthenticatedSidebarLayoutRouteChildren = const AuthenticatedSidebarLayoutRouteChildren: AuthenticatedSidebarLayoutRouteChildren =
{ {
AuthenticatedSidebarLayoutFilesRoute: AuthenticatedSidebarLayoutFilesRoute, AuthenticatedSidebarLayoutFilesSplatRoute:
AuthenticatedSidebarLayoutFilesSplatRoute,
} }
const AuthenticatedSidebarLayoutRouteWithChildren = const AuthenticatedSidebarLayoutRouteWithChildren =

View File

@@ -0,0 +1,15 @@
import { joinPath, PATH_SEPARATOR } from "@fileone/path"
import { createFileRoute } from "@tanstack/react-router"
import { FilesPage } from "@/files/files-page"
export const Route = createFileRoute("/_authenticated/_sidebar-layout/files/$")(
{
component: RouteComponent,
},
)
function RouteComponent() {
const { _splat } = Route.useParams()
const path = _splat ? joinPath("", _splat) : PATH_SEPARATOR
return <FilesPage path={path} />
}

View File

@@ -1,6 +0,0 @@
import { createFileRoute } from "@tanstack/react-router"
import { FilesPage } from "@/files/files-page"
export const Route = createFileRoute("/_authenticated/_sidebar-layout/files")({
component: FilesPage,
})