copit 1.0.1 → 1.0.2

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 (2) hide show
  1. package/dist/index.js +313 -199
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -34567,7 +34567,7 @@ var require_stack_utils = __commonJS((exports, module) => {
34567
34567
  });
34568
34568
 
34569
34569
  // src/index.tsx
34570
- var import_react31 = __toESM(require_react(), 1);
34570
+ var import_react34 = __toESM(require_react(), 1);
34571
34571
 
34572
34572
  // node_modules/ink/build/render.js
34573
34573
  import { Stream } from "node:stream";
@@ -40749,24 +40749,17 @@ function useAtom(atom2, options) {
40749
40749
  ];
40750
40750
  }
40751
40751
 
40752
- // src/features/app/App.tsx
40753
- var import_react30 = __toESM(require_react(), 1);
40752
+ // src/App.tsx
40753
+ var import_react33 = __toESM(require_react(), 1);
40754
40754
 
40755
40755
  // src/features/store/atoms.ts
40756
40756
  var viewAtom = atom("history");
40757
- var usernameAtom = atom("");
40758
- var isAuthenticatedAtom = atom(false);
40759
- var authTokenAtom = atom("");
40760
- var repositoriesAtom = atom([]);
40761
- var selectedRepositoryAtom = atom(null);
40762
- var filesAtom = atom([]);
40763
- var selectedFileAtom = atom(null);
40764
40757
  var searchQueryAtom = atom("");
40765
40758
  var isLoadingAtom = atom(false);
40766
40759
  var errorAtom = atom(null);
40767
40760
 
40768
40761
  // src/features/repository/RepositoryList.tsx
40769
- var import_react26 = __toESM(require_react(), 1);
40762
+ var import_react27 = __toESM(require_react(), 1);
40770
40763
 
40771
40764
  // node_modules/ink-select-input/build/Indicator.js
40772
40765
  var import_react23 = __toESM(require_react(), 1);
@@ -41743,6 +41736,24 @@ function TextInput({ value: originalValue, placeholder = "", focus = true, mask,
41743
41736
  }
41744
41737
  var build_default = TextInput;
41745
41738
 
41739
+ // src/features/auth/atoms.ts
41740
+ var usernameAtom = atom("");
41741
+ var isAuthenticatedAtom = atom(false);
41742
+ var authTokenAtom = atom("");
41743
+
41744
+ // src/features/auth/useAuthToken.ts
41745
+ var useAuthToken = () => {
41746
+ const token = useAtomValue(authTokenAtom);
41747
+ const username = useAtomValue(usernameAtom);
41748
+ return {
41749
+ token,
41750
+ username
41751
+ };
41752
+ };
41753
+
41754
+ // src/features/repository/useRepositories.ts
41755
+ var import_react26 = __toESM(require_react(), 1);
41756
+
41746
41757
  // node_modules/fuse.js/dist/fuse.mjs
41747
41758
  function isArray(value) {
41748
41759
  return !Array.isArray ? getTag(value) === "[object Array]" : Array.isArray(value);
@@ -43037,6 +43048,10 @@ Fuse.config = Config;
43037
43048
  register(ExtendedSearch);
43038
43049
  }
43039
43050
 
43051
+ // src/features/repository/atoms.ts
43052
+ var repositoriesAtom = atom([]);
43053
+ var selectedRepositoryAtom = atom(null);
43054
+
43040
43055
  // node_modules/universal-user-agent/index.js
43041
43056
  function getUserAgent() {
43042
43057
  if (typeof navigator === "object" && "userAgent" in navigator) {
@@ -47118,52 +47133,84 @@ var downloadFile = async (downloadUrl) => {
47118
47133
  return response.text();
47119
47134
  };
47120
47135
 
47121
- // src/features/repository/RepositoryList.tsx
47122
- var RepositoryList = () => {
47123
- const [username] = useAtom(usernameAtom);
47136
+ // src/features/repository/useRepositories.ts
47137
+ var useRepositories = () => {
47124
47138
  const [repositories, setRepositories] = useAtom(repositoriesAtom);
47125
- const [searchQuery, setSearchQuery] = useAtom(searchQueryAtom);
47126
- const [isLoading, setLoading] = useAtom(isLoadingAtom);
47127
- const [error, setError] = useAtom(errorAtom);
47128
- const [, selectRepository] = useAtom(selectedRepositoryAtom);
47129
- const [, setView] = useAtom(viewAtom);
47130
- const [authToken] = useAtom(authTokenAtom);
47139
+ const [selectedRepository, setSelectedRepository] = useAtom(selectedRepositoryAtom);
47131
47140
  const [filteredRepos, setFilteredRepos] = import_react26.useState([]);
47132
- use_input_default((input, key) => {
47133
- if (key.tab) {
47134
- setView("history");
47135
- setSearchQuery("");
47141
+ const [isLoading, setLoading] = import_react26.useState(false);
47142
+ const [error, setError] = import_react26.useState(null);
47143
+ const loadRepositories = async (authToken) => {
47144
+ if (!authToken)
47145
+ return;
47146
+ setLoading(true);
47147
+ setError(null);
47148
+ try {
47149
+ const repos = await fetchUserRepositories(authToken);
47150
+ setRepositories(repos);
47151
+ setFilteredRepos(repos);
47152
+ } catch (err) {
47153
+ setError(err instanceof Error ? err.message : "Failed to load repositories");
47154
+ } finally {
47155
+ setLoading(false);
47136
47156
  }
47137
- });
47138
- import_react26.useEffect(() => {
47139
- const loadRepositories = async () => {
47140
- if (!authToken)
47141
- return;
47142
- setLoading(true);
47143
- setError(null);
47144
- try {
47145
- const repos = await fetchUserRepositories(authToken);
47146
- setRepositories(repos);
47147
- } catch (err) {
47148
- setError(err instanceof Error ? err.message : "Failed to load repositories");
47149
- } finally {
47150
- setLoading(false);
47151
- }
47152
- };
47153
- loadRepositories();
47154
- }, [username, authToken, setRepositories, setLoading, setError]);
47155
- import_react26.useEffect(() => {
47156
- if (searchQuery.trim() === "") {
47157
+ };
47158
+ const selectRepository = (repo) => {
47159
+ setSelectedRepository(repo);
47160
+ };
47161
+ const filterRepositories = (query) => {
47162
+ if (query.trim() === "") {
47157
47163
  setFilteredRepos(repositories);
47158
47164
  } else {
47159
47165
  const fuse = new Fuse(repositories, {
47160
47166
  keys: ["name", "description"],
47161
47167
  threshold: 0.3
47162
47168
  });
47163
- const results = fuse.search(searchQuery);
47169
+ const results = fuse.search(query);
47164
47170
  setFilteredRepos(results.map((result) => result.item));
47165
47171
  }
47166
- }, [searchQuery, repositories]);
47172
+ };
47173
+ import_react26.useEffect(() => {
47174
+ setFilteredRepos(repositories);
47175
+ }, [repositories]);
47176
+ return {
47177
+ repositories,
47178
+ selectedRepository,
47179
+ filteredRepos,
47180
+ isLoading,
47181
+ error,
47182
+ loadRepositories,
47183
+ selectRepository,
47184
+ filterRepositories
47185
+ };
47186
+ };
47187
+
47188
+ // src/features/repository/RepositoryList.tsx
47189
+ var RepositoryList = () => {
47190
+ const { username, token: authToken } = useAuthToken();
47191
+ const [searchQuery, setSearchQuery] = useAtom(searchQueryAtom);
47192
+ const setView = useSetAtom(viewAtom);
47193
+ const {
47194
+ repositories,
47195
+ filteredRepos,
47196
+ isLoading,
47197
+ error,
47198
+ loadRepositories,
47199
+ selectRepository,
47200
+ filterRepositories
47201
+ } = useRepositories();
47202
+ use_input_default((input, key) => {
47203
+ if (key.tab) {
47204
+ setView("history");
47205
+ setSearchQuery("");
47206
+ }
47207
+ });
47208
+ import_react27.useEffect(() => {
47209
+ loadRepositories(authToken);
47210
+ }, [username, authToken]);
47211
+ import_react27.useEffect(() => {
47212
+ filterRepositories(searchQuery);
47213
+ }, [searchQuery]);
47167
47214
  const handleSelect = (item) => {
47168
47215
  const repo = repositories.find((r) => r.fullName === item.value);
47169
47216
  if (repo) {
@@ -47173,14 +47220,14 @@ var RepositoryList = () => {
47173
47220
  }
47174
47221
  };
47175
47222
  if (isLoading) {
47176
- return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
47223
+ return /* @__PURE__ */ import_react27.default.createElement(Box_default, {
47177
47224
  flexDirection: "column"
47178
- }, /* @__PURE__ */ import_react26.default.createElement(Text, null, "Loading repositories..."));
47225
+ }, /* @__PURE__ */ import_react27.default.createElement(Text, null, "Loading repositories..."));
47179
47226
  }
47180
47227
  if (error) {
47181
- return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
47228
+ return /* @__PURE__ */ import_react27.default.createElement(Box_default, {
47182
47229
  flexDirection: "column"
47183
- }, /* @__PURE__ */ import_react26.default.createElement(Text, {
47230
+ }, /* @__PURE__ */ import_react27.default.createElement(Text, {
47184
47231
  color: "red"
47185
47232
  }, "Error: ", error));
47186
47233
  }
@@ -47191,36 +47238,49 @@ var RepositoryList = () => {
47191
47238
  const MAX_VISIBLE_ITEMS = 10;
47192
47239
  const limitedItems = items.slice(0, MAX_VISIBLE_ITEMS);
47193
47240
  const hasMore = items.length > MAX_VISIBLE_ITEMS;
47194
- return /* @__PURE__ */ import_react26.default.createElement(Box_default, {
47241
+ return /* @__PURE__ */ import_react27.default.createElement(Box_default, {
47195
47242
  flexDirection: "column"
47196
- }, /* @__PURE__ */ import_react26.default.createElement(Box_default, {
47243
+ }, /* @__PURE__ */ import_react27.default.createElement(Box_default, {
47197
47244
  marginBottom: 1,
47198
47245
  flexDirection: "row",
47199
47246
  justifyContent: "space-between"
47200
- }, /* @__PURE__ */ import_react26.default.createElement(Box_default, null, /* @__PURE__ */ import_react26.default.createElement(Text, {
47247
+ }, /* @__PURE__ */ import_react27.default.createElement(Box_default, null, /* @__PURE__ */ import_react27.default.createElement(Text, {
47201
47248
  bold: true,
47202
47249
  color: "cyan"
47203
- }, "\uD83D\uDCC1 Browse Files")), /* @__PURE__ */ import_react26.default.createElement(Box_default, null, /* @__PURE__ */ import_react26.default.createElement(Text, {
47250
+ }, "\uD83D\uDCC1 Browse Files")), /* @__PURE__ */ import_react27.default.createElement(Box_default, null, /* @__PURE__ */ import_react27.default.createElement(Text, {
47204
47251
  dimColor: true
47205
- }, "[Tab] "), /* @__PURE__ */ import_react26.default.createElement(Text, null, "\uD83D\uDCCB Download History"))), /* @__PURE__ */ import_react26.default.createElement(Box_default, {
47252
+ }, "[Tab] "), /* @__PURE__ */ import_react27.default.createElement(Text, null, "\uD83D\uDCCB Download History"))), /* @__PURE__ */ import_react27.default.createElement(Box_default, {
47206
47253
  marginBottom: 1
47207
- }, /* @__PURE__ */ import_react26.default.createElement(Text, null, "Search: "), /* @__PURE__ */ import_react26.default.createElement(build_default, {
47254
+ }, /* @__PURE__ */ import_react27.default.createElement(Text, null, "Search: "), /* @__PURE__ */ import_react27.default.createElement(build_default, {
47208
47255
  value: searchQuery,
47209
47256
  onChange: setSearchQuery
47210
- })), limitedItems.length > 0 ? /* @__PURE__ */ import_react26.default.createElement(import_react26.default.Fragment, null, /* @__PURE__ */ import_react26.default.createElement(SelectInput_default, {
47257
+ })), limitedItems.length > 0 ? /* @__PURE__ */ import_react27.default.createElement(import_react27.default.Fragment, null, /* @__PURE__ */ import_react27.default.createElement(SelectInput_default, {
47211
47258
  items: limitedItems,
47212
47259
  onSelect: handleSelect
47213
- }), hasMore && searchQuery.trim() === "" && /* @__PURE__ */ import_react26.default.createElement(Box_default, {
47260
+ }), hasMore && searchQuery.trim() === "" && /* @__PURE__ */ import_react27.default.createElement(Box_default, {
47214
47261
  marginTop: 1
47215
- }, /* @__PURE__ */ import_react26.default.createElement(Text, {
47262
+ }, /* @__PURE__ */ import_react27.default.createElement(Text, {
47216
47263
  dimColor: true
47217
- }, "... and ", items.length - MAX_VISIBLE_ITEMS, " more repositories. Type to search."))) : /* @__PURE__ */ import_react26.default.createElement(Text, {
47264
+ }, "... and ", items.length - MAX_VISIBLE_ITEMS, " more repositories. Type to search."))) : /* @__PURE__ */ import_react27.default.createElement(Text, {
47218
47265
  dimColor: true
47219
47266
  }, "No repositories found"));
47220
47267
  };
47221
47268
 
47222
47269
  // src/features/file/FileList.tsx
47223
- var import_react27 = __toESM(require_react(), 1);
47270
+ var import_react29 = __toESM(require_react(), 1);
47271
+
47272
+ // src/features/repository/useSelectedRepository.ts
47273
+ var useSelectedRepository = () => {
47274
+ const selectedRepository = useAtomValue(selectedRepositoryAtom);
47275
+ return selectedRepository;
47276
+ };
47277
+
47278
+ // src/features/file/useFiles.ts
47279
+ var import_react28 = __toESM(require_react(), 1);
47280
+
47281
+ // src/features/file/atoms.ts
47282
+ var filesAtom = atom([]);
47283
+ var selectedFileAtom = atom(null);
47224
47284
 
47225
47285
  // src/features/download/download.ts
47226
47286
  import { promises as fs3 } from "fs";
@@ -47281,19 +47341,101 @@ var downloadAndSaveFile = async (downloadUrl, filePath, repositoryName) => {
47281
47341
  });
47282
47342
  };
47283
47343
 
47344
+ // src/features/file/useFiles.ts
47345
+ var useFiles = () => {
47346
+ const [files, setFiles] = useAtom(filesAtom);
47347
+ const [selectedFile, setSelectedFile] = useAtom(selectedFileAtom);
47348
+ const [filteredFiles, setFilteredFiles] = import_react28.useState([]);
47349
+ const [isLoading, setLoading] = import_react28.useState(false);
47350
+ const [error, setError] = import_react28.useState(null);
47351
+ const [isDownloading, setIsDownloading] = import_react28.useState(false);
47352
+ const [downloadStatus, setDownloadStatus] = import_react28.useState("");
47353
+ const loadFiles = async (repository, authToken) => {
47354
+ if (!repository || !authToken)
47355
+ return;
47356
+ setLoading(true);
47357
+ setError(null);
47358
+ try {
47359
+ const [owner, repo] = repository.fullName.split("/");
47360
+ const items = await fetchAllRepositoryFiles(owner, repo, authToken);
47361
+ setFiles(items);
47362
+ setFilteredFiles(items);
47363
+ } catch (err) {
47364
+ setError(err instanceof Error ? err.message : "Failed to load files");
47365
+ } finally {
47366
+ setLoading(false);
47367
+ }
47368
+ };
47369
+ const selectFile = (file) => {
47370
+ setSelectedFile(file);
47371
+ };
47372
+ const filterFiles = (query) => {
47373
+ if (query.trim() === "") {
47374
+ setFilteredFiles(files);
47375
+ } else {
47376
+ const fuse = new Fuse(files, {
47377
+ keys: ["name", "path"],
47378
+ threshold: 0.3
47379
+ });
47380
+ const results = fuse.search(query);
47381
+ setFilteredFiles(results.map((result) => result.item));
47382
+ }
47383
+ };
47384
+ const downloadFile2 = async (file, repository) => {
47385
+ if (!file.downloadUrl)
47386
+ return;
47387
+ selectFile(file);
47388
+ setIsDownloading(true);
47389
+ setDownloadStatus(`Downloading ${file.name}...`);
47390
+ try {
47391
+ await downloadAndSaveFile(file.downloadUrl, file.path, repository.fullName);
47392
+ setDownloadStatus(`✅ Downloaded: ${file.path}`);
47393
+ setTimeout(() => {
47394
+ setDownloadStatus("");
47395
+ selectFile(null);
47396
+ }, 2000);
47397
+ } catch (err) {
47398
+ setDownloadStatus(`❌ Failed to download: ${err instanceof Error ? err.message : "Unknown error"}`);
47399
+ } finally {
47400
+ setIsDownloading(false);
47401
+ }
47402
+ };
47403
+ import_react28.useEffect(() => {
47404
+ setFilteredFiles(files);
47405
+ }, [files]);
47406
+ return {
47407
+ files,
47408
+ selectedFile,
47409
+ filteredFiles,
47410
+ isLoading,
47411
+ error,
47412
+ isDownloading,
47413
+ downloadStatus,
47414
+ loadFiles,
47415
+ selectFile,
47416
+ filterFiles,
47417
+ downloadFile: downloadFile2
47418
+ };
47419
+ };
47420
+
47284
47421
  // src/features/file/FileList.tsx
47285
47422
  var FileList = () => {
47286
- const [selectedRepository] = useAtom(selectedRepositoryAtom);
47287
- const [files, setFiles] = useAtom(filesAtom);
47423
+ const selectedRepository = useSelectedRepository();
47288
47424
  const [searchQuery, setSearchQuery] = useAtom(searchQueryAtom);
47289
- const [isLoading, setLoading] = useAtom(isLoadingAtom);
47290
- const [error, setError] = useAtom(errorAtom);
47291
- const [, selectFile] = useAtom(selectedFileAtom);
47292
- const [, setView] = useAtom(viewAtom);
47293
- const [authToken] = useAtom(authTokenAtom);
47294
- const [filteredFiles, setFilteredFiles] = import_react27.useState([]);
47295
- const [isDownloading, setIsDownloading] = import_react27.useState(false);
47296
- const [downloadStatus, setDownloadStatus] = import_react27.useState("");
47425
+ const setView = useSetAtom(viewAtom);
47426
+ const { token: authToken } = useAuthToken();
47427
+ const {
47428
+ files,
47429
+ filteredFiles,
47430
+ isLoading,
47431
+ error,
47432
+ isDownloading,
47433
+ downloadStatus,
47434
+ loadFiles,
47435
+ selectFile,
47436
+ filterFiles,
47437
+ downloadFile: downloadFile2
47438
+ } = useFiles();
47297
47439
  use_input_default(async (input, key) => {
47298
47440
  if (key.escape) {
47299
47441
  setView("repositories");
@@ -47301,68 +47443,32 @@ var FileList = () => {
47301
47443
  selectFile(null);
47302
47444
  }
47303
47445
  });
47304
- import_react27.useEffect(() => {
47305
- if (!selectedRepository)
47306
- return;
47307
- const loadFiles = async () => {
47308
- setLoading(true);
47309
- setError(null);
47310
- try {
47311
- const [owner, repo] = selectedRepository.fullName.split("/");
47312
- const items2 = await fetchAllRepositoryFiles(owner, repo, authToken);
47313
- setFiles(items2);
47314
- } catch (err) {
47315
- setError(err instanceof Error ? err.message : "Failed to load files");
47316
- } finally {
47317
- setLoading(false);
47318
- }
47319
- };
47320
- loadFiles();
47321
- }, [selectedRepository, authToken, setFiles, setLoading, setError]);
47322
- import_react27.useEffect(() => {
47323
- if (searchQuery.trim() === "") {
47324
- setFilteredFiles(files);
47325
- } else {
47326
- const fuse = new Fuse(files, {
47327
- keys: ["name", "path"],
47328
- threshold: 0.3
47329
- });
47330
- const results = fuse.search(searchQuery);
47331
- setFilteredFiles(results.map((result) => result.item));
47446
+ import_react29.useEffect(() => {
47447
+ if (selectedRepository) {
47448
+ loadFiles(selectedRepository, authToken);
47332
47449
  }
47333
- }, [searchQuery, files]);
47450
+ }, [selectedRepository, authToken]);
47451
+ import_react29.useEffect(() => {
47452
+ filterFiles(searchQuery);
47453
+ }, [searchQuery]);
47334
47454
  const handleSelect = async (item) => {
47335
47455
  const file = files.find((f) => f.path === item.value);
47336
- if (file && file.downloadUrl) {
47337
- selectFile(file);
47338
- setIsDownloading(true);
47339
- setDownloadStatus(`Downloading ${file.name}...`);
47340
- try {
47341
- await downloadAndSaveFile(file.downloadUrl, file.path, selectedRepository?.fullName || "unknown");
47342
- setDownloadStatus(`✅ Downloaded: ${file.path}`);
47343
- setTimeout(() => {
47344
- setDownloadStatus("");
47345
- selectFile(null);
47346
- }, 2000);
47347
- } catch (err) {
47348
- setDownloadStatus(`❌ Failed to download: ${err instanceof Error ? err.message : "Unknown error"}`);
47349
- } finally {
47350
- setIsDownloading(false);
47351
- }
47456
+ if (file && selectedRepository) {
47457
+ await downloadFile2(file, selectedRepository);
47352
47458
  }
47353
47459
  };
47354
47460
  if (!selectedRepository) {
47355
47461
  return null;
47356
47462
  }
47357
47463
  if (isLoading) {
47358
- return /* @__PURE__ */ import_react27.default.createElement(Box_default, {
47464
+ return /* @__PURE__ */ import_react29.default.createElement(Box_default, {
47359
47465
  flexDirection: "column"
47360
- }, /* @__PURE__ */ import_react27.default.createElement(Text, null, "Loading files..."));
47466
+ }, /* @__PURE__ */ import_react29.default.createElement(Text, null, "Loading files..."));
47361
47467
  }
47362
47468
  if (error) {
47363
- return /* @__PURE__ */ import_react27.default.createElement(Box_default, {
47469
+ return /* @__PURE__ */ import_react29.default.createElement(Box_default, {
47364
47470
  flexDirection: "column"
47365
- }, /* @__PURE__ */ import_react27.default.createElement(Text, {
47471
+ }, /* @__PURE__ */ import_react29.default.createElement(Text, {
47366
47472
  color: "red"
47367
47473
  }, "Error: ", error));
47368
47474
  }
@@ -47373,52 +47479,52 @@ var FileList = () => {
47373
47479
  const MAX_VISIBLE_ITEMS = 10;
47374
47480
  const limitedItems = items.slice(0, MAX_VISIBLE_ITEMS);
47375
47481
  const hasMore = items.length > MAX_VISIBLE_ITEMS;
47376
- return /* @__PURE__ */ import_react27.default.createElement(Box_default, {
47482
+ return /* @__PURE__ */ import_react29.default.createElement(Box_default, {
47377
47483
  flexDirection: "column"
47378
- }, /* @__PURE__ */ import_react27.default.createElement(Box_default, {
47484
+ }, /* @__PURE__ */ import_react29.default.createElement(Box_default, {
47379
47485
  marginBottom: 1
47380
- }, /* @__PURE__ */ import_react27.default.createElement(Text, {
47486
+ }, /* @__PURE__ */ import_react29.default.createElement(Text, {
47381
47487
  bold: true
47382
- }, selectedRepository.fullName)), /* @__PURE__ */ import_react27.default.createElement(Box_default, {
47488
+ }, selectedRepository.fullName)), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
47383
47489
  marginBottom: 1
47384
- }, /* @__PURE__ */ import_react27.default.createElement(Text, null, "Search files: "), /* @__PURE__ */ import_react27.default.createElement(build_default, {
47490
+ }, /* @__PURE__ */ import_react29.default.createElement(Text, null, "Search files: "), /* @__PURE__ */ import_react29.default.createElement(build_default, {
47385
47491
  value: searchQuery,
47386
47492
  onChange: setSearchQuery
47387
- })), limitedItems.length > 0 ? /* @__PURE__ */ import_react27.default.createElement(import_react27.default.Fragment, null, /* @__PURE__ */ import_react27.default.createElement(SelectInput_default, {
47493
+ })), limitedItems.length > 0 ? /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, /* @__PURE__ */ import_react29.default.createElement(SelectInput_default, {
47388
47494
  items: limitedItems,
47389
47495
  onSelect: handleSelect
47390
- }), hasMore && searchQuery.trim() === "" && /* @__PURE__ */ import_react27.default.createElement(Box_default, {
47496
+ }), hasMore && searchQuery.trim() === "" && /* @__PURE__ */ import_react29.default.createElement(Box_default, {
47391
47497
  marginTop: 1
47392
- }, /* @__PURE__ */ import_react27.default.createElement(Text, {
47498
+ }, /* @__PURE__ */ import_react29.default.createElement(Text, {
47393
47499
  dimColor: true
47394
- }, "... and ", items.length - MAX_VISIBLE_ITEMS, " more files. Type to search."))) : /* @__PURE__ */ import_react27.default.createElement(Text, {
47500
+ }, "... and ", items.length - MAX_VISIBLE_ITEMS, " more files. Type to search."))) : /* @__PURE__ */ import_react29.default.createElement(Text, {
47395
47501
  dimColor: true
47396
- }, "No files found"), downloadStatus && /* @__PURE__ */ import_react27.default.createElement(Box_default, {
47502
+ }, "No files found"), downloadStatus && /* @__PURE__ */ import_react29.default.createElement(Box_default, {
47397
47503
  marginTop: 1
47398
- }, /* @__PURE__ */ import_react27.default.createElement(Text, null, downloadStatus)), isDownloading && /* @__PURE__ */ import_react27.default.createElement(Box_default, {
47504
+ }, /* @__PURE__ */ import_react29.default.createElement(Text, null, downloadStatus)), isDownloading && /* @__PURE__ */ import_react29.default.createElement(Box_default, {
47399
47505
  marginTop: 1
47400
- }, /* @__PURE__ */ import_react27.default.createElement(Text, {
47506
+ }, /* @__PURE__ */ import_react29.default.createElement(Text, {
47401
47507
  color: "yellow"
47402
47508
  }, "Downloading...")));
47403
47509
  };
47404
47510
 
47405
47511
  // src/features/history/HistoryList.tsx
47406
- var import_react28 = __toESM(require_react(), 1);
47512
+ var import_react30 = __toESM(require_react(), 1);
47407
47513
  var HistoryList = () => {
47408
- const [, setView] = useAtom(viewAtom);
47514
+ const setView = useSetAtom(viewAtom);
47409
47515
  const [searchQuery, setSearchQuery] = useAtom(searchQueryAtom);
47410
- const [history, setHistory] = import_react28.useState([]);
47411
- const [filteredHistory, setFilteredHistory] = import_react28.useState([]);
47412
- const [isLoading, setIsLoading] = import_react28.useState(false);
47413
- const [isDownloading, setIsDownloading] = import_react28.useState(false);
47414
- const [downloadStatus, setDownloadStatus] = import_react28.useState("");
47516
+ const [history, setHistory] = import_react30.useState([]);
47517
+ const [filteredHistory, setFilteredHistory] = import_react30.useState([]);
47518
+ const [isLoading, setIsLoading] = import_react30.useState(false);
47519
+ const [isDownloading, setIsDownloading] = import_react30.useState(false);
47520
+ const [downloadStatus, setDownloadStatus] = import_react30.useState("");
47415
47521
  use_input_default((input, key) => {
47416
47522
  if (key.tab) {
47417
47523
  setView("repositories");
47418
47524
  setSearchQuery("");
47419
47525
  }
47420
47526
  });
47421
- import_react28.useEffect(() => {
47527
+ import_react30.useEffect(() => {
47422
47528
  const loadHistoryData = async () => {
47423
47529
  setIsLoading(true);
47424
47530
  try {
@@ -47432,7 +47538,7 @@ var HistoryList = () => {
47432
47538
  };
47433
47539
  loadHistoryData();
47434
47540
  }, []);
47435
- import_react28.useEffect(() => {
47541
+ import_react30.useEffect(() => {
47436
47542
  if (searchQuery.trim() === "") {
47437
47543
  setFilteredHistory(history);
47438
47544
  } else {
@@ -47465,9 +47571,9 @@ var HistoryList = () => {
47465
47571
  }
47466
47572
  };
47467
47573
  if (isLoading) {
47468
- return /* @__PURE__ */ import_react28.default.createElement(Box_default, {
47574
+ return /* @__PURE__ */ import_react30.default.createElement(Box_default, {
47469
47575
  flexDirection: "column"
47470
- }, /* @__PURE__ */ import_react28.default.createElement(Text, null, "Loading history..."));
47576
+ }, /* @__PURE__ */ import_react30.default.createElement(Text, null, "Loading history..."));
47471
47577
  }
47472
47578
  const items = filteredHistory.map((item) => ({
47473
47579
  label: `\uD83D\uDCC4 ${item.repositoryName}/${item.filePath}`,
@@ -47476,46 +47582,46 @@ var HistoryList = () => {
47476
47582
  const MAX_VISIBLE_ITEMS = 10;
47477
47583
  const limitedItems = items.slice(0, MAX_VISIBLE_ITEMS);
47478
47584
  const hasMore = items.length > MAX_VISIBLE_ITEMS;
47479
- return /* @__PURE__ */ import_react28.default.createElement(Box_default, {
47585
+ return /* @__PURE__ */ import_react30.default.createElement(Box_default, {
47480
47586
  flexDirection: "column"
47481
- }, /* @__PURE__ */ import_react28.default.createElement(Box_default, {
47587
+ }, /* @__PURE__ */ import_react30.default.createElement(Box_default, {
47482
47588
  marginBottom: 1,
47483
47589
  flexDirection: "row",
47484
47590
  justifyContent: "space-between"
47485
- }, /* @__PURE__ */ import_react28.default.createElement(Box_default, null, /* @__PURE__ */ import_react28.default.createElement(Text, {
47591
+ }, /* @__PURE__ */ import_react30.default.createElement(Box_default, null, /* @__PURE__ */ import_react30.default.createElement(Text, {
47486
47592
  bold: true,
47487
47593
  color: "cyan"
47488
- }, "\uD83D\uDCCB Download History")), /* @__PURE__ */ import_react28.default.createElement(Box_default, null, /* @__PURE__ */ import_react28.default.createElement(Text, {
47594
+ }, "\uD83D\uDCCB Download History")), /* @__PURE__ */ import_react30.default.createElement(Box_default, null, /* @__PURE__ */ import_react30.default.createElement(Text, {
47489
47595
  dimColor: true
47490
- }, "[Tab] "), /* @__PURE__ */ import_react28.default.createElement(Text, null, "\uD83D\uDCC1 Browse Files"))), /* @__PURE__ */ import_react28.default.createElement(Box_default, {
47596
+ }, "[Tab] "), /* @__PURE__ */ import_react30.default.createElement(Text, null, "\uD83D\uDCC1 Browse Files"))), /* @__PURE__ */ import_react30.default.createElement(Box_default, {
47491
47597
  marginBottom: 1
47492
- }, /* @__PURE__ */ import_react28.default.createElement(Text, null, "Search: "), /* @__PURE__ */ import_react28.default.createElement(build_default, {
47598
+ }, /* @__PURE__ */ import_react30.default.createElement(Text, null, "Search: "), /* @__PURE__ */ import_react30.default.createElement(build_default, {
47493
47599
  value: searchQuery,
47494
47600
  onChange: setSearchQuery
47495
- })), limitedItems.length > 0 ? /* @__PURE__ */ import_react28.default.createElement(import_react28.default.Fragment, null, /* @__PURE__ */ import_react28.default.createElement(SelectInput_default, {
47601
+ })), limitedItems.length > 0 ? /* @__PURE__ */ import_react30.default.createElement(import_react30.default.Fragment, null, /* @__PURE__ */ import_react30.default.createElement(SelectInput_default, {
47496
47602
  items: limitedItems,
47497
47603
  onSelect: handleSelect
47498
- }), hasMore && searchQuery.trim() === "" && /* @__PURE__ */ import_react28.default.createElement(Box_default, {
47604
+ }), hasMore && searchQuery.trim() === "" && /* @__PURE__ */ import_react30.default.createElement(Box_default, {
47499
47605
  marginTop: 1
47500
- }, /* @__PURE__ */ import_react28.default.createElement(Text, {
47606
+ }, /* @__PURE__ */ import_react30.default.createElement(Text, {
47501
47607
  dimColor: true
47502
- }, "... and ", items.length - MAX_VISIBLE_ITEMS, " more files. Type to search."))) : /* @__PURE__ */ import_react28.default.createElement(Box_default, {
47608
+ }, "... and ", items.length - MAX_VISIBLE_ITEMS, " more files. Type to search."))) : /* @__PURE__ */ import_react30.default.createElement(Box_default, {
47503
47609
  flexDirection: "column"
47504
- }, /* @__PURE__ */ import_react28.default.createElement(Text, {
47610
+ }, /* @__PURE__ */ import_react30.default.createElement(Text, {
47505
47611
  dimColor: true
47506
- }, "No download history found"), /* @__PURE__ */ import_react28.default.createElement(Text, {
47612
+ }, "No download history found"), /* @__PURE__ */ import_react30.default.createElement(Text, {
47507
47613
  dimColor: true
47508
- }, "Download some files to see them here!")), downloadStatus && /* @__PURE__ */ import_react28.default.createElement(Box_default, {
47614
+ }, "Download some files to see them here!")), downloadStatus && /* @__PURE__ */ import_react30.default.createElement(Box_default, {
47509
47615
  marginTop: 1
47510
- }, /* @__PURE__ */ import_react28.default.createElement(Text, null, downloadStatus)), isDownloading && /* @__PURE__ */ import_react28.default.createElement(Box_default, {
47616
+ }, /* @__PURE__ */ import_react30.default.createElement(Text, null, downloadStatus)), isDownloading && /* @__PURE__ */ import_react30.default.createElement(Box_default, {
47511
47617
  marginTop: 1
47512
- }, /* @__PURE__ */ import_react28.default.createElement(Text, {
47618
+ }, /* @__PURE__ */ import_react30.default.createElement(Text, {
47513
47619
  color: "yellow"
47514
47620
  }, "Downloading...")));
47515
47621
  };
47516
47622
 
47517
47623
  // src/features/auth/AuthScreen.tsx
47518
- var import_react29 = __toESM(require_react(), 1);
47624
+ var import_react31 = __toESM(require_react(), 1);
47519
47625
  import { spawn } from "child_process";
47520
47626
 
47521
47627
  // node_modules/@octokit/endpoint/dist-bundle/index.js
@@ -48368,18 +48474,15 @@ var getStoredAuth = async () => {
48368
48474
  };
48369
48475
 
48370
48476
  // src/features/auth/AuthScreen.tsx
48371
- var AuthScreen = ({
48372
- onSuccess,
48373
- onError
48374
- }) => {
48375
- const [status, setStatus] = import_react29.useState("Initializing authentication...");
48376
- const [verificationData, setVerificationData] = import_react29.useState(null);
48477
+ var AuthScreen = ({ onSuccess, onError }) => {
48478
+ const [status, setStatus] = import_react31.useState("Initializing authentication...");
48479
+ const [verificationData, setVerificationData] = import_react31.useState(null);
48377
48480
  const openBrowser = (url) => {
48378
48481
  const platform2 = process.platform;
48379
48482
  const command = platform2 === "darwin" ? "open" : platform2 === "win32" ? "start" : "xdg-open";
48380
48483
  spawn(command, [url], { detached: true, stdio: "ignore" });
48381
48484
  };
48382
- import_react29.useEffect(() => {
48485
+ import_react31.useEffect(() => {
48383
48486
  const authenticate = async () => {
48384
48487
  try {
48385
48488
  setStatus("Starting authentication...");
@@ -48397,39 +48500,38 @@ var AuthScreen = ({
48397
48500
  };
48398
48501
  authenticate();
48399
48502
  }, [onSuccess, onError]);
48400
- return /* @__PURE__ */ import_react29.default.createElement(Box_default, {
48503
+ return /* @__PURE__ */ import_react31.default.createElement(Box_default, {
48401
48504
  flexDirection: "column",
48402
48505
  padding: 1
48403
- }, /* @__PURE__ */ import_react29.default.createElement(Box_default, {
48506
+ }, /* @__PURE__ */ import_react31.default.createElement(Box_default, {
48404
48507
  marginBottom: 1
48405
- }, /* @__PURE__ */ import_react29.default.createElement(Text, {
48508
+ }, /* @__PURE__ */ import_react31.default.createElement(Text, {
48406
48509
  bold: true,
48407
48510
  color: "cyan"
48408
- }, "GitHub Authentication Required")), /* @__PURE__ */ import_react29.default.createElement(Box_default, {
48511
+ }, "GitHub Authentication Required")), /* @__PURE__ */ import_react31.default.createElement(Box_default, {
48409
48512
  marginBottom: 1
48410
- }, /* @__PURE__ */ import_react29.default.createElement(Text, null, status)), verificationData && /* @__PURE__ */ import_react29.default.createElement(Box_default, {
48513
+ }, /* @__PURE__ */ import_react31.default.createElement(Text, null, status)), verificationData && /* @__PURE__ */ import_react31.default.createElement(Box_default, {
48411
48514
  flexDirection: "column",
48412
48515
  marginBottom: 1
48413
- }, /* @__PURE__ */ import_react29.default.createElement(Text, null, "Visit:", " ", /* @__PURE__ */ import_react29.default.createElement(Text, {
48516
+ }, /* @__PURE__ */ import_react31.default.createElement(Text, null, "Visit:", " ", /* @__PURE__ */ import_react31.default.createElement(Text, {
48414
48517
  bold: true,
48415
48518
  color: "blue"
48416
- }, verificationData.verificationUri)), /* @__PURE__ */ import_react29.default.createElement(Text, null, "Code:", " ", /* @__PURE__ */ import_react29.default.createElement(Text, {
48519
+ }, verificationData.verificationUri)), /* @__PURE__ */ import_react31.default.createElement(Text, null, "Code:", " ", /* @__PURE__ */ import_react31.default.createElement(Text, {
48417
48520
  bold: true,
48418
48521
  color: "yellow"
48419
- }, verificationData.userCode))), /* @__PURE__ */ import_react29.default.createElement(Box_default, null, /* @__PURE__ */ import_react29.default.createElement(Text, {
48522
+ }, verificationData.userCode))), /* @__PURE__ */ import_react31.default.createElement(Box_default, null, /* @__PURE__ */ import_react31.default.createElement(Text, {
48420
48523
  dimColor: true
48421
48524
  }, "This is a one-time setup. Credentials will be saved securely.")));
48422
48525
  };
48423
48526
 
48424
- // src/features/app/App.tsx
48425
- var App2 = () => {
48426
- const [view] = useAtom(viewAtom);
48527
+ // src/features/auth/useAuth.ts
48528
+ var import_react32 = __toESM(require_react(), 1);
48529
+ var useAuth = () => {
48427
48530
  const [isAuthenticated, setIsAuthenticated] = useAtom(isAuthenticatedAtom);
48428
- const [, setUsername] = useAtom(usernameAtom);
48429
- const [, setAuthToken] = useAtom(authTokenAtom);
48430
- const [authError, setAuthError] = import_react30.useState("");
48431
- const [isInitializing, setIsInitializing] = import_react30.useState(true);
48432
- import_react30.useEffect(() => {
48531
+ const setUsername = useSetAtom(usernameAtom);
48532
+ const setAuthToken = useSetAtom(authTokenAtom);
48533
+ const [isInitializing, setIsInitializing] = import_react32.useState(true);
48534
+ import_react32.useEffect(() => {
48433
48535
  const checkAuth = async () => {
48434
48536
  const authResult = await getStoredAuth();
48435
48537
  if (authResult) {
@@ -48446,6 +48548,18 @@ var App2 = () => {
48446
48548
  setUsername(username);
48447
48549
  setAuthToken(token);
48448
48550
  };
48551
+ return {
48552
+ isAuthenticated,
48553
+ isInitializing,
48554
+ handleAuthSuccess
48555
+ };
48556
+ };
48557
+
48558
+ // src/App.tsx
48559
+ var App2 = () => {
48560
+ const view = useAtomValue(viewAtom);
48561
+ const { isAuthenticated, isInitializing, handleAuthSuccess } = useAuth();
48562
+ const [authError, setAuthError] = import_react33.useState("");
48449
48563
  const handleAuthError = (error) => {
48450
48564
  setAuthError(error);
48451
48565
  };
@@ -48455,56 +48569,56 @@ var App2 = () => {
48455
48569
  }
48456
48570
  });
48457
48571
  if (isInitializing) {
48458
- return /* @__PURE__ */ import_react30.default.createElement(Box_default, {
48572
+ return /* @__PURE__ */ import_react33.default.createElement(Box_default, {
48459
48573
  flexDirection: "column",
48460
48574
  padding: 1
48461
- }, /* @__PURE__ */ import_react30.default.createElement(Text, null, "Loading..."));
48575
+ }, /* @__PURE__ */ import_react33.default.createElement(Text, null, "Loading..."));
48462
48576
  }
48463
48577
  if (!isAuthenticated) {
48464
- return /* @__PURE__ */ import_react30.default.createElement(Box_default, {
48578
+ return /* @__PURE__ */ import_react33.default.createElement(Box_default, {
48465
48579
  flexDirection: "column",
48466
48580
  padding: 1
48467
- }, /* @__PURE__ */ import_react30.default.createElement(AuthScreen, {
48581
+ }, /* @__PURE__ */ import_react33.default.createElement(AuthScreen, {
48468
48582
  onSuccess: handleAuthSuccess,
48469
48583
  onError: handleAuthError
48470
- }), authError && /* @__PURE__ */ import_react30.default.createElement(Box_default, {
48584
+ }), authError && /* @__PURE__ */ import_react33.default.createElement(Box_default, {
48471
48585
  marginTop: 1
48472
- }, /* @__PURE__ */ import_react30.default.createElement(Text, {
48586
+ }, /* @__PURE__ */ import_react33.default.createElement(Text, {
48473
48587
  color: "red"
48474
48588
  }, "Error: ", authError)));
48475
48589
  }
48476
- return /* @__PURE__ */ import_react30.default.createElement(Box_default, {
48590
+ return /* @__PURE__ */ import_react33.default.createElement(Box_default, {
48477
48591
  flexDirection: "column",
48478
48592
  padding: 1
48479
- }, /* @__PURE__ */ import_react30.default.createElement(Box_default, {
48593
+ }, /* @__PURE__ */ import_react33.default.createElement(Box_default, {
48480
48594
  marginBottom: 1,
48481
48595
  flexDirection: "row",
48482
48596
  justifyContent: "space-between",
48483
48597
  alignItems: "center"
48484
- }, /* @__PURE__ */ import_react30.default.createElement(Text, {
48598
+ }, /* @__PURE__ */ import_react33.default.createElement(Text, {
48485
48599
  bold: true,
48486
48600
  color: "cyan"
48487
- }, "GitHub File Fetcher TUI"), /* @__PURE__ */ import_react30.default.createElement(Box_default, {
48601
+ }, "GitHub File Fetcher TUI"), /* @__PURE__ */ import_react33.default.createElement(Box_default, {
48488
48602
  flexDirection: "row"
48489
- }, /* @__PURE__ */ import_react30.default.createElement(Text, {
48603
+ }, /* @__PURE__ */ import_react33.default.createElement(Text, {
48490
48604
  color: view === "history" ? "cyan" : "gray"
48491
- }, "\uD83D\uDCCB History"), /* @__PURE__ */ import_react30.default.createElement(Text, {
48605
+ }, "\uD83D\uDCCB History"), /* @__PURE__ */ import_react33.default.createElement(Text, {
48492
48606
  dimColor: true
48493
- }, " | "), /* @__PURE__ */ import_react30.default.createElement(Text, {
48607
+ }, " | "), /* @__PURE__ */ import_react33.default.createElement(Text, {
48494
48608
  color: view === "repositories" || view === "files" ? "cyan" : "gray"
48495
- }, "\uD83D\uDCC1 Browse"))), /* @__PURE__ */ import_react30.default.createElement(Box_default, {
48609
+ }, "\uD83D\uDCC1 Browse"))), /* @__PURE__ */ import_react33.default.createElement(Box_default, {
48496
48610
  borderStyle: "single",
48497
48611
  flexDirection: "column",
48498
48612
  padding: 1
48499
- }, view === "history" && /* @__PURE__ */ import_react30.default.createElement(HistoryList, null), view === "repositories" && /* @__PURE__ */ import_react30.default.createElement(RepositoryList, null), view === "files" && /* @__PURE__ */ import_react30.default.createElement(FileList, null)), /* @__PURE__ */ import_react30.default.createElement(Box_default, {
48613
+ }, view === "history" && /* @__PURE__ */ import_react33.default.createElement(HistoryList, null), view === "repositories" && /* @__PURE__ */ import_react33.default.createElement(RepositoryList, null), view === "files" && /* @__PURE__ */ import_react33.default.createElement(FileList, null)), /* @__PURE__ */ import_react33.default.createElement(Box_default, {
48500
48614
  marginTop: 1
48501
- }, /* @__PURE__ */ import_react30.default.createElement(Text, {
48615
+ }, /* @__PURE__ */ import_react33.default.createElement(Text, {
48502
48616
  dimColor: true
48503
48617
  }, "[↑/↓] Navigate [Enter] ", view === "files" ? "Download" : "Select", " ", view !== "files" ? "[Tab] Switch" : "", " [Esc]", " ", view === "files" ? "Back" : "Exit")));
48504
48618
  };
48505
48619
 
48506
48620
  // src/index.tsx
48507
- var app = render_default(/* @__PURE__ */ import_react31.default.createElement(Provider, null, /* @__PURE__ */ import_react31.default.createElement(App2, null)));
48621
+ var app = render_default(/* @__PURE__ */ import_react34.default.createElement(Provider, null, /* @__PURE__ */ import_react34.default.createElement(App2, null)));
48508
48622
  process.on("SIGINT", () => {
48509
48623
  app.unmount();
48510
48624
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "copit",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "GitHub File Fetcher TUI - Download files from GitHub repositories with OAuth authentication, fuzzy search, and download history",
5
5
  "module": "src/index.tsx",
6
6
  "type": "module",