@interactive-inc/docs-client 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.tanstack/tmp/router-generator-Eq20Yx/50b11a0f488e61cf92364a7e585c1448 +123 -0
- package/.tanstack/tmp/router-generator-KQIYSa/aecb094e4891f9475b91de590abc8809 +117 -0
- package/.tanstack/tmp/router-generator-ekW2pI/aecb094e4891f9475b91de590abc8809 +117 -0
- package/app/components/archived-file-list-view.tsx +99 -0
- package/app/components/directory-file-list-view.tsx +32 -0
- package/app/components/directory-file-tree-node.tsx +96 -0
- package/app/components/directory-layout-sidebar.tsx +181 -0
- package/app/components/directory-page-view.tsx +208 -0
- package/app/components/directory-table-view.tsx +383 -0
- package/app/components/editable-table-cell/boolean-editable-cell.tsx +44 -0
- package/app/components/editable-table-cell/multi-boolean-editable-cell.tsx +59 -0
- package/app/components/editable-table-cell/multi-number-editable-cell.tsx +62 -0
- package/app/components/editable-table-cell/multi-relation-editable-cell.tsx +19 -0
- package/app/components/editable-table-cell/multi-select-number-editable-cell.tsx +82 -0
- package/app/components/editable-table-cell/multi-select-text-editable-cell.tsx +82 -0
- package/app/components/editable-table-cell/multi-text-editable-cell.tsx +62 -0
- package/app/components/editable-table-cell/number-editable-cell.tsx +54 -0
- package/app/components/editable-table-cell/relation-editable-cell.tsx +20 -0
- package/app/components/editable-table-cell/select-number-editable-cell.tsx +43 -0
- package/app/components/editable-table-cell/select-text-editable-cell.tsx +33 -0
- package/app/components/editable-table-cell/string-editable-cell.tsx +42 -0
- package/app/components/editable-table-cell.tsx +129 -0
- package/app/components/file-card.tsx +120 -0
- package/app/components/file-tree-icon.tsx +26 -0
- package/app/components/file-view/archive-restore-banner.tsx +86 -0
- package/app/components/file-view/csv-file-view.tsx +50 -0
- package/app/components/file-view/default-file-view.tsx +11 -0
- package/app/components/file-view/editable-csv-cell.tsx +37 -0
- package/app/components/file-view/editable-front-matter-view.tsx +105 -0
- package/app/components/file-view/file-csv-columns-view.tsx +181 -0
- package/app/components/file-view/file-csv-table.tsx +142 -0
- package/app/components/file-view/file-header.tsx +115 -0
- package/app/components/file-view/file-page-view.tsx +165 -0
- package/app/components/file-view/front-matter-input-field.tsx +157 -0
- package/app/components/file-view/front-matter-view.tsx +30 -0
- package/app/components/file-view/json-file-editor.tsx +38 -0
- package/app/components/file-view/markdown-file-view.tsx +71 -0
- package/app/components/file-view/multi-relation-select.tsx +129 -0
- package/app/components/file-view/render-front-matter-value.tsx +57 -0
- package/app/components/file-view/single-relation-select.tsx +41 -0
- package/app/components/file-view/sortable-csv-row.tsx +57 -0
- package/app/components/file-view/sortable-header.tsx +30 -0
- package/app/components/file-view/utils.ts +8 -0
- package/app/components/milestones-editor.tsx +95 -0
- package/app/components/page-header.tsx +20 -0
- package/app/components/project-view/feature-item.tsx +139 -0
- package/app/components/project-view/project-page-group.tsx +125 -0
- package/app/components/project-view/project-view.tsx +230 -0
- package/app/components/project-view/unlinked-features-section.tsx +73 -0
- package/app/components/providers.tsx +21 -0
- package/app/components/reload-button.tsx +25 -0
- package/app/components/sidebar-button.tsx +13 -0
- package/app/components/ui/alert.tsx +66 -0
- package/app/components/ui/badge.tsx +46 -0
- package/app/components/ui/button.tsx +59 -0
- package/app/components/ui/card.tsx +92 -0
- package/app/components/ui/dialog.tsx +133 -0
- package/app/components/ui/emoji-picker.tsx +133 -0
- package/app/components/ui/icons.tsx +3 -0
- package/app/components/ui/input.tsx +21 -0
- package/app/components/ui/select.tsx +183 -0
- package/app/components/ui/separator.tsx +26 -0
- package/app/components/ui/sheet.tsx +137 -0
- package/app/components/ui/sidebar.tsx +724 -0
- package/app/components/ui/simple-sidebar.tsx +50 -0
- package/app/components/ui/skeleton.tsx +13 -0
- package/app/components/ui/sonner.tsx +23 -0
- package/app/components/ui/table.tsx +114 -0
- package/app/components/ui/tabs.tsx +53 -0
- package/app/components/ui/textarea.tsx +18 -0
- package/app/components/ui/tooltip.tsx +59 -0
- package/app/components/vscode-button.tsx +23 -0
- package/app/hooks/use-directory-query.ts +22 -0
- package/app/hooks/use-file-properties-mutation.ts +38 -0
- package/app/hooks/use-file-query.ts +21 -0
- package/app/hooks/use-mobile.ts +19 -0
- package/app/main.css +122 -0
- package/app/main.tsx +38 -0
- package/app/routes/$.index.tsx +25 -0
- package/app/routes/$.tsx +20 -0
- package/app/routes/__root.tsx +20 -0
- package/app/routes/_index.tsx +17 -0
- package/app/routes/apps.client.$.tsx +21 -0
- package/build/assets/index.css +1 -0
- package/build/assets/index.js +298 -0
- package/build/index.d.ts +2 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +23 -0
- package/components.json +21 -0
- package/package.json +54 -0
- package/tsconfig.index.json +17 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ReactNode } from "react"
|
|
2
|
+
import { Button } from "@/components/ui/button"
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
title: string
|
|
6
|
+
children: ReactNode
|
|
7
|
+
isOpen: boolean
|
|
8
|
+
onClose: () => void
|
|
9
|
+
onOpen: () => void
|
|
10
|
+
footer: ReactNode
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function SimpleSidebar(props: Props) {
|
|
14
|
+
return (
|
|
15
|
+
<>
|
|
16
|
+
<div
|
|
17
|
+
className={`border-r transition-all duration-200 ${
|
|
18
|
+
props.isOpen ? "w-64" : "w-0 overflow-hidden"
|
|
19
|
+
}`}
|
|
20
|
+
>
|
|
21
|
+
<div className="flex h-full flex-col">
|
|
22
|
+
<div className="flex items-center justify-between border-b p-4">
|
|
23
|
+
<h2 className="font-semibold">{props.title}</h2>
|
|
24
|
+
<Button
|
|
25
|
+
variant="ghost"
|
|
26
|
+
size="sm"
|
|
27
|
+
onClick={props.onClose}
|
|
28
|
+
className="h-8 w-8 p-0"
|
|
29
|
+
>
|
|
30
|
+
{"<"}
|
|
31
|
+
</Button>
|
|
32
|
+
</div>
|
|
33
|
+
<div className="flex-1 overflow-auto p-4">{props.children}</div>
|
|
34
|
+
<div className="border-t p-4">{props.footer}</div>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
{!props.isOpen && (
|
|
39
|
+
<Button
|
|
40
|
+
variant="ghost"
|
|
41
|
+
size="sm"
|
|
42
|
+
onClick={props.onOpen}
|
|
43
|
+
className="absolute top-16 left-4 h-8 w-8 p-0"
|
|
44
|
+
>
|
|
45
|
+
{">"}
|
|
46
|
+
</Button>
|
|
47
|
+
)}
|
|
48
|
+
</>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils"
|
|
2
|
+
|
|
3
|
+
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
|
4
|
+
return (
|
|
5
|
+
<div
|
|
6
|
+
data-slot="skeleton"
|
|
7
|
+
className={cn("animate-pulse rounded-md bg-accent", className)}
|
|
8
|
+
{...props}
|
|
9
|
+
/>
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export { Skeleton }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useTheme } from "next-themes"
|
|
2
|
+
import { Toaster as Sonner, type ToasterProps } from "sonner"
|
|
3
|
+
|
|
4
|
+
const Toaster = ({ ...props }: ToasterProps) => {
|
|
5
|
+
const { theme = "system" } = useTheme()
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<Sonner
|
|
9
|
+
theme={theme as ToasterProps["theme"]}
|
|
10
|
+
className="toaster group"
|
|
11
|
+
style={
|
|
12
|
+
{
|
|
13
|
+
"--normal-bg": "var(--popover)",
|
|
14
|
+
"--normal-text": "var(--popover-foreground)",
|
|
15
|
+
"--normal-border": "var(--border)",
|
|
16
|
+
} as React.CSSProperties
|
|
17
|
+
}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { Toaster }
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
function Table({ className, ...props }: React.ComponentProps<"table">) {
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
data-slot="table-container"
|
|
9
|
+
className="relative w-full overflow-x-auto"
|
|
10
|
+
>
|
|
11
|
+
<table
|
|
12
|
+
data-slot="table"
|
|
13
|
+
className={cn("w-full caption-bottom text-sm", className)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
</div>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
|
|
21
|
+
return (
|
|
22
|
+
<thead
|
|
23
|
+
data-slot="table-header"
|
|
24
|
+
className={cn("[&_tr]:border-b", className)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
|
|
31
|
+
return (
|
|
32
|
+
<tbody
|
|
33
|
+
data-slot="table-body"
|
|
34
|
+
className={cn("[&_tr:last-child]:border-0", className)}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
|
|
41
|
+
return (
|
|
42
|
+
<tfoot
|
|
43
|
+
data-slot="table-footer"
|
|
44
|
+
className={cn(
|
|
45
|
+
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
|
|
46
|
+
className,
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
|
54
|
+
return (
|
|
55
|
+
<tr
|
|
56
|
+
data-slot="table-row"
|
|
57
|
+
className={cn(
|
|
58
|
+
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
|
59
|
+
className,
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
|
67
|
+
return (
|
|
68
|
+
<th
|
|
69
|
+
data-slot="table-head"
|
|
70
|
+
className={cn(
|
|
71
|
+
"h-10 whitespace-nowrap px-2 text-left align-middle font-medium text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
|
72
|
+
className,
|
|
73
|
+
)}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
|
80
|
+
return (
|
|
81
|
+
<td
|
|
82
|
+
data-slot="table-cell"
|
|
83
|
+
className={cn(
|
|
84
|
+
"whitespace-nowrap p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
|
85
|
+
className,
|
|
86
|
+
)}
|
|
87
|
+
{...props}
|
|
88
|
+
/>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function TableCaption({
|
|
93
|
+
className,
|
|
94
|
+
...props
|
|
95
|
+
}: React.ComponentProps<"caption">) {
|
|
96
|
+
return (
|
|
97
|
+
<caption
|
|
98
|
+
data-slot="table-caption"
|
|
99
|
+
className={cn("mt-4 text-muted-foreground text-sm", className)}
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export {
|
|
106
|
+
Table,
|
|
107
|
+
TableHeader,
|
|
108
|
+
TableBody,
|
|
109
|
+
TableFooter,
|
|
110
|
+
TableHead,
|
|
111
|
+
TableRow,
|
|
112
|
+
TableCell,
|
|
113
|
+
TableCaption,
|
|
114
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Tabs as TabsPrimitive } from "radix-ui"
|
|
2
|
+
import * as React from "react"
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
|
|
6
|
+
const Tabs = TabsPrimitive.Root
|
|
7
|
+
|
|
8
|
+
const TabsList = React.forwardRef<
|
|
9
|
+
React.ElementRef<typeof TabsPrimitive.List>,
|
|
10
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
|
11
|
+
>(({ className, ...props }, ref) => (
|
|
12
|
+
<TabsPrimitive.List
|
|
13
|
+
ref={ref}
|
|
14
|
+
className={cn(
|
|
15
|
+
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
|
|
16
|
+
className,
|
|
17
|
+
)}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
))
|
|
21
|
+
TabsList.displayName = TabsPrimitive.List.displayName
|
|
22
|
+
|
|
23
|
+
const TabsTrigger = React.forwardRef<
|
|
24
|
+
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
|
25
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
|
26
|
+
>(({ className, ...props }, ref) => (
|
|
27
|
+
<TabsPrimitive.Trigger
|
|
28
|
+
ref={ref}
|
|
29
|
+
className={cn(
|
|
30
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 font-medium text-sm ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
|
|
31
|
+
className,
|
|
32
|
+
)}
|
|
33
|
+
{...props}
|
|
34
|
+
/>
|
|
35
|
+
))
|
|
36
|
+
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
|
37
|
+
|
|
38
|
+
const TabsContent = React.forwardRef<
|
|
39
|
+
React.ElementRef<typeof TabsPrimitive.Content>,
|
|
40
|
+
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
|
41
|
+
>(({ className, ...props }, ref) => (
|
|
42
|
+
<TabsPrimitive.Content
|
|
43
|
+
ref={ref}
|
|
44
|
+
className={cn(
|
|
45
|
+
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
46
|
+
className,
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
))
|
|
51
|
+
TabsContent.displayName = TabsPrimitive.Content.displayName
|
|
52
|
+
|
|
53
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
|
6
|
+
return (
|
|
7
|
+
<textarea
|
|
8
|
+
data-slot="textarea"
|
|
9
|
+
className={cn(
|
|
10
|
+
"field-sizing-content flex min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs outline-none transition-[color,box-shadow] placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40",
|
|
11
|
+
className,
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { Textarea }
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Tooltip as TooltipPrimitive } from "radix-ui"
|
|
2
|
+
import type * as React from "react"
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
|
|
6
|
+
function TooltipProvider({
|
|
7
|
+
delayDuration = 0,
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
|
10
|
+
return (
|
|
11
|
+
<TooltipPrimitive.Provider
|
|
12
|
+
data-slot="tooltip-provider"
|
|
13
|
+
delayDuration={delayDuration}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function Tooltip({
|
|
20
|
+
...props
|
|
21
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
|
22
|
+
return (
|
|
23
|
+
<TooltipProvider>
|
|
24
|
+
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
25
|
+
</TooltipProvider>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function TooltipTrigger({
|
|
30
|
+
...props
|
|
31
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
|
32
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function TooltipContent({
|
|
36
|
+
className,
|
|
37
|
+
sideOffset = 0,
|
|
38
|
+
children,
|
|
39
|
+
...props
|
|
40
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
|
41
|
+
return (
|
|
42
|
+
<TooltipPrimitive.Portal>
|
|
43
|
+
<TooltipPrimitive.Content
|
|
44
|
+
data-slot="tooltip-content"
|
|
45
|
+
sideOffset={sideOffset}
|
|
46
|
+
className={cn(
|
|
47
|
+
"fade-in-0 zoom-in-95 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in text-balance rounded-md bg-primary px-3 py-1.5 text-primary-foreground text-xs data-[state=closed]:animate-out",
|
|
48
|
+
className,
|
|
49
|
+
)}
|
|
50
|
+
{...props}
|
|
51
|
+
>
|
|
52
|
+
{children}
|
|
53
|
+
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-primary fill-primary" />
|
|
54
|
+
</TooltipPrimitive.Content>
|
|
55
|
+
</TooltipPrimitive.Portal>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ExternalLink } from "lucide-react"
|
|
2
|
+
import { getVSCodeFileLink } from "@/lib/get-vscode-link"
|
|
3
|
+
import { Button } from "./ui/button"
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
cwd: string
|
|
7
|
+
filePath: string
|
|
8
|
+
size?: "sm" | "default" | "lg" | "icon"
|
|
9
|
+
variant?: "default" | "secondary" | "ghost" | "outline"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function VscodeButton(props: Props) {
|
|
13
|
+
// filePathは既に絶対パスとして渡されることを期待
|
|
14
|
+
const href = getVSCodeFileLink(props.filePath)
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<a href={href} className="block">
|
|
18
|
+
<Button size={props.size || "sm"} variant={props.variant || "secondary"}>
|
|
19
|
+
<ExternalLink />
|
|
20
|
+
</Button>
|
|
21
|
+
</a>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useSuspenseQuery } from "@tanstack/react-query"
|
|
2
|
+
import { apiClient } from "@/lib/api-client"
|
|
3
|
+
import { getDirectoryPath } from "@/lib/open-csv/get-directory-path"
|
|
4
|
+
|
|
5
|
+
const endpoint = apiClient.api.directories[":path{.+}"]
|
|
6
|
+
|
|
7
|
+
export function useDirectoryQuery(filePath: string) {
|
|
8
|
+
const directoryPath = getDirectoryPath(filePath)
|
|
9
|
+
|
|
10
|
+
const path = directoryPath.startsWith("/")
|
|
11
|
+
? directoryPath.substring(1)
|
|
12
|
+
: directoryPath
|
|
13
|
+
|
|
14
|
+
return useSuspenseQuery({
|
|
15
|
+
queryKey: [endpoint.$url({ param: { path } })],
|
|
16
|
+
async queryFn() {
|
|
17
|
+
const resp = await endpoint.$get({ param: { path } })
|
|
18
|
+
|
|
19
|
+
return resp.json()
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useMutation } from "@tanstack/react-query"
|
|
2
|
+
import { apiClient } from "@/lib/api-client"
|
|
3
|
+
import { normalizePath } from "@/lib/path-utils"
|
|
4
|
+
|
|
5
|
+
type Params = {
|
|
6
|
+
path: string
|
|
7
|
+
field?: string
|
|
8
|
+
value?: unknown
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const endpoint = apiClient.api.files[":path{.+}"]
|
|
12
|
+
|
|
13
|
+
export function useFilePropertiesMutation() {
|
|
14
|
+
return useMutation({
|
|
15
|
+
async mutationFn(params: Params) {
|
|
16
|
+
const normalizedPath = normalizePath(params.path)
|
|
17
|
+
|
|
18
|
+
const path = normalizedPath.startsWith("/")
|
|
19
|
+
? normalizedPath.substring(1)
|
|
20
|
+
: normalizedPath
|
|
21
|
+
|
|
22
|
+
const properties = params.field ? { [params.field]: params.value } : {}
|
|
23
|
+
|
|
24
|
+
const response = await endpoint.$put({
|
|
25
|
+
param: { path },
|
|
26
|
+
json: {
|
|
27
|
+
properties,
|
|
28
|
+
content: null,
|
|
29
|
+
title: null,
|
|
30
|
+
description: null,
|
|
31
|
+
isArchived: null,
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
return response.json()
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useSuspenseQuery } from "@tanstack/react-query"
|
|
2
|
+
import { apiClient } from "@/lib/api-client"
|
|
3
|
+
|
|
4
|
+
const endpoint = apiClient.api.files[":path{.+}"]
|
|
5
|
+
|
|
6
|
+
export function useFileQuery(currentPath: string) {
|
|
7
|
+
const normalizedPath = currentPath.replace(/^docs\//, "")
|
|
8
|
+
|
|
9
|
+
const path = normalizedPath.startsWith("/")
|
|
10
|
+
? normalizedPath.substring(1)
|
|
11
|
+
: normalizedPath
|
|
12
|
+
|
|
13
|
+
return useSuspenseQuery({
|
|
14
|
+
queryKey: [endpoint.$url({ param: { path } })],
|
|
15
|
+
async queryFn() {
|
|
16
|
+
const resp = await endpoint.$get({ param: { path } })
|
|
17
|
+
|
|
18
|
+
return resp.json()
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 768
|
|
4
|
+
|
|
5
|
+
export function useIsMobile() {
|
|
6
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
|
|
7
|
+
|
|
8
|
+
React.useEffect(() => {
|
|
9
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
|
10
|
+
const onChange = () => {
|
|
11
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
12
|
+
}
|
|
13
|
+
mql.addEventListener("change", onChange)
|
|
14
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
15
|
+
return () => mql.removeEventListener("change", onChange)
|
|
16
|
+
}, [])
|
|
17
|
+
|
|
18
|
+
return !!isMobile
|
|
19
|
+
}
|
package/app/main.css
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@plugin "tailwindcss-animate";
|
|
3
|
+
|
|
4
|
+
@custom-variant dark (&:is(.dark *));
|
|
5
|
+
|
|
6
|
+
:root {
|
|
7
|
+
--radius: 0.65rem;
|
|
8
|
+
--background: oklch(1 0 0);
|
|
9
|
+
--foreground: oklch(0.145 0 0);
|
|
10
|
+
--card: oklch(1 0 0);
|
|
11
|
+
--card-foreground: oklch(0.145 0 0);
|
|
12
|
+
--popover: oklch(1 0 0);
|
|
13
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
14
|
+
--primary: oklch(0.205 0 0);
|
|
15
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
16
|
+
--secondary: oklch(0.97 0 0);
|
|
17
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
18
|
+
--muted: oklch(0.97 0 0);
|
|
19
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
20
|
+
--accent: oklch(0.97 0 0);
|
|
21
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
22
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
23
|
+
--border: oklch(0.922 0 0);
|
|
24
|
+
--input: oklch(0.922 0 0);
|
|
25
|
+
--ring: oklch(0.708 0 0);
|
|
26
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
27
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
28
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
29
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
30
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
31
|
+
--radius: 0.625rem;
|
|
32
|
+
--sidebar: oklch(0.985 0 0);
|
|
33
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
34
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
35
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
36
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
37
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
38
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
39
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.dark {
|
|
43
|
+
--background: oklch(0.145 0 0);
|
|
44
|
+
--foreground: oklch(0.985 0 0);
|
|
45
|
+
--card: oklch(0.205 0 0);
|
|
46
|
+
--card-foreground: oklch(0.985 0 0);
|
|
47
|
+
--popover: oklch(0.205 0 0);
|
|
48
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
49
|
+
--primary: oklch(0.922 0 0);
|
|
50
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
51
|
+
--secondary: oklch(0.269 0 0);
|
|
52
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
53
|
+
--muted: oklch(0.269 0 0);
|
|
54
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
55
|
+
--accent: oklch(0.269 0 0);
|
|
56
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
57
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
58
|
+
--border: oklch(1 0 0 / 10%);
|
|
59
|
+
--input: oklch(1 0 0 / 15%);
|
|
60
|
+
--ring: oklch(0.556 0 0);
|
|
61
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
62
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
63
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
64
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
65
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
66
|
+
--sidebar: oklch(0.205 0 0);
|
|
67
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
68
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
69
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
70
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
71
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
72
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
73
|
+
--sidebar-ring: oklch(0.556 0 0);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@theme inline {
|
|
77
|
+
--color-background: var(--background);
|
|
78
|
+
--color-foreground: var(--foreground);
|
|
79
|
+
--color-card: var(--card);
|
|
80
|
+
--color-card-foreground: var(--card-foreground);
|
|
81
|
+
--color-popover: var(--popover);
|
|
82
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
83
|
+
--color-primary: var(--primary);
|
|
84
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
85
|
+
--color-secondary: var(--secondary);
|
|
86
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
87
|
+
--color-muted: var(--muted);
|
|
88
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
89
|
+
--color-accent: var(--accent);
|
|
90
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
91
|
+
--color-destructive: var(--destructive);
|
|
92
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
93
|
+
--color-border: var(--border);
|
|
94
|
+
--color-input: var(--input);
|
|
95
|
+
--color-ring: var(--ring);
|
|
96
|
+
--color-chart-1: var(--chart-1);
|
|
97
|
+
--color-chart-2: var(--chart-2);
|
|
98
|
+
--color-chart-3: var(--chart-3);
|
|
99
|
+
--color-chart-4: var(--chart-4);
|
|
100
|
+
--color-chart-5: var(--chart-5);
|
|
101
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
102
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
103
|
+
--radius-lg: var(--radius);
|
|
104
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
105
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
106
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
107
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
108
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
109
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
110
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
111
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
112
|
+
--color-sidebar: var(--sidebar);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@layer base {
|
|
116
|
+
* {
|
|
117
|
+
@apply border-border outline-ring/50;
|
|
118
|
+
}
|
|
119
|
+
body {
|
|
120
|
+
@apply bg-background text-foreground;
|
|
121
|
+
}
|
|
122
|
+
}
|
package/app/main.tsx
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
|
2
|
+
import { createRouter, RouterProvider } from "@tanstack/react-router"
|
|
3
|
+
import { Suspense } from "react"
|
|
4
|
+
import { createRoot } from "react-dom/client"
|
|
5
|
+
import { Toaster } from "@/components/ui/sonner"
|
|
6
|
+
import { routeTree } from "@/lib/route-tree.gen"
|
|
7
|
+
|
|
8
|
+
import "@/main.css"
|
|
9
|
+
|
|
10
|
+
const router = createRouter({
|
|
11
|
+
routeTree,
|
|
12
|
+
defaultPreload: "intent",
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
declare module "@tanstack/react-router" {
|
|
16
|
+
interface Register {
|
|
17
|
+
router: typeof router
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const container = document.getElementById("app")
|
|
22
|
+
|
|
23
|
+
if (container === null) {
|
|
24
|
+
throw new Error("No container found")
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const root = createRoot(container)
|
|
28
|
+
|
|
29
|
+
const queryClient = new QueryClient()
|
|
30
|
+
|
|
31
|
+
root.render(
|
|
32
|
+
<QueryClientProvider client={queryClient}>
|
|
33
|
+
<Suspense fallback={null}>
|
|
34
|
+
<RouterProvider router={router} />
|
|
35
|
+
<Toaster />
|
|
36
|
+
</Suspense>
|
|
37
|
+
</QueryClientProvider>,
|
|
38
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createFileRoute, useLocation } from "@tanstack/react-router"
|
|
2
|
+
import { DirectoryPageView } from "@/components/directory-page-view"
|
|
3
|
+
import { FilePageView } from "@/components/file-view/file-page-view"
|
|
4
|
+
|
|
5
|
+
export const Route = createFileRoute("/$/")({
|
|
6
|
+
component: Component,
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
function Component() {
|
|
10
|
+
const pathname = useLocation({ select: (location) => location.pathname })
|
|
11
|
+
|
|
12
|
+
const supportedExtensions = [".md", ".csv", ".json", ".mermaid"]
|
|
13
|
+
|
|
14
|
+
const isFile = supportedExtensions.some((ext) => pathname.endsWith(ext))
|
|
15
|
+
|
|
16
|
+
if (pathname.startsWith("/apps")) {
|
|
17
|
+
return null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (isFile) {
|
|
21
|
+
return <FilePageView filePath={pathname} />
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return <DirectoryPageView key={pathname} currentPath={pathname} />
|
|
25
|
+
}
|
package/app/routes/$.tsx
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createFileRoute, Outlet, useLocation } from "@tanstack/react-router"
|
|
2
|
+
import { Suspense } from "react"
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute("/$")({
|
|
5
|
+
component: Component,
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
function Component() {
|
|
9
|
+
return (
|
|
10
|
+
<Suspense
|
|
11
|
+
fallback={
|
|
12
|
+
<div className="flex h-[50vh] items-center justify-center">
|
|
13
|
+
<div className="text-muted-foreground">読み込み中...</div>
|
|
14
|
+
</div>
|
|
15
|
+
}
|
|
16
|
+
>
|
|
17
|
+
<Outlet />
|
|
18
|
+
</Suspense>
|
|
19
|
+
)
|
|
20
|
+
}
|