@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.
Files changed (91) hide show
  1. package/.tanstack/tmp/router-generator-Eq20Yx/50b11a0f488e61cf92364a7e585c1448 +123 -0
  2. package/.tanstack/tmp/router-generator-KQIYSa/aecb094e4891f9475b91de590abc8809 +117 -0
  3. package/.tanstack/tmp/router-generator-ekW2pI/aecb094e4891f9475b91de590abc8809 +117 -0
  4. package/app/components/archived-file-list-view.tsx +99 -0
  5. package/app/components/directory-file-list-view.tsx +32 -0
  6. package/app/components/directory-file-tree-node.tsx +96 -0
  7. package/app/components/directory-layout-sidebar.tsx +181 -0
  8. package/app/components/directory-page-view.tsx +208 -0
  9. package/app/components/directory-table-view.tsx +383 -0
  10. package/app/components/editable-table-cell/boolean-editable-cell.tsx +44 -0
  11. package/app/components/editable-table-cell/multi-boolean-editable-cell.tsx +59 -0
  12. package/app/components/editable-table-cell/multi-number-editable-cell.tsx +62 -0
  13. package/app/components/editable-table-cell/multi-relation-editable-cell.tsx +19 -0
  14. package/app/components/editable-table-cell/multi-select-number-editable-cell.tsx +82 -0
  15. package/app/components/editable-table-cell/multi-select-text-editable-cell.tsx +82 -0
  16. package/app/components/editable-table-cell/multi-text-editable-cell.tsx +62 -0
  17. package/app/components/editable-table-cell/number-editable-cell.tsx +54 -0
  18. package/app/components/editable-table-cell/relation-editable-cell.tsx +20 -0
  19. package/app/components/editable-table-cell/select-number-editable-cell.tsx +43 -0
  20. package/app/components/editable-table-cell/select-text-editable-cell.tsx +33 -0
  21. package/app/components/editable-table-cell/string-editable-cell.tsx +42 -0
  22. package/app/components/editable-table-cell.tsx +129 -0
  23. package/app/components/file-card.tsx +120 -0
  24. package/app/components/file-tree-icon.tsx +26 -0
  25. package/app/components/file-view/archive-restore-banner.tsx +86 -0
  26. package/app/components/file-view/csv-file-view.tsx +50 -0
  27. package/app/components/file-view/default-file-view.tsx +11 -0
  28. package/app/components/file-view/editable-csv-cell.tsx +37 -0
  29. package/app/components/file-view/editable-front-matter-view.tsx +105 -0
  30. package/app/components/file-view/file-csv-columns-view.tsx +181 -0
  31. package/app/components/file-view/file-csv-table.tsx +142 -0
  32. package/app/components/file-view/file-header.tsx +115 -0
  33. package/app/components/file-view/file-page-view.tsx +165 -0
  34. package/app/components/file-view/front-matter-input-field.tsx +157 -0
  35. package/app/components/file-view/front-matter-view.tsx +30 -0
  36. package/app/components/file-view/json-file-editor.tsx +38 -0
  37. package/app/components/file-view/markdown-file-view.tsx +71 -0
  38. package/app/components/file-view/multi-relation-select.tsx +129 -0
  39. package/app/components/file-view/render-front-matter-value.tsx +57 -0
  40. package/app/components/file-view/single-relation-select.tsx +41 -0
  41. package/app/components/file-view/sortable-csv-row.tsx +57 -0
  42. package/app/components/file-view/sortable-header.tsx +30 -0
  43. package/app/components/file-view/utils.ts +8 -0
  44. package/app/components/milestones-editor.tsx +95 -0
  45. package/app/components/page-header.tsx +20 -0
  46. package/app/components/project-view/feature-item.tsx +139 -0
  47. package/app/components/project-view/project-page-group.tsx +125 -0
  48. package/app/components/project-view/project-view.tsx +230 -0
  49. package/app/components/project-view/unlinked-features-section.tsx +73 -0
  50. package/app/components/providers.tsx +21 -0
  51. package/app/components/reload-button.tsx +25 -0
  52. package/app/components/sidebar-button.tsx +13 -0
  53. package/app/components/ui/alert.tsx +66 -0
  54. package/app/components/ui/badge.tsx +46 -0
  55. package/app/components/ui/button.tsx +59 -0
  56. package/app/components/ui/card.tsx +92 -0
  57. package/app/components/ui/dialog.tsx +133 -0
  58. package/app/components/ui/emoji-picker.tsx +133 -0
  59. package/app/components/ui/icons.tsx +3 -0
  60. package/app/components/ui/input.tsx +21 -0
  61. package/app/components/ui/select.tsx +183 -0
  62. package/app/components/ui/separator.tsx +26 -0
  63. package/app/components/ui/sheet.tsx +137 -0
  64. package/app/components/ui/sidebar.tsx +724 -0
  65. package/app/components/ui/simple-sidebar.tsx +50 -0
  66. package/app/components/ui/skeleton.tsx +13 -0
  67. package/app/components/ui/sonner.tsx +23 -0
  68. package/app/components/ui/table.tsx +114 -0
  69. package/app/components/ui/tabs.tsx +53 -0
  70. package/app/components/ui/textarea.tsx +18 -0
  71. package/app/components/ui/tooltip.tsx +59 -0
  72. package/app/components/vscode-button.tsx +23 -0
  73. package/app/hooks/use-directory-query.ts +22 -0
  74. package/app/hooks/use-file-properties-mutation.ts +38 -0
  75. package/app/hooks/use-file-query.ts +21 -0
  76. package/app/hooks/use-mobile.ts +19 -0
  77. package/app/main.css +122 -0
  78. package/app/main.tsx +38 -0
  79. package/app/routes/$.index.tsx +25 -0
  80. package/app/routes/$.tsx +20 -0
  81. package/app/routes/__root.tsx +20 -0
  82. package/app/routes/_index.tsx +17 -0
  83. package/app/routes/apps.client.$.tsx +21 -0
  84. package/build/assets/index.css +1 -0
  85. package/build/assets/index.js +298 -0
  86. package/build/index.d.ts +2 -0
  87. package/build/index.d.ts.map +1 -0
  88. package/build/index.js +23 -0
  89. package/components.json +21 -0
  90. package/package.json +54 -0
  91. package/tsconfig.index.json +17 -0
