@anymux/connect 0.1.0

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 (78) hide show
  1. package/dist/GitBrowser-BLgTNQyd.js +905 -0
  2. package/dist/GitBrowser-BLgTNQyd.js.map +1 -0
  3. package/dist/GitBrowser-CIyWiuX-.js +3 -0
  4. package/dist/ObjectStorageBrowser-B2YkUxMl.js +3 -0
  5. package/dist/ObjectStorageBrowser-B_25Emfu.js +267 -0
  6. package/dist/ObjectStorageBrowser-B_25Emfu.js.map +1 -0
  7. package/dist/RepoPicker-BprFGOn7.js +3 -0
  8. package/dist/RepoPicker-CoHMiJ-3.js +168 -0
  9. package/dist/RepoPicker-CoHMiJ-3.js.map +1 -0
  10. package/dist/index.d.ts +697 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +2539 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/registry.d.ts +2 -0
  15. package/dist/registry.js +3 -0
  16. package/dist/scope-labels-B4VAwoL6.js +582 -0
  17. package/dist/scope-labels-B4VAwoL6.js.map +1 -0
  18. package/dist/scope-labels-DvdJLcSL.d.ts +50 -0
  19. package/dist/scope-labels-DvdJLcSL.d.ts.map +1 -0
  20. package/package.json +87 -0
  21. package/src/adapters/adapter-registry.ts +177 -0
  22. package/src/auth/auth-client.ts +101 -0
  23. package/src/auth/token-manager.ts +27 -0
  24. package/src/components/ActionHistoryPanel.tsx +137 -0
  25. package/src/components/CapabilityCell.tsx +97 -0
  26. package/src/components/CapabilityError.tsx +50 -0
  27. package/src/components/CapabilityPanel.tsx +530 -0
  28. package/src/components/CapabilityPill.tsx +56 -0
  29. package/src/components/ConnectButton.tsx +149 -0
  30. package/src/components/ConnectedMenu.tsx +142 -0
  31. package/src/components/ConnectionStatus.tsx +28 -0
  32. package/src/components/CredentialForm.tsx +246 -0
  33. package/src/components/FullScreenBrowser.tsx +84 -0
  34. package/src/components/GitBrowser.tsx +705 -0
  35. package/src/components/GitHubRepoPicker.tsx +125 -0
  36. package/src/components/ObjectStorageBrowser.tsx +176 -0
  37. package/src/components/RepoPicker.tsx +93 -0
  38. package/src/components/ServiceCard.tsx +77 -0
  39. package/src/components/ServiceCardGrid.tsx +141 -0
  40. package/src/components/ServiceDashboard.tsx +84 -0
  41. package/src/components/ServiceIcon.tsx +37 -0
  42. package/src/components/ServiceRow.tsx +50 -0
  43. package/src/components/useAdapter.ts +33 -0
  44. package/src/demos/ServiceDashboardDemo.tsx +108 -0
  45. package/src/index.ts +68 -0
  46. package/src/models/ActionNotificationModel.ts +72 -0
  47. package/src/models/ConnectionManagerModel.ts +410 -0
  48. package/src/models/CredentialFormModel.ts +111 -0
  49. package/src/models/DashboardModel.ts +157 -0
  50. package/src/models/GitHostBrowserModel.ts +89 -0
  51. package/src/models/GitRepoBrowserModel.ts +285 -0
  52. package/src/models/ObjectStorageBrowserModel.ts +131 -0
  53. package/src/models/RepoPickerModel.ts +132 -0
  54. package/src/registry/service-registry.ts +46 -0
  55. package/src/registry/services/apple.ts +22 -0
  56. package/src/registry/services/bitbucket.ts +24 -0
  57. package/src/registry/services/box.ts +22 -0
  58. package/src/registry/services/browser-fs.ts +19 -0
  59. package/src/registry/services/dropbox.ts +22 -0
  60. package/src/registry/services/flickr.ts +22 -0
  61. package/src/registry/services/gitea.ts +24 -0
  62. package/src/registry/services/github.ts +24 -0
  63. package/src/registry/services/gitlab.ts +24 -0
  64. package/src/registry/services/google.ts +24 -0
  65. package/src/registry/services/icloud.ts +23 -0
  66. package/src/registry/services/indexeddb.ts +19 -0
  67. package/src/registry/services/instagram.ts +22 -0
  68. package/src/registry/services/microsoft.ts +24 -0
  69. package/src/registry/services/s3.ts +21 -0
  70. package/src/registry/services/webdav.ts +21 -0
  71. package/src/registry.ts +4 -0
  72. package/src/types/connection-state.ts +33 -0
  73. package/src/types/connection.ts +11 -0
  74. package/src/types/optional-deps.d.ts +149 -0
  75. package/src/types/service.ts +18 -0
  76. package/src/types/user-profile.ts +21 -0
  77. package/src/utils/action-toast.ts +53 -0
  78. package/src/utils/scope-labels.ts +91 -0
@@ -0,0 +1,905 @@
1
+ import { flow, makeAutoObservable } from "mobx";
2
+ import { toast } from "sonner";
3
+ import React, { Suspense, useCallback, useEffect, useRef, useState } from "react";
4
+ import { observer } from "mobx-react-lite";
5
+ import { AlertCircle, Check, ChevronDown, ChevronLeft, ChevronRight, Diff, FolderTree, GitBranch, GitCommit, GitPullRequest, Loader2, Tag } from "lucide-react";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { BrowserError } from "@anymux/ui/components/browser-error";
8
+
9
+ //#region src/models/GitRepoBrowserModel.ts
10
+ const HOST_PAGE_SIZE = 25;
11
+ const COMMITS_PAGE_SIZE = 25;
12
+ var GitRepoBrowserModel = class {
13
+ constructor(gitRepo, _gitHost, createFileSystem, onError) {
14
+ this.gitRepo = gitRepo;
15
+ this._gitHost = _gitHost;
16
+ this.createFileSystem = createFileSystem;
17
+ this.onError = onError;
18
+ this.branches = [];
19
+ this.tags = [];
20
+ this.currentRef = "";
21
+ this.fileSystem = null;
22
+ this.loading = true;
23
+ this.error = null;
24
+ this.activeTab = "files";
25
+ this.commits = [];
26
+ this.commitsLoading = false;
27
+ this.commitsPage = 0;
28
+ this.hasMoreCommits = false;
29
+ this.selectedCommitSha = void 0;
30
+ this.diffEntries = [];
31
+ this.diffLoading = false;
32
+ this.prs = [];
33
+ this.issues = [];
34
+ this.hostLoading = false;
35
+ this.hostError = null;
36
+ this.prPage = 0;
37
+ this.issuePage = 0;
38
+ this.hasMorePrs = false;
39
+ this.hasMoreIssues = false;
40
+ this.initialize = flow(function* () {
41
+ this.loading = true;
42
+ this.error = null;
43
+ try {
44
+ const [branchList, tagList] = yield Promise.all([this.gitRepo.listBranches(), this.gitRepo.listTags().catch(() => [])]);
45
+ this.branches = branchList;
46
+ this.tags = tagList;
47
+ const defaultBranch = branchList.find((b) => b.isDefault);
48
+ const initialRef = defaultBranch?.name ?? branchList[0]?.name ?? "main";
49
+ this.currentRef = initialRef;
50
+ const fs = yield this.createFileSystem(initialRef);
51
+ this.fileSystem = fs;
52
+ } catch (err) {
53
+ const msg = err instanceof Error ? err.message : "Failed to load repository";
54
+ this.error = msg;
55
+ this.onError?.({ message: msg });
56
+ } finally {
57
+ this.loading = false;
58
+ }
59
+ });
60
+ this.switchRef = flow(function* (ref) {
61
+ if (ref === this.currentRef) return;
62
+ this.currentRef = ref;
63
+ this.loading = true;
64
+ this.error = null;
65
+ this.selectedCommitSha = void 0;
66
+ this.diffEntries = [];
67
+ this.commitsPage = 0;
68
+ try {
69
+ const fs = yield this.createFileSystem(ref);
70
+ this.fileSystem = fs;
71
+ } catch (err) {
72
+ const msg = err instanceof Error ? err.message : "Failed to switch ref";
73
+ this.error = msg;
74
+ this.onError?.({ message: msg });
75
+ } finally {
76
+ this.loading = false;
77
+ }
78
+ if (this.activeTab === "commits") this.loadCommits();
79
+ });
80
+ this.loadCommits = flow(function* () {
81
+ if (!this.currentRef) return;
82
+ this.commitsLoading = true;
83
+ try {
84
+ const fetchCount = COMMITS_PAGE_SIZE * (this.commitsPage + 1);
85
+ const commitList = yield this.gitRepo.listCommits({
86
+ ref: this.currentRef,
87
+ maxCount: fetchCount + 1
88
+ });
89
+ const start = this.commitsPage * COMMITS_PAGE_SIZE;
90
+ this.commits = commitList.slice(start, start + COMMITS_PAGE_SIZE);
91
+ this.hasMoreCommits = commitList.length > start + COMMITS_PAGE_SIZE;
92
+ } catch (err) {
93
+ console.error("[GitRepoBrowserModel] Failed to load commits:", err);
94
+ this.commits = [];
95
+ this.hasMoreCommits = false;
96
+ } finally {
97
+ this.commitsLoading = false;
98
+ }
99
+ });
100
+ this.selectCommit = flow(function* (commit) {
101
+ this.selectedCommitSha = commit.sha;
102
+ if (commit.parents.length === 0) {
103
+ this.diffEntries = [];
104
+ return;
105
+ }
106
+ this.diffLoading = true;
107
+ try {
108
+ const entries = yield this.gitRepo.diff(commit.parents[0], commit.sha);
109
+ this.diffEntries = entries;
110
+ } catch {
111
+ this.diffEntries = [];
112
+ } finally {
113
+ this.diffLoading = false;
114
+ }
115
+ });
116
+ this.loadPRs = flow(function* () {
117
+ if (!this._gitHost) return;
118
+ this.hostLoading = true;
119
+ this.hostError = null;
120
+ try {
121
+ const data = yield this._gitHost.listPullRequests({
122
+ state: "all",
123
+ maxResults: HOST_PAGE_SIZE * (this.prPage + 1)
124
+ });
125
+ const start = this.prPage * HOST_PAGE_SIZE;
126
+ this.prs = data.slice(start, start + HOST_PAGE_SIZE);
127
+ this.hasMorePrs = data.length > start + HOST_PAGE_SIZE;
128
+ } catch (err) {
129
+ this.hostError = err instanceof Error ? err.message : "Failed to load";
130
+ } finally {
131
+ this.hostLoading = false;
132
+ }
133
+ });
134
+ this.loadIssues = flow(function* () {
135
+ if (!this._gitHost) return;
136
+ this.hostLoading = true;
137
+ this.hostError = null;
138
+ try {
139
+ const data = yield this._gitHost.listIssues({
140
+ state: "all",
141
+ maxResults: HOST_PAGE_SIZE * (this.issuePage + 1)
142
+ });
143
+ const start = this.issuePage * HOST_PAGE_SIZE;
144
+ this.issues = data.slice(start, start + HOST_PAGE_SIZE);
145
+ this.hasMoreIssues = data.length > start + HOST_PAGE_SIZE;
146
+ } catch (err) {
147
+ this.hostError = err instanceof Error ? err.message : "Failed to load";
148
+ } finally {
149
+ this.hostLoading = false;
150
+ }
151
+ });
152
+ makeAutoObservable(this, {});
153
+ }
154
+ /** Update the gitHost adapter (e.g. when loaded asynchronously after construction) */
155
+ setGitHost(host) {
156
+ this._gitHost = host;
157
+ }
158
+ get headSha() {
159
+ return this.branches.find((b) => b.name === this.currentRef)?.sha;
160
+ }
161
+ get hasGitHost() {
162
+ return !!this._gitHost;
163
+ }
164
+ setActiveTab(tab) {
165
+ this.activeTab = tab;
166
+ if (tab === "commits" && this.currentRef) this.loadCommits();
167
+ else if (tab === "prs" && this._gitHost) this.loadPRs();
168
+ else if (tab === "issues" && this._gitHost) this.loadIssues();
169
+ }
170
+ nextCommitsPage() {
171
+ this.commitsPage += 1;
172
+ this.loadCommits();
173
+ }
174
+ prevCommitsPage() {
175
+ this.commitsPage = Math.max(0, this.commitsPage - 1);
176
+ this.loadCommits();
177
+ }
178
+ nextPrPage() {
179
+ this.prPage += 1;
180
+ this.loadPRs();
181
+ }
182
+ prevPrPage() {
183
+ this.prPage = Math.max(0, this.prPage - 1);
184
+ this.loadPRs();
185
+ }
186
+ nextIssuePage() {
187
+ this.issuePage += 1;
188
+ this.loadIssues();
189
+ }
190
+ prevIssuePage() {
191
+ this.issuePage = Math.max(0, this.issuePage - 1);
192
+ this.loadIssues();
193
+ }
194
+ retryPRs() {
195
+ this.hostError = null;
196
+ this.prPage = 0;
197
+ this.loadPRs();
198
+ }
199
+ retryIssues() {
200
+ this.hostError = null;
201
+ this.issuePage = 0;
202
+ this.loadIssues();
203
+ }
204
+ selectBranch(branch) {
205
+ this.switchRef(branch.name);
206
+ }
207
+ };
208
+
209
+ //#endregion
210
+ //#region src/models/GitHostBrowserModel.ts
211
+ const PAGE_SIZE = 25;
212
+ var GitHostBrowserModel = class {
213
+ constructor(gitHost) {
214
+ this.activeTab = "prs";
215
+ this.prs = [];
216
+ this.issues = [];
217
+ this.hasMorePrs = false;
218
+ this.hasMoreIssues = false;
219
+ this.page = 0;
220
+ this.loading = false;
221
+ this.error = null;
222
+ this.loadData = flow(function* loadData() {
223
+ this.loading = true;
224
+ this.error = null;
225
+ try {
226
+ if (this.activeTab === "prs") {
227
+ const data = yield this.gitHost.listPullRequests({
228
+ state: "all",
229
+ maxResults: PAGE_SIZE * (this.page + 1)
230
+ });
231
+ const start = this.page * PAGE_SIZE;
232
+ this.prs = data.slice(start, start + PAGE_SIZE);
233
+ this.hasMorePrs = data.length > start + PAGE_SIZE;
234
+ } else {
235
+ const data = yield this.gitHost.listIssues({
236
+ state: "all",
237
+ maxResults: PAGE_SIZE * (this.page + 1)
238
+ });
239
+ const start = this.page * PAGE_SIZE;
240
+ this.issues = data.slice(start, start + PAGE_SIZE);
241
+ this.hasMoreIssues = data.length > start + PAGE_SIZE;
242
+ }
243
+ } catch (err) {
244
+ this.error = err instanceof Error ? err.message : "Failed to load data";
245
+ } finally {
246
+ this.loading = false;
247
+ }
248
+ });
249
+ this.gitHost = gitHost;
250
+ makeAutoObservable(this, { gitHost: false });
251
+ }
252
+ get currentItems() {
253
+ return this.activeTab === "prs" ? this.prs : this.issues;
254
+ }
255
+ get hasMore() {
256
+ return this.activeTab === "prs" ? this.hasMorePrs : this.hasMoreIssues;
257
+ }
258
+ get showPagination() {
259
+ return this.page > 0 || this.hasMore;
260
+ }
261
+ setActiveTab(tab) {
262
+ this.activeTab = tab;
263
+ this.page = 0;
264
+ this.loadData();
265
+ }
266
+ nextPage() {
267
+ if (!this.hasMore) return;
268
+ this.page += 1;
269
+ this.loadData();
270
+ }
271
+ prevPage() {
272
+ if (this.page <= 0) return;
273
+ this.page -= 1;
274
+ this.loadData();
275
+ }
276
+ retry() {
277
+ this.error = null;
278
+ this.page = 0;
279
+ this.loadData();
280
+ }
281
+ };
282
+
283
+ //#endregion
284
+ //#region src/utils/action-toast.ts
285
+ /**
286
+ * Show a toast notification for a user action, with optional undo.
287
+ * Records the action in the ActionNotificationModel for history.
288
+ */
289
+ function showActionToast(model, type, description, options) {
290
+ const actionId = model.record(type, description, options?.undo);
291
+ if (options?.undo) toast(description, {
292
+ duration: options.duration ?? 5e3,
293
+ action: {
294
+ label: "Undo",
295
+ onClick: async () => {
296
+ try {
297
+ await options.undo();
298
+ model.markUndone(actionId);
299
+ toast.success("Action undone");
300
+ } catch (err) {
301
+ toast.error(`Undo failed: ${err instanceof Error ? err.message : "Unknown error"}`);
302
+ }
303
+ }
304
+ }
305
+ });
306
+ else toast.success(description, { duration: options?.duration ?? 3e3 });
307
+ return actionId;
308
+ }
309
+ /** Show an error toast */
310
+ function showErrorToast(message, context) {
311
+ toast.error(context ? `${context}: ${message}` : message);
312
+ }
313
+ /** Show an info toast */
314
+ function showInfoToast(message) {
315
+ toast.info(message);
316
+ }
317
+
318
+ //#endregion
319
+ //#region src/components/GitBrowser.tsx
320
+ const FileBrowser = React.lazy(() => import("@anymux/fs-ui").then((m) => ({ default: m.FileBrowser })));
321
+ const CommitList = React.lazy(() => import("@anymux/fs-ui").then((m) => ({ default: m.CommitList })));
322
+ const BranchList = React.lazy(() => import("@anymux/fs-ui").then((m) => ({ default: m.BranchList })));
323
+ const DiffViewer = React.lazy(() => import("@anymux/fs-ui").then((m) => ({ default: m.DiffViewer })));
324
+ function formatRelativeTime(date) {
325
+ const now = Date.now();
326
+ const diffMs = now - new Date(date).getTime();
327
+ const seconds = Math.floor(diffMs / 1e3);
328
+ const minutes = Math.floor(seconds / 60);
329
+ const hours = Math.floor(minutes / 60);
330
+ const days = Math.floor(hours / 24);
331
+ const weeks = Math.floor(days / 7);
332
+ const months = Math.floor(days / 30);
333
+ if (seconds < 60) return "just now";
334
+ if (minutes < 60) return `${minutes}m ago`;
335
+ if (hours < 24) return `${hours}h ago`;
336
+ if (days < 7) return `${days}d ago`;
337
+ if (weeks < 5) return `${weeks}w ago`;
338
+ if (months < 12) return `${months}mo ago`;
339
+ return new Date(date).toLocaleDateString();
340
+ }
341
+ function LoadingSpinner() {
342
+ return /* @__PURE__ */ jsx("div", {
343
+ className: "flex items-center justify-center py-8",
344
+ children: /* @__PURE__ */ jsx(Loader2, { className: "h-5 w-5 text-gray-400 animate-spin" })
345
+ });
346
+ }
347
+ function RefSelector({ branches, tags, currentRef, onSelectRef }) {
348
+ const [isOpen, setIsOpen] = useState(false);
349
+ const [filterTab, setFilterTab] = useState("branches");
350
+ const [search, setSearch] = useState("");
351
+ const dropdownRef = useRef(null);
352
+ useEffect(() => {
353
+ function handleClickOutside(e) {
354
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target)) setIsOpen(false);
355
+ }
356
+ if (isOpen) {
357
+ document.addEventListener("mousedown", handleClickOutside);
358
+ return () => document.removeEventListener("mousedown", handleClickOutside);
359
+ }
360
+ }, [isOpen]);
361
+ const items = filterTab === "branches" ? branches.map((b) => ({
362
+ type: "branch",
363
+ name: b.name,
364
+ sha: b.sha,
365
+ isDefault: b.isDefault
366
+ })) : tags.map((t) => ({
367
+ type: "tag",
368
+ name: t.name,
369
+ sha: t.sha
370
+ }));
371
+ const filtered = search ? items.filter((i) => i.name.toLowerCase().includes(search.toLowerCase())) : items;
372
+ return /* @__PURE__ */ jsxs("div", {
373
+ ref: dropdownRef,
374
+ className: "relative",
375
+ children: [/* @__PURE__ */ jsxs("button", {
376
+ onClick: () => setIsOpen(!isOpen),
377
+ className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors",
378
+ children: [
379
+ /* @__PURE__ */ jsx(GitBranch, { className: "h-3.5 w-3.5 text-gray-500" }),
380
+ /* @__PURE__ */ jsx("span", {
381
+ className: "max-w-[160px] truncate",
382
+ title: currentRef,
383
+ children: currentRef
384
+ }),
385
+ /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 text-gray-400" })
386
+ ]
387
+ }), isOpen && /* @__PURE__ */ jsxs("div", {
388
+ className: "absolute left-0 top-full mt-1 z-50 w-72 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg overflow-hidden",
389
+ children: [
390
+ /* @__PURE__ */ jsx("div", {
391
+ className: "p-2 border-b border-gray-200 dark:border-gray-700",
392
+ children: /* @__PURE__ */ jsx("input", {
393
+ type: "text",
394
+ placeholder: "Filter branches/tags...",
395
+ value: search,
396
+ onChange: (e) => setSearch(e.target.value),
397
+ className: "w-full px-2.5 py-1.5 text-xs rounded-md border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-900 focus:outline-none focus:ring-1 focus:ring-blue-500",
398
+ autoFocus: true
399
+ })
400
+ }),
401
+ /* @__PURE__ */ jsxs("div", {
402
+ className: "flex border-b border-gray-200 dark:border-gray-700",
403
+ children: [/* @__PURE__ */ jsxs("button", {
404
+ onClick: () => {
405
+ setFilterTab("branches");
406
+ setSearch("");
407
+ },
408
+ className: `flex-1 flex items-center justify-center gap-1.5 px-3 py-1.5 text-xs font-medium transition-colors ${filterTab === "branches" ? "border-b-2 border-blue-500 text-blue-600 dark:text-blue-400" : "text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"}`,
409
+ children: [/* @__PURE__ */ jsx(GitBranch, { className: "h-3 w-3" }), "Branches"]
410
+ }), /* @__PURE__ */ jsxs("button", {
411
+ onClick: () => {
412
+ setFilterTab("tags");
413
+ setSearch("");
414
+ },
415
+ className: `flex-1 flex items-center justify-center gap-1.5 px-3 py-1.5 text-xs font-medium transition-colors ${filterTab === "tags" ? "border-b-2 border-blue-500 text-blue-600 dark:text-blue-400" : "text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"}`,
416
+ children: [/* @__PURE__ */ jsx(Tag, { className: "h-3 w-3" }), "Tags"]
417
+ })]
418
+ }),
419
+ /* @__PURE__ */ jsx("div", {
420
+ className: "max-h-64 overflow-auto",
421
+ children: filtered.length === 0 ? /* @__PURE__ */ jsx("div", {
422
+ className: "px-3 py-4 text-center text-xs text-gray-400",
423
+ children: search ? "No matches found" : `No ${filterTab} found`
424
+ }) : filtered.map((item) => /* @__PURE__ */ jsxs("button", {
425
+ onClick: () => {
426
+ onSelectRef(item.name, item.type);
427
+ setIsOpen(false);
428
+ setSearch("");
429
+ },
430
+ className: "flex items-center gap-2 w-full px-3 py-2 text-left hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors",
431
+ children: [
432
+ /* @__PURE__ */ jsx("span", {
433
+ className: "w-4 flex-shrink-0",
434
+ children: item.name === currentRef && /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5 text-blue-500" })
435
+ }),
436
+ /* @__PURE__ */ jsx("span", {
437
+ className: "text-xs truncate flex-1",
438
+ title: item.name,
439
+ children: item.name
440
+ }),
441
+ item.isDefault && /* @__PURE__ */ jsx("span", {
442
+ className: "px-1.5 py-0.5 text-[9px] font-medium bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400 rounded flex-shrink-0",
443
+ children: "default"
444
+ })
445
+ ]
446
+ }, `${item.type}-${item.name}`))
447
+ })
448
+ ]
449
+ })]
450
+ });
451
+ }
452
+ const GitRepoBrowser = observer(({ gitRepo, owner, repo, createFileSystem, gitHost, onError, actionNotifications }) => {
453
+ const [model] = useState(() => new GitRepoBrowserModel(gitRepo, gitHost, createFileSystem, onError));
454
+ useEffect(() => {
455
+ if (gitHost && !model.hasGitHost) model.setGitHost(gitHost);
456
+ }, [gitHost, model]);
457
+ const handleNotify = useCallback((type, message) => {
458
+ if (type === "success" && actionNotifications) {
459
+ const actionType = message.startsWith("Renamed") ? "rename" : message.startsWith("Deleted") ? "delete" : message.startsWith("Created") ? "create" : message.startsWith("Uploaded") || message.includes("upload") ? "upload" : "create";
460
+ showActionToast(actionNotifications, actionType, message);
461
+ } else if (type === "error") showErrorToast(message);
462
+ else toast[type](message);
463
+ }, [actionNotifications]);
464
+ useEffect(() => {
465
+ model.initialize();
466
+ }, [model]);
467
+ if (model.loading && !model.fileSystem) return /* @__PURE__ */ jsx(LoadingSpinner, {});
468
+ if (model.error && !model.fileSystem) return /* @__PURE__ */ jsx(BrowserError, {
469
+ error: model.error,
470
+ context: `${owner}/${repo}`,
471
+ onRetry: () => window.location.reload()
472
+ });
473
+ const sidebarTabs = [
474
+ {
475
+ id: "files",
476
+ label: "Files",
477
+ icon: /* @__PURE__ */ jsx(FolderTree, { className: "h-3.5 w-3.5" })
478
+ },
479
+ {
480
+ id: "branches",
481
+ label: "Branches",
482
+ icon: /* @__PURE__ */ jsx(GitBranch, { className: "h-3.5 w-3.5" })
483
+ },
484
+ {
485
+ id: "commits",
486
+ label: "Commits",
487
+ icon: /* @__PURE__ */ jsx(GitCommit, { className: "h-3.5 w-3.5" })
488
+ },
489
+ ...model.hasGitHost ? [{
490
+ id: "prs",
491
+ label: "PRs",
492
+ icon: /* @__PURE__ */ jsx(GitPullRequest, { className: "h-3.5 w-3.5" })
493
+ }, {
494
+ id: "issues",
495
+ label: "Issues",
496
+ icon: /* @__PURE__ */ jsx(AlertCircle, { className: "h-3.5 w-3.5" })
497
+ }] : []
498
+ ];
499
+ return /* @__PURE__ */ jsxs("div", {
500
+ className: "flex flex-col h-full",
501
+ children: [
502
+ /* @__PURE__ */ jsxs("div", {
503
+ className: "flex items-center gap-3 px-3 py-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 flex-shrink-0",
504
+ children: [/* @__PURE__ */ jsx(RefSelector, {
505
+ branches: model.branches,
506
+ tags: model.tags,
507
+ currentRef: model.currentRef,
508
+ onSelectRef: (ref) => model.switchRef(ref)
509
+ }), /* @__PURE__ */ jsxs("div", {
510
+ className: "flex items-center gap-1.5 text-xs text-gray-500 dark:text-gray-400 min-w-0",
511
+ children: [
512
+ /* @__PURE__ */ jsx("span", {
513
+ className: "font-medium text-gray-700 dark:text-gray-300 truncate",
514
+ title: owner,
515
+ children: owner
516
+ }),
517
+ /* @__PURE__ */ jsx("span", { children: "/" }),
518
+ /* @__PURE__ */ jsx("span", {
519
+ className: "font-medium text-gray-700 dark:text-gray-300 truncate",
520
+ title: repo,
521
+ children: repo
522
+ })
523
+ ]
524
+ })]
525
+ }),
526
+ /* @__PURE__ */ jsx("div", {
527
+ className: "flex border-b border-gray-200 dark:border-gray-700 flex-shrink-0",
528
+ children: sidebarTabs.map((tab) => /* @__PURE__ */ jsxs("button", {
529
+ onClick: () => model.setActiveTab(tab.id),
530
+ className: `flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium border-b-2 transition-colors ${model.activeTab === tab.id ? "border-blue-500 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"}`,
531
+ children: [tab.icon, tab.label]
532
+ }, tab.id))
533
+ }),
534
+ /* @__PURE__ */ jsxs("div", {
535
+ className: "flex-1 min-h-0 relative flex flex-col",
536
+ children: [
537
+ model.loading && /* @__PURE__ */ jsx("div", {
538
+ className: "absolute inset-0 bg-white/60 dark:bg-gray-900/60 z-10 flex items-center justify-center",
539
+ children: /* @__PURE__ */ jsx(Loader2, { className: "h-5 w-5 text-gray-400 animate-spin" })
540
+ }),
541
+ model.error && model.fileSystem && /* @__PURE__ */ jsx("div", {
542
+ className: "px-4 py-2 bg-red-50 dark:bg-red-900/20 text-xs text-red-600 dark:text-red-400 border-b border-red-200 dark:border-red-800 flex-shrink-0",
543
+ children: model.error
544
+ }),
545
+ model.activeTab === "files" && model.fileSystem && /* @__PURE__ */ jsx("div", {
546
+ className: "flex-1 min-h-0",
547
+ children: /* @__PURE__ */ jsx(Suspense, {
548
+ fallback: /* @__PURE__ */ jsx(LoadingSpinner, {}),
549
+ children: /* @__PURE__ */ jsx(FileBrowser, {
550
+ fileSystem: model.fileSystem,
551
+ className: "h-full",
552
+ onError,
553
+ onNotify: handleNotify
554
+ }, model.currentRef)
555
+ })
556
+ }),
557
+ model.activeTab === "branches" && /* @__PURE__ */ jsx("div", {
558
+ className: "flex-1 min-h-0",
559
+ children: /* @__PURE__ */ jsx(Suspense, {
560
+ fallback: /* @__PURE__ */ jsx(LoadingSpinner, {}),
561
+ children: /* @__PURE__ */ jsx(BranchList, {
562
+ branches: model.branches,
563
+ currentBranch: model.currentRef,
564
+ onSelectBranch: (branch) => model.selectBranch(branch),
565
+ className: "h-full"
566
+ })
567
+ })
568
+ }),
569
+ model.activeTab === "commits" && /* @__PURE__ */ jsx("div", {
570
+ className: "flex-1 min-h-0 flex flex-col",
571
+ children: model.commitsLoading ? /* @__PURE__ */ jsx(LoadingSpinner, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [
572
+ /* @__PURE__ */ jsx(Suspense, {
573
+ fallback: /* @__PURE__ */ jsx(LoadingSpinner, {}),
574
+ children: /* @__PURE__ */ jsx(CommitList, {
575
+ commits: model.commits,
576
+ headSha: model.headSha,
577
+ selectedSha: model.selectedCommitSha,
578
+ onSelectCommit: (commit) => model.selectCommit(commit),
579
+ className: model.selectedCommitSha ? "max-h-[50%] overflow-auto flex-shrink-0" : "flex-1 overflow-auto"
580
+ })
581
+ }),
582
+ (model.commitsPage > 0 || model.hasMoreCommits) && /* @__PURE__ */ jsxs("div", {
583
+ className: "flex items-center justify-between px-4 py-2 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 flex-shrink-0",
584
+ children: [
585
+ /* @__PURE__ */ jsxs("button", {
586
+ onClick: () => model.prevCommitsPage(),
587
+ disabled: model.commitsPage === 0,
588
+ className: "inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:pointer-events-none",
589
+ children: [/* @__PURE__ */ jsx(ChevronLeft, { className: "h-3.5 w-3.5" }), " Previous"]
590
+ }),
591
+ /* @__PURE__ */ jsxs("span", {
592
+ className: "text-xs text-gray-500",
593
+ children: ["Page ", model.commitsPage + 1]
594
+ }),
595
+ /* @__PURE__ */ jsxs("button", {
596
+ onClick: () => model.nextCommitsPage(),
597
+ disabled: !model.hasMoreCommits,
598
+ className: "inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:pointer-events-none",
599
+ children: ["Next ", /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5" })]
600
+ })
601
+ ]
602
+ }),
603
+ model.selectedCommitSha && /* @__PURE__ */ jsxs("div", {
604
+ className: "flex-1 min-h-0 border-t border-gray-200 dark:border-gray-700 overflow-auto",
605
+ children: [/* @__PURE__ */ jsxs("div", {
606
+ className: "flex items-center gap-2 px-3 py-1.5 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 flex-shrink-0",
607
+ children: [/* @__PURE__ */ jsx(Diff, { className: "h-3.5 w-3.5 text-gray-400" }), /* @__PURE__ */ jsxs("span", {
608
+ className: "text-[10px] font-medium text-gray-600 dark:text-gray-300",
609
+ children: [
610
+ "Changes in",
611
+ " ",
612
+ /* @__PURE__ */ jsx("code", {
613
+ className: "font-mono text-blue-600 dark:text-blue-400",
614
+ children: model.selectedCommitSha.slice(0, 7)
615
+ })
616
+ ]
617
+ })]
618
+ }), model.diffLoading ? /* @__PURE__ */ jsx(LoadingSpinner, {}) : /* @__PURE__ */ jsx(Suspense, {
619
+ fallback: /* @__PURE__ */ jsx(LoadingSpinner, {}),
620
+ children: /* @__PURE__ */ jsx(DiffViewer, {
621
+ entries: model.diffEntries,
622
+ className: "p-2"
623
+ })
624
+ })]
625
+ })
626
+ ] })
627
+ }),
628
+ model.activeTab === "prs" && model.hasGitHost && /* @__PURE__ */ jsxs("div", {
629
+ className: "flex-1 min-h-0 flex flex-col",
630
+ children: [
631
+ model.hostLoading && /* @__PURE__ */ jsx(LoadingSpinner, {}),
632
+ !model.hostLoading && model.hostError && /* @__PURE__ */ jsx(BrowserError, {
633
+ error: model.hostError,
634
+ context: "Pull Requests",
635
+ onRetry: () => model.retryPRs()
636
+ }),
637
+ !model.hostLoading && !model.hostError && (model.prs.length === 0 ? /* @__PURE__ */ jsx("div", {
638
+ className: "flex items-center justify-center py-8 text-xs text-gray-400",
639
+ children: "No pull requests found"
640
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
641
+ className: "flex-1 overflow-auto divide-y divide-gray-100 dark:divide-gray-800",
642
+ children: model.prs.map((pr) => /* @__PURE__ */ jsxs("div", {
643
+ className: "flex items-start gap-3 px-4 py-2.5 hover:bg-gray-50 dark:hover:bg-gray-800/50",
644
+ children: [/* @__PURE__ */ jsx(GitPullRequest, { className: "h-4 w-4 text-gray-400 shrink-0 mt-0.5" }), /* @__PURE__ */ jsxs("div", {
645
+ className: "flex-1 min-w-0",
646
+ children: [/* @__PURE__ */ jsxs("div", {
647
+ className: "flex items-center gap-2",
648
+ children: [/* @__PURE__ */ jsx("span", {
649
+ className: "text-sm truncate",
650
+ title: pr.title,
651
+ children: pr.title
652
+ }), /* @__PURE__ */ jsx("span", {
653
+ className: `px-1.5 py-0.5 text-[10px] font-medium rounded ${pr.state === "open" ? "text-green-600 bg-green-50 dark:text-green-400 dark:bg-green-900/30" : pr.state === "merged" ? "text-purple-600 bg-purple-50 dark:text-purple-400 dark:bg-purple-900/30" : "text-red-600 bg-red-50 dark:text-red-400 dark:bg-red-900/30"}`,
654
+ children: pr.state
655
+ })]
656
+ }), /* @__PURE__ */ jsxs("span", {
657
+ className: "text-xs text-gray-500",
658
+ children: [
659
+ "#",
660
+ pr.number,
661
+ " · ",
662
+ pr.author,
663
+ " · ",
664
+ formatRelativeTime(pr.createdAt)
665
+ ]
666
+ })]
667
+ })]
668
+ }, pr.number))
669
+ }), (model.prPage > 0 || model.hasMorePrs) && /* @__PURE__ */ jsxs("div", {
670
+ className: "flex items-center justify-between px-4 py-2 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 flex-shrink-0",
671
+ children: [
672
+ /* @__PURE__ */ jsxs("button", {
673
+ onClick: () => model.prevPrPage(),
674
+ disabled: model.prPage === 0,
675
+ className: "inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:pointer-events-none",
676
+ children: [/* @__PURE__ */ jsx(ChevronLeft, { className: "h-3.5 w-3.5" }), " Previous"]
677
+ }),
678
+ /* @__PURE__ */ jsxs("span", {
679
+ className: "text-xs text-gray-500",
680
+ children: ["Page ", model.prPage + 1]
681
+ }),
682
+ /* @__PURE__ */ jsxs("button", {
683
+ onClick: () => model.nextPrPage(),
684
+ disabled: !model.hasMorePrs,
685
+ className: "inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:pointer-events-none",
686
+ children: ["Next ", /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5" })]
687
+ })
688
+ ]
689
+ })] }))
690
+ ]
691
+ }),
692
+ model.activeTab === "issues" && model.hasGitHost && /* @__PURE__ */ jsxs("div", {
693
+ className: "flex-1 min-h-0 flex flex-col",
694
+ children: [
695
+ model.hostLoading && /* @__PURE__ */ jsx(LoadingSpinner, {}),
696
+ !model.hostLoading && model.hostError && /* @__PURE__ */ jsx(BrowserError, {
697
+ error: model.hostError,
698
+ context: "Issues",
699
+ onRetry: () => model.retryIssues()
700
+ }),
701
+ !model.hostLoading && !model.hostError && (model.issues.length === 0 ? /* @__PURE__ */ jsx("div", {
702
+ className: "flex items-center justify-center py-8 text-xs text-gray-400",
703
+ children: "No issues found"
704
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
705
+ className: "flex-1 overflow-auto divide-y divide-gray-100 dark:divide-gray-800",
706
+ children: model.issues.map((issue) => /* @__PURE__ */ jsxs("div", {
707
+ className: "flex items-start gap-3 px-4 py-2.5 hover:bg-gray-50 dark:hover:bg-gray-800/50",
708
+ children: [/* @__PURE__ */ jsx(AlertCircle, { className: `h-4 w-4 shrink-0 mt-0.5 ${issue.state === "open" ? "text-green-500" : "text-red-400"}` }), /* @__PURE__ */ jsxs("div", {
709
+ className: "flex-1 min-w-0",
710
+ children: [/* @__PURE__ */ jsxs("div", {
711
+ className: "flex items-center gap-2",
712
+ children: [/* @__PURE__ */ jsx("span", {
713
+ className: "text-sm truncate",
714
+ title: issue.title,
715
+ children: issue.title
716
+ }), issue.labels.map((l) => /* @__PURE__ */ jsx("span", {
717
+ className: "px-1.5 py-0.5 text-[10px] font-medium rounded bg-blue-50 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400",
718
+ children: l
719
+ }, l))]
720
+ }), /* @__PURE__ */ jsxs("span", {
721
+ className: "text-xs text-gray-500",
722
+ children: [
723
+ "#",
724
+ issue.number,
725
+ " · ",
726
+ issue.author,
727
+ " · ",
728
+ formatRelativeTime(issue.createdAt)
729
+ ]
730
+ })]
731
+ })]
732
+ }, issue.number))
733
+ }), (model.issuePage > 0 || model.hasMoreIssues) && /* @__PURE__ */ jsxs("div", {
734
+ className: "flex items-center justify-between px-4 py-2 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 flex-shrink-0",
735
+ children: [
736
+ /* @__PURE__ */ jsxs("button", {
737
+ onClick: () => model.prevIssuePage(),
738
+ disabled: model.issuePage === 0,
739
+ className: "inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:pointer-events-none",
740
+ children: [/* @__PURE__ */ jsx(ChevronLeft, { className: "h-3.5 w-3.5" }), " Previous"]
741
+ }),
742
+ /* @__PURE__ */ jsxs("span", {
743
+ className: "text-xs text-gray-500",
744
+ children: ["Page ", model.issuePage + 1]
745
+ }),
746
+ /* @__PURE__ */ jsxs("button", {
747
+ onClick: () => model.nextIssuePage(),
748
+ disabled: !model.hasMoreIssues,
749
+ className: "inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:pointer-events-none",
750
+ children: ["Next ", /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5" })]
751
+ })
752
+ ]
753
+ })] }))
754
+ ]
755
+ })
756
+ ]
757
+ })
758
+ ]
759
+ });
760
+ });
761
+ function EmptyState({ message }) {
762
+ return /* @__PURE__ */ jsx("div", {
763
+ className: "flex items-center justify-center py-8 text-xs text-gray-400",
764
+ children: message
765
+ });
766
+ }
767
+ function statusColor(status) {
768
+ switch (status) {
769
+ case "open": return "text-green-600 bg-green-50 dark:text-green-400 dark:bg-green-900/30";
770
+ case "merged": return "text-purple-600 bg-purple-50 dark:text-purple-400 dark:bg-purple-900/30";
771
+ case "closed": return "text-red-600 bg-red-50 dark:text-red-400 dark:bg-red-900/30";
772
+ default: return "text-gray-600 bg-gray-50";
773
+ }
774
+ }
775
+ const GitHostBrowser = observer(({ gitHost }) => {
776
+ const [model] = useState(() => new GitHostBrowserModel(gitHost));
777
+ useEffect(() => {
778
+ model.loadData();
779
+ }, [model]);
780
+ const tabDefs = [{
781
+ id: "prs",
782
+ label: "Pull Requests",
783
+ icon: /* @__PURE__ */ jsx(GitPullRequest, { className: "h-3.5 w-3.5" })
784
+ }, {
785
+ id: "issues",
786
+ label: "Issues",
787
+ icon: /* @__PURE__ */ jsx(AlertCircle, { className: "h-3.5 w-3.5" })
788
+ }];
789
+ return /* @__PURE__ */ jsxs("div", {
790
+ className: "flex flex-col h-full",
791
+ children: [
792
+ /* @__PURE__ */ jsxs("div", {
793
+ className: "flex items-center gap-2 px-4 py-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800",
794
+ children: [/* @__PURE__ */ jsx(GitPullRequest, { className: "h-4 w-4 text-gray-500" }), /* @__PURE__ */ jsx("span", {
795
+ className: "text-xs font-medium text-gray-700 dark:text-gray-300",
796
+ children: "Git Host Browser"
797
+ })]
798
+ }),
799
+ /* @__PURE__ */ jsx("div", {
800
+ className: "flex border-b border-gray-200 dark:border-gray-700",
801
+ children: tabDefs.map((tab) => /* @__PURE__ */ jsxs("button", {
802
+ onClick: () => model.setActiveTab(tab.id),
803
+ className: `flex items-center gap-1.5 px-4 py-2 text-xs font-medium border-b-2 transition-colors ${model.activeTab === tab.id ? "border-blue-500 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"}`,
804
+ children: [tab.icon, tab.label]
805
+ }, tab.id))
806
+ }),
807
+ /* @__PURE__ */ jsxs("div", {
808
+ className: "flex-1 overflow-auto",
809
+ children: [
810
+ model.loading && /* @__PURE__ */ jsx(LoadingSpinner, {}),
811
+ !model.loading && model.error && /* @__PURE__ */ jsx(BrowserError, {
812
+ error: model.error,
813
+ context: "Git Host",
814
+ onRetry: () => model.retry()
815
+ }),
816
+ !model.loading && !model.error && model.activeTab === "prs" && (model.prs.length === 0 ? /* @__PURE__ */ jsx(EmptyState, { message: "No pull requests found" }) : /* @__PURE__ */ jsx("div", {
817
+ className: "divide-y divide-gray-100 dark:divide-gray-800",
818
+ children: model.prs.map((pr) => /* @__PURE__ */ jsxs("div", {
819
+ className: "flex items-start gap-3 px-4 py-2.5 hover:bg-gray-50 dark:hover:bg-gray-800/50",
820
+ children: [/* @__PURE__ */ jsx(GitPullRequest, { className: "h-4 w-4 text-gray-400 shrink-0 mt-0.5" }), /* @__PURE__ */ jsxs("div", {
821
+ className: "flex-1 min-w-0",
822
+ children: [/* @__PURE__ */ jsxs("div", {
823
+ className: "flex items-center gap-2",
824
+ children: [/* @__PURE__ */ jsx("span", {
825
+ className: "text-sm truncate",
826
+ title: pr.title,
827
+ children: pr.title
828
+ }), /* @__PURE__ */ jsx("span", {
829
+ className: `px-1.5 py-0.5 text-[10px] font-medium rounded ${statusColor(pr.state)}`,
830
+ children: pr.state
831
+ })]
832
+ }), /* @__PURE__ */ jsxs("span", {
833
+ className: "text-xs text-gray-500",
834
+ children: [
835
+ "#",
836
+ pr.number,
837
+ " · ",
838
+ pr.author,
839
+ " · ",
840
+ formatRelativeTime(pr.createdAt)
841
+ ]
842
+ })]
843
+ })]
844
+ }, pr.number))
845
+ })),
846
+ !model.loading && !model.error && model.activeTab === "issues" && (model.issues.length === 0 ? /* @__PURE__ */ jsx(EmptyState, { message: "No issues found" }) : /* @__PURE__ */ jsx("div", {
847
+ className: "divide-y divide-gray-100 dark:divide-gray-800",
848
+ children: model.issues.map((issue) => /* @__PURE__ */ jsxs("div", {
849
+ className: "flex items-start gap-3 px-4 py-2.5 hover:bg-gray-50 dark:hover:bg-gray-800/50",
850
+ children: [/* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4 text-green-500 shrink-0 mt-0.5" }), /* @__PURE__ */ jsxs("div", {
851
+ className: "flex-1 min-w-0",
852
+ children: [/* @__PURE__ */ jsxs("div", {
853
+ className: "flex items-center gap-2",
854
+ children: [/* @__PURE__ */ jsx("span", {
855
+ className: "text-sm truncate",
856
+ title: issue.title,
857
+ children: issue.title
858
+ }), issue.labels.map((l) => /* @__PURE__ */ jsx("span", {
859
+ className: "px-1.5 py-0.5 text-[10px] font-medium rounded bg-blue-50 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400",
860
+ children: l
861
+ }, l))]
862
+ }), /* @__PURE__ */ jsxs("span", {
863
+ className: "text-xs text-gray-500",
864
+ children: [
865
+ "#",
866
+ issue.number,
867
+ " · ",
868
+ issue.author,
869
+ " · ",
870
+ formatRelativeTime(issue.createdAt)
871
+ ]
872
+ })]
873
+ })]
874
+ }, issue.number))
875
+ }))
876
+ ]
877
+ }),
878
+ model.showPagination && !model.loading && !model.error && model.currentItems.length > 0 && /* @__PURE__ */ jsxs("div", {
879
+ className: "flex items-center justify-between px-4 py-2 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800",
880
+ children: [
881
+ /* @__PURE__ */ jsxs("button", {
882
+ onClick: () => model.prevPage(),
883
+ disabled: model.page === 0,
884
+ className: "inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:pointer-events-none",
885
+ children: [/* @__PURE__ */ jsx(ChevronLeft, { className: "h-3.5 w-3.5" }), "Previous"]
886
+ }),
887
+ /* @__PURE__ */ jsxs("span", {
888
+ className: "text-xs text-gray-500",
889
+ children: ["Page ", model.page + 1]
890
+ }),
891
+ /* @__PURE__ */ jsxs("button", {
892
+ onClick: () => model.nextPage(),
893
+ disabled: !model.hasMore,
894
+ className: "inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:pointer-events-none",
895
+ children: ["Next", /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5" })]
896
+ })
897
+ ]
898
+ })
899
+ ]
900
+ });
901
+ });
902
+
903
+ //#endregion
904
+ export { GitHostBrowser, GitHostBrowserModel, GitRepoBrowser, GitRepoBrowserModel, showActionToast, showErrorToast, showInfoToast };
905
+ //# sourceMappingURL=GitBrowser-BLgTNQyd.js.map