a very good jj gui
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

style: code formatting and trailing comma fixes

+191 -202
-1
apps/desktop/src-tauri/src/repo/log.rs
··· 50 50 }) 51 51 .collect::<Result<Vec<_>>>()?; 52 52 53 - 54 53 let immutable_expression = RevsetExpression::root(); 55 54 let immutable_revset = immutable_expression.evaluate(repo.as_ref())?; 56 55 let immutable_ids: Vec<CommitId> = immutable_revset.iter().collect::<Result<Vec<_>, _>>()?;
+6 -6
apps/desktop/src-tauri/src/watcher.rs
··· 1 1 use notify::{RecommendedWatcher, RecursiveMode}; 2 - use notify_debouncer_mini::{new_debouncer, DebounceEventResult, Debouncer}; 2 + use notify_debouncer_mini::{DebounceEventResult, Debouncer, new_debouncer}; 3 3 use std::collections::HashMap; 4 4 use std::path::PathBuf; 5 5 use std::sync::Mutex; ··· 27 27 let jj_repo_path = repo_path.join(".jj").join("repo"); 28 28 29 29 if !jj_repo_path.exists() { 30 - return Err(format!( 31 - "Not a jj repository: {}", 32 - repo_path.display() 33 - )); 30 + return Err(format!("Not a jj repository: {}", repo_path.display())); 34 31 } 35 32 36 33 let app_handle = app.clone(); ··· 40 37 Duration::from_millis(500), 41 38 move |result: DebounceEventResult| { 42 39 if result.is_ok() { 43 - let _ = app_handle.emit("repo-changed", repo_path_clone.to_string_lossy().to_string()); 40 + let _ = app_handle.emit( 41 + "repo-changed", 42 + repo_path_clone.to_string_lossy().to_string(), 43 + ); 44 44 } 45 45 }, 46 46 )
+1 -4
apps/desktop/src/components/CommandPalette.tsx
··· 43 43 <CommandEmpty>No projects found.</CommandEmpty> 44 44 <CommandGroup heading="Projects"> 45 45 {projects.map((project) => ( 46 - <CommandItem 47 - key={project.id} 48 - onSelect={() => handleSelectProject(project)} 49 - > 46 + <CommandItem key={project.id} onSelect={() => handleSelectProject(project)}> 50 47 <FolderOpen className="mr-2 h-4 w-4" /> 51 48 <span>{project.name}</span> 52 49 </CommandItem>
+48 -43
apps/desktop/src/components/ui/button.tsx
··· 1 - import { Button as ButtonPrimitive } from "@base-ui/react/button" 2 - import { cva, type VariantProps } from "class-variance-authority" 1 + import { Button as ButtonPrimitive } from "@base-ui/react/button"; 2 + import { cva, type VariantProps } from "class-variance-authority"; 3 3 4 - import { cn } from "@/lib/utils" 4 + import { cn } from "@/lib/utils"; 5 5 6 6 const buttonVariants = cva( 7 - "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-sm border border-transparent bg-clip-padding text-xs font-medium focus-visible:ring-1 aria-invalid:ring-1 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none", 8 - { 9 - variants: { 10 - variant: { 11 - default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", 12 - outline: "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground", 13 - secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground", 14 - ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground", 15 - destructive: "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30", 16 - link: "text-primary underline-offset-4 hover:underline", 17 - }, 18 - size: { 19 - default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", 20 - xs: "h-6 gap-1 px-2 text-xs has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", 21 - sm: "h-7 gap-1 px-2.5 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", 22 - lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3", 23 - icon: "size-8", 24 - "icon-xs": "size-6 [&_svg:not([class*='size-'])]:size-3", 25 - "icon-sm": "size-7", 26 - "icon-lg": "size-9", 27 - }, 28 - }, 29 - defaultVariants: { 30 - variant: "default", 31 - size: "default", 32 - }, 33 - } 34 - ) 7 + "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-sm border border-transparent bg-clip-padding text-xs font-medium focus-visible:ring-1 aria-invalid:ring-1 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none", 8 + { 9 + variants: { 10 + variant: { 11 + default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", 12 + outline: 13 + "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground", 14 + secondary: 15 + "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground", 16 + ghost: 17 + "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground", 18 + destructive: 19 + "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30", 20 + link: "text-primary underline-offset-4 hover:underline", 21 + }, 22 + size: { 23 + default: 24 + "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", 25 + xs: "h-6 gap-1 px-2 text-xs has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", 26 + sm: "h-7 gap-1 px-2.5 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", 27 + lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3", 28 + icon: "size-8", 29 + "icon-xs": "size-6 [&_svg:not([class*='size-'])]:size-3", 30 + "icon-sm": "size-7", 31 + "icon-lg": "size-9", 32 + }, 33 + }, 34 + defaultVariants: { 35 + variant: "default", 36 + size: "default", 37 + }, 38 + }, 39 + ); 35 40 36 41 function Button({ 37 - className, 38 - variant = "default", 39 - size = "default", 40 - ...props 42 + className, 43 + variant = "default", 44 + size = "default", 45 + ...props 41 46 }: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) { 42 - return ( 43 - <ButtonPrimitive 44 - data-slot="button" 45 - className={cn(buttonVariants({ variant, size, className }))} 46 - {...props} 47 - /> 48 - ) 47 + return ( 48 + <ButtonPrimitive 49 + data-slot="button" 50 + className={cn(buttonVariants({ variant, size, className }))} 51 + {...props} 52 + /> 53 + ); 49 54 } 50 55 51 - export { Button, buttonVariants } 56 + export { Button, buttonVariants };
+11 -26
apps/desktop/src/components/ui/command.tsx
··· 14 14 } from "@/components/ui/dialog"; 15 15 import { InputGroup, InputGroupAddon } from "@/components/ui/input-group"; 16 16 17 - function Command({ 18 - className, 19 - ...props 20 - }: React.ComponentProps<typeof CommandPrimitive>) { 17 + function Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) { 21 18 return ( 22 19 <CommandPrimitive 23 20 data-slot="command" 24 21 className={cn( 25 22 "bg-popover text-popover-foreground rounded-sm flex size-full flex-col overflow-hidden", 26 - className 23 + className, 27 24 )} 28 25 {...props} 29 26 /> ··· 65 62 ...props 66 63 }: React.ComponentProps<typeof CommandPrimitive.Input>) { 67 64 return ( 68 - <div 69 - data-slot="command-input-wrapper" 70 - className="border-b pb-0" 71 - > 65 + <div data-slot="command-input-wrapper" className="border-b pb-0"> 72 66 <InputGroup className="bg-input/30 border-input/30 h-8 border-none shadow-none! *:data-[slot=input-group-addon]:pl-2!"> 73 67 <CommandPrimitive.Input 74 68 data-slot="command-input" 75 69 className={cn( 76 70 "w-full text-xs outline-hidden disabled:cursor-not-allowed disabled:opacity-50", 77 - className 71 + className, 78 72 )} 79 73 {...props} 80 74 /> ··· 86 80 ); 87 81 } 88 82 89 - function CommandList({ 90 - className, 91 - ...props 92 - }: React.ComponentProps<typeof CommandPrimitive.List>) { 83 + function CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) { 93 84 return ( 94 85 <CommandPrimitive.List 95 86 data-slot="command-list" 96 87 className={cn( 97 88 "no-scrollbar max-h-72 scroll-py-0 outline-none overflow-x-hidden overflow-y-auto", 98 - className 89 + className, 99 90 )} 100 91 {...props} 101 92 /> ··· 124 115 data-slot="command-group" 125 116 className={cn( 126 117 "text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs", 127 - className 118 + className, 128 119 )} 129 120 {...props} 130 121 /> ··· 144 135 ); 145 136 } 146 137 147 - function CommandItem({ 148 - className, 149 - ...props 150 - }: React.ComponentProps<typeof CommandPrimitive.Item>) { 138 + function CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) { 151 139 return ( 152 140 <CommandPrimitive.Item 153 141 data-slot="command-item" 154 142 className={cn( 155 143 "data-selected:bg-muted data-selected:text-foreground data-selected:*:[svg]:text-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-2 text-xs outline-hidden select-none [&_svg:not([class*='size-'])]:size-4 in-data-[slot=dialog-content]:rounded-sm! group/command-item data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", 156 - className 144 + className, 157 145 )} 158 146 {...props} 159 147 /> 160 148 ); 161 149 } 162 150 163 - function CommandShortcut({ 164 - className, 165 - ...props 166 - }: React.ComponentProps<"span">) { 151 + function CommandShortcut({ className, ...props }: React.ComponentProps<"span">) { 167 152 return ( 168 153 <span 169 154 data-slot="command-shortcut" 170 155 className={cn( 171 156 "text-muted-foreground group-data-selected/command-item:text-foreground ml-auto text-xs tracking-widest", 172 - className 157 + className, 173 158 )} 174 159 {...props} 175 160 />
+102 -114
apps/desktop/src/components/ui/dialog.tsx
··· 1 - import * as React from "react" 2 - import { Dialog as DialogPrimitive } from "@base-ui/react/dialog" 1 + import * as React from "react"; 2 + import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"; 3 3 4 - import { cn } from "@/lib/utils" 5 - import { Button } from "@/components/ui/button" 6 - import { XIcon } from "lucide-react" 4 + import { cn } from "@/lib/utils"; 5 + import { Button } from "@/components/ui/button"; 6 + import { XIcon } from "lucide-react"; 7 7 8 8 function Dialog({ ...props }: DialogPrimitive.Root.Props) { 9 - return <DialogPrimitive.Root data-slot="dialog" {...props} /> 9 + return <DialogPrimitive.Root data-slot="dialog" {...props} />; 10 10 } 11 11 12 12 function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) { 13 - return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} /> 13 + return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />; 14 14 } 15 15 16 16 function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) { 17 - return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} /> 17 + return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />; 18 18 } 19 19 20 20 function DialogClose({ ...props }: DialogPrimitive.Close.Props) { 21 - return <DialogPrimitive.Close data-slot="dialog-close" {...props} /> 21 + return <DialogPrimitive.Close data-slot="dialog-close" {...props} />; 22 22 } 23 23 24 - function DialogOverlay({ 25 - className, 26 - ...props 27 - }: DialogPrimitive.Backdrop.Props) { 28 - return ( 29 - <DialogPrimitive.Backdrop 30 - data-slot="dialog-overlay" 31 - className={cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50", className)} 32 - {...props} 33 - /> 34 - ) 24 + function DialogOverlay({ className, ...props }: DialogPrimitive.Backdrop.Props) { 25 + return ( 26 + <DialogPrimitive.Backdrop 27 + data-slot="dialog-overlay" 28 + className={cn( 29 + "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50", 30 + className, 31 + )} 32 + {...props} 33 + /> 34 + ); 35 35 } 36 36 37 37 function DialogContent({ 38 - className, 39 - children, 40 - showCloseButton = true, 41 - ...props 38 + className, 39 + children, 40 + showCloseButton = true, 41 + ...props 42 42 }: DialogPrimitive.Popup.Props & { 43 - showCloseButton?: boolean 43 + showCloseButton?: boolean; 44 44 }) { 45 - return ( 46 - <DialogPortal> 47 - <DialogOverlay /> 48 - <DialogPrimitive.Popup 49 - data-slot="dialog-content" 50 - className={cn( 51 - "bg-background data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-foreground/10 grid max-w-[calc(100%-2rem)] gap-4 rounded-sm p-4 text-xs/relaxed ring-1 duration-100 sm:max-w-sm fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 outline-none", 52 - className 53 - )} 54 - {...props} 55 - > 56 - {children} 57 - {showCloseButton && ( 58 - <DialogPrimitive.Close 59 - data-slot="dialog-close" 60 - render={ 61 - <Button 62 - variant="ghost" 63 - className="absolute top-2 right-2" 64 - size="icon-sm" 65 - /> 66 - } 67 - > 68 - <XIcon 69 - /> 70 - <span className="sr-only">Close</span> 71 - </DialogPrimitive.Close> 72 - )} 73 - </DialogPrimitive.Popup> 74 - </DialogPortal> 75 - ) 45 + return ( 46 + <DialogPortal> 47 + <DialogOverlay /> 48 + <DialogPrimitive.Popup 49 + data-slot="dialog-content" 50 + className={cn( 51 + "bg-background data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-foreground/10 grid max-w-[calc(100%-2rem)] gap-4 rounded-sm p-4 text-xs/relaxed ring-1 duration-100 sm:max-w-sm fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 outline-none", 52 + className, 53 + )} 54 + {...props} 55 + > 56 + {children} 57 + {showCloseButton && ( 58 + <DialogPrimitive.Close 59 + data-slot="dialog-close" 60 + render={<Button variant="ghost" className="absolute top-2 right-2" size="icon-sm" />} 61 + > 62 + <XIcon /> 63 + <span className="sr-only">Close</span> 64 + </DialogPrimitive.Close> 65 + )} 66 + </DialogPrimitive.Popup> 67 + </DialogPortal> 68 + ); 76 69 } 77 70 78 71 function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { 79 - return ( 80 - <div 81 - data-slot="dialog-header" 82 - className={cn("gap-1 text-left flex flex-col", className)} 83 - {...props} 84 - /> 85 - ) 72 + return ( 73 + <div 74 + data-slot="dialog-header" 75 + className={cn("gap-1 text-left flex flex-col", className)} 76 + {...props} 77 + /> 78 + ); 86 79 } 87 80 88 81 function DialogFooter({ 89 - className, 90 - showCloseButton = false, 91 - children, 92 - ...props 82 + className, 83 + showCloseButton = false, 84 + children, 85 + ...props 93 86 }: React.ComponentProps<"div"> & { 94 - showCloseButton?: boolean 87 + showCloseButton?: boolean; 95 88 }) { 96 - return ( 97 - <div 98 - data-slot="dialog-footer" 99 - className={cn( 100 - "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", 101 - className 102 - )} 103 - {...props} 104 - > 105 - {children} 106 - {showCloseButton && ( 107 - <DialogPrimitive.Close render={<Button variant="outline" />}> 108 - Close 109 - </DialogPrimitive.Close> 110 - )} 111 - </div> 112 - ) 89 + return ( 90 + <div 91 + data-slot="dialog-footer" 92 + className={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)} 93 + {...props} 94 + > 95 + {children} 96 + {showCloseButton && ( 97 + <DialogPrimitive.Close render={<Button variant="outline" />}>Close</DialogPrimitive.Close> 98 + )} 99 + </div> 100 + ); 113 101 } 114 102 115 103 function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) { 116 - return ( 117 - <DialogPrimitive.Title 118 - data-slot="dialog-title" 119 - className={cn("text-sm font-medium", className)} 120 - {...props} 121 - /> 122 - ) 104 + return ( 105 + <DialogPrimitive.Title 106 + data-slot="dialog-title" 107 + className={cn("text-sm font-medium", className)} 108 + {...props} 109 + /> 110 + ); 123 111 } 124 112 125 - function DialogDescription({ 126 - className, 127 - ...props 128 - }: DialogPrimitive.Description.Props) { 129 - return ( 130 - <DialogPrimitive.Description 131 - data-slot="dialog-description" 132 - className={cn("text-muted-foreground *:[a]:hover:text-foreground text-xs/relaxed *:[a]:underline *:[a]:underline-offset-3", className)} 133 - {...props} 134 - /> 135 - ) 113 + function DialogDescription({ className, ...props }: DialogPrimitive.Description.Props) { 114 + return ( 115 + <DialogPrimitive.Description 116 + data-slot="dialog-description" 117 + className={cn( 118 + "text-muted-foreground *:[a]:hover:text-foreground text-xs/relaxed *:[a]:underline *:[a]:underline-offset-3", 119 + className, 120 + )} 121 + {...props} 122 + /> 123 + ); 136 124 } 137 125 138 126 export { 139 - Dialog, 140 - DialogClose, 141 - DialogContent, 142 - DialogDescription, 143 - DialogFooter, 144 - DialogHeader, 145 - DialogOverlay, 146 - DialogPortal, 147 - DialogTitle, 148 - DialogTrigger, 149 - } 127 + Dialog, 128 + DialogClose, 129 + DialogContent, 130 + DialogDescription, 131 + DialogFooter, 132 + DialogHeader, 133 + DialogOverlay, 134 + DialogPortal, 135 + DialogTitle, 136 + DialogTrigger, 137 + };
+3 -1
apps/desktop/src/components/ui/input-group.tsx
··· 44 44 align = "inline-start", 45 45 ...props 46 46 }: React.ComponentProps<"fieldset"> & VariantProps<typeof inputGroupAddonVariants>) { 47 - const handleFocusInput = (e: React.MouseEvent<HTMLFieldSetElement> | React.KeyboardEvent<HTMLFieldSetElement>) => { 47 + const handleFocusInput = ( 48 + e: React.MouseEvent<HTMLFieldSetElement> | React.KeyboardEvent<HTMLFieldSetElement>, 49 + ) => { 48 50 if ((e.target as HTMLElement).closest("button")) { 49 51 return; 50 52 }
+18 -3
apps/desktop/src/mocks/tauri-core.ts
··· 207 207 new_count: 4, 208 208 lines: [ 209 209 { line_type: "context", content: "fn main() {", old_line_number: 1, new_line_number: 1 }, 210 - { line_type: "remove", content: ' println!("old");', old_line_number: 2, new_line_number: null }, 211 - { line_type: "add", content: ' println!("new");', old_line_number: null, new_line_number: 2 }, 212 - { line_type: "add", content: ' println!("extra");', old_line_number: null, new_line_number: 3 }, 210 + { 211 + line_type: "remove", 212 + content: ' println!("old");', 213 + old_line_number: 2, 214 + new_line_number: null, 215 + }, 216 + { 217 + line_type: "add", 218 + content: ' println!("new");', 219 + old_line_number: null, 220 + new_line_number: 2, 221 + }, 222 + { 223 + line_type: "add", 224 + content: ' println!("extra");', 225 + old_line_number: null, 226 + new_line_number: 3, 227 + }, 213 228 { line_type: "context", content: "}", old_line_number: 3, new_line_number: 4 }, 214 229 ], 215 230 },
+1 -4
apps/desktop/src/mocks/tauri-event.ts
··· 6 6 7 7 type EventCallback<T> = (event: Event<T>) => void; 8 8 9 - export async function listen<T>( 10 - _event: string, 11 - _handler: EventCallback<T>, 12 - ): Promise<UnlistenFn> { 9 + export async function listen<T>(_event: string, _handler: EventCallback<T>): Promise<UnlistenFn> { 13 10 // In mock mode, never emit events - just return a no-op unlisten 14 11 return () => {}; 15 12 }
+1
apps/desktop/src/styles/index.css
··· 103 103 @apply font-sans; 104 104 } 105 105 } 106 +