@tablestore/tablestore-agent-cli 0.1.6 → 0.1.8

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 (154) hide show
  1. package/dist/dashboard-runtime/dashboard/.next/BUILD_ID +1 -1
  2. package/dist/dashboard-runtime/dashboard/.next/build-manifest.json +3 -3
  3. package/dist/dashboard-runtime/dashboard/.next/prerender-manifest.json +3 -3
  4. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/chat/page.js.nft.json +1 -1
  5. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/chat/page_client-reference-manifest.js +1 -1
  6. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/documents/page.js.nft.json +1 -1
  7. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/documents/page_client-reference-manifest.js +1 -1
  8. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/page.js.nft.json +1 -1
  9. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/page_client-reference-manifest.js +1 -1
  10. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/qa/page.js.nft.json +1 -1
  11. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/qa/page_client-reference-manifest.js +1 -1
  12. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/retrieval/page.js.nft.json +1 -1
  13. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/retrieval/page_client-reference-manifest.js +1 -1
  14. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/settings/page.js.nft.json +1 -1
  15. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/[name]/settings/page_client-reference-manifest.js +1 -1
  16. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/create/page_client-reference-manifest.js +1 -1
  17. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/page.js.nft.json +1 -1
  18. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/knowledge-base/page_client-reference-manifest.js +1 -1
  19. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/llm-providers/[id]/page_client-reference-manifest.js +1 -1
  20. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/llm-providers/new/page_client-reference-manifest.js +1 -1
  21. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/llm-providers/page.js.nft.json +1 -1
  22. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/llm-providers/page_client-reference-manifest.js +1 -1
  23. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/messages/page.js.nft.json +1 -1
  24. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/messages/page_client-reference-manifest.js +1 -1
  25. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/page.js.nft.json +1 -1
  26. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/page_client-reference-manifest.js +1 -1
  27. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/requests/page.js.nft.json +1 -1
  28. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/requests/page_client-reference-manifest.js +1 -1
  29. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/search/page.js.nft.json +1 -1
  30. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/search/page_client-reference-manifest.js +1 -1
  31. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/settings/page.js.nft.json +1 -1
  32. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/settings/page_client-reference-manifest.js +1 -1
  33. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/units/page.js.nft.json +1 -1
  34. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/[store]/units/page_client-reference-manifest.js +1 -1
  35. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/create/page_client-reference-manifest.js +1 -1
  36. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/page.js.nft.json +1 -1
  37. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/memory/page_client-reference-manifest.js +1 -1
  38. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/projects/page.js.nft.json +1 -1
  39. package/dist/dashboard-runtime/dashboard/.next/server/app/(dashboard)/projects/page_client-reference-manifest.js +1 -1
  40. package/dist/dashboard-runtime/dashboard/.next/server/app/_global-error.html +1 -1
  41. package/dist/dashboard-runtime/dashboard/.next/server/app/_global-error.rsc +1 -1
  42. package/dist/dashboard-runtime/dashboard/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  43. package/dist/dashboard-runtime/dashboard/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  44. package/dist/dashboard-runtime/dashboard/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  45. package/dist/dashboard-runtime/dashboard/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  46. package/dist/dashboard-runtime/dashboard/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  47. package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  48. package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.html +1 -1
  49. package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.rsc +2 -2
  50. package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  51. package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  52. package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  53. package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  54. package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  55. package/dist/dashboard-runtime/dashboard/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  56. package/dist/dashboard-runtime/dashboard/.next/server/app/api/v1/knowledge-bases/[name]/documents/upload/route.js.nft.json +1 -1
  57. package/dist/dashboard-runtime/dashboard/.next/server/app/index.html +1 -1
  58. package/dist/dashboard-runtime/dashboard/.next/server/app/index.rsc +2 -2
  59. package/dist/dashboard-runtime/dashboard/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  60. package/dist/dashboard-runtime/dashboard/.next/server/app/index.segments/_full.segment.rsc +2 -2
  61. package/dist/dashboard-runtime/dashboard/.next/server/app/index.segments/_head.segment.rsc +1 -1
  62. package/dist/dashboard-runtime/dashboard/.next/server/app/index.segments/_index.segment.rsc +2 -2
  63. package/dist/dashboard-runtime/dashboard/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  64. package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
  65. package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.html +1 -1
  66. package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.rsc +3 -3
  67. package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.segments/_full.segment.rsc +3 -3
  68. package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.segments/_head.segment.rsc +1 -1
  69. package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.segments/_index.segment.rsc +2 -2
  70. package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.segments/_tree.segment.rsc +2 -2
  71. package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.segments/onboarding/__PAGE__.segment.rsc +2 -2
  72. package/dist/dashboard-runtime/dashboard/.next/server/app/onboarding.segments/onboarding.segment.rsc +1 -1
  73. package/dist/dashboard-runtime/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
  74. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/0b1r_app_(dashboard)_knowledge-base_[name]_documents_documents-client_tsx_0g2uxmt._.js +1 -1
  75. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/[root-of-the-server]__09_30hi._.js +3 -0
  76. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/[root-of-the-server]__0kaiok7._.js +3 -0
  77. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/[root-of-the-server]__0l937k4._.js +3 -0
  78. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0-dlo4s._.js +1 -1
  79. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_01pp~xx._.js +3 -0
  80. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/{_0476v7r._.js → _08j6p-z._.js} +2 -2
  81. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/{_0ct0n55._.js → _09eom0_._.js} +2 -2
  82. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/{_0mzxmfh._.js → _0f-may2._.js} +2 -2
  83. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0fp_nnb._.js +3 -0
  84. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0l6jz-5._.js +3 -0
  85. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/{_0fg6iap._.js → _0ntv66s._.js} +2 -2
  86. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0sc0tpz._.js +3 -0
  87. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0tyxwr3._.js +3 -0
  88. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0u9md36._.js +1 -1
  89. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/dashboard_src_065m1.j._.js +1 -1
  90. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/dashboard_src_app_(dashboard)_knowledge-base_[name]_layout-client_tsx_05momc5._.js +1 -1
  91. package/dist/dashboard-runtime/dashboard/.next/server/middleware-build-manifest.js +3 -3
  92. package/dist/dashboard-runtime/dashboard/.next/server/pages/404.html +1 -1
  93. package/dist/dashboard-runtime/dashboard/.next/server/pages/500.html +1 -1
  94. package/dist/dashboard-runtime/dashboard/.next/server/server-reference-manifest.js +1 -1
  95. package/dist/dashboard-runtime/dashboard/.next/server/server-reference-manifest.json +1 -1
  96. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0-xqk6wfme5fu.js +1 -0
  97. package/dist/dashboard-runtime/dashboard/.next/static/chunks/{0ltmkqogn8g3t.js → 00.fyg_t3xjc~.js} +1 -1
  98. package/dist/dashboard-runtime/dashboard/.next/static/chunks/01k-.wkndubwy.js +138 -0
  99. package/dist/dashboard-runtime/dashboard/.next/static/chunks/07r22x5uhjxjb.js +1 -0
  100. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0cbu3udngtx-v.js +4 -0
  101. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0h7d69g2fdgs6.js +138 -0
  102. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0iv0g360z0ja5.js +1 -0
  103. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0jf4ytf.izmzh.js +1 -0
  104. package/dist/dashboard-runtime/dashboard/.next/static/chunks/{17nx_vdk_qemm.js → 0m42mmyxmgn-i.js} +1 -1
  105. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0odz1whf-ozqn.css +1 -0
  106. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0ouxv_2qvf6.9.js +1 -0
  107. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0rbkgj._oqsb3.js +1 -0
  108. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0u6u.649ou67b.js +1 -0
  109. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0vf7~eysvh9bq.js +1 -0
  110. package/dist/dashboard-runtime/dashboard/.next/static/chunks/{09qsmnfvk8dm3.js → 0wmaq.ay~r2sn.js} +1 -1
  111. package/dist/dashboard-runtime/dashboard/.next/static/chunks/{0i5aqiffpm9jr.js → 0xb0_9t-vmo6e.js} +1 -1
  112. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0yp_2ea75d19v.js +1 -0
  113. package/dist/dashboard-runtime/dashboard/.next/static/chunks/{0s3mvx9_dr13c.js → 11cizim1aug~b.js} +1 -1
  114. package/dist/dashboard-runtime/dashboard/.next/static/chunks/13-2ei295hl0-.js +1 -0
  115. package/dist/dashboard-runtime/dashboard/src/app/(dashboard)/knowledge-base/[name]/qa/page.tsx +23 -1
  116. package/dist/dashboard-runtime/dashboard/src/components/chat/chat-mount-config.tsx +29 -8
  117. package/dist/dashboard-runtime/dashboard/src/components/documents/doc-upload-dialog.tsx +370 -91
  118. package/dist/dashboard-runtime/dashboard/src/components/knowledge-base/kb-card.tsx +8 -5
  119. package/dist/dashboard-runtime/dashboard/src/components/knowledge-base/kb-header.tsx +23 -13
  120. package/dist/dashboard-runtime/dashboard/src/components/memory/memory-header.tsx +8 -12
  121. package/dist/dashboard-runtime/dashboard/src/components/qa/qa-chat-panel.tsx +45 -4
  122. package/dist/dashboard-runtime/dashboard/src/components/qa/qa-message.tsx +636 -36
  123. package/dist/dashboard-runtime/dashboard/src/components/shared/confirm-dialog.tsx +8 -1
  124. package/dist/dashboard-runtime/dashboard/src/components/shared/refresh-button.tsx +102 -86
  125. package/dist/dashboard-runtime/dashboard/src/hooks/use-qa-chat.ts +261 -58
  126. package/dist/dashboard-runtime/dashboard/src/lib/chat-message-ops.ts +76 -0
  127. package/dist/dashboard-runtime/dashboard/src/lib/dev-flags.ts +24 -0
  128. package/dist/dashboard-runtime/dashboard/src/types/llm-chat.ts +19 -0
  129. package/package.json +1 -1
  130. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/[root-of-the-server]__0i0y9.r._.js +0 -3
  131. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/[root-of-the-server]__0r_6wpx._.js +0 -3
  132. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/[root-of-the-server]__0y8rrcu._.js +0 -3
  133. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_02e0c00._.js +0 -3
  134. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0doczxg._.js +0 -3
  135. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0e9y.xc._.js +0 -3
  136. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0ti~om_._.js +0 -3
  137. package/dist/dashboard-runtime/dashboard/.next/server/chunks/ssr/_0~s~e-0._.js +0 -3
  138. package/dist/dashboard-runtime/dashboard/.next/static/chunks/055_bo6_3nt-s.js +0 -1
  139. package/dist/dashboard-runtime/dashboard/.next/static/chunks/07l4dodn5t6l4.js +0 -4
  140. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0a5ass8u78k90.js +0 -1
  141. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0j~63ld9lbb25.js +0 -1
  142. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0l6znnjqw.4yw.js +0 -1
  143. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0o-c2g8y5pw-c.js +0 -1
  144. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0o0i2ke2jbdvq.js +0 -138
  145. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0prl6hk4v0k3i.js +0 -1
  146. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0u2lo2lzizxy_.js +0 -1
  147. package/dist/dashboard-runtime/dashboard/.next/static/chunks/0v.hno.6q74nn.css +0 -1
  148. package/dist/dashboard-runtime/dashboard/.next/static/chunks/14ti2gx~1fiub.js +0 -1
  149. package/dist/dashboard-runtime/dashboard/.next/static/chunks/14vqmdm7sosj2.js +0 -1
  150. package/dist/dashboard-runtime/dashboard/.next/static/chunks/17h~onnkksf44.js +0 -138
  151. package/dist/dashboard-runtime/dashboard/.next/static/chunks/186jubvt5xyuj.js +0 -1
  152. /package/dist/dashboard-runtime/dashboard/.next/static/{MjeKF5uLzEvAqWkR9O6Ts → BGnMIk4OPjFx_qvLFMTBc}/_buildManifest.js +0 -0
  153. /package/dist/dashboard-runtime/dashboard/.next/static/{MjeKF5uLzEvAqWkR9O6Ts → BGnMIk4OPjFx_qvLFMTBc}/_clientMiddlewareManifest.js +0 -0
  154. /package/dist/dashboard-runtime/dashboard/.next/static/{MjeKF5uLzEvAqWkR9O6Ts → BGnMIk4OPjFx_qvLFMTBc}/_ssgManifest.js +0 -0
