From dfcec238dce0b0a10c447a59848c0ff0c649be74 Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Fri, 14 Nov 2025 18:17:38 -0700 Subject: [PATCH 1/7] Add menu bar --- web/package.json | 1 + web/src/app.css | 1 + .../lib/components/menu-bar/MenuBar.svelte | 96 +++++ .../settings-popover/SettingsPopover.svelte | 44 -- .../components/settings/SettingsDialog.svelte | 77 ++++ .../SettingsGroup.svelte} | 5 +- .../ShikiThemeSelector.svelte | 0 .../SimpleRadioGroup.svelte | 0 web/src/lib/diff-viewer.svelte.ts | 2 + web/src/routes/+page.svelte | 392 ++++++++---------- web/src/routes/ActionsPopover.svelte | 37 -- web/src/routes/OpenDiffDialog.svelte | 9 +- 12 files changed, 348 insertions(+), 316 deletions(-) create mode 100644 web/src/lib/components/menu-bar/MenuBar.svelte delete mode 100644 web/src/lib/components/settings-popover/SettingsPopover.svelte create mode 100644 web/src/lib/components/settings/SettingsDialog.svelte rename web/src/lib/components/{settings-popover/SettingsPopoverGroup.svelte => settings/SettingsGroup.svelte} (80%) rename web/src/lib/components/{settings-popover => settings}/ShikiThemeSelector.svelte (100%) rename web/src/lib/components/{settings-popover => settings}/SimpleRadioGroup.svelte (100%) delete mode 100644 web/src/routes/ActionsPopover.svelte diff --git a/web/package.json b/web/package.json index a0bad4f..a574bdc 100644 --- a/web/package.json +++ b/web/package.json @@ -40,6 +40,7 @@ "svelte-adapter-bun": "^1.0.1", "svelte-check": "^4.3.3", "tailwindcss": "^4.1.17", + "tw-animate-css": "^1.4.0", "typescript": "^5.9.3", "typescript-eslint": "^8.46.3", "vite": "^7.2.2", diff --git a/web/src/app.css b/web/src/app.css index c4176c9..ad33c77 100644 --- a/web/src/app.css +++ b/web/src/app.css @@ -1,5 +1,6 @@ @import "tailwindcss"; +@import "tw-animate-css"; @plugin "@iconify/tailwind4" { prefixes: octicon; } diff --git a/web/src/lib/components/menu-bar/MenuBar.svelte b/web/src/lib/components/menu-bar/MenuBar.svelte new file mode 100644 index 0000000..57ca547 --- /dev/null +++ b/web/src/lib/components/menu-bar/MenuBar.svelte @@ -0,0 +1,96 @@ + + + + + diffs.dev + + + + + + GitHub Repository + + + + + + Chrome Extension + + + + + + Firefox Add-on + + + + { + viewer.settingsDialogOpen = true; + }} + > + Open Settings + + + + + + File + + + { + viewer.openDiffDialogOpen = true; + }} + > + Open + + + + + + View + + + { + viewer.expandAll(); + }} + > + Expand All + + { + viewer.collapseAll(); + }} + > + Collapse All + + + + + diff --git a/web/src/lib/components/settings-popover/SettingsPopover.svelte b/web/src/lib/components/settings-popover/SettingsPopover.svelte deleted file mode 100644 index 4b733d2..0000000 --- a/web/src/lib/components/settings-popover/SettingsPopover.svelte +++ /dev/null @@ -1,44 +0,0 @@ - - - - -{#snippet sectionHeader(text: string)} -
{text}
-{/snippet} - -{#snippet globalThemeSetting()} - -
- -
-
-{/snippet} - - - - - - - - {@render children?.()} - - - - diff --git a/web/src/lib/components/settings/SettingsDialog.svelte b/web/src/lib/components/settings/SettingsDialog.svelte new file mode 100644 index 0000000..1e4cef9 --- /dev/null +++ b/web/src/lib/components/settings/SettingsDialog.svelte @@ -0,0 +1,77 @@ + + + + +{#snippet sectionHeader(text: string)} +
{text}
+{/snippet} + +{#snippet globalThemeSetting()} + +
+ +
+
+{/snippet} + + + + + +
+ Settings + + + +
+ +
+ {@render globalThemeSetting()} + + + + + + + + + +
+ Sidebar location + +
+
+
+
+
+
diff --git a/web/src/lib/components/settings-popover/SettingsPopoverGroup.svelte b/web/src/lib/components/settings/SettingsGroup.svelte similarity index 80% rename from web/src/lib/components/settings-popover/SettingsPopoverGroup.svelte rename to web/src/lib/components/settings/SettingsGroup.svelte index a8a2517..d57087e 100644 --- a/web/src/lib/components/settings-popover/SettingsPopoverGroup.svelte +++ b/web/src/lib/components/settings/SettingsGroup.svelte @@ -1,6 +1,3 @@ - - - - - - - - - - { - viewer.expandAll(); - open = false; - }} - > - Expand All - - { - viewer.collapseAll(); - open = false; - }} - > - Collapse All - - - - - diff --git a/web/src/routes/OpenDiffDialog.svelte b/web/src/routes/OpenDiffDialog.svelte index d0dbb7c..d04f571 100644 --- a/web/src/routes/OpenDiffDialog.svelte +++ b/web/src/routes/OpenDiffDialog.svelte @@ -280,13 +280,14 @@ {/snippet} - Open new diff - + -
+
Open New Diff From 012c5a896559567e96232e5041ce75f6a3b67c70 Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Fri, 14 Nov 2025 18:19:49 -0700 Subject: [PATCH 2/7] Remove unused vars/imports --- web/src/lib/components/settings/SettingsDialog.svelte | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/web/src/lib/components/settings/SettingsDialog.svelte b/web/src/lib/components/settings/SettingsDialog.svelte index 1e4cef9..80881b4 100644 --- a/web/src/lib/components/settings/SettingsDialog.svelte +++ b/web/src/lib/components/settings/SettingsDialog.svelte @@ -4,12 +4,10 @@ From a6519cde619b15c66421c3348abd45999e01ce26 Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Fri, 14 Nov 2025 18:57:55 -0700 Subject: [PATCH 3/7] Add keybinds --- .../lib/components/menu-bar/MenuBar.svelte | 15 ++++++--- web/src/lib/diff-viewer.svelte.ts | 15 +++++++++ web/src/lib/keybinds.svelte.ts | 31 +++++++++++++++++++ 3 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 web/src/lib/keybinds.svelte.ts diff --git a/web/src/lib/components/menu-bar/MenuBar.svelte b/web/src/lib/components/menu-bar/MenuBar.svelte index 57ca547..543bc55 100644 --- a/web/src/lib/components/menu-bar/MenuBar.svelte +++ b/web/src/lib/components/menu-bar/MenuBar.svelte @@ -1,10 +1,15 @@ +{#snippet keybind(key: string)} + {Keybinds.getModifierKey()}+{key} +{/snippet} + diffs.dev @@ -45,12 +50,13 @@ { - viewer.settingsDialogOpen = true; + viewer.openSettingsDialog(); }} > Open Settings + {@render keybind(",")} @@ -60,12 +66,13 @@ { - viewer.openDiffDialogOpen = true; + viewer.openOpenDiffDialog(); }} > Open + {@render keybind("O")} diff --git a/web/src/lib/diff-viewer.svelte.ts b/web/src/lib/diff-viewer.svelte.ts index afa747e..41ff4d7 100644 --- a/web/src/lib/diff-viewer.svelte.ts +++ b/web/src/lib/diff-viewer.svelte.ts @@ -17,6 +17,7 @@ import { VList } from "virtua/svelte"; import { Context, Debounced, watch } from "runed"; import { MediaQuery } from "svelte/reactivity"; import { ProgressBarState } from "$lib/components/progress-bar/index.svelte"; +import { Keybinds } from "./keybinds.svelte"; export const GITHUB_URL_PARAM = "github_url"; export const PATCH_URL_PARAM = "patch_url"; @@ -241,6 +242,20 @@ export class MultiFileDiffViewerState { private constructor() { // Make sure to revoke object URLs when the component is destroyed onDestroy(() => this.clearImages()); + + const keybinds = new Keybinds(); + keybinds.registerModifierBind("o", () => this.openOpenDiffDialog()); + keybinds.registerModifierBind(",", () => this.openSettingsDialog()); + } + + openOpenDiffDialog() { + this.openDiffDialogOpen = true; + this.settingsDialogOpen = false; + } + + openSettingsDialog() { + this.settingsDialogOpen = true; + this.openDiffDialogOpen = false; } filterFile(file: FileDetails): boolean { diff --git a/web/src/lib/keybinds.svelte.ts b/web/src/lib/keybinds.svelte.ts new file mode 100644 index 0000000..a11c73b --- /dev/null +++ b/web/src/lib/keybinds.svelte.ts @@ -0,0 +1,31 @@ +import { onMount } from "svelte"; +import { on } from "svelte/events"; + +export class Keybinds { + private static readonly IS_MAC = typeof navigator !== "undefined" && navigator.userAgent.includes("Mac"); + + static getModifierKey() { + return Keybinds.IS_MAC ? "⌘" : "Ctrl"; + } + + private readonly binds = new Map void>(); + + constructor() { + onMount(() => { + return on(document, "keydown", (event) => { + const modifierPressed = Keybinds.IS_MAC ? event.metaKey : event.ctrlKey; + if (modifierPressed) { + const bindAction = this.binds.get(event.key.toLowerCase()); + if (bindAction) { + bindAction(); + event.preventDefault(); + } + } + }); + }); + } + + registerModifierBind(key: string, action: () => void) { + this.binds.set(key.toLowerCase(), action); + } +} From a466e484ba6622fdf2a49a4916b363cfc197994a Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Fri, 14 Nov 2025 19:04:38 -0700 Subject: [PATCH 4/7] Fix cursor on menu items --- bun.lock | 3 +++ web/package.json | 1 + web/src/lib/components/menu-bar/MenuBar.svelte | 8 ++++---- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/bun.lock b/bun.lock index edc8ce7..5029b3b 100644 --- a/bun.lock +++ b/bun.lock @@ -43,6 +43,7 @@ "svelte-adapter-bun": "^1.0.1", "svelte-check": "^4.3.3", "tailwindcss": "^4.1.17", + "tw-animate-css": "^1.4.0", "typescript": "^5.9.3", "typescript-eslint": "^8.46.3", "vite": "^7.2.2", @@ -1037,6 +1038,8 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], diff --git a/web/package.json b/web/package.json index a574bdc..e8909ec 100644 --- a/web/package.json +++ b/web/package.json @@ -10,6 +10,7 @@ "preview": "vite preview", "prepare": "svelte-kit sync || echo ''", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:all": "bun run check && bun run lint && vitest run", "test": "vitest", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "format": "prettier --write .", diff --git a/web/src/lib/components/menu-bar/MenuBar.svelte b/web/src/lib/components/menu-bar/MenuBar.svelte index 543bc55..9554132 100644 --- a/web/src/lib/components/menu-bar/MenuBar.svelte +++ b/web/src/lib/components/menu-bar/MenuBar.svelte @@ -50,7 +50,7 @@ { viewer.openSettingsDialog(); }} @@ -66,7 +66,7 @@ { viewer.openOpenDiffDialog(); }} @@ -82,7 +82,7 @@ { viewer.expandAll(); }} @@ -90,7 +90,7 @@ Expand All { viewer.collapseAll(); }} From 0c6f5d83733dc8013f0832a4bbc9af56e0d89245 Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Fri, 14 Nov 2025 19:11:32 -0700 Subject: [PATCH 5/7] Fix sidebar covering menu bar items --- web/src/routes/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/routes/+page.svelte b/web/src/routes/+page.svelte index a1acb13..7e903de 100644 --- a/web/src/routes/+page.svelte +++ b/web/src/routes/+page.svelte @@ -118,7 +118,7 @@ class="absolute top-0 z-10 flex h-full w-full flex-col bg-neutral data-[collapsed=true]:hidden data-[side=left]:left-0 data-[side=left]:border-e data-[side=right]:right-0 data-[side=right]:order-10 data-[side=right]:border-s - md:w-[350px] md:shadow-md lg:static + md:w-[350px] md:shadow-md lg:static lg:z-0 lg:h-auto lg:shadow-none" data-side={globalOptions.sidebarLocation} data-collapsed={viewer.sidebarCollapsed} From c9971995df56089462ab0baff53bd1419810dc4b Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Fri, 14 Nov 2025 19:42:22 -0700 Subject: [PATCH 6/7] Adjust sidebar auto-close behavior --- .../lib/components/menu-bar/MenuBar.svelte | 6 +- web/src/routes/+page.svelte | 182 +--------------- web/src/routes/Sidebar.svelte | 194 ++++++++++++++++++ 3 files changed, 203 insertions(+), 179 deletions(-) create mode 100644 web/src/routes/Sidebar.svelte diff --git a/web/src/lib/components/menu-bar/MenuBar.svelte b/web/src/lib/components/menu-bar/MenuBar.svelte index 9554132..0e27ddc 100644 --- a/web/src/lib/components/menu-bar/MenuBar.svelte +++ b/web/src/lib/components/menu-bar/MenuBar.svelte @@ -14,7 +14,7 @@ diffs.dev - + File - + { @@ -80,7 +80,7 @@ View - + { diff --git a/web/src/routes/+page.svelte b/web/src/routes/+page.svelte index 7e903de..14afebd 100644 --- a/web/src/routes/+page.svelte +++ b/web/src/routes/+page.svelte @@ -1,82 +1,26 @@ @@ -112,107 +58,8 @@
-
-
-
-
- - - {#if viewer.fileTreeFilterDebounced.current} - - {/if} -
- -
- {#if viewer.filteredFileDetails.length !== viewer.fileDetails.length} -
- Showing {viewer.filteredFileDetails.length} of {viewer.fileDetails.length} files -
- {/if} -
-
- {#snippet fileSnippet(value: FileDetails)} -
scrollToFileClick(e, value.index)} - use:focusFileDoubleClick={{ index: value.index }} - onkeydown={(e) => e.key === "Enter" && viewer.scrollToFile(value.index)} - role="button" - tabindex="0" - id={"file-tree-file-" + value.index} - > - - {value.toFile.substring(value.toFile.lastIndexOf("/") + 1)} - viewer.toggleChecked(value.index)} - checked={viewer.fileStates[value.index].checked} - /> -
- {/snippet} - - {#snippet nodeRenderer({ node, collapsed, toggleCollapse })} - {@const folderIcon = collapsed ? "octicon--file-directory-fill-16" : "octicon--file-directory-open-fill-16"} - {#if node.data.type === "file"} - {@render fileSnippet(node.data.data as FileDetails)} - {:else} -
e.key === "Enter" && toggleCollapse()} - role="button" - tabindex="0" - > - - {node.data.data} - {#if collapsed} - - {:else} - - {/if} -
- {/if} - {/snippet} - {#snippet childWrapper({ node, collapsed, children })} -
- {@render children({ node })} -
- {/snippet} -
-
-
-
+
+
{#if viewer.diffMetadata !== null}
@@ -285,20 +132,3 @@
- - diff --git a/web/src/routes/Sidebar.svelte b/web/src/routes/Sidebar.svelte new file mode 100644 index 0000000..ad1be4f --- /dev/null +++ b/web/src/routes/Sidebar.svelte @@ -0,0 +1,194 @@ + + +
+
+
+ + + {#if viewer.fileTreeFilterDebounced.current} + + {/if} +
+ +
+ {#if viewer.filteredFileDetails.length !== viewer.fileDetails.length} +
+ Showing {viewer.filteredFileDetails.length} of {viewer.fileDetails.length} files +
+ {/if} +
+
+ {#snippet fileSnippet(value: FileDetails)} +
scrollToFileClick(e, value.index)} + use:focusFileDoubleClick={{ index: value.index }} + onkeydown={(e) => e.key === "Enter" && viewer.scrollToFile(value.index)} + role="button" + tabindex="0" + id={"file-tree-file-" + value.index} + > + + {value.toFile.substring(value.toFile.lastIndexOf("/") + 1)} + viewer.toggleChecked(value.index)} + checked={viewer.fileStates[value.index].checked} + /> +
+ {/snippet} + + {#snippet nodeRenderer({ node, collapsed, toggleCollapse })} + {@const folderIcon = collapsed ? "octicon--file-directory-fill-16" : "octicon--file-directory-open-fill-16"} + {#if node.data.type === "file"} + {@render fileSnippet(node.data.data as FileDetails)} + {:else} +
e.key === "Enter" && toggleCollapse()} + role="button" + tabindex="0" + > + + {node.data.data} + {#if collapsed} + + {:else} + + {/if} +
+ {/if} + {/snippet} + {#snippet childWrapper({ node, collapsed, children })} +
+ {@render children({ node })} +
+ {/snippet} +
+
+
+
+ + From 27db22ab660b5c17713da813e3b27dc2329f9789 Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Fri, 14 Nov 2025 20:19:59 -0700 Subject: [PATCH 7/7] Shrink settings dialog --- web/src/lib/components/settings/SettingsDialog.svelte | 2 +- web/src/routes/OpenDiffDialog.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/lib/components/settings/SettingsDialog.svelte b/web/src/lib/components/settings/SettingsDialog.svelte index 80881b4..e00547a 100644 --- a/web/src/lib/components/settings/SettingsDialog.svelte +++ b/web/src/lib/components/settings/SettingsDialog.svelte @@ -38,7 +38,7 @@ class="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" />
Settings diff --git a/web/src/routes/OpenDiffDialog.svelte b/web/src/routes/OpenDiffDialog.svelte index d04f571..3c2c45c 100644 --- a/web/src/routes/OpenDiffDialog.svelte +++ b/web/src/routes/OpenDiffDialog.svelte @@ -285,7 +285,7 @@ class="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" />
Open New Diff