@unciatech/file-manager 0.0.33 → 0.0.34

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/dist/cli.js CHANGED
@@ -1,21 +1,5 @@
1
1
  #!/usr/bin/env node
2
-
3
- // cli.ts
4
- import fs from "fs";
5
- import path from "path";
6
- import { execSync } from "child_process";
7
- import readline from "readline";
8
- var args = process.argv.slice(2);
9
- var command = args[0];
10
- var projectName = args[1];
11
- var rl = readline.createInterface({
12
- input: process.stdin,
13
- output: process.stdout
14
- });
15
- var askQuestion = (query) => {
16
- return new Promise((resolve) => rl.question(query, resolve));
17
- };
18
- var getTemplate = (basePath = "/media") => `"use client";
2
+ import t from'fs';import i from'path';import {execSync}from'child_process';import g from'readline';var p=process.argv.slice(2),v=p[0],l=p[1],u=g.createInterface({input:process.stdin,output:process.stdout}),x=o=>new Promise(e=>u.question(o,e)),h=(o="/media")=>`"use client";
19
3
 
20
4
  import React, { Suspense } from "react";
21
5
  import { FileManager, MockProvider } from "@unciatech/file-manager";
@@ -30,117 +14,150 @@ export default function FileManagerDemo() {
30
14
  allowedFileTypes={["audios", "videos", "images", "files"]}
31
15
  viewMode="grid"
32
16
  provider={mockProvider}
33
- basePath="${basePath}"
17
+ basePath="${o}"
34
18
  />
35
19
  </Suspense>
36
20
  </div>
37
21
  );
38
22
  }