@@ -24,6 +24,7 @@ import {
24
24
  import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
25
25
  import { Checkbox } from "@/components/ui/checkbox";
26
26
  import { useUploadDocuments } from "@/hooks/use-upload-documents";
27
+ import { cn } from "@/lib/utils";
27
28
  import type { MetadataField } from "@/types/knowledge-base";
28
29
 
29
30
  interface DocUploadDialogProps {
@@ -93,6 +94,107 @@ interface FolderFilterOptions {
93
94
  recursive: boolean;
94
95
  }
95
96
 
97
+ /**
98
+ * Minimal subset of the FileSystemEntry API we rely on. Typescript ships
99
+ * loose types under DOM lib, but they don't reliably expose
100
+ * `createReader().readEntries(...)` — declare what we use.
101
+ */
102
+ interface FsEntry {
103
+ isFile: boolean;
104
+ isDirectory: boolean;
105
+ name: string;
106
+ fullPath: string;
107
+ }
108
+ interface FsFileEntry extends FsEntry {
109
+ isFile: true;
110
+ file(success: (file: File) => void, error?: (e: Error) => void): void;
111
+ }
112
+ interface FsDirEntry extends FsEntry {
113
+ isDirectory: true;
114
+ createReader(): {
115
+ readEntries(success: (entries: FsEntry[]) => void, error?: (e: Error) => void): void;
116
+ };
117
+ }
118
+
119
+ /** Tag a File with a relative path so the rest of the pipeline (filter, list,
120
+ * display) can treat drag-dropped folder uploads identically to picker-based
121
+ * ones. `webkitRelativePath` is normally a read-only inherited getter, but
122
+ * defining a same-named own data property shadows it cleanly across browsers.
123
+ */
124
+ function tagRelativePath(file: File, path: string): File {
125
+ try {
126
+ Object.defineProperty(file, "webkitRelativePath", {
127
+ value: path,
128
+ configurable: true,
129
+ writable: false,
130
+ });
131
+ } catch {
132
+ // Some engines may freeze the prototype shape; fall back to a parallel
133
+ // own property so filterFolderFiles' fallback (`|| file.name`) at least
134
+ // surfaces a reasonable label.
135
+ }
136
+ return file;
137
+ }
138
+
139
+ /** Recursively walk a FileSystemEntry tree and resolve a flat File[] with
140
+ * webkitRelativePath populated to the root-relative path. */
141
+ async function walkEntry(entry: FsEntry, parentPath = ""): Promise<File[]> {
142
+ if (entry.isFile) {
143
+ const fileEntry = entry as FsFileEntry;
144
+ return new Promise<File[]>((resolve) => {
145
+ fileEntry.file(
146
+ (file) => {
147
+ const relPath = parentPath ? `${parentPath}/${file.name}` : file.name;
148
+ resolve([tagRelativePath(file, relPath)]);
149
+ },
150
+ () => resolve([]),
151
+ );
152
+ });
153
+ }
154
+ if (entry.isDirectory) {
155
+ const dirEntry = entry as FsDirEntry;
156
+ const reader = dirEntry.createReader();
157
+ // readEntries returns at most ~100 entries per call; loop until empty.
158
+ const collected: FsEntry[] = [];
159
+ await new Promise<void>((resolve) => {
160
+ const readBatch = () => {
161
+ reader.readEntries(
162
+ (batch) => {
163
+ if (batch.length === 0) {
164
+ resolve();
165
+ return;
166
+ }
167
+ collected.push(...batch);
168
+ readBatch();
169
+ },
170
+ () => resolve(),
171
+ );
172
+ };
173
+ readBatch();
174
+ });
175
+ const here = parentPath ? `${parentPath}/${entry.name}` : entry.name;
176
+ const nested = await Promise.all(collected.map((child) => walkEntry(child, here)));
177
+ return nested.flat();
178
+ }
179
+ return [];
180
+ }
181
+
182
+ /** Pull entries off a DataTransferItemList. Items without `webkitGetAsEntry`
183
+ * (older browsers) silently fall back to whatever the dataTransfer.files
184
+ * list resolved to. */
185
+ function extractEntries(items: DataTransferItemList): FsEntry[] {
186
+ const out: FsEntry[] = [];
187
+ for (let i = 0; i < items.length; i++) {
188
+ const item = items[i];
189
+ if (item.kind !== "file") continue;
190
+ const entry = (item as DataTransferItem & {
191
+ webkitGetAsEntry?: () => FsEntry | null;
192
+ }).webkitGetAsEntry?.();
193
+ if (entry) out.push(entry);
194
+ }
195
+ return out;
196
+ }
197
+
96
198
  /** Apply include / exclude / recursive filters to a batch of picked files. */
97
199
  function filterFolderFiles(list: File[], opts: FolderFilterOptions): File[] {
98
200
  const { include, exclude, recursive } = opts;
@@ -136,6 +238,7 @@ export function DocUploadDialog({
136
238
  const [excludeInput, setExcludeInput] = useState("");
137
239
  const [recursive, setRecursive] = useState(true);
138
240
  const folderInputRef = useRef<HTMLInputElement | null>(null);
241
+ const fileInputRef = useRef<HTMLInputElement | null>(null);
139
242
 
140
243
  const hasSchemaFields = metadataFields && metadataFields.length > 0;
141
244
  const uploadDocuments = useUploadDocuments();
@@ -157,6 +260,8 @@ export function DocUploadDialog({
157
260
 
158
261
  const handleDragOver = useCallback((event: React.DragEvent) => {
159
262
  event.preventDefault();
263
+ // `move` cursor confuses folder drops; `copy` reads as "I'll add this".
264
+ if (event.dataTransfer) event.dataTransfer.dropEffect = "copy";
160
265
  setIsDragOver(true);
161
266
  }, []);
162
267
 
@@ -164,11 +269,137 @@ export function DocUploadDialog({
164
269
  setIsDragOver(false);
165
270
  }, []);
166
271
 
167
- const handleDrop = useCallback((event: React.DragEvent) => {
272
+ /** Walk a list of FileSystemDirectoryEntry trees and store the resulting
273
+ * File[] in folderRaw. Shared between drag-drop and the toast action.
274
+ * Declared before handleDropFileMode so its callback identity is stable
275
+ * for that hook's dependency array. */
276
+ const importDirEntries = useCallback(async (dirs: FsEntry[]) => {
277
+ const all = await Promise.all(dirs.map((d) => walkEntry(d)));
278
+ const flat = all.flat();
279
+ if (flat.length > 0) {
280
+ setFolderRaw((prev) => [...prev, ...flat]);
281
+ }
282
+ }, []);
283
+
284
+ /**
285
+ * File-mode drop. Tolerates the user dragging folders by (a) silently
286
+ * extracting all leaf files from any dropped folder via webkitGetAsEntry
287
+ * and (b) hinting via toast that 「按文件夹」 mode supports filtering. The
288
+ * old behaviour of forwarding raw `dataTransfer.files` would have surfaced
289
+ * a 0-byte directory entry to the API and failed the upload.
290
+ */
291
+ const handleDropFileMode = useCallback(async (event: React.DragEvent) => {
168
292
  event.preventDefault();
169
293
  setIsDragOver(false);
170
- const droppedFiles = Array.from(event.dataTransfer.files);
171
- setFiles((prev) => [...prev, ...droppedFiles]);
294
+
295
+ const entries = extractEntries(event.dataTransfer.items);
296
+ if (entries.length === 0) {
297
+ // Browser without webkitGetAsEntry — fall back to the file list. Any
298
+ // raw "directory" entry will show up as a zero-byte File with empty
299
+ // type, which we filter out below as a defensive measure.
300
+ const fallback = Array.from(event.dataTransfer.files).filter(
301
+ (f) => f.size > 0 || f.type !== "",
302
+ );
303
+ if (fallback.length > 0) {
304
+ setFiles((prev) => [...prev, ...fallback]);
305
+ }
306
+ return;
307
+ }
308
+
309
+ const fileEntries = entries.filter((e) => e.isFile);
310
+ const dirEntries = entries.filter((e) => e.isDirectory);
311
+
312
+ // Resolve File objects from the file entries directly (no recursion in
313
+ // file mode — the user explicitly chose 「按文件」).
314
+ const directFiles = (
315
+ await Promise.all(
316
+ fileEntries.map(
317
+ (e) =>
318
+ new Promise<File | null>((resolve) => {
319
+ (e as FsFileEntry).file(
320
+ (file) => resolve(file),
321
+ () => resolve(null),
322
+ );
323
+ }),
324
+ ),
325
+ )
326
+ ).filter((f): f is File => f !== null);
327
+
328
+ if (directFiles.length > 0) {
329
+ setFiles((prev) => [...prev, ...directFiles]);
330
+ }
331
+
332
+ if (dirEntries.length > 0) {
333
+ const folderNames = dirEntries.map((d) => d.name).join("、");
334
+ toast.info(
335
+ directFiles.length > 0
336
+ ? `已忽略 ${dirEntries.length} 个文件夹(${folderNames})`
337
+ : `检测到文件夹(${folderNames}),已自动切换到「按文件夹」模式`,
338
+ {
339
+ description: "可在「按文件夹」模式按后缀筛选并递归上传",
340
+ action: {
341
+ label: "切换至按文件夹",
342
+ onClick: () => {
343
+ setMode("folder");
344
+ // Auto-import the dropped folders so the user doesn't repeat
345
+ // the gesture.
346
+ void importDirEntries(dirEntries);
347
+ },
348
+ },
349
+ },
350
+ );
351
+
352
+ // Auto-switch and import when the user dropped only folders. They
353
+ // clearly intended a folder upload — don't make them click the toast.
354
+ if (directFiles.length === 0) {
355
+ setMode("folder");
356
+ await importDirEntries(dirEntries);
357
+ }
358
+ }
359
+ }, [importDirEntries]);
360
+
361
+ /**
362
+ * Folder-mode drop. Walks any dropped FileSystemDirectoryEntry tree to
363
+ * produce a flat File[] with webkitRelativePath populated, so the existing
364
+ * include/exclude/recursive filter pipeline works identically to a folder
365
+ * picked through `<input webkitdirectory>`.
366
+ */
367
+ const handleDropFolderMode = useCallback(async (event: React.DragEvent) => {
368
+ event.preventDefault();
369
+ setIsDragOver(false);
370
+
371
+ const entries = extractEntries(event.dataTransfer.items);
372
+ if (entries.length === 0) {
373
+ toast.error("当前浏览器不支持文件夹拖拽,请使用「选择文件夹」按钮");
374
+ return;
375
+ }
376
+
377
+ const dirs = entries.filter((e) => e.isDirectory);
378
+ const looseFiles = entries.filter((e) => e.isFile);
379
+
380
+ if (dirs.length === 0 && looseFiles.length > 0) {
381
+ toast.info("拖拽内容均为文件,已合并到当前列表(不会经过过滤)");
382
+ }
383
+
384
+ const dirFiles = (await Promise.all(dirs.map((d) => walkEntry(d)))).flat();
385
+ const looseFileObjs = (
386
+ await Promise.all(
387
+ looseFiles.map(
388
+ (e) =>
389
+ new Promise<File | null>((resolve) => {
390
+ (e as FsFileEntry).file(
391
+ (file) => resolve(tagRelativePath(file, file.name)),
392
+ () => resolve(null),
393
+ );
394
+ }),
395
+ ),
396
+ )
397
+ ).filter((f): f is File => f !== null);
398
+
399
+ const merged = [...dirFiles, ...looseFileObjs];
400
+ if (merged.length > 0) {
401
+ setFolderRaw((prev) => [...prev, ...merged]);
402
+ }
172
403
  }, []);
173
404
 
174
405
  const handleFileSelect = useCallback(
@@ -311,75 +542,114 @@ export function DocUploadDialog({
311
542
  <DialogHeader>
312
543
  <DialogTitle>上传文档</DialogTitle>
313
544
  <DialogDescription>
314
- 将文档上传到知识库 {knowledgeBaseName}
545
+ 将文档上传到知识库 <span className="font-medium text-foreground">{knowledgeBaseName}</span>
315
546
  </DialogDescription>
316
547
  </DialogHeader>
317
548
 
318
549
  <div className="space-y-4 min-w-0">
319
550
  <Tabs value={mode} onValueChange={handleModeChange}>
320
551
  <TabsList className="grid w-full grid-cols-2">
321
- <TabsTrigger value="file">文件</TabsTrigger>
322
- <TabsTrigger value="folder">文件夹</TabsTrigger>
552
+ <TabsTrigger value="file" className="gap-2">
553
+ <FileText className="h-3.5 w-3.5" />
554
+ 按文件
555
+ </TabsTrigger>
556
+ <TabsTrigger value="folder" className="gap-2">
557
+ <FolderOpen className="h-3.5 w-3.5" />
558
+ 按文件夹
559
+ </TabsTrigger>
323
560
  </TabsList>
324
561
 
325
562
  {/* ---- File mode ---- */}
326
563
  <TabsContent value="file" className="space-y-3">
327
564
  <div
328
- className={`relative flex flex-col items-center justify-center rounded-lg border-2 border-dashed p-8 transition-colors ${
565
+ className={cn(
566
+ "relative flex h-36 flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed transition-colors",
329
567
  isDragOver
330
568
  ? "border-[hsl(var(--primary))] bg-[hsl(var(--primary))]/5"
331
- : "border-[hsl(var(--border))] hover:border-[hsl(var(--primary))]/50"
332
- }`}
569
+ : "border-[hsl(var(--border))] bg-[hsl(var(--secondary))]/40 hover:border-[hsl(var(--primary))]/50 hover:bg-[hsl(var(--primary))]/[0.03]",
570
+ )}
333
571
  onDragOver={handleDragOver}
334
572
  onDragLeave={handleDragLeave}
335
- onDrop={handleDrop}
573
+ onDrop={handleDropFileMode}
336
574
  >
337
- <Upload className="mb-2 h-8 w-8 text-[hsl(var(--muted-foreground))]" />
575
+ <Upload className="h-7 w-7 text-[hsl(var(--muted-foreground))]" />
338
576
  <p className="text-sm text-[hsl(var(--muted-foreground))]">
339
- 拖拽文件到此处,或{" "}
340
- <label className="cursor-pointer text-[hsl(var(--primary))] hover:underline">
341
- 点击选择
342
- <input
343
- type="file"
344
- multiple
345
- className="hidden"
346
- onChange={handleFileSelect}
347
- />
348
- </label>
577
+ 拖拽文件到此处
349
578
  </p>
579
+ <Button
580
+ type="button"
581
+ variant="outline"
582
+ size="sm"
583
+ onClick={() => fileInputRef.current?.click()}
584
+ >
585
+ 选择文件
586
+ </Button>
587
+ <input
588
+ ref={fileInputRef}
589
+ type="file"
590
+ multiple
591
+ className="hidden"
592
+ onChange={handleFileSelect}
593
+ />
350
594
  </div>
351
595
 
352
596
  {files.length > 0 && (
353
- <div className="space-y-2 max-h-32 overflow-y-auto">
354
- {files.map((file, index) => (
355
- <div
356
- key={`${file.name}-${index}`}
357
- className="flex items-center justify-between rounded-md border px-3 py-2 text-sm"
597
+ <div className="space-y-2">
598
+ <div className="flex items-center justify-between text-xs text-[hsl(var(--muted-foreground))]">
599
+ <span>
600
+ 已选择{" "}
601
+ <span className="font-medium text-foreground">{files.length}</span>{" "}
602
+ 个文件
603
+ </span>
604
+ <button
605
+ type="button"
606
+ className="hover:text-foreground transition-colors"
607
+ onClick={() => setFiles([])}
358
608
  >
359
- <div className="flex items-center gap-2 min-w-0">
360
- <FileText className="h-4 w-4 shrink-0 text-[hsl(var(--muted-foreground))]" />
361
- <span className="truncate">{file.name}</span>
362
- </div>
363
- <Button
364
- variant="ghost"
365
- size="icon"
366
- className="h-6 w-6 shrink-0"
367
- onClick={() => removeFile(index)}
609
+ 全部清除
610
+ </button>
611
+ </div>
612
+ <div className="max-h-32 overflow-y-auto space-y-1.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--secondary))]/30 p-1.5">
613
+ {files.map((file, index) => (
614
+ <div
615
+ key={`${file.name}-${index}`}
616
+ className="flex items-center justify-between rounded-md bg-[hsl(var(--background))] px-2.5 py-1.5 text-sm shadow-sm"
368
617
  >
369
- <X className="h-3 w-3" />
370
- </Button>
371
- </div>
372
- ))}
618
+ <div className="flex items-center gap-2 min-w-0">
619
+ <FileText className="h-3.5 w-3.5 shrink-0 text-[hsl(var(--muted-foreground))]" />
620
+ <span className="truncate">{file.name}</span>
621
+ </div>
622
+ <Button
623
+ variant="ghost"
624
+ size="icon"
625
+ className="h-6 w-6 shrink-0"
626
+ onClick={() => removeFile(index)}
627
+ >
628
+ <X className="h-3 w-3" />
629
+ </Button>
630
+ </div>
631
+ ))}
632
+ </div>
373
633
  </div>
374
634
  )}
375
635
  </TabsContent>
376
636
 
377
637
  {/* ---- Folder mode ---- */}
378
638
  <TabsContent value="folder" className="space-y-3">
379
- <div className="flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-[hsl(var(--border))] p-6">
380
- <FolderOpen className="mb-2 h-8 w-8 text-[hsl(var(--muted-foreground))]" />
381
- <p className="text-sm text-[hsl(var(--muted-foreground))] mb-3">
382
- 选择一个文件夹,自动读取其中的文件
639
+ <div
640
+ className={cn(
641
+ "relative flex h-36 flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed transition-colors",
642
+ isDragOver
643
+ ? "border-[hsl(var(--primary))] bg-[hsl(var(--primary))]/5"
644
+ : "border-[hsl(var(--border))] bg-[hsl(var(--secondary))]/40 hover:border-[hsl(var(--primary))]/50 hover:bg-[hsl(var(--primary))]/[0.03]",
645
+ )}
646
+ onDragOver={handleDragOver}
647
+ onDragLeave={handleDragLeave}
648
+ onDrop={handleDropFolderMode}
649
+ >
650
+ <FolderOpen className="h-7 w-7 text-[hsl(var(--muted-foreground))]" />
651
+ <p className="text-sm text-[hsl(var(--muted-foreground))]">
652
+ 拖拽文件夹到此处,或选择
383
653
  </p>
384
654
  <Button
385
655
  type="button"
@@ -404,59 +674,68 @@ export function DocUploadDialog({
404
674
  />
405
675
  </div>
406
676
 
407
- <div className="grid grid-cols-2 gap-3">
408
- <div className="space-y-1">
409
- <Label className="text-xs">包含后缀(可选)</Label>
410
- <Input
411
- placeholder=".md, .pdf, .txt"
412
- value={includeInput}
413
- onChange={(e) => setIncludeInput(e.target.value)}
414
- />
677
+ <div className="rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--secondary))]/30 p-3 space-y-3">
678
+ <div className="text-xs font-medium text-foreground">过滤选项</div>
679
+ <div className="grid grid-cols-2 gap-3">
680
+ <div className="space-y-1">
681
+ <Label className="text-xs text-muted-foreground">包含后缀</Label>
682
+ <Input
683
+ placeholder=".md, .pdf, .txt"
684
+ value={includeInput}
685
+ onChange={(e) => setIncludeInput(e.target.value)}
686
+ className="h-8 bg-background"
687
+ />
688
+ </div>
689
+ <div className="space-y-1">
690
+ <Label className="text-xs text-muted-foreground">排除后缀</Label>
691
+ <Input
692
+ placeholder=".log, .tmp"
693
+ value={excludeInput}
694
+ onChange={(e) => setExcludeInput(e.target.value)}
695
+ className="h-8 bg-background"
696
+ />
697
+ </div>
415
698
  </div>
416
- <div className="space-y-1">
417
- <Label className="text-xs">排除后缀(可选)</Label>
418
- <Input
419
- placeholder=".log, .tmp"
420
- value={excludeInput}
421
- onChange={(e) => setExcludeInput(e.target.value)}
699
+ <label className="flex cursor-pointer items-center gap-2 text-sm text-foreground">
700
+ <Checkbox
701
+ checked={recursive}
702
+ onCheckedChange={(v) => setRecursive(v === true)}
422
703
  />
423
- </div>
704
+ 递归包含子目录
705
+ </label>
424
706
  </div>
425
707
 
426
- <label className="flex items-center gap-2 text-sm text-[hsl(var(--muted-foreground))]">
427
- <Checkbox
428
- checked={recursive}
429
- onCheckedChange={(v) => setRecursive(v === true)}
430
- />
431
- 递归包含子目录
432
- </label>
433
-
434
708
  {folderRaw.length > 0 && (
435
- <div className="text-xs text-[hsl(var(--muted-foreground))]">
436
- 已选择 {folderRaw.length} 个文件,过滤后将上传{" "}
437
- <span className="font-medium text-[hsl(var(--foreground))]">
438
- {folderFiltered.length}
439
- </span>{" "}
440
-
441
- </div>
442
- )}
443
-
444
- {folderFiltered.length > 0 && (
445
- <div className="space-y-1 max-h-32 overflow-y-auto overflow-x-hidden rounded-md border p-2 min-w-0">
446
- {folderFiltered.slice(0, 50).map((file, index) => (
447
- <div
448
- key={`${file.webkitRelativePath || file.name}-${index}`}
449
- className="flex items-center gap-2 text-xs min-w-0"
450
- >
451
- <FileText className="h-3 w-3 shrink-0 text-[hsl(var(--muted-foreground))]" />
452
- <span className="truncate">
453
- {file.webkitRelativePath || file.name}
454
- </span>
455
- </div>
456
- ))}
457
- {folderFiltered.length > 50 && (
458
- <div className="text-xs text-[hsl(var(--muted-foreground))] pt-1">
459
- ... 还有 {folderFiltered.length - 50} 个文件未展示
709
+ <div className="space-y-2">
710
+ <div className="flex items-center justify-between text-xs text-[hsl(var(--muted-foreground))]">
711
+ <span>
712
+ 已选择{" "}
713
+ <span className="font-medium text-foreground">{folderRaw.length}</span>{" "}
714
+ 个文件,过滤后将上传{" "}
715
+ <span className="font-medium text-[hsl(var(--primary))]">
716
+ {folderFiltered.length}
717
+ </span>{" "}
718
+
719
+ </span>
720
+ </div>
721
+ {folderFiltered.length > 0 && (
722
+ <div className="max-h-32 overflow-y-auto overflow-x-hidden rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--secondary))]/30 p-1.5 min-w-0 space-y-1">
723
+ {folderFiltered.slice(0, 50).map((file, index) => (
724
+ <div
725
+ key={`${file.webkitRelativePath || file.name}-${index}`}
726
+ className="flex items-center gap-2 rounded-md bg-[hsl(var(--background))] px-2.5 py-1 text-xs shadow-sm min-w-0"
727
+ >
728
+ <FileText className="h-3 w-3 shrink-0 text-[hsl(var(--muted-foreground))]" />
729
+ <span className="truncate">
730
+ {file.webkitRelativePath || file.name}
731
+ </span>
732
+ </div>
733
+ ))}
734
+ {folderFiltered.length > 50 && (
735
+ <div className="text-xs text-[hsl(var(--muted-foreground))] pt-1 px-2">
736
+ ... 还有 {folderFiltered.length - 50} 个文件未展示
737
+ </div>
738
+ )}
460
739
  </div>
461
740
  )}
462
741
  </div>
@@ -2,7 +2,7 @@
2
2
 
3
3
  import Link from "next/link";
4
4
  import { useRouter } from "next/navigation";
5
- import { MoreHorizontal, Trash2, FileText, Search, Settings } from "lucide-react";
5
+ import { BookOpenText, MoreHorizontal, Trash2, FileText, Search, Settings } from "lucide-react";
6
6
  // Note: router is still used for dropdown menu navigation items
7
7
  import { CardContent, CardInteractive } from "@/components/ui/card";
8
8
  import {
@@ -44,10 +44,13 @@ export function KBCard({ knowledgeBase, onDelete }: KBCardProps) {
44
44
  className="group relative flex h-full cursor-pointer flex-col"
45
45
  >
46
46
  <CardContent className="flex flex-col flex-1 p-5">
47
- <div className="flex items-start justify-between">
48
- <h3 className="text-base font-semibold text-foreground">
49
- {knowledgeBaseName}
50
- </h3>
47
+ <div className="flex items-start justify-between gap-3">
48
+ <div className="flex min-w-0 items-center gap-2">
49
+ <BookOpenText className="h-4 w-4 shrink-0 text-primary" />
50
+ <h3 className="truncate text-base font-semibold text-foreground">
51
+ {knowledgeBaseName}
52
+ </h3>
53
+ </div>
51
54
 
52
55
  <div
53
56
  className="opacity-0 transition-opacity duration-150 delay-75 group-hover:opacity-100 focus-within:opacity-100"
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { Badge } from "@/components/ui/badge";
4
4
  import { Button } from "@/components/ui/button";
5
- import { Trash2 } from "lucide-react";
5
+ import { BookOpenText, Trash2 } from "lucide-react";
6
6
  import { RelativeTime } from "@/components/shared/relative-time";
7
7
  import { SubspaceSwitcher } from "@/components/knowledge-base/subspace-switcher";
8
8
  import type { KnowledgeBase } from "@/types/knowledge-base";
@@ -24,13 +24,21 @@ export function KBHeader({ knowledgeBase, onDelete }: KBHeaderProps) {
24
24
 
25
25
  return (
26
26
  <div className="space-y-4">
27
- <div className="flex items-start justify-between">
28
- <div className="space-y-1">
29
- <h1 className="font-display text-[32px] font-semibold leading-tight tracking-tight text-foreground">
30
- {knowledgeBaseName}
31
- </h1>
27
+ <div className="flex items-start justify-between gap-4">
28
+ <div className="min-w-0 space-y-2">
29
+ <div className="flex items-center gap-3">
30
+ <div className="rounded-lg bg-[hsl(var(--primary)/0.1)] p-2 text-primary">
31
+ <BookOpenText className="h-5 w-5" />
32
+ </div>
33
+ <h1 className="truncate font-display text-[32px] font-semibold leading-tight tracking-tight text-foreground">
34
+ {knowledgeBaseName}
35
+ </h1>
36
+ <Badge variant="secondary" className="border-0 bg-[hsl(var(--primary-50))] text-primary">
37
+ Knowledge Base
38
+ </Badge>
39
+ </div>
32
40
  {description && (
33
- <p className="text-sm text-muted-foreground max-w-2xl">
41
+ <p className="max-w-3xl text-sm text-muted-foreground">
34
42
  {description}
35
43
  </p>
36
44
  )}
@@ -61,14 +69,16 @@ export function KBHeader({ knowledgeBase, onDelete }: KBHeaderProps) {
61
69
  </div>
62
70
  )}
63
71
 
64
- <div className="flex items-center gap-4 text-xs text-muted-foreground">
72
+ <div className="flex flex-wrap items-center gap-x-4 gap-y-2 text-xs text-muted-foreground">
65
73
  {subspace && (
66
- <span className="inline-flex items-center gap-1">
67
- <span className="h-1.5 w-1.5 rounded-full bg-green-500" />
68
- 已开启子空间隔离
69
- </span>
74
+ <>
75
+ <span className="inline-flex items-center gap-1.5">
76
+ <span className="h-1.5 w-1.5 rounded-full bg-emerald-500 shadow-[0_0_0_3px_rgb(16,185,129,0.15)]" />
77
+ 已开启子空间隔离
78
+ </span>
79
+ <SubspaceSwitcher />
80
+ </>
70
81
  )}
71
- <SubspaceSwitcher />
72
82
  <span>
73
83
  创建于 <RelativeTime timestamp={createdAt} />
74
84
  </span>