@@ -0,0 +1,123 @@
1
+ /* eslint-disable */
2
+
3
+ // @ts-nocheck
4
+
5
+ // noinspection JSUnusedGlobalSymbols
6
+
7
+ // This file was automatically generated by TanStack Router.
8
+ // You should NOT make any changes in this file as it will be overwritten.
9
+ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10
+
11
+ import { createRootRoute } from '@tanstack/react-router'
12
+
13
+ import { Route as SplatRouteImport } from './../routes/$'
14
+ import { Route as IndexRouteImport } from './../routes/index'
15
+ import { Route as SplatIndexRouteImport } from './../routes/$.index'
16
+ import { Route as AppClientPageRouteImport } from './../routes/app.client.page'
17
+
18
+ const rootRouteImport = createRootRoute()
19
+
20
+ const SplatRoute = SplatRouteImport.update({
21
+ id: '/$',
22
+ path: '/$',
23
+ getParentRoute: () => rootRouteImport,
24
+ } as any)
25
+ const IndexRoute = IndexRouteImport.update({
26
+ id: '/',
27
+ path: '/',
28
+ getParentRoute: () => rootRouteImport,
29
+ } as any)
30
+ const SplatIndexRoute = SplatIndexRouteImport.update({
31
+ id: '/',
32
+ path: '/',
33
+ getParentRoute: () => SplatRoute,
34
+ } as any)
35
+ const AppClientPageRoute = AppClientPageRouteImport.update({
36
+ id: '/app/client/page',
37
+ path: '/app/client/page',
38
+ getParentRoute: () => rootRouteImport,
39
+ } as any)
40
+
41
+ export interface FileRoutesByFullPath {
42
+ '/': typeof IndexRoute
43
+ '/$': typeof SplatRouteWithChildren
44
+ '/$/': typeof SplatIndexRoute
45
+ '/app/client/page': typeof AppClientPageRoute
46
+ }
47
+ export interface FileRoutesByTo {
48
+ '/': typeof IndexRoute
49
+ '/$': typeof SplatIndexRoute
50
+ '/app/client/page': typeof AppClientPageRoute
51
+ }
52
+ export interface FileRoutesById {
53
+ __root__: typeof rootRouteImport
54
+ '/': typeof IndexRoute
55
+ '/$': typeof SplatRouteWithChildren
56
+ '/$/': typeof SplatIndexRoute
57
+ '/app/client/page': typeof AppClientPageRoute
58
+ }
59
+ export interface FileRouteTypes {
60
+ fileRoutesByFullPath: FileRoutesByFullPath
61
+ fullPaths: '/' | '/$' | '/$/' | '/app/client/page'
62
+ fileRoutesByTo: FileRoutesByTo
63
+ to: '/' | '/$' | '/app/client/page'
64
+ id: '__root__' | '/' | '/$' | '/$/' | '/app/client/page'
65
+ fileRoutesById: FileRoutesById
66
+ }
67
+ export interface RootRouteChildren {
68
+ IndexRoute: typeof IndexRoute
69
+ SplatRoute: typeof SplatRouteWithChildren
70
+ AppClientPageRoute: typeof AppClientPageRoute
71
+ }
72
+
73
+ declare module '@tanstack/react-router' {
74
+ interface FileRoutesByPath {
75
+ '/$': {
76
+ id: '/$'
77
+ path: '/$'
78
+ fullPath: '/$'
79
+ preLoaderRoute: typeof SplatRouteImport
80
+ parentRoute: typeof rootRouteImport
81
+ }
82
+ '/': {
83
+ id: '/'
84
+ path: '/'
85
+ fullPath: '/'
86
+ preLoaderRoute: typeof IndexRouteImport
87
+ parentRoute: typeof rootRouteImport
88
+ }
89
+ '/$/': {
90
+ id: '/$/'
91
+ path: '/'
92
+ fullPath: '/$/'
93
+ preLoaderRoute: typeof SplatIndexRouteImport
94
+ parentRoute: typeof SplatRoute
95
+ }
96
+ '/app/client/page': {
97
+ id: '/app/client/page'
98
+ path: '/app/client/page'
99
+ fullPath: '/app/client/page'
100
+ preLoaderRoute: typeof AppClientPageRouteImport
101
+ parentRoute: typeof rootRouteImport
102
+ }
103
+ }
104
+ }
105
+
106
+ interface SplatRouteChildren {
107
+ SplatIndexRoute: typeof SplatIndexRoute
108
+ }
109
+
110
+ const SplatRouteChildren: SplatRouteChildren = {
111
+ SplatIndexRoute: SplatIndexRoute,
112
+ }
113
+
114
+ const SplatRouteWithChildren = SplatRoute._addFileChildren(SplatRouteChildren)
115
+
116
+ const rootRouteChildren: RootRouteChildren = {
117
+ IndexRoute: IndexRoute,
118
+ SplatRoute: SplatRouteWithChildren,
119
+ AppClientPageRoute: AppClientPageRoute,
120
+ }
121
+ export const routeTree = rootRouteImport
122
+ ._addFileChildren(rootRouteChildren)
123
+ ._addFileTypes<FileRouteTypes>()
@@ -0,0 +1,117 @@
1
+ /* eslint-disable */
2
+
3
+ // @ts-nocheck
4
+
5
+ // noinspection JSUnusedGlobalSymbols
6
+
7
+ // This file was automatically generated by TanStack Router.
8
+ // You should NOT make any changes in this file as it will be overwritten.
9
+ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10
+
11
+ import { Route as rootRouteImport } from './../routes/__root'
12
+ import { Route as IndexRouteImport } from './../routes/_index'
13
+ import { Route as SplatRouteImport } from './../routes/$'
14
+ import { Route as SplatIndexRouteImport } from './../routes/$.index'
15
+ import { Route as AppClientPageRouteImport } from './../routes/app.client.page'
16
+
17
+ const IndexRoute = IndexRouteImport.update({
18
+ id: '/_index',
19
+ getParentRoute: () => rootRouteImport,
20
+ } as any)
21
+ const SplatRoute = SplatRouteImport.update({
22
+ id: '/$',
23
+ path: '/$',
24
+ getParentRoute: () => rootRouteImport,
25
+ } as any)
26
+ const SplatIndexRoute = SplatIndexRouteImport.update({
27
+ id: '/',
28
+ path: '/',
29
+ getParentRoute: () => SplatRoute,
30
+ } as any)
31
+ const AppClientPageRoute = AppClientPageRouteImport.update({
32
+ id: '/app/client/page',
33
+ path: '/app/client/page',
34
+ getParentRoute: () => rootRouteImport,
35
+ } as any)
36
+
37
+ export interface FileRoutesByFullPath {
38
+ '/$': typeof SplatRouteWithChildren
39
+ '/$/': typeof SplatIndexRoute
40
+ '/app/client/page': typeof AppClientPageRoute
41
+ }
42
+ export interface FileRoutesByTo {
43
+ '/$': typeof SplatIndexRoute
44
+ '/app/client/page': typeof AppClientPageRoute
45
+ }
46
+ export interface FileRoutesById {
47
+ __root__: typeof rootRouteImport
48
+ '/$': typeof SplatRouteWithChildren
49
+ '/_index': typeof IndexRoute
50
+ '/$/': typeof SplatIndexRoute
51
+ '/app/client/page': typeof AppClientPageRoute
52
+ }
53
+ export interface FileRouteTypes {
54
+ fileRoutesByFullPath: FileRoutesByFullPath
55
+ fullPaths: '/$' | '/$/' | '/app/client/page'
56
+ fileRoutesByTo: FileRoutesByTo
57
+ to: '/$' | '/app/client/page'
58
+ id: '__root__' | '/$' | '/_index' | '/$/' | '/app/client/page'
59
+ fileRoutesById: FileRoutesById
60
+ }
61
+ export interface RootRouteChildren {
62
+ SplatRoute: typeof SplatRouteWithChildren
63
+ IndexRoute: typeof IndexRoute
64
+ AppClientPageRoute: typeof AppClientPageRoute
65
+ }
66
+
67
+ declare module '@tanstack/react-router' {
68
+ interface FileRoutesByPath {
69
+ '/_index': {
70
+ id: '/_index'
71
+ path: ''
72
+ fullPath: ''
73
+ preLoaderRoute: typeof IndexRouteImport
74
+ parentRoute: typeof rootRouteImport
75
+ }
76
+ '/$': {
77
+ id: '/$'
78
+ path: '/$'
79
+ fullPath: '/$'
80
+ preLoaderRoute: typeof SplatRouteImport
81
+ parentRoute: typeof rootRouteImport
82
+ }
83
+ '/$/': {
84
+ id: '/$/'
85
+ path: '/'
86
+ fullPath: '/$/'
87
+ preLoaderRoute: typeof SplatIndexRouteImport
88
+ parentRoute: typeof SplatRoute
89
+ }
90
+ '/app/client/page': {
91
+ id: '/app/client/page'
92
+ path: '/app/client/page'
93
+ fullPath: '/app/client/page'
94
+ preLoaderRoute: typeof AppClientPageRouteImport
95
+ parentRoute: typeof rootRouteImport
96
+ }
97
+ }
98
+ }
99
+
100
+ interface SplatRouteChildren {
101
+ SplatIndexRoute: typeof SplatIndexRoute
102
+ }
103
+
104
+ const SplatRouteChildren: SplatRouteChildren = {
105
+ SplatIndexRoute: SplatIndexRoute,
106
+ }
107
+
108
+ const SplatRouteWithChildren = SplatRoute._addFileChildren(SplatRouteChildren)
109
+
110
+ const rootRouteChildren: RootRouteChildren = {
111
+ SplatRoute: SplatRouteWithChildren,
112
+ IndexRoute: IndexRoute,
113
+ AppClientPageRoute: AppClientPageRoute,
114
+ }
115
+ export const routeTree = rootRouteImport
116
+ ._addFileChildren(rootRouteChildren)
117
+ ._addFileTypes<FileRouteTypes>()
@@ -0,0 +1,117 @@
1
+ /* eslint-disable */
2
+
3
+ // @ts-nocheck
4
+
5
+ // noinspection JSUnusedGlobalSymbols
6
+
7
+ // This file was automatically generated by TanStack Router.
8
+ // You should NOT make any changes in this file as it will be overwritten.
9
+ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10
+
11
+ import { Route as rootRouteImport } from './../routes/__root'
12
+ import { Route as IndexRouteImport } from './../routes/_index'
13
+ import { Route as SplatRouteImport } from './../routes/$'
14
+ import { Route as SplatIndexRouteImport } from './../routes/$.index'
15
+ import { Route as AppClientPageRouteImport } from './../routes/app.client.page'
16
+
17
+ const IndexRoute = IndexRouteImport.update({
18
+ id: '/_index',
19
+ getParentRoute: () => rootRouteImport,
20
+ } as any)
21
+ const SplatRoute = SplatRouteImport.update({
22
+ id: '/$',
23
+ path: '/$',
24
+ getParentRoute: () => rootRouteImport,
25
+ } as any)
26
+ const SplatIndexRoute = SplatIndexRouteImport.update({
27
+ id: '/',
28
+ path: '/',
29
+ getParentRoute: () => SplatRoute,
30
+ } as any)
31
+ const AppClientPageRoute = AppClientPageRouteImport.update({
32
+ id: '/app/client/page',
33
+ path: '/app/client/page',
34
+ getParentRoute: () => rootRouteImport,
35
+ } as any)
36
+
37
+ export interface FileRoutesByFullPath {
38
+ '/$': typeof SplatRouteWithChildren
39
+ '/$/': typeof SplatIndexRoute
40
+ '/app/client/page': typeof AppClientPageRoute
41
+ }
42
+ export interface FileRoutesByTo {
43
+ '/$': typeof SplatIndexRoute
44
+ '/app/client/page': typeof AppClientPageRoute
45
+ }
46
+ export interface FileRoutesById {
47
+ __root__: typeof rootRouteImport
48
+ '/$': typeof SplatRouteWithChildren
49
+ '/_index': typeof IndexRoute
50
+ '/$/': typeof SplatIndexRoute
51
+ '/app/client/page': typeof AppClientPageRoute
52
+ }
53
+ export interface FileRouteTypes {
54
+ fileRoutesByFullPath: FileRoutesByFullPath
55
+ fullPaths: '/$' | '/$/' | '/app/client/page'
56
+ fileRoutesByTo: FileRoutesByTo
57
+ to: '/$' | '/app/client/page'
58
+ id: '__root__' | '/$' | '/_index' | '/$/' | '/app/client/page'
59
+ fileRoutesById: FileRoutesById
60
+ }
61
+ export interface RootRouteChildren {
62
+ SplatRoute: typeof SplatRouteWithChildren
63
+ IndexRoute: typeof IndexRoute
64
+ AppClientPageRoute: typeof AppClientPageRoute
65
+ }
66
+
67
+ declare module '@tanstack/react-router' {
68
+ interface FileRoutesByPath {
69
+ '/_index': {
70
+ id: '/_index'
71
+ path: ''
72
+ fullPath: ''
73
+ preLoaderRoute: typeof IndexRouteImport
74
+ parentRoute: typeof rootRouteImport
75
+ }
76
+ '/$': {
77
+ id: '/$'
78
+ path: '/$'
79
+ fullPath: '/$'
80
+ preLoaderRoute: typeof SplatRouteImport
81
+ parentRoute: typeof rootRouteImport
82
+ }
83
+ '/$/': {
84
+ id: '/$/'
85
+ path: '/'
86
+ fullPath: '/$/'
87
+ preLoaderRoute: typeof SplatIndexRouteImport
88
+ parentRoute: typeof SplatRoute
89
+ }
90
+ '/app/client/page': {
91
+ id: '/app/client/page'
92
+ path: '/app/client/page'
93
+ fullPath: '/app/client/page'
94
+ preLoaderRoute: typeof AppClientPageRouteImport
95
+ parentRoute: typeof rootRouteImport
96
+ }
97
+ }
98
+ }
99
+
100
+ interface SplatRouteChildren {
101
+ SplatIndexRoute: typeof SplatIndexRoute
102
+ }
103
+
104
+ const SplatRouteChildren: SplatRouteChildren = {
105
+ SplatIndexRoute: SplatIndexRoute,
106
+ }
107
+
108
+ const SplatRouteWithChildren = SplatRoute._addFileChildren(SplatRouteChildren)
109
+
110
+ const rootRouteChildren: RootRouteChildren = {
111
+ SplatRoute: SplatRouteWithChildren,
112
+ IndexRoute: IndexRoute,
113
+ AppClientPageRoute: AppClientPageRoute,
114
+ }
115
+ export const routeTree = rootRouteImport
116
+ ._addFileChildren(rootRouteChildren)
117
+ ._addFileTypes<FileRouteTypes>()
@@ -0,0 +1,99 @@
1
+ import { useMutation } from "@tanstack/react-query"
2
+ import { Link } from "@tanstack/react-router"
3
+ import { useState } from "react"
4
+ import { Button } from "@/components/ui/button"
5
+ import { Card } from "@/components/ui/card"
6
+ import { apiClient } from "@/lib/api-client"
7
+ import { normalizePath } from "@/lib/path-utils"
8
+ import type { DocFile, DocFileMd } from "@/lib/types"
9
+
10
+ function isDocFileMd(file: DocFile): file is DocFileMd {
11
+ return 'frontMatter' in file && 'title' in file
12
+ }
13
+
14
+ type Props = {
15
+ files: DocFile[]
16
+ directoryPath: string
17
+ refetch: () => void
18
+ }
19
+
20
+ export function ArchivedFileListView(props: Props) {
21
+ const [isExpanded, setIsExpanded] = useState(false)
22
+
23
+ const restoreFileMutation = useMutation({
24
+ async mutationFn(filePath: string) {
25
+ const endpoint = apiClient.api.files[":path{.+}"]
26
+ const resp = await endpoint.$put({
27
+ param: { path: normalizePath(filePath) },
28
+ json: {
29
+ properties: null,
30
+ content: null,
31
+ title: null,
32
+ description: null,
33
+ isArchived: false,
34
+ },
35
+ })
36
+ return resp.json()
37
+ },
38
+ onSuccess() {
39
+ props.refetch()
40
+ },
41
+ })
42
+
43
+ if (!props.files || props.files.length === 0) {
44
+ return null
45
+ }
46
+
47
+ const handleRestore = (filePath: string) => {
48
+ restoreFileMutation.mutate(filePath)
49
+ }
50
+
51
+ return (
52
+ <div className="space-y-2">
53
+ <Button
54
+ onClick={() => setIsExpanded(!isExpanded)}
55
+ className="flex items-center gap-2"
56
+ variant={"secondary"}
57
+ >
58
+ <span>📦</span>
59
+ <span>{props.files.filter(isDocFileMd).length}件のファイルが整理されています。</span>
60
+ <span className="text-xs">{isExpanded ? "▼" : "▶"}</span>
61
+ </Button>
62
+ {isExpanded && (
63
+ <Card className="gap-0 rounded-md p-0">
64
+ <div className="divide-y">
65
+ {props.files.filter(isDocFileMd).map((file) => (
66
+ <div
67
+ key={file.path.path}
68
+ className="flex items-center justify-between p-2"
69
+ >
70
+ <div className="flex-1">
71
+ <div className="font-medium text-sm">
72
+ <Link
73
+ to="/$"
74
+ params={{ _splat: normalizePath(file.path.path) }}
75
+ className="text-blue-600 hover:underline"
76
+ >
77
+ {file.content.title || file.path.name}
78
+ </Link>
79
+ </div>
80
+ {file.content.description && (
81
+ <div className="text-xs">{file.content.description}</div>
82
+ )}
83
+ </div>
84
+ <Button
85
+ variant={"outline"}
86
+ size={"sm"}
87
+ onClick={() => handleRestore(file.path.path)}
88
+ disabled={restoreFileMutation.isPending}
89
+ >
90
+ {"復元"}
91
+ </Button>
92
+ </div>
93
+ ))}
94
+ </div>
95
+ </Card>
96
+ )}
97
+ </div>
98
+ )
99
+ }
@@ -0,0 +1,32 @@
1
+ import { Card } from "@/components/ui/card"
2
+ import type { DocFile, DocFileUnknown } from "@/lib/types"
3
+ import { FileCard } from "./file-card"
4
+
5
+ function isDocFileUnknown(file: DocFile): file is DocFileUnknown {
6
+ return 'extension' in file
7
+ }
8
+
9
+ type Props = {
10
+ files: DocFile[]
11
+ onDataChanged?: () => void
12
+ }
13
+
14
+ export function DirectoryFileListView(props: Props) {
15
+ if (!props.files || props.files.length === 0) {
16
+ return null
17
+ }
18
+
19
+ return (
20
+ <Card className="rounded-md p-2">
21
+ <div className="space-y-2">
22
+ {props.files.filter(isDocFileUnknown).map((file) => (
23
+ <FileCard
24
+ key={file.path.path}
25
+ file={file}
26
+ onDataChanged={props.onDataChanged}
27
+ />
28
+ ))}
29
+ </div>
30
+ </Card>
31
+ )
32
+ }
@@ -0,0 +1,96 @@
1
+ import { Link } from "@tanstack/react-router"
2
+ import { SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar"
3
+ import type { DocFileNode } from "@/lib/types"
4
+ import { normalizePath, getDirectoryPath } from "@/lib/path-utils"
5
+ import { FileTreeIcon } from "./file-tree-icon"
6
+
7
+ type Props = {
8
+ node: {
9
+ type: 'file' | 'directory'
10
+ name: string
11
+ path: string
12
+ icon?: string
13
+ title?: string
14
+ children?: unknown[]
15
+ }
16
+ depth?: number
17
+ currentPath?: string
18
+ onSelectDirectory?: (path: string) => void
19
+ openPaths?: Set<string>
20
+ onToggleOpen?: (path: string) => void
21
+ selectedDirectory?: string
22
+ }
23
+
24
+ export function DirectoryFileTreeNode(props: Props) {
25
+ const node = props.node
26
+ const isDirectory = node.type === "directory"
27
+
28
+
29
+ const depth = props.depth || 0
30
+ const isOpen = props.openPaths?.has(normalizePath(node.path)) ?? false
31
+
32
+ if (isDirectory) {
33
+ const isSelected = props.selectedDirectory === normalizePath(node.path)
34
+ const handleClick = (e: React.MouseEvent) => {
35
+ if (props.onSelectDirectory) {
36
+ e.preventDefault()
37
+ props.onSelectDirectory(normalizePath(node.path))
38
+ }
39
+ }
40
+
41
+ return (
42
+ <>
43
+ <SidebarMenuItem>
44
+ <SidebarMenuButton
45
+ style={{ paddingLeft: 8 + depth * 8 }}
46
+ className={isSelected ? "bg-sidebar-accent font-medium" : ""}
47
+ onClick={handleClick}
48
+ >
49
+ {node.icon && (
50
+ <span className="text-base">{node.icon}</span>
51
+ )}
52
+ <span>{node.title}</span>
53
+ </SidebarMenuButton>
54
+ </SidebarMenuItem>
55
+ {isOpen &&
56
+ node.children &&
57
+ node.children
58
+ .filter((child: unknown): child is typeof node =>
59
+ typeof child === 'object' &&
60
+ child !== null &&
61
+ 'type' in child &&
62
+ (child as typeof node).type === "directory"
63
+ )
64
+ .map((child) => (
65
+ <DirectoryFileTreeNode
66
+ key={child.path}
67
+ node={child}
68
+ depth={depth + 1}
69
+ currentPath={props.currentPath}
70
+ onSelectDirectory={props.onSelectDirectory}
71
+ openPaths={props.openPaths}
72
+ onToggleOpen={props.onToggleOpen}
73
+ selectedDirectory={props.selectedDirectory}
74
+ />
75
+ ))}
76
+ </>
77
+ )
78
+ }
79
+
80
+ const formattedParentPath = getDirectoryPath(node.path)
81
+
82
+ if (props.selectedDirectory !== formattedParentPath) {
83
+ return null
84
+ }
85
+
86
+ return (
87
+ <Link to="/$" params={{ _splat: normalizePath(node.path) }}>
88
+ <SidebarMenuItem>
89
+ <SidebarMenuButton style={{ paddingLeft: 8 + depth * 8 }}>
90
+ <FileTreeIcon fileName={node.name} />
91
+ {node.title !== node.name ? `${node.title} | ${node.name}` : node.name}
92
+ </SidebarMenuButton>
93
+ </SidebarMenuItem>
94
+ </Link>
95
+ )
96
+ }