39
- `;
40
- async function main() {
41
- if (command !== "init") {
42
- console.log("Usage: npx @unciatech/file-manager init [project-name]");
43
- process.exit(0);
44
- }
45
- if (!projectName) {
46
- console.log("\u{1F680} Generating <FileManagerDemo /> component in the current project...");
47
- let targetDir2 = process.cwd();
48
- if (fs.existsSync(path.join(process.cwd(), "components"))) {
49
- targetDir2 = path.join(process.cwd(), "components");
50
- } else if (fs.existsSync(path.join(process.cwd(), "src", "components"))) {
51
- targetDir2 = path.join(process.cwd(), "src", "components");
52
- }
53
- const targetFile = path.join(targetDir2, "FileManagerDemo.tsx");
54
- if (fs.existsSync(targetFile)) {
55
- console.error(`\u274C Error: ${targetFile} already exists.`);
56
- process.exit(1);
57
- }
58
- fs.writeFileSync(targetFile, getTemplate("/"), "utf-8");
59
- console.log(`\u2705 Success! Created ${targetFile}`);
60
- console.log("");
61
- console.log("You can now import and render <FileManagerDemo /> anywhere in your application.");
62
- console.log("Don't forget to configure your Tailwind CSS content to scan the library for styles!");
63
- process.exit(0);
64
- }
65
- console.log(`
66
- \u{1F680} Initializing a new application: ${projectName}
67
- `);
68
- console.log("Which framework would you like to use?");
69
- console.log(" 1) Next.js (App Router, Tailwind v4)");
70
- console.log(" 2) Vite (React, Tailwind v4)");
71
- console.log(" 3) Cancel");
72
- const choice = await askQuestion("\nSelect an option (1-3): ");
73
- const targetDir = path.join(process.cwd(), projectName);
74
- if (fs.existsSync(targetDir)) {
75
- console.error(`
76
- \u274C Error: Directory "${projectName}" already exists in ${process.cwd()}.`);
77
- console.error(` Please choose a different project name or delete the existing directory first.`);
78
- rl.close();
79
- process.exit(1);
80
- }
81
- try {
82
- if (choice === "1") {
83
- await scaffoldNextjs(projectName, targetDir);
84
- } else if (choice === "2") {
85
- await scaffoldVite(projectName, targetDir);
86
- } else {
87
- console.log("Canceled.");
88
- process.exit(0);
89
- }
90
- } catch (error) {
91
- console.error("\n\u274C Scaffolding failed:", error);
92
- process.exit(1);
93
- }
94
- process.exit(0);
95
- }
96
- async function scaffoldNextjs(projectName2, targetDir) {
97
- console.log("\n\u{1F4E6} Creating Next.js application (this may take a minute)...");
98
- execSync(`npx create-next-app@latest ${projectName2} --ts --tailwind --eslint --app --src-dir --import-alias "@/*" --use-npm`, { stdio: "inherit" });
99
- console.log("\n\u{1F4E6} Installing dependencies (@unciatech/file-manager, tailwindcss-animate)...");
100
- execSync("npm install @unciatech/file-manager tailwindcss-animate", { cwd: targetDir, stdio: "inherit" });
101
- const componentsDir = path.join(targetDir, "src", "components");
102
- if (!fs.existsSync(componentsDir)) fs.mkdirSync(componentsDir, { recursive: true });
103
- fs.writeFileSync(path.join(componentsDir, "FileManagerDemo.tsx"), getTemplate("/media"), "utf-8");
104
- const pagePath = path.join(targetDir, "src", "app", "page.tsx");
105
- fs.writeFileSync(pagePath, `import FileManagerDemo from "@/components/FileManagerDemo";
23
+ `;async function w(){if(v!=="init"&&(console.log("Usage: npx @unciatech/file-manager init [project-name]"),process.exit(0)),!l){console.log("\u{1F680} Generating <FileManagerDemo /> component in the current project...");let s=process.cwd();t.existsSync(i.join(process.cwd(),"components"))?s=i.join(process.cwd(),"components"):t.existsSync(i.join(process.cwd(),"src","components"))&&(s=i.join(process.cwd(),"src","components"));let a=i.join(s,"FileManagerDemo.tsx");t.existsSync(a)&&(console.error(`\u274C Error: ${a} already exists.`),process.exit(1)),t.writeFileSync(a,h("/"),"utf-8"),console.log(`\u2705 Success! Created ${a}`),console.log(""),console.log("You can now import and render <FileManagerDemo /> anywhere in your application."),console.log("Don't forget to configure your Tailwind CSS content to scan the library for styles!"),process.exit(0);}console.log(`
24
+ \u{1F680} Initializing a new application: ${l}
25
+ `),console.log("Which framework would you like to use?"),console.log(" 1) Next.js (App Router, Tailwind v4)"),console.log(" 2) Vite (React, Tailwind v4)"),console.log(" 3) Cancel");let o=await x(`
26
+ Select an option (1-3): `),e=i.join(process.cwd(),l);t.existsSync(e)&&(console.error(`
27
+ \u274C Error: Directory "${l}" already exists in ${process.cwd()}.`),console.error(" Please choose a different project name or delete the existing directory first."),u.close(),process.exit(1));try{o==="1"?await b(l,e):o==="2"?await y(l,e):(console.log("Canceled."),process.exit(0));}catch(s){console.error(`
28
+ \u274C Scaffolding failed:`,s),process.exit(1);}process.exit(0);}async function b(o,e){console.log(`
29
+ \u{1F4E6} Creating Next.js application (this may take a minute)...`),execSync(`npx create-next-app@latest ${o} --ts --tailwind --eslint --app --src-dir --import-alias "@/*" --use-npm`,{stdio:"inherit"}),console.log(`
30
+ \u{1F4E6} Installing dependencies (@unciatech/file-manager, tailwindcss-animate)...`),execSync("npm install @unciatech/file-manager tailwindcss-animate",{cwd:e,stdio:"inherit"});let s=i.join(e,"src","app","page.tsx");t.writeFileSync(s,`"use client";
31
+
32
+ import { redirect } from "next/navigation";
106
33
 
