gitxp 0.0.1

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 (48) hide show
  1. package/.output/nitro.json +17 -0
  2. package/.output/public/assets/index-BGHvBNtP.js +11 -0
  3. package/.output/public/assets/routes-DuE8S6pX.js +60 -0
  4. package/.output/public/assets/styles-D5xbQNYu.css +1 -0
  5. package/.output/server/_chunks/ssr-renderer.mjs +26 -0
  6. package/.output/server/_libs/@floating-ui/core+[...].mjs +671 -0
  7. package/.output/server/_libs/@floating-ui/dom+[...].mjs +649 -0
  8. package/.output/server/_libs/@floating-ui/react-dom+[...].mjs +856 -0
  9. package/.output/server/_libs/@radix-ui/react-arrow+[...].mjs +258 -0
  10. package/.output/server/_libs/@radix-ui/react-dialog+[...].mjs +1862 -0
  11. package/.output/server/_libs/@radix-ui/react-popper+[...].mjs +320 -0
  12. package/.output/server/_libs/@radix-ui/react-tooltip+[...].mjs +534 -0
  13. package/.output/server/_libs/@tanstack/db+[...].mjs +14680 -0
  14. package/.output/server/_libs/@tanstack/react-router+[...].mjs +14563 -0
  15. package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +126 -0
  16. package/.output/server/_libs/@tanstack/router-core+[...].mjs +3636 -0
  17. package/.output/server/_libs/class-variance-authority+clsx.mjs +69 -0
  18. package/.output/server/_libs/cmdk.mjs +504 -0
  19. package/.output/server/_libs/h3+rou3+srvx.mjs +1361 -0
  20. package/.output/server/_libs/h3-v2.mjs +285 -0
  21. package/.output/server/_libs/lucide-react.mjs +158 -0
  22. package/.output/server/_libs/radix-ui__primitive.mjs +44 -0
  23. package/.output/server/_libs/radix-ui__react-context.mjs +108 -0
  24. package/.output/server/_libs/tailwind-merge.mjs +3380 -0
  25. package/.output/server/_libs/tanstack__history.mjs +384 -0
  26. package/.output/server/_libs/tanstack__query-core.mjs +2225 -0
  27. package/.output/server/_libs/tanstack__react-db.mjs +242 -0
  28. package/.output/server/_libs/tanstack__react-query.mjs +140 -0
  29. package/.output/server/_libs/ufo.mjs +64 -0
  30. package/.output/server/_runtime.mjs +35 -0
  31. package/.output/server/_ssr/empty-plugin-adapters-D9UWiqvJ.mjs +5 -0
  32. package/.output/server/_ssr/events-BNrmOvgU.mjs +13 -0
  33. package/.output/server/_ssr/notifications-DUoENv6E.mjs +514 -0
  34. package/.output/server/_ssr/router-DsUHD4bT.mjs +179 -0
  35. package/.output/server/_ssr/routes-DIFfr242.mjs +982 -0
  36. package/.output/server/_ssr/ssr.mjs +1854 -0
  37. package/.output/server/_ssr/start-5Z2QO8AU.mjs +4 -0
  38. package/.output/server/_tanstack-start-manifest_v-CrD0YpZr.mjs +20 -0
  39. package/.output/server/index.mjs +310 -0
  40. package/.output/server/node_modules/tslib/modules/index.js +70 -0
  41. package/.output/server/node_modules/tslib/modules/package.json +3 -0
  42. package/.output/server/node_modules/tslib/package.json +47 -0
  43. package/.output/server/node_modules/tslib/tslib.js +484 -0
  44. package/.output/server/package.json +9 -0
  45. package/LICENSE +21 -0
  46. package/README.md +325 -0
  47. package/bin/gitxp.js +85 -0
  48. package/package.json +92 -0
@@ -0,0 +1,982 @@
1
+ import { i as __toESM } from "../_runtime.mjs";
2
+ import { u as require_react } from "../_libs/@floating-ui/react-dom+[...].mjs";
3
+ import { _ as useNavigate, g as Link } from "../_libs/@tanstack/react-router+[...].mjs";
4
+ import { i as Slot, l as require_jsx_runtime } from "../_libs/@radix-ui/react-arrow+[...].mjs";
5
+ import { i as useQueryClient, n as useQuery, t as useMutation } from "../_libs/tanstack__react-query.mjs";
6
+ import { a as webUrlFor, c as getSyncStatus, d as retryFailedMutations, f as syncNow, i as views, l as markNotificationDone, n as Route$1, o as getFailedMutations, r as reasonLabel, s as getNotifications, u as markNotificationRead } from "./router-DsUHD4bT.mjs";
7
+ import { l as createCollection } from "../_libs/@tanstack/db+[...].mjs";
8
+ import { t as useLiveQuery } from "../_libs/tanstack__react-db.mjs";
9
+ import { n as clsx, t as cva } from "../_libs/class-variance-authority+clsx.mjs";
10
+ import { n as Search, r as PanelLeft, t as X } from "../_libs/lucide-react.mjs";
11
+ import { a as Root3, i as Provider, n as Content2, o as Trigger, r as Portal, t as Arrow2 } from "../_libs/@radix-ui/react-tooltip+[...].mjs";
12
+ import { a as DialogOverlay$1, i as DialogDescription$1, n as DialogClose, o as DialogPortal$1, r as DialogContent$1, s as DialogTitle$1, t as Dialog } from "../_libs/@radix-ui/react-dialog+[...].mjs";
13
+ import { t as twMerge } from "../_libs/tailwind-merge.mjs";
14
+ import { t as _e } from "../_libs/cmdk.mjs";
15
+ //#region node_modules/.nitro/vite/services/ssr/assets/routes-DIFfr242.js
16
+ var import_react = /* @__PURE__ */ __toESM(require_react());
17
+ var import_jsx_runtime = require_jsx_runtime();
18
+ var MOBILE_BREAKPOINT = 768;
19
+ function useIsMobile() {
20
+ const [isMobile, setIsMobile] = import_react.useState(void 0);
21
+ import_react.useEffect(() => {
22
+ const mql = window.matchMedia(`(max-width: 767px)`);
23
+ const onChange = () => {
24
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
25
+ };
26
+ mql.addEventListener("change", onChange);
27
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
28
+ return () => mql.removeEventListener("change", onChange);
29
+ }, []);
30
+ return !!isMobile;
31
+ }
32
+ function cn(...inputs) {
33
+ return twMerge(clsx(inputs));
34
+ }
35
+ var buttonVariants = cva("inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
36
+ variants: {
37
+ variant: {
38
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
39
+ destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
40
+ outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
41
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
42
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
43
+ link: "text-primary underline-offset-4 hover:underline"
44
+ },
45
+ size: {
46
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
47
+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
48
+ sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
49
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
50
+ icon: "size-9",
51
+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
52
+ "icon-sm": "size-8",
53
+ "icon-lg": "size-10"
54
+ }
55
+ },
56
+ defaultVariants: {
57
+ variant: "default",
58
+ size: "default"
59
+ }
60
+ });
61
+ function Button({ className, variant = "default", size = "default", asChild = false, ...props }) {
62
+ const Comp = asChild ? Slot : "button";
63
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Comp, {
64
+ "data-slot": "button",
65
+ "data-variant": variant,
66
+ "data-size": size,
67
+ className: cn(buttonVariants({
68
+ variant,
69
+ size,
70
+ className
71
+ })),
72
+ ...props
73
+ });
74
+ }
75
+ function Sheet({ ...props }) {
76
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog, {
77
+ "data-slot": "sheet",
78
+ ...props
79
+ });
80
+ }
81
+ function SheetPortal({ ...props }) {
82
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogPortal$1, {
83
+ "data-slot": "sheet-portal",
84
+ ...props
85
+ });
86
+ }
87
+ function SheetOverlay({ className, ...props }) {
88
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogOverlay$1, {
89
+ "data-slot": "sheet-overlay",
90
+ className: cn("fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0", className),
91
+ ...props
92
+ });
93
+ }
94
+ function SheetContent({ className, children, side = "right", showCloseButton = true, ...props }) {
95
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SheetPortal, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SheetOverlay, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogContent$1, {
96
+ "data-slot": "sheet-content",
97
+ className: cn("fixed z-50 flex flex-col gap-4 bg-background shadow-lg transition ease-in-out data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:animate-in data-[state=open]:duration-500", side === "right" && "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", side === "left" && "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", side === "top" && "inset-x-0 top-0 h-auto border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", side === "bottom" && "inset-x-0 bottom-0 h-auto border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", className),
98
+ ...props,
99
+ children: [children, showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogClose, {
100
+ className: "absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-secondary",
101
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, { className: "size-4" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
102
+ className: "sr-only",
103
+ children: "Close"
104
+ })]
105
+ })]
106
+ })] });
107
+ }
108
+ function SheetHeader({ className, ...props }) {
109
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
110
+ "data-slot": "sheet-header",
111
+ className: cn("flex flex-col gap-1.5 p-4", className),
112
+ ...props
113
+ });
114
+ }
115
+ function SheetTitle({ className, ...props }) {
116
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogTitle$1, {
117
+ "data-slot": "sheet-title",
118
+ className: cn("font-semibold text-foreground", className),
119
+ ...props
120
+ });
121
+ }
122
+ function SheetDescription({ className, ...props }) {
123
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogDescription$1, {
124
+ "data-slot": "sheet-description",
125
+ className: cn("text-sm text-muted-foreground", className),
126
+ ...props
127
+ });
128
+ }
129
+ function TooltipProvider({ delayDuration = 0, ...props }) {
130
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Provider, {
131
+ "data-slot": "tooltip-provider",
132
+ delayDuration,
133
+ ...props
134
+ });
135
+ }
136
+ function Tooltip$1({ ...props }) {
137
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Root3, {
138
+ "data-slot": "tooltip",
139
+ ...props
140
+ });
141
+ }
142
+ function TooltipTrigger({ ...props }) {
143
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Trigger, {
144
+ "data-slot": "tooltip-trigger",
145
+ ...props
146
+ });
147
+ }
148
+ function TooltipContent({ className, sideOffset = 0, children, ...props }) {
149
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Content2, {
150
+ "data-slot": "tooltip-content",
151
+ sideOffset,
152
+ className: cn("z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95", className),
153
+ ...props,
154
+ children: [children, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Arrow2, { className: "z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" })]
155
+ }) });
156
+ }
157
+ var SIDEBAR_COOKIE_NAME = "sidebar_state";
158
+ var SIDEBAR_COOKIE_MAX_AGE = 604800;
159
+ var SIDEBAR_WIDTH = "16rem";
160
+ var SIDEBAR_WIDTH_MOBILE = "18rem";
161
+ var SIDEBAR_WIDTH_ICON = "3rem";
162
+ var SIDEBAR_KEYBOARD_SHORTCUT = "b";
163
+ var SidebarContext = import_react.createContext(null);
164
+ function useSidebar() {
165
+ const context = import_react.useContext(SidebarContext);
166
+ if (!context) throw new Error("useSidebar must be used within a SidebarProvider.");
167
+ return context;
168
+ }
169
+ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: setOpenProp, className, style, children, ...props }) {
170
+ const isMobile = useIsMobile();
171
+ const [openMobile, setOpenMobile] = import_react.useState(false);
172
+ const [_open, _setOpen] = import_react.useState(defaultOpen);
173
+ const open = openProp ?? _open;
174
+ const setOpen = import_react.useCallback((value) => {
175
+ const openState = typeof value === "function" ? value(open) : value;
176
+ if (setOpenProp) setOpenProp(openState);
177
+ else _setOpen(openState);
178
+ document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
179
+ }, [setOpenProp, open]);
180
+ const toggleSidebar = import_react.useCallback(() => {
181
+ return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
182
+ }, [
183
+ isMobile,
184
+ setOpen,
185
+ setOpenMobile
186
+ ]);
187
+ import_react.useEffect(() => {
188
+ const handleKeyDown = (event) => {
189
+ if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
190
+ event.preventDefault();
191
+ toggleSidebar();
192
+ }
193
+ };
194
+ window.addEventListener("keydown", handleKeyDown);
195
+ return () => window.removeEventListener("keydown", handleKeyDown);
196
+ }, [toggleSidebar]);
197
+ const state = open ? "expanded" : "collapsed";
198
+ const contextValue = import_react.useMemo(() => ({
199
+ state,
200
+ open,
201
+ setOpen,
202
+ isMobile,
203
+ openMobile,
204
+ setOpenMobile,
205
+ toggleSidebar
206
+ }), [
207
+ state,
208
+ open,
209
+ setOpen,
210
+ isMobile,
211
+ openMobile,
212
+ setOpenMobile,
213
+ toggleSidebar
214
+ ]);
215
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarContext.Provider, {
216
+ value: contextValue,
217
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipProvider, {
218
+ delayDuration: 0,
219
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
220
+ "data-slot": "sidebar-wrapper",
221
+ style: {
222
+ "--sidebar-width": SIDEBAR_WIDTH,
223
+ "--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
224
+ ...style
225
+ },
226
+ className: cn("group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar", className),
227
+ ...props,
228
+ children
229
+ })
230
+ })
231
+ });
232
+ }
233
+ function Sidebar({ side = "left", variant = "sidebar", collapsible = "offcanvas", className, children, ...props }) {
234
+ const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
235
+ if (collapsible === "none") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
236
+ "data-slot": "sidebar",
237
+ className: cn("flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground", className),
238
+ ...props,
239
+ children
240
+ });
241
+ if (isMobile) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Sheet, {
242
+ open: openMobile,
243
+ onOpenChange: setOpenMobile,
244
+ ...props,
245
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SheetContent, {
246
+ "data-sidebar": "sidebar",
247
+ "data-slot": "sidebar",
248
+ "data-mobile": "true",
249
+ className: "w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden",
250
+ style: { "--sidebar-width": SIDEBAR_WIDTH_MOBILE },
251
+ side,
252
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SheetHeader, {
253
+ className: "sr-only",
254
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SheetTitle, { children: "Sidebar" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SheetDescription, { children: "Displays the mobile sidebar." })]
255
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
256
+ className: "flex h-full w-full flex-col",
257
+ children
258
+ })]
259
+ })
260
+ });
261
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
262
+ className: "group peer hidden text-sidebar-foreground md:block",
263
+ "data-state": state,
264
+ "data-collapsible": state === "collapsed" ? collapsible : "",
265
+ "data-variant": variant,
266
+ "data-side": side,
267
+ "data-slot": "sidebar",
268
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
269
+ "data-slot": "sidebar-gap",
270
+ className: cn("relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear", "group-data-[collapsible=offcanvas]:w-0", "group-data-[side=right]:rotate-180", variant === "floating" || variant === "inset" ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)")
271
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
272
+ "data-slot": "sidebar-container",
273
+ className: cn("fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex", side === "left" ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]", variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l", className),
274
+ ...props,
275
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
276
+ "data-sidebar": "sidebar",
277
+ "data-slot": "sidebar-inner",
278
+ className: "flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow-sm",
279
+ children
280
+ })
281
+ })]
282
+ });
283
+ }
284
+ function SidebarTrigger({ className, onClick, ...props }) {
285
+ const { toggleSidebar } = useSidebar();
286
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
287
+ "data-sidebar": "trigger",
288
+ "data-slot": "sidebar-trigger",
289
+ variant: "ghost",
290
+ size: "icon",
291
+ className: cn("size-7", className),
292
+ onClick: (event) => {
293
+ onClick?.(event);
294
+ toggleSidebar();
295
+ },
296
+ ...props,
297
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PanelLeft, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
298
+ className: "sr-only",
299
+ children: "Toggle Sidebar"
300
+ })]
301
+ });
302
+ }
303
+ function SidebarInset({ className, ...props }) {
304
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("main", {
305
+ "data-slot": "sidebar-inset",
306
+ className: cn("relative flex w-full flex-1 flex-col bg-background", "md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2", className),
307
+ ...props
308
+ });
309
+ }
310
+ function SidebarContent({ className, ...props }) {
311
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
312
+ "data-slot": "sidebar-content",
313
+ "data-sidebar": "content",
314
+ className: cn("flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden", className),
315
+ ...props
316
+ });
317
+ }
318
+ function SidebarGroup({ className, ...props }) {
319
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
320
+ "data-slot": "sidebar-group",
321
+ "data-sidebar": "group",
322
+ className: cn("relative flex w-full min-w-0 flex-col p-2", className),
323
+ ...props
324
+ });
325
+ }
326
+ function SidebarGroupLabel({ className, asChild = false, ...props }) {
327
+ const Comp = asChild ? Slot : "div";
328
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Comp, {
329
+ "data-slot": "sidebar-group-label",
330
+ "data-sidebar": "group-label",
331
+ className: cn("flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0", className),
332
+ ...props
333
+ });
334
+ }
335
+ function SidebarGroupContent({ className, ...props }) {
336
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
337
+ "data-slot": "sidebar-group-content",
338
+ "data-sidebar": "group-content",
339
+ className: cn("w-full text-sm", className),
340
+ ...props
341
+ });
342
+ }
343
+ function SidebarMenu({ className, ...props }) {
344
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
345
+ "data-slot": "sidebar-menu",
346
+ "data-sidebar": "menu",
347
+ className: cn("flex w-full min-w-0 flex-col gap-1", className),
348
+ ...props
349
+ });
350
+ }
351
+ function SidebarMenuItem({ className, ...props }) {
352
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", {
353
+ "data-slot": "sidebar-menu-item",
354
+ "data-sidebar": "menu-item",
355
+ className: cn("group/menu-item relative", className),
356
+ ...props
357
+ });
358
+ }
359
+ var sidebarMenuButtonVariants = cva("peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", {
360
+ variants: {
361
+ variant: {
362
+ default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
363
+ outline: "bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_var(--sidebar-accent)]"
364
+ },
365
+ size: {
366
+ default: "h-8 text-sm",
367
+ sm: "h-7 text-xs",
368
+ lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!"
369
+ }
370
+ },
371
+ defaultVariants: {
372
+ variant: "default",
373
+ size: "default"
374
+ }
375
+ });
376
+ function SidebarMenuButton({ asChild = false, isActive = false, variant = "default", size = "default", tooltip, className, ...props }) {
377
+ const Comp = asChild ? Slot : "button";
378
+ const { isMobile, state } = useSidebar();
379
+ const button = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Comp, {
380
+ "data-slot": "sidebar-menu-button",
381
+ "data-sidebar": "menu-button",
382
+ "data-size": size,
383
+ "data-active": isActive,
384
+ className: cn(sidebarMenuButtonVariants({
385
+ variant,
386
+ size
387
+ }), className),
388
+ ...props
389
+ });
390
+ if (!tooltip) return button;
391
+ if (typeof tooltip === "string") tooltip = { children: tooltip };
392
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tooltip$1, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipTrigger, {
393
+ asChild: true,
394
+ children: button
395
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipContent, {
396
+ side: "right",
397
+ align: "center",
398
+ hidden: state !== "collapsed" || isMobile,
399
+ ...tooltip
400
+ })] });
401
+ }
402
+ function SidebarMenuBadge({ className, ...props }) {
403
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
404
+ "data-slot": "sidebar-menu-badge",
405
+ "data-sidebar": "menu-badge",
406
+ className: cn("pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium text-sidebar-foreground tabular-nums select-none", "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground", "peer-data-[size=sm]/menu-button:top-1", "peer-data-[size=default]/menu-button:top-1.5", "peer-data-[size=lg]/menu-button:top-2.5", "group-data-[collapsible=icon]:hidden", className),
407
+ ...props
408
+ });
409
+ }
410
+ function AppSidebar({ notifications, activeView }) {
411
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Sidebar, {
412
+ collapsible: "icon",
413
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarContent, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SidebarGroup, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarGroupLabel, { children: "gitxp" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarGroupContent, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarMenu, { children: views.map((view) => {
414
+ const count = notifications.filter(view.match).length;
415
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SidebarMenuItem, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarMenuButton, {
416
+ asChild: true,
417
+ isActive: view.id === activeView,
418
+ tooltip: view.label,
419
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Link, {
420
+ to: "/",
421
+ search: { view: view.id },
422
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: view.label })
423
+ })
424
+ }), count > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarMenuBadge, { children: count }) : null] }, view.id);
425
+ }) }) })] }) })
426
+ });
427
+ }
428
+ function Dialog$1({ ...props }) {
429
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog, {
430
+ "data-slot": "dialog",
431
+ ...props
432
+ });
433
+ }
434
+ function DialogPortal({ ...props }) {
435
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogPortal$1, {
436
+ "data-slot": "dialog-portal",
437
+ ...props
438
+ });
439
+ }
440
+ function DialogOverlay({ className, ...props }) {
441
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogOverlay$1, {
442
+ "data-slot": "dialog-overlay",
443
+ className: cn("fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0", className),
444
+ ...props
445
+ });
446
+ }
447
+ function DialogContent({ className, children, showCloseButton = true, ...props }) {
448
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogPortal, {
449
+ "data-slot": "dialog-portal",
450
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogOverlay, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogContent$1, {
451
+ "data-slot": "dialog-content",
452
+ className: cn("fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg", className),
453
+ ...props,
454
+ children: [children, showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogClose, {
455
+ "data-slot": "dialog-close",
456
+ className: "absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
457
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
458
+ className: "sr-only",
459
+ children: "Close"
460
+ })]
461
+ })]
462
+ })]
463
+ });
464
+ }
465
+ function DialogHeader({ className, ...props }) {
466
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
467
+ "data-slot": "dialog-header",
468
+ className: cn("flex flex-col gap-2 text-center sm:text-left", className),
469
+ ...props
470
+ });
471
+ }
472
+ function DialogTitle({ className, ...props }) {
473
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogTitle$1, {
474
+ "data-slot": "dialog-title",
475
+ className: cn("text-lg leading-none font-semibold", className),
476
+ ...props
477
+ });
478
+ }
479
+ function DialogDescription({ className, ...props }) {
480
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogDescription$1, {
481
+ "data-slot": "dialog-description",
482
+ className: cn("text-sm text-muted-foreground", className),
483
+ ...props
484
+ });
485
+ }
486
+ function Command$1({ className, ...props }) {
487
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(_e, {
488
+ "data-slot": "command",
489
+ className: cn("flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", className),
490
+ ...props
491
+ });
492
+ }
493
+ function CommandDialog({ title = "Command Palette", description = "Search for a command to run...", children, className, showCloseButton = true, ...props }) {
494
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Dialog$1, {
495
+ ...props,
496
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogHeader, {
497
+ className: "sr-only",
498
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogTitle, { children: title }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogDescription, { children: description })]
499
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogContent, {
500
+ className: cn("overflow-hidden p-0", className),
501
+ showCloseButton,
502
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Command$1, {
503
+ className: "**:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5",
504
+ children
505
+ })
506
+ })]
507
+ });
508
+ }
509
+ function CommandInput({ className, ...props }) {
510
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
511
+ "data-slot": "command-input-wrapper",
512
+ className: "flex h-9 items-center gap-2 border-b px-3",
513
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Search, { className: "size-4 shrink-0 opacity-50" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(_e.Input, {
514
+ "data-slot": "command-input",
515
+ className: cn("flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50", className),
516
+ ...props
517
+ })]
518
+ });
519
+ }
520
+ function CommandList({ className, ...props }) {
521
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(_e.List, {
522
+ "data-slot": "command-list",
523
+ className: cn("max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className),
524
+ ...props
525
+ });
526
+ }
527
+ function CommandEmpty({ ...props }) {
528
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(_e.Empty, {
529
+ "data-slot": "command-empty",
530
+ className: "py-6 text-center text-sm",
531
+ ...props
532
+ });
533
+ }
534
+ function CommandGroup({ className, ...props }) {
535
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(_e.Group, {
536
+ "data-slot": "command-group",
537
+ className: cn("overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground", className),
538
+ ...props
539
+ });
540
+ }
541
+ function CommandItem({ className, ...props }) {
542
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(_e.Item, {
543
+ "data-slot": "command-item",
544
+ className: cn("relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground", className),
545
+ ...props
546
+ });
547
+ }
548
+ function CommandShortcut({ className, ...props }) {
549
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
550
+ "data-slot": "command-shortcut",
551
+ className: cn("ml-auto text-xs tracking-widest text-muted-foreground", className),
552
+ ...props
553
+ });
554
+ }
555
+ function CommandPalette({ onSelectView, onOpenSelected, onRefresh, onDoneSelected, onReadSelected, canOpenSelected }) {
556
+ const [open, setOpen] = (0, import_react.useState)(false);
557
+ (0, import_react.useEffect)(() => {
558
+ const onKeyDown = (event) => {
559
+ if (event.key === "k" && (event.metaKey || event.ctrlKey)) {
560
+ event.preventDefault();
561
+ setOpen((previous) => !previous);
562
+ }
563
+ };
564
+ window.addEventListener("keydown", onKeyDown);
565
+ return () => {
566
+ window.removeEventListener("keydown", onKeyDown);
567
+ };
568
+ }, []);
569
+ const run = (action) => {
570
+ setOpen(false);
571
+ action();
572
+ };
573
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CommandDialog, {
574
+ open,
575
+ onOpenChange: setOpen,
576
+ title: "Commands",
577
+ description: "Jump to a view or act on the selection",
578
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandInput, { placeholder: "Type a command..." }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CommandList, { children: [
579
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandEmpty, { children: "No matching command" }),
580
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandGroup, {
581
+ heading: "Views",
582
+ children: views.map((view) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandItem, {
583
+ value: `view ${view.label}`,
584
+ onSelect: () => run(() => onSelectView(view.id)),
585
+ children: view.label
586
+ }, view.id))
587
+ }),
588
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CommandGroup, {
589
+ heading: "Actions",
590
+ children: [
591
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandItem, {
592
+ value: "sync refresh",
593
+ onSelect: () => run(onRefresh),
594
+ children: "Sync now"
595
+ }),
596
+ canOpenSelected ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CommandItem, {
597
+ value: "open github",
598
+ onSelect: () => run(onOpenSelected),
599
+ children: ["Open on GitHub", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandShortcut, { children: "Enter" })]
600
+ }) : null,
601
+ onDoneSelected === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CommandItem, {
602
+ value: "done archive",
603
+ onSelect: () => run(onDoneSelected),
604
+ children: ["Mark as done", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandShortcut, { children: "e" })]
605
+ }),
606
+ onReadSelected === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(CommandItem, {
607
+ value: "read",
608
+ onSelect: () => run(onReadSelected),
609
+ children: ["Mark as read", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandShortcut, { children: "r" })]
610
+ })
611
+ ]
612
+ })
613
+ ] })]
614
+ });
615
+ }
616
+ function FailedActions() {
617
+ const queryClient = useQueryClient();
618
+ const failed = useQuery({
619
+ queryKey: ["failed-mutations"],
620
+ queryFn: () => getFailedMutations(),
621
+ refetchInterval: 1e4
622
+ });
623
+ const retry = useMutation({
624
+ mutationFn: () => retryFailedMutations(),
625
+ onSettled: () => queryClient.invalidateQueries({ queryKey: ["failed-mutations"] })
626
+ });
627
+ const rows = failed.data ?? [];
628
+ if (rows.length === 0) return null;
629
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
630
+ className: "flex items-center gap-2 border-b bg-destructive/10 px-3 py-2 text-xs",
631
+ children: [
632
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
633
+ className: "text-destructive",
634
+ children: [
635
+ rows.length,
636
+ " action",
637
+ rows.length === 1 ? "" : "s",
638
+ " failed"
639
+ ]
640
+ }),
641
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
642
+ className: "truncate text-muted-foreground",
643
+ children: rows[0].lastError
644
+ }),
645
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
646
+ size: "sm",
647
+ variant: "outline",
648
+ className: "ml-auto h-6 shrink-0 text-xs",
649
+ disabled: retry.isPending,
650
+ onClick: () => retry.mutate(),
651
+ children: retry.isPending ? "Retrying" : "Retry"
652
+ })
653
+ ]
654
+ });
655
+ }
656
+ function NotificationList({ notifications, selectedId, onSelect, onOpen, onDone, onRead, emptyMessage }) {
657
+ const listRef = (0, import_react.useRef)(null);
658
+ const selectedIndex = notifications.findIndex((notification) => notification.thread_id === selectedId);
659
+ (0, import_react.useEffect)(() => {
660
+ listRef.current?.querySelector("[data-selected=\"true\"]")?.scrollIntoView({ block: "nearest" });
661
+ }, [selectedId]);
662
+ const move = (delta) => {
663
+ if (notifications.length === 0) return;
664
+ onSelect(notifications[Math.min(Math.max(selectedIndex === -1 ? 0 : selectedIndex + delta, 0), notifications.length - 1)].thread_id);
665
+ };
666
+ const handleKeyDown = (event) => {
667
+ if (event.key === "ArrowDown" || event.key === "j") {
668
+ event.preventDefault();
669
+ move(1);
670
+ return;
671
+ }
672
+ if (event.key === "ArrowUp" || event.key === "k") {
673
+ event.preventDefault();
674
+ move(-1);
675
+ return;
676
+ }
677
+ if (event.key === "Enter") {
678
+ event.preventDefault();
679
+ onOpen();
680
+ return;
681
+ }
682
+ if (selectedId === void 0 || notifications.length === 0) return;
683
+ if (event.key === "e") {
684
+ event.preventDefault();
685
+ const next = notifications[Math.min(selectedIndex + 1, notifications.length - 1)];
686
+ onDone(selectedId);
687
+ if (next.thread_id !== selectedId) onSelect(next.thread_id);
688
+ return;
689
+ }
690
+ if (event.key === "r") {
691
+ event.preventDefault();
692
+ onRead(selectedId);
693
+ }
694
+ };
695
+ if (notifications.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
696
+ className: "flex h-full items-center justify-center p-8 text-sm text-muted-foreground",
697
+ children: emptyMessage
698
+ });
699
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
700
+ ref: listRef,
701
+ tabIndex: 0,
702
+ onKeyDown: handleKeyDown,
703
+ className: "h-full overflow-y-auto outline-none",
704
+ "aria-label": "Notifications",
705
+ children: notifications.map((notification) => {
706
+ const selected = notification.thread_id === selectedId;
707
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", {
708
+ "data-selected": selected,
709
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
710
+ type: "button",
711
+ onClick: () => onSelect(notification.thread_id),
712
+ onDoubleClick: onOpen,
713
+ className: cn("flex w-full flex-col gap-1 border-l-2 px-3 py-2 text-left", selected ? "border-l-primary bg-accent" : "border-l-transparent hover:bg-accent/50"),
714
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
715
+ className: "flex items-baseline gap-2",
716
+ children: [
717
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cn("size-1.5 shrink-0 rounded-full", notification.unread === 1 ? "bg-primary" : "bg-transparent") }),
718
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
719
+ className: "truncate font-mono text-xs text-muted-foreground",
720
+ children: notification.repo_full_name
721
+ }),
722
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
723
+ className: "ml-auto shrink-0 text-xs text-muted-foreground",
724
+ children: reasonLabel(notification.reason)
725
+ })
726
+ ]
727
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
728
+ className: cn("line-clamp-2 pl-3.5 text-sm", notification.unread === 1 ? "font-medium" : "text-muted-foreground"),
729
+ children: notification.subject_title
730
+ })]
731
+ })
732
+ }, notification.thread_id);
733
+ })
734
+ });
735
+ }
736
+ var badgeVariants = cva("inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3", {
737
+ variants: { variant: {
738
+ default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
739
+ secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
740
+ destructive: "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",
741
+ outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
742
+ ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
743
+ link: "text-primary underline-offset-4 [a&]:hover:underline"
744
+ } },
745
+ defaultVariants: { variant: "default" }
746
+ });
747
+ function Badge({ className, variant = "default", asChild = false, ...props }) {
748
+ const Comp = asChild ? Slot : "span";
749
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Comp, {
750
+ "data-slot": "badge",
751
+ "data-variant": variant,
752
+ className: cn(badgeVariants({ variant }), className),
753
+ ...props
754
+ });
755
+ }
756
+ function NotificationPreview({ notification }) {
757
+ if (notification === void 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
758
+ className: "flex h-full items-center justify-center p-8 text-sm text-muted-foreground",
759
+ children: "Select a notification"
760
+ });
761
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
762
+ className: "flex h-full flex-col gap-4 overflow-y-auto p-6",
763
+ children: [
764
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
765
+ className: "flex flex-col gap-2",
766
+ children: [
767
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
768
+ className: "font-mono text-xs text-muted-foreground",
769
+ children: notification.repo_full_name
770
+ }),
771
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", {
772
+ className: "text-lg font-medium",
773
+ children: notification.subject_title
774
+ }),
775
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
776
+ className: "flex flex-wrap items-center gap-2",
777
+ children: [
778
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
779
+ variant: "secondary",
780
+ children: reasonLabel(notification.reason)
781
+ }),
782
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
783
+ variant: "outline",
784
+ children: notification.subject_type
785
+ }),
786
+ notification.unread === 1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, { children: "Unread" }) : null
787
+ ]
788
+ })
789
+ ]
790
+ }),
791
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dl", {
792
+ className: "grid grid-cols-[auto_1fr] gap-x-4 gap-y-1 text-sm",
793
+ children: [
794
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
795
+ className: "text-muted-foreground",
796
+ children: "Updated"
797
+ }),
798
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: new Date(notification.updated_at).toLocaleString() }),
799
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
800
+ className: "text-muted-foreground",
801
+ children: "Last read"
802
+ }),
803
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: notification.last_read_at === null ? "Never" : new Date(notification.last_read_at).toLocaleString() })
804
+ ]
805
+ }),
806
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
807
+ asChild: true,
808
+ variant: "outline",
809
+ size: "sm",
810
+ className: "self-start",
811
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
812
+ href: webUrlFor(notification),
813
+ target: "_blank",
814
+ rel: "noreferrer",
815
+ children: "Open on GitHub"
816
+ })
817
+ }),
818
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
819
+ className: "mt-auto text-xs text-muted-foreground",
820
+ children: "The pull request itself, its diff and its checks land here next."
821
+ })
822
+ ]
823
+ });
824
+ }
825
+ var notificationsCollection = createCollection({
826
+ id: "notifications",
827
+ getKey: (row) => row.thread_id,
828
+ sync: {
829
+ rowUpdateMode: "full",
830
+ sync: ({ begin, write, commit, markReady, markError, truncate, collection }) => {
831
+ let cancelled = false;
832
+ let inFlight;
833
+ let queued = false;
834
+ const apply = async () => {
835
+ const rows = await getNotifications();
836
+ if (cancelled) return;
837
+ begin();
838
+ truncate();
839
+ for (const row of rows) write({
840
+ type: "insert",
841
+ value: row
842
+ });
843
+ commit();
844
+ markReady();
845
+ };
846
+ const load = () => {
847
+ if (inFlight) {
848
+ queued = true;
849
+ return inFlight;
850
+ }
851
+ inFlight = apply().catch((error) => {
852
+ if (cancelled) return;
853
+ console.error("notifications sync failed", error);
854
+ if (collection.status === "loading") markError(error);
855
+ }).finally(() => {
856
+ inFlight = void 0;
857
+ if (queued && !cancelled) {
858
+ queued = false;
859
+ load();
860
+ }
861
+ });
862
+ return inFlight;
863
+ };
864
+ if (typeof window === "undefined") return () => {
865
+ cancelled = true;
866
+ };
867
+ const source = new EventSource("/api/events");
868
+ source.addEventListener("change", () => {
869
+ load();
870
+ });
871
+ load();
872
+ return () => {
873
+ cancelled = true;
874
+ source.close();
875
+ };
876
+ }
877
+ },
878
+ onUpdate: async ({ transaction }) => {
879
+ for (const mutation of transaction.mutations) if (mutation.changes.unread === 0) await markNotificationRead({ data: mutation.key });
880
+ },
881
+ onDelete: async ({ transaction }) => {
882
+ for (const mutation of transaction.mutations) await markNotificationDone({ data: mutation.key });
883
+ }
884
+ });
885
+ function Inbox() {
886
+ const { view, thread } = Route$1.useSearch();
887
+ const navigate = useNavigate({ from: Route$1.fullPath });
888
+ const initial = Route$1.useLoaderData();
889
+ const { data, isLoading } = useLiveQuery((q) => q.from({ notification: notificationsCollection }));
890
+ const rows = isLoading ? initial : data;
891
+ const firstSyncPending = useQuery({
892
+ queryKey: ["sync-status"],
893
+ queryFn: () => getSyncStatus(),
894
+ refetchInterval: (query) => query.state.data?.hasSynced === true ? false : 2e3
895
+ }).data?.hasSynced !== true;
896
+ const all = (0, import_react.useMemo)(() => [...rows].sort((a, b) => b.updated_at.localeCompare(a.updated_at)), [rows]);
897
+ const active = views.find((candidate) => candidate.id === view) ?? views[0];
898
+ const visible = (0, import_react.useMemo)(() => all.filter(active.match), [all, active]);
899
+ const selected = visible.find((notification) => notification.thread_id === thread);
900
+ const select = (threadId) => {
901
+ navigate({
902
+ search: (previous) => ({
903
+ ...previous,
904
+ thread: threadId
905
+ }),
906
+ replace: true
907
+ });
908
+ };
909
+ const selectView = (next) => {
910
+ navigate({
911
+ search: { view: next },
912
+ replace: true
913
+ });
914
+ };
915
+ const markDone = (threadId) => {
916
+ notificationsCollection.delete(threadId);
917
+ };
918
+ const markRead = (threadId) => {
919
+ notificationsCollection.update(threadId, (draft) => {
920
+ draft.unread = 0;
921
+ });
922
+ };
923
+ const openSelected = () => {
924
+ if (selected === void 0) return;
925
+ window.open(webUrlFor(selected), "_blank", "noreferrer");
926
+ };
927
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(SidebarProvider, { children: [
928
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppSidebar, {
929
+ notifications: all,
930
+ activeView: active.id
931
+ }),
932
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarInset, {
933
+ className: "h-svh overflow-hidden",
934
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
935
+ className: "grid h-full grid-cols-[minmax(280px,380px)_1fr]",
936
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
937
+ className: "flex min-h-0 flex-col border-r",
938
+ children: [
939
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
940
+ className: "flex items-center gap-2 border-b px-3 py-2",
941
+ children: [
942
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarTrigger, {}),
943
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
944
+ className: "text-sm font-medium",
945
+ children: active.label
946
+ }),
947
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
948
+ className: "text-xs text-muted-foreground",
949
+ children: firstSyncPending && all.length === 0 ? "syncing" : visible.length
950
+ }),
951
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("kbd", {
952
+ className: "ml-auto rounded border px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground",
953
+ children: "⌘K"
954
+ })
955
+ ]
956
+ }),
957
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FailedActions, {}),
958
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NotificationList, {
959
+ notifications: visible,
960
+ selectedId: selected?.thread_id,
961
+ onSelect: select,
962
+ onOpen: openSelected,
963
+ onDone: markDone,
964
+ onRead: markRead,
965
+ emptyMessage: firstSyncPending && all.length === 0 ? "Fetching your notifications from GitHub..." : "Nothing here"
966
+ })
967
+ ]
968
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NotificationPreview, { notification: selected })]
969
+ })
970
+ }),
971
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommandPalette, {
972
+ onSelectView: selectView,
973
+ onOpenSelected: openSelected,
974
+ onRefresh: () => void syncNow(),
975
+ onDoneSelected: selected === void 0 ? void 0 : () => markDone(selected.thread_id),
976
+ onReadSelected: selected === void 0 ? void 0 : () => markRead(selected.thread_id),
977
+ canOpenSelected: selected !== void 0
978
+ })
979
+ ] });
980
+ }
981
+ //#endregion
982
+ export { Inbox as component };