stow-cli 1.0.2 → 1.0.3

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 (56) hide show
  1. package/dist/{app-2A2CFVBC.js → app-JPUDM4LJ.js} +1 -1
  2. package/dist/backfill-QZTVNJZM.js +61 -0
  3. package/dist/buckets-JQKZ3PH3.js +63 -0
  4. package/dist/{chunk-ZDVARBCV.js → chunk-ELSDWMEB.js} +8 -2
  5. package/dist/chunk-WAQSCS57.js +38 -0
  6. package/dist/cli.js +508 -15
  7. package/dist/drops-QXGTYB5E.js +39 -0
  8. package/dist/files-OCYICIAM.js +165 -0
  9. package/dist/health-YMZTZUFX.js +52 -0
  10. package/dist/jobs-6JY4SVV5.js +92 -0
  11. package/dist/jobs-OKKGKSIH.js +83 -0
  12. package/dist/maintenance-EZUHQK5Q.js +86 -0
  13. package/dist/profiles-GWLBEEIT.js +50 -0
  14. package/dist/queues-EQX7EJZF.js +51 -0
  15. package/dist/search-2PXM6XQ6.js +108 -0
  16. package/dist/tags-LSVEIIUF.js +46 -0
  17. package/dist/{upload-MI6VFGTC.js → upload-ESYKBYHA.js} +3 -3
  18. package/dist/{whoami-754O5IML.js → whoami-4UYMSRVP.js} +1 -1
  19. package/package.json +1 -1
  20. package/dist/app-2H4TLSN7.js +0 -239
  21. package/dist/app-HYCPA7GA.js +0 -239
  22. package/dist/app-IZGSPZPX.js +0 -239
  23. package/dist/app-UGUM75MC.js +0 -239
  24. package/dist/app-XPOEAZJC.js +0 -239
  25. package/dist/app-YITP5APT.js +0 -233
  26. package/dist/chunk-2AORPTQB.js +0 -23
  27. package/dist/chunk-FEMMZ4YZ.js +0 -125
  28. package/dist/chunk-LYCXXF2T.js +0 -79
  29. package/dist/chunk-MHRMBH4Y.js +0 -36
  30. package/dist/chunk-R5CCBTXZ.js +0 -79
  31. package/dist/chunk-YRHPOFJT.js +0 -115
  32. package/dist/cli.d.ts +0 -1
  33. package/dist/delete-AECEJX5W.js +0 -18
  34. package/dist/delete-KYOZEODD.js +0 -18
  35. package/dist/delete-OLOAJRRO.js +0 -18
  36. package/dist/delete-V4EY4UBG.js +0 -18
  37. package/dist/list-7A3VZA2T.js +0 -97
  38. package/dist/list-DHXVIMRI.js +0 -94
  39. package/dist/list-DJEAKEZJ.js +0 -106
  40. package/dist/list-DQRU6QHO.js +0 -106
  41. package/dist/list-I5A6LTHX.js +0 -106
  42. package/dist/list-KEQPJY7I.js +0 -109
  43. package/dist/list-Z3MPT6MI.js +0 -109
  44. package/dist/open-2YNHG3MA.js +0 -15
  45. package/dist/upload-3NS5O3UL.js +0 -120
  46. package/dist/upload-B2PGW3AN.js +0 -125
  47. package/dist/upload-DQDBDIDI.js +0 -92
  48. package/dist/upload-FGNGNPC3.js +0 -93
  49. package/dist/upload-HKUPWTK2.js +0 -173
  50. package/dist/upload-JDVSJVWK.js +0 -173
  51. package/dist/upload-K4H7ZVRW.js +0 -98
  52. package/dist/whoami-2SLCNVKP.js +0 -27
  53. package/dist/whoami-DOMX3Z5K.js +0 -28
  54. package/dist/whoami-IPMCUEUH.js +0 -27
  55. package/dist/whoami-JSQA2IDN.js +0 -27
  56. package/dist/whoami-RKH5HHPR.js +0 -27
@@ -0,0 +1,46 @@
1
+ import {
2
+ formatTable,
3
+ outputJson
4
+ } from "./chunk-ELSDWMEB.js";
5
+ import {
6
+ createStow
7
+ } from "./chunk-JYOMHKFS.js";
8
+ import "./chunk-OZ7QQTIZ.js";
9
+
10
+ // src/commands/tags.ts
11
+ async function listTags(options) {
12
+ const stow = createStow();
13
+ const data = await stow.tags.list();
14
+ if (options.json) {
15
+ outputJson(data);
16
+ return;
17
+ }
18
+ if (data.tags.length === 0) {
19
+ console.log("No tags yet. Create one with: stow tags create <name>");
20
+ return;
21
+ }
22
+ const rows = data.tags.map((t) => [t.name, t.slug, t.color ?? "\u2014", t.id]);
23
+ console.log(formatTable(["Name", "Slug", "Color", "ID"], rows));
24
+ }
25
+ async function createTag(name, options) {
26
+ const stow = createStow();
27
+ const tag = await stow.tags.create({
28
+ name,
29
+ ...options.color ? { color: options.color } : {}
30
+ });
31
+ if (options.json) {
32
+ outputJson(tag);
33
+ return;
34
+ }
35
+ console.log(`Created tag: ${tag.name}`);
36
+ }
37
+ async function deleteTag(id) {
38
+ const stow = createStow();
39
+ await stow.tags.delete(id);
40
+ console.log(`Deleted tag: ${id}`);
41
+ }
42
+ export {
43
+ createTag,
44
+ deleteTag,
45
+ listTags
46
+ };
@@ -1,9 +1,9 @@
1
+ import {
2
+ formatBytes
3
+ } from "./chunk-ELSDWMEB.js";
1
4
  import {
2
5
  createStow
3
6
  } from "./chunk-JYOMHKFS.js";
4
- import {
5
- formatBytes
6
- } from "./chunk-ZDVARBCV.js";
7
7
  import "./chunk-OZ7QQTIZ.js";
8
8
 
9
9
  // src/commands/upload.ts
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  formatBytes
3
- } from "./chunk-ZDVARBCV.js";
3
+ } from "./chunk-ELSDWMEB.js";
4
4
  import {
5
5
  createStow
6
6
  } from "./chunk-JYOMHKFS.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stow-cli",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
5
  "description": "CLI for Stow file storage",
6
6
  "license": "MIT",
@@ -1,239 +0,0 @@
1
- import {
2
- formatBytes
3
- } from "./chunk-ZDVARBCV.js";
4
- import {
5
- apiRequest
6
- } from "./chunk-FEMMZ4YZ.js";
7
- import "./chunk-2AORPTQB.js";
8
-
9
- // src/interactive/app.tsx
10
- import { render } from "ink";
11
- import { useEffect as useEffect3, useState as useState3 } from "react";
12
-
13
- // src/interactive/components/bucket-list.tsx
14
- import { Box as Box3, Text as Text3, useApp, useInput } from "ink";
15
- import Spinner from "ink-spinner";
16
- import { useEffect, useState } from "react";
17
-
18
- // src/interactive/components/header.tsx
19
- import { Box, Text } from "ink";
20
- import { jsx, jsxs } from "react/jsx-runtime";
21
- function Header({ path, email, size }) {
22
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
23
- /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", width: "100%", children: [
24
- /* @__PURE__ */ jsxs(Text, { bold: true, children: [
25
- "STOW",
26
- path ? ` > ${path}` : ""
27
- ] }),
28
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: size || email || "" })
29
- ] }),
30
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(50) })
31
- ] });
32
- }
33
-
34
- // src/interactive/components/status-bar.tsx
35
- import { Box as Box2, Text as Text2 } from "ink";
36
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
37
- function StatusBar({ hints }) {
38
- return /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: hints.map((h, i) => /* @__PURE__ */ jsx2(Box2, { marginRight: 2, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
39
- "[",
40
- h.key,
41
- "]",
42
- h.label,
43
- i < hints.length - 1 ? "" : ""
44
- ] }) }, h.key)) });
45
- }
46
-
47
- // src/interactive/components/bucket-list.tsx
48
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
49
- function BucketList({ onSelect, email }) {
50
- const { exit } = useApp();
51
- const [buckets, setBuckets] = useState([]);
52
- const [loading, setLoading] = useState(true);
53
- const [error, setError] = useState(null);
54
- const [cursor, setCursor] = useState(0);
55
- useEffect(() => {
56
- apiRequest("/api/buckets").then((data) => {
57
- setBuckets(data.buckets);
58
- setLoading(false);
59
- }).catch((err) => {
60
- setError(err instanceof Error ? err.message : String(err));
61
- setLoading(false);
62
- });
63
- }, []);
64
- useInput((input, key) => {
65
- if (input === "q") {
66
- exit();
67
- return;
68
- }
69
- if (key.upArrow) {
70
- setCursor((c) => Math.max(0, c - 1));
71
- } else if (key.downArrow) {
72
- setCursor((c) => Math.min(buckets.length - 1, c + 1));
73
- } else if (key.return && buckets.length > 0) {
74
- onSelect(buckets[cursor]);
75
- }
76
- });
77
- if (loading) {
78
- return /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { children: [
79
- /* @__PURE__ */ jsx3(Spinner, { type: "dots" }),
80
- " Loading buckets..."
81
- ] }) });
82
- }
83
- if (error) {
84
- return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: /* @__PURE__ */ jsxs3(Text3, { color: "red", children: [
85
- "Error: ",
86
- error
87
- ] }) });
88
- }
89
- return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
90
- /* @__PURE__ */ jsx3(Header, { email }),
91
- buckets.length === 0 ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "No buckets yet. Press 'n' to create one." }) : buckets.map((b, i) => /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { color: i === cursor ? "cyan" : void 0, children: [
92
- i === cursor ? "\u25B8 " : " ",
93
- b.name.padEnd(20),
94
- `${b.fileCount} files`.padEnd(14),
95
- formatBytes(b.usageBytes)
96
- ] }) }, b.id)),
97
- /* @__PURE__ */ jsx3(
98
- StatusBar,
99
- {
100
- hints: [
101
- { key: "\u2191\u2193", label: "navigate" },
102
- { key: "\u21B5", label: "open" },
103
- { key: "q", label: "uit" }
104
- ]
105
- }
106
- )
107
- ] });
108
- }
109
-
110
- // src/interactive/components/file-list.tsx
111
- import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
112
- import Spinner2 from "ink-spinner";
113
- import { useEffect as useEffect2, useState as useState2 } from "react";
114
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
115
- function FileList({ bucket, onBack }) {
116
- const [files, setFiles] = useState2([]);
117
- const [loading, setLoading] = useState2(true);
118
- const [error, setError] = useState2(null);
119
- const [cursor, setCursor] = useState2(0);
120
- const [nextCursor, setNextCursor] = useState2(null);
121
- const [copied, setCopied] = useState2(false);
122
- useEffect2(() => {
123
- loadFiles();
124
- }, []);
125
- function loadFiles(pageCursor) {
126
- setLoading(true);
127
- const params = new URLSearchParams({ bucket: bucket.name });
128
- if (pageCursor) {
129
- params.set("cursor", pageCursor);
130
- }
131
- apiRequest(
132
- `/api/files?${params}`
133
- ).then((data) => {
134
- setFiles(
135
- (prev) => pageCursor ? [...prev, ...data.files] : data.files
136
- );
137
- setNextCursor(data.nextCursor);
138
- setLoading(false);
139
- }).catch((err) => {
140
- setError(err instanceof Error ? err.message : String(err));
141
- setLoading(false);
142
- });
143
- }
144
- useInput2((input, key) => {
145
- if (key.escape || key.backspace || key.leftArrow && !loading) {
146
- onBack();
147
- return;
148
- }
149
- if (key.upArrow) {
150
- setCursor((c) => Math.max(0, c - 1));
151
- } else if (key.downArrow) {
152
- setCursor((c) => {
153
- const next = Math.min(files.length - 1, c + 1);
154
- if (next >= files.length - 3 && nextCursor && !loading) {
155
- loadFiles(nextCursor);
156
- }
157
- return next;
158
- });
159
- } else if (input === "c" && files.length > 0) {
160
- import("clipboardy").then(({ default: clipboard }) => {
161
- clipboard.write(files[cursor].url).then(() => {
162
- setCopied(true);
163
- setTimeout(() => setCopied(false), 2e3);
164
- });
165
- });
166
- }
167
- });
168
- if (error) {
169
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
170
- /* @__PURE__ */ jsxs4(Text4, { color: "red", children: [
171
- "Error: ",
172
- error
173
- ] }),
174
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Press Esc to go back." })
175
- ] });
176
- }
177
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
178
- /* @__PURE__ */ jsx4(Header, { path: bucket.name, size: formatBytes(bucket.usageBytes) }),
179
- loading && files.length === 0 && /* @__PURE__ */ jsxs4(Text4, { children: [
180
- /* @__PURE__ */ jsx4(Spinner2, { type: "dots" }),
181
- " Loading files..."
182
- ] }),
183
- !loading && files.length === 0 && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "No files in this bucket." }),
184
- files.length > 0 && files.map((f, i) => /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { color: i === cursor ? "cyan" : void 0, children: [
185
- i === cursor ? "\u25B8 " : " ",
186
- f.key.padEnd(28),
187
- formatBytes(f.size).padEnd(10),
188
- f.lastModified.split("T")[0]
189
- ] }) }, f.key)),
190
- loading && files.length > 0 && /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
191
- /* @__PURE__ */ jsx4(Spinner2, { type: "dots" }),
192
- " Loading more..."
193
- ] }),
194
- copied && /* @__PURE__ */ jsx4(Text4, { color: "green", children: "Copied URL to clipboard!" }),
195
- /* @__PURE__ */ jsx4(
196
- StatusBar,
197
- {
198
- hints: [
199
- { key: "\u2191\u2193", label: "navigate" },
200
- { key: "c", label: "opy url" },
201
- { key: "\u2190", label: "back" }
202
- ]
203
- }
204
- )
205
- ] });
206
- }
207
-
208
- // src/interactive/app.tsx
209
- import { jsx as jsx5 } from "react/jsx-runtime";
210
- function App() {
211
- const [screen, setScreen] = useState3({ type: "buckets" });
212
- const [email, setEmail] = useState3();
213
- useEffect3(() => {
214
- apiRequest("/api/whoami").then((data) => setEmail(data.user.email)).catch(() => {
215
- });
216
- }, []);
217
- if (screen.type === "files") {
218
- return /* @__PURE__ */ jsx5(
219
- FileList,
220
- {
221
- bucket: screen.bucket,
222
- onBack: () => setScreen({ type: "buckets" })
223
- }
224
- );
225
- }
226
- return /* @__PURE__ */ jsx5(
227
- BucketList,
228
- {
229
- email,
230
- onSelect: (bucket) => setScreen({ type: "files", bucket })
231
- }
232
- );
233
- }
234
- function startInteractive() {
235
- render(/* @__PURE__ */ jsx5(App, {}));
236
- }
237
- export {
238
- startInteractive
239
- };
@@ -1,239 +0,0 @@
1
- import {
2
- formatBytes
3
- } from "./chunk-ZDVARBCV.js";
4
- import {
5
- apiRequest
6
- } from "./chunk-R5CCBTXZ.js";
7
- import "./chunk-2AORPTQB.js";
8
-
9
- // src/interactive/app.tsx
10
- import { render } from "ink";
11
- import { useEffect as useEffect3, useState as useState3 } from "react";
12
-
13
- // src/interactive/components/bucket-list.tsx
14
- import { Box as Box3, Text as Text3, useApp, useInput } from "ink";
15
- import Spinner from "ink-spinner";
16
- import { useEffect, useState } from "react";
17
-
18
- // src/interactive/components/header.tsx
19
- import { Box, Text } from "ink";
20
- import { jsx, jsxs } from "react/jsx-runtime";
21
- function Header({ path, email, size }) {
22
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
23
- /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", width: "100%", children: [
24
- /* @__PURE__ */ jsxs(Text, { bold: true, children: [
25
- "STOW",
26
- path ? ` > ${path}` : ""
27
- ] }),
28
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: size || email || "" })
29
- ] }),
30
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(50) })
31
- ] });
32
- }
33
-
34
- // src/interactive/components/status-bar.tsx
35
- import { Box as Box2, Text as Text2 } from "ink";
36
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
37
- function StatusBar({ hints }) {
38
- return /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: hints.map((h, i) => /* @__PURE__ */ jsx2(Box2, { marginRight: 2, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
39
- "[",
40
- h.key,
41
- "]",
42
- h.label,
43
- i < hints.length - 1 ? "" : ""
44
- ] }) }, h.key)) });
45
- }
46
-
47
- // src/interactive/components/bucket-list.tsx
48
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
49
- function BucketList({ onSelect, email }) {
50
- const { exit } = useApp();
51
- const [buckets, setBuckets] = useState([]);
52
- const [loading, setLoading] = useState(true);
53
- const [error, setError] = useState(null);
54
- const [cursor, setCursor] = useState(0);
55
- useEffect(() => {
56
- apiRequest("/api/buckets").then((data) => {
57
- setBuckets(data.buckets);
58
- setLoading(false);
59
- }).catch((err) => {
60
- setError(err instanceof Error ? err.message : String(err));
61
- setLoading(false);
62
- });
63
- }, []);
64
- useInput((input, key) => {
65
- if (input === "q") {
66
- exit();
67
- return;
68
- }
69
- if (key.upArrow) {
70
- setCursor((c) => Math.max(0, c - 1));
71
- } else if (key.downArrow) {
72
- setCursor((c) => Math.min(buckets.length - 1, c + 1));
73
- } else if (key.return && buckets.length > 0) {
74
- onSelect(buckets[cursor]);
75
- }
76
- });
77
- if (loading) {
78
- return /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { children: [
79
- /* @__PURE__ */ jsx3(Spinner, { type: "dots" }),
80
- " Loading buckets..."
81
- ] }) });
82
- }
83
- if (error) {
84
- return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: /* @__PURE__ */ jsxs3(Text3, { color: "red", children: [
85
- "Error: ",
86
- error
87
- ] }) });
88
- }
89
- return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
90
- /* @__PURE__ */ jsx3(Header, { email }),
91
- buckets.length === 0 ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "No buckets yet. Press 'n' to create one." }) : buckets.map((b, i) => /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsxs3(Text3, { color: i === cursor ? "cyan" : void 0, children: [
92
- i === cursor ? "\u25B8 " : " ",
93
- b.name.padEnd(20),
94
- `${b.fileCount} files`.padEnd(14),
95
- formatBytes(b.usageBytes)
96
- ] }) }, b.id)),
97
- /* @__PURE__ */ jsx3(
98
- StatusBar,
99
- {
100
- hints: [
101
- { key: "\u2191\u2193", label: "navigate" },
102
- { key: "\u21B5", label: "open" },
103
- { key: "q", label: "uit" }
104
- ]
105
- }
106
- )
107
- ] });
108
- }
109
-
110
- // src/interactive/components/file-list.tsx
111
- import { Box as Box4, Text as Text4, useInput as useInput2 } from "ink";
112
- import Spinner2 from "ink-spinner";
113
- import { useEffect as useEffect2, useState as useState2 } from "react";
114
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
115
- function FileList({ bucket, onBack }) {
116
- const [files, setFiles] = useState2([]);
117
- const [loading, setLoading] = useState2(true);
118
- const [error, setError] = useState2(null);
119
- const [cursor, setCursor] = useState2(0);
120
- const [nextCursor, setNextCursor] = useState2(null);
121
- const [copied, setCopied] = useState2(false);
122
- useEffect2(() => {
123
- loadFiles();
124
- }, []);
125
- function loadFiles(pageCursor) {
126
- setLoading(true);
127
- const params = new URLSearchParams({ bucket: bucket.name });
128
- if (pageCursor) {
129
- params.set("cursor", pageCursor);
130
- }
131
- apiRequest(
132
- `/api/files?${params}`
133
- ).then((data) => {
134
- setFiles(
135
- (prev) => pageCursor ? [...prev, ...data.files] : data.files
136
- );
137
- setNextCursor(data.nextCursor);
138
- setLoading(false);
139
- }).catch((err) => {
140
- setError(err instanceof Error ? err.message : String(err));
141
- setLoading(false);
142
- });
143
- }
144
- useInput2((input, key) => {
145
- if (key.escape || key.backspace || key.leftArrow && !loading) {
146
- onBack();
147
- return;
148
- }
149
- if (key.upArrow) {
150
- setCursor((c) => Math.max(0, c - 1));
151
- } else if (key.downArrow) {
152
- setCursor((c) => {
153
- const next = Math.min(files.length - 1, c + 1);
154
- if (next >= files.length - 3 && nextCursor && !loading) {
155
- loadFiles(nextCursor);
156
- }
157
- return next;
158
- });
159
- } else if (input === "c" && files.length > 0) {
160
- import("clipboardy").then(({ default: clipboard }) => {
161
- clipboard.write(files[cursor].url).then(() => {
162
- setCopied(true);
163
- setTimeout(() => setCopied(false), 2e3);
164
- });
165
- });
166
- }
167
- });
168
- if (error) {
169
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
170
- /* @__PURE__ */ jsxs4(Text4, { color: "red", children: [
171
- "Error: ",
172
- error
173
- ] }),
174
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Press Esc to go back." })
175
- ] });
176
- }
177
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
178
- /* @__PURE__ */ jsx4(Header, { path: bucket.name, size: formatBytes(bucket.usageBytes) }),
179
- loading && files.length === 0 && /* @__PURE__ */ jsxs4(Text4, { children: [
180
- /* @__PURE__ */ jsx4(Spinner2, { type: "dots" }),
181
- " Loading files..."
182
- ] }),
183
- !loading && files.length === 0 && /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "No files in this bucket." }),
184
- files.length > 0 && files.map((f, i) => /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { color: i === cursor ? "cyan" : void 0, children: [
185
- i === cursor ? "\u25B8 " : " ",
186
- f.key.padEnd(28),
187
- formatBytes(f.size).padEnd(10),
188
- f.lastModified.split("T")[0]
189
- ] }) }, f.key)),
190
- loading && files.length > 0 && /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
191
- /* @__PURE__ */ jsx4(Spinner2, { type: "dots" }),
192
- " Loading more..."
193
- ] }),
194
- copied && /* @__PURE__ */ jsx4(Text4, { color: "green", children: "Copied URL to clipboard!" }),
195
- /* @__PURE__ */ jsx4(
196
- StatusBar,
197
- {
198
- hints: [
199
- { key: "\u2191\u2193", label: "navigate" },
200
- { key: "c", label: "opy url" },
201
- { key: "\u2190", label: "back" }
202
- ]
203
- }
204
- )
205
- ] });
206
- }
207
-
208
- // src/interactive/app.tsx
209
- import { jsx as jsx5 } from "react/jsx-runtime";
210
- function App() {
211
- const [screen, setScreen] = useState3({ type: "buckets" });
212
- const [email, setEmail] = useState3();
213
- useEffect3(() => {
214
- apiRequest("/api/whoami").then((data) => setEmail(data.user.email)).catch(() => {
215
- });
216
- }, []);
217
- if (screen.type === "files") {
218
- return /* @__PURE__ */ jsx5(
219
- FileList,
220
- {
221
- bucket: screen.bucket,
222
- onBack: () => setScreen({ type: "buckets" })
223
- }
224
- );
225
- }
226
- return /* @__PURE__ */ jsx5(
227
- BucketList,
228
- {
229
- email,
230
- onSelect: (bucket) => setScreen({ type: "files", bucket })
231
- }
232
- );
233
- }
234
- function startInteractive() {
235
- render(/* @__PURE__ */ jsx5(App, {}));
236
- }
237
- export {
238
- startInteractive
239
- };