107
34
  export default function Home() {
35
+ redirect("/media");
36
+ }
37
+ `);let a=i.join(e,"src","app","media","[[...path]]");t.mkdirSync(a,{recursive:true}),t.writeFileSync(i.join(a,"page.tsx"),`"use client";
38
+
39
+ import { Suspense, useState } from "react";
40
+ import { FileManager, MockProvider } from "@unciatech/file-manager";
41
+ import Link from "next/link";
42
+
43
+ function MediaPageContent() {
44
+ const [provider] = useState(() => new MockProvider());
45
+
108
46
  return (
109
- <main className="min-h-screen bg-neutral-50">
110
- <FileManagerDemo />
111
- </main>
47
+ <div className="h-screen w-full flex flex-col">
48
+ <div className="flex gap-4 p-4 border-b border-border items-center justify-between">
49
+ <h1 className="text-xl font-bold">Full Page View</h1>
50
+ <Link
51
+ href="/modal-demo"
52
+ className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 cursor-pointer text-sm font-medium"
53
+ >
54
+ Go to Modal Demo
55
+ </Link>
56
+ </div>
57
+ <div className="flex-1 relative overflow-hidden">
58
+ <FileManager
59
+ allowedFileTypes={["audios", "videos", "images", "files"]}
60
+ viewMode="grid"
61
+ provider={provider}
62
+ basePath="/media"
63
+ />
64
+ </div>
65
+ </div>
112
66
  );
113
67
  }
114
- `);
115
- const mediaRouteDir = path.join(targetDir, "src", "app", "media", "[[...path]]");
116
- fs.mkdirSync(mediaRouteDir, { recursive: true });
117
- fs.writeFileSync(
118
- path.join(mediaRouteDir, "page.tsx"),
119
- `import FileManagerDemo from "@/components/FileManagerDemo";
120
68
 
121
69
  export default function MediaPage() {
122
70
  return (
123
- <main className="min-h-screen bg-neutral-50">
124
- <FileManagerDemo />
125
- </main>
71
+ <Suspense fallback={<div className="p-4">Loading Media Library...</div>}>
72
+ <MediaPageContent />
73
+ </Suspense>
74
+ );
75
+ }
76
+ `);let d=i.join(e,"src","app","modal-demo");t.mkdirSync(d,{recursive:true}),t.writeFileSync(i.join(d,"page.tsx"),`"use client";
77
+
78
+ import { Suspense, useState } from "react";
79
+ import { FileManagerModal, MockProvider } from "@unciatech/file-manager";
80
+ import type { FileMetaData } from "@unciatech/file-manager";
81
+ import Link from "next/link";
82
+
83
+ function ModalDemoContent() {
84
+ const [provider] = useState(() => new MockProvider());
85
+ const [isOpen, setIsOpen] = useState(false);
86
+ const [selectedFiles, setSelectedFiles] = useState<FileMetaData[]>([]);
87
+
88
+ return (
89
+ <div className="p-10 flex flex-col items-start gap-6 min-h-screen w-full">
90
+ <div>
91
+ <h1 className="text-3xl font-bold mb-2">File Manager Demo</h1>
92
+ <p className="text-gray-500">Select files using the modal or browse the full page view.</p>
93
+ </div>
94
+
95
+ <div className="flex gap-4">
96
+ <button
97
+ onClick={() => setIsOpen(true)}
98
+ className="px-6 py-2.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 cursor-pointer text-sm font-medium shadow-sm transition-colors"
99
+ >
100
+ Open File Picker Modal
101
+ </button>
102
+ <Link
103
+ href="/media"
104
+ className="px-6 py-2.5 bg-zinc-100 dark:bg-zinc-800 text-zinc-900 dark:text-zinc-100 rounded-md hover:bg-zinc-200 dark:hover:bg-zinc-700 cursor-pointer text-sm font-medium transition-colors"
105
+ >
106
+ Go to Full Page View
107
+ </Link>
108
+ </div>
109
+
110
+ {selectedFiles.length > 0 && (
111
+ <div className="w-full mt-8">
112
+ <h2 className="text-xl font-semibold mb-4 border-b pb-2">
113
+ Selected Files ({selectedFiles.length})
114
+ </h2>
115
+ <div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4">
116
+ {selectedFiles.map((file, idx) => (
117
+ <div key={idx} className="border rounded-lg p-3 flex flex-col items-center gap-3 shadow-sm">
118
+ <div className="w-full aspect-square bg-gray-100 dark:bg-gray-800 rounded-md overflow-hidden flex items-center justify-center relative">
119
+ {file.url && file.mime?.startsWith("image/") ? (
120
+ <img src={file.url} alt={file.name} className="w-full h-full object-cover" />
121
+ ) : (
122
+ <span className="text-gray-400 font-mono text-xs p-2 overflow-hidden text-ellipsis">
123
+ {file.ext?.toUpperCase() || "FILE"}
124
+ </span>
125
+ )}
126
+ </div>
127
+ <div className="w-full text-center">
128
+ <p className="text-sm font-medium truncate w-full" title={file.name}>{file.name}</p>
129
+ <p className="text-xs text-gray-500 mt-0.5">{(file.size / 1024).toFixed(1)} KB</p>
130
+ </div>
131
+ </div>
132
+ ))}
133
+ </div>
134
+ </div>
135
+ )}
136
+
137
+ <FileManagerModal
138
+ open={isOpen}
139
+ onClose={() => setIsOpen(false)}
140
+ provider={provider}
141
+ allowedFileTypes={["images", "videos", "audios", "files"]}
142
+ onFilesSelected={(files: FileMetaData[]) => {
143
+ setSelectedFiles(files);
144
+ setIsOpen(false);
145
+ }}
146
+ basePath="/media"
147
+ />
148
+ </div>
126
149
  );
127
150
  }
128
- `
151
+
152
+ export default function ModalDemoPage() {
153
+ return (
154
+ <Suspense fallback={<div className="p-10">Loading...</div>}>
155
+ <ModalDemoContent />
156
+ </Suspense>
129
157
  );
130
- const layoutPath = path.join(targetDir, "src", "app", "layout.tsx");
131
- if (fs.existsSync(layoutPath)) {
132
- let layoutContent = fs.readFileSync(layoutPath, "utf8");
133
- if (!layoutContent.includes("@unciatech/file-manager/styles")) {
134
- layoutContent = layoutContent.replace(
135
- /^(import type)/m,
136
- `import '@unciatech/file-manager/styles';
137
- $1`
138
- );
139
- fs.writeFileSync(layoutPath, layoutContent);
140
- }
141
- }
142
- const cssPath = path.join(targetDir, "src", "app", "globals.css");
143
- fs.writeFileSync(cssPath, `@import "tailwindcss";
158
+ }
159
+ `);let r=i.join(e,"src","app","layout.tsx");if(t.existsSync(r)){let n=t.readFileSync(r,"utf8");n.includes("@unciatech/file-manager/styles")||(n=n.replace(/^(import type)/m,`import '@unciatech/file-manager/styles';
160
+ $1`),t.writeFileSync(r,n));}let m=i.join(e,"src","app","globals.css");t.writeFileSync(m,`@import "tailwindcss";
144
161
  @import "@unciatech/file-manager/styles";
145
162
  @import "tw-animate-css";
146
163
 
@@ -150,17 +167,9 @@ $1`
150
167
  --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
151
168
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
152
169
  }
153
- `);
154
- printSuccess(projectName2);
155
- }
156
- async function scaffoldVite(projectName2, targetDir) {
157
- console.log("\n\u{1F4E6} Creating Vite React application...");
158
- execSync(`npm create vite@latest ${projectName2} -- --template react-ts`, { stdio: "inherit" });
159
- console.log("\n\u{1F4E6} Installing dependencies (Tailwind + File Manager)...");
160
- execSync("npm install", { cwd: targetDir, stdio: "inherit" });
161
- execSync("npm install tailwindcss @tailwindcss/vite @unciatech/file-manager", { cwd: targetDir, stdio: "inherit" });
162
- const viteConfigPath = path.join(targetDir, "vite.config.ts");
163
- const viteConfig = `import { defineConfig } from 'vite'
170
+ `),f(o);}async function y(o,e){console.log(`
171
+ \u{1F4E6} Creating Vite React application...`),execSync(`npm create vite@latest ${o} -- --template react-ts`,{stdio:"inherit"}),console.log(`
172
+ \u{1F4E6} Installing dependencies (Tailwind + File Manager + React Router)...`),execSync("npm install",{cwd:e,stdio:"inherit"}),execSync("npm install tailwindcss @tailwindcss/vite @unciatech/file-manager react-router-dom",{cwd:e,stdio:"inherit"});let s=i.join(e,"vite.config.ts");t.writeFileSync(s,`import { defineConfig } from 'vite'
164
173
  import react from '@vitejs/plugin-react'
165
174
  import tailwindcss from '@tailwindcss/vite'
166
175
 
@@ -170,38 +179,134 @@ export default defineConfig({
170
179
  tailwindcss(),
171
180
  ],
172
181
  })
173
- `;
174
- fs.writeFileSync(viteConfigPath, viteConfig);
175
- const cssPath = path.join(targetDir, "src", "index.css");
176
- fs.writeFileSync(cssPath, `@import "tailwindcss";
182
+ `);let d=i.join(e,"src","index.css");t.writeFileSync(d,`@import "tailwindcss";
177
183
  @import "@unciatech/file-manager/styles";
178
184
  @source "../node_modules/@unciatech/file-manager";
179
- `);
180
- const componentsDir = path.join(targetDir, "src", "components");
181
- if (!fs.existsSync(componentsDir)) fs.mkdirSync(componentsDir, { recursive: true });
182
- fs.writeFileSync(path.join(componentsDir, "FileManagerDemo.tsx"), getTemplate("/"), "utf-8");
183
- const appPath = path.join(targetDir, "src", "App.tsx");
184
- fs.writeFileSync(appPath, `import FileManagerDemo from "./components/FileManagerDemo";
185
+ `);let r=i.join(e,"src","main.tsx");t.writeFileSync(r,`import React from 'react'
186
+ import ReactDOM from 'react-dom/client'
187
+ import { BrowserRouter } from 'react-router-dom'
188
+ import App from './App.tsx'
189
+ import './index.css'
190
+
191
+ ReactDOM.createRoot(document.getElementById('root')!).render(
192
+ <React.StrictMode>
193
+ <BrowserRouter>
194
+ <App />
195
+ </BrowserRouter>
196
+ </React.StrictMode>,
197
+ )
198
+ `);let m=i.join(e,"src","App.tsx");t.writeFileSync(m,`import { useState } from 'react'
199
+ import { useNavigate, Routes, Route, Link } from 'react-router-dom'
200
+ import { FileManager, FileManagerModal, MockProvider } from '@unciatech/file-manager'
201
+ import type { FileMetaData } from '@unciatech/file-manager'
202
+
203
+ const provider = new MockProvider()
204
+
205
+ function FullPage() {
206
+ const navigate = useNavigate()
185
207
 
186
- function App() {
187
208
  return (
188
- <div className="min-h-screen bg-neutral-50">
189
- <FileManagerDemo />
209
+ <div className="h-screen w-full flex flex-col">
210
+ <div className="flex gap-4 p-4 border-b border-border items-center justify-between">
211
+ <h1 className="text-xl font-bold">Full Page View</h1>
212
+ <Link
213
+ to="/"
214
+ className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 cursor-pointer text-sm font-medium"
215
+ >
216
+ Back to Modal Demo
217
+ </Link>
218
+ </div>
219
+
220
+ <div className="flex-1 relative overflow-hidden">
221
+ <FileManager
222
+ provider={provider}
223
+ basePath="full"
224
+ allowedFileTypes={["images", "videos", "audios", "files"]}
225
+ viewMode="grid"
226
+ onNavigate={(url, opts) => navigate(url, { replace: opts?.replace })}
227
+ />
228
+ </div>
190
229
  </div>
191
- );
230
+ )
192
231
  }
193
232
 
194
- export default App;
195
- `);
196
- printSuccess(projectName2, "npm run dev");
233
+ function ModalDemo() {
234
+ const navigate = useNavigate()
235
+ const [isModalOpen, setIsModalOpen] = useState(false)
236
+ const [selectedFiles, setSelectedFiles] = useState<FileMetaData[]>([])
237
+
238
+ return (
239
+ <div className="p-10 flex flex-col items-start gap-6 min-h-screen w-full bg-background text-foreground">
240
+ <div>
241
+ <h1 className="text-3xl font-bold mb-2">File Manager Demo</h1>
242
+ <p className="text-muted-foreground">Select files using the modal or browse the full page view.</p>
243
+ </div>
244
+
245
+ <div className="flex gap-4">
246
+ <button
247
+ onClick={() => setIsModalOpen(true)}
248
+ className="px-6 py-2.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 cursor-pointer text-sm font-medium shadow-sm transition-colors"
249
+ >
250
+ Open File Picker Modal
251
+ </button>
252
+ <Link
253
+ to="/full"
254
+ className="px-6 py-2.5 bg-zinc-100 dark:bg-zinc-800 text-zinc-900 dark:text-zinc-100 rounded-md hover:bg-zinc-200 dark:hover:bg-zinc-700 cursor-pointer text-sm font-medium transition-colors"
255
+ >
256
+ Go to Full Page View
257
+ </Link>
258
+ </div>
259
+
260
+ {selectedFiles.length > 0 && (
261
+ <div className="w-full mt-8">
262
+ <h2 className="text-xl font-semibold mb-4 border-b pb-2">Selected Files ({selectedFiles.length})</h2>
263
+ <div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4">
264
+ {selectedFiles.map((file, idx) => (
265
+ <div key={idx} className="border border-border rounded-lg p-3 flex flex-col items-center gap-3 bg-card shadow-sm">
266
+ <div className="w-full aspect-square bg-muted rounded-md overflow-hidden flex items-center justify-center relative">
267
+ {file.url && file.mime?.startsWith('image/') ? (
268
+ <img src={file.url} alt={file.name} className="w-full h-full object-cover" />
269
+ ) : (
270
+ <span className="text-muted-foreground font-mono text-xs p-2 overflow-hidden text-ellipsis">{file.ext?.toUpperCase() || 'FILE'}</span>
271
+ )}
272
+ </div>
273
+ <div className="w-full text-center">
274
+ <p className="text-sm font-medium truncate w-full" title={file.name}>{file.name}</p>
275
+ <p className="text-xs text-muted-foreground mt-0.5">{(file.size / 1024).toFixed(1)} KB</p>
276
+ </div>
277
+ </div>
278
+ ))}
279
+ </div>
280
+ </div>
281
+ )}
282
+
283
+ <FileManagerModal
284
+ open={isModalOpen}
285
+ onClose={() => setIsModalOpen(false)}
286
+ provider={provider}
287
+ basePath="/"
288
+ allowedFileTypes={["images", "videos", "audios", "files"]}
289
+ onFilesSelected={(files: FileMetaData[]) => {
290
+ setSelectedFiles(files)
291
+ }}
292
+ onNavigate={(url, opts) => navigate(url, { replace: opts?.replace })}
293
+ />
294
+ </div>
295
+ )
197
296
  }
198
- function printSuccess(projectName2, devCmd = "npm run dev") {
199
- console.log("\n=========================================");
200
- console.log("\u{1F389} Your Media Library application is ready!");
201
- console.log("=========================================");
202
- console.log("\nNext steps:");
203
- console.log(` cd ${projectName2}`);
204
- console.log(` ${devCmd}`);
205
- console.log("\nEnjoy building! \u{1F5C2}\uFE0F\n");
297
+
298
+ function App() {
299
+ return (
300
+ <Routes>
301
+ <Route path="/" element={<ModalDemo />} />
302
+ <Route path="/full/*" element={<FullPage />} />
303
+ </Routes>
304
+ )
206
305
  }
207
- main();
306
+
307
+ export default App
308
+ `);let n=i.join(e,"src","App.css");t.existsSync(n)&&t.unlinkSync(n),f(o,"npm run dev");}function f(o,e="npm run dev"){console.log(`
309
+ =========================================`),console.log("\u{1F389} Your Media Library application is ready!"),console.log("========================================="),console.log(`
310
+ Next steps:`),console.log(` cd ${o}`),console.log(` ${e}`),console.log(`
311
+ Enjoy building! \u{1F5C2}\uFE0F
312
+ `);}w();