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 a0bad4f..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 .", @@ -40,6 +41,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..0e27ddc --- /dev/null +++ b/web/src/lib/components/menu-bar/MenuBar.svelte @@ -0,0 +1,103 @@ + + +{#snippet keybind(key: string)} + {Keybinds.getModifierKey()}+{key} +{/snippet} + + + + diffs.dev + + + + + + GitHub Repository + + + + + + Chrome Extension + + + + + + Firefox Add-on + + + + { + viewer.openSettingsDialog(); + }} + > + Open Settings + {@render keybind(",")} + + + + + + File + + + { + viewer.openOpenDiffDialog(); + }} + > + Open + {@render keybind("O")} + + + + + + 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..e00547a --- /dev/null +++ b/web/src/lib/components/settings/SettingsDialog.svelte @@ -0,0 +1,74 @@ + + + + +{#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 @@ - - @@ -107,260 +47,88 @@ /> -{#snippet settingsPopover()} - - {@render globalThemeSetting()} - - - - - - - - - -
- Sidebar location - -
-
-
-{/snippet} - {#if viewer.loadingState.loading}
{/if} -
-
-
-
- - - {#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} - +
+ +
{/if} -
- - - {@render settingsPopover()} - -
-

Info

- - - GitHub Repository - - - - Chrome Extension - - - - Firefox Add-on - -
-
+
+ + +
-
-
- - - -
-
- i} bind:this={viewer.vlist}> - {#snippet children(value, index)} -
- - {#if !viewer.fileStates[index].collapsed && value.type === "image"} - {@const image = value.image} -
- {#if image.load} - {#if image.fileA !== null && image.fileB !== null} - {#await Promise.all([image.fileA.getValue(), image.fileB.getValue()])} -
- {:then images} - - {/await} +
+ i} bind:this={viewer.vlist}> + {#snippet children(value, index)} +
+ + {#if !viewer.fileStates[index].collapsed && value.type === "image"} + {@const image = value.image} +
+ {#if image.load} + {#if image.fileA !== null && image.fileB !== null} + {#await Promise.all([image.fileA.getValue(), image.fileB.getValue()])} +
+ {:then images} + + {/await} + {:else} + {#await requireEitherImage(image).getValue()} +
+ {:then file} + + {/await} + {/if} {:else} - {#await requireEitherImage(image).getValue()} -
- {:then file} - - {/await} +
+ +
{/if} - {:else} -
- -
- {/if} -
- {/if} - {#if !viewer.fileStates[index].collapsed && value.type === "text" && (!value.patchHeaderDiffOnly || !globalOptions.omitPatchHeaderOnlyHunks)} -
- viewer.searchResults.then((r) => r.lines.get(value))} - activeSearchResult={viewer.activeSearchResult && viewer.activeSearchResult.file === value - ? viewer.activeSearchResult.idx - : undefined} - cache={viewer.diffViewCache} - cacheKey={value} - /> -
- {/if} -
- {/snippet} -
+
+ {/if} + {#if !viewer.fileStates[index].collapsed && value.type === "text" && (!value.patchHeaderDiffOnly || !globalOptions.omitPatchHeaderOnlyHunks)} +
+ viewer.searchResults.then((r) => r.lines.get(value))} + activeSearchResult={viewer.activeSearchResult && viewer.activeSearchResult.file === value + ? viewer.activeSearchResult.idx + : undefined} + cache={viewer.diffViewCache} + cacheKey={value} + /> +
+ {/if} +
+ {/snippet} + +
- - diff --git a/web/src/routes/ActionsPopover.svelte b/web/src/routes/ActionsPopover.svelte deleted file mode 100644 index eb87134..0000000 --- a/web/src/routes/ActionsPopover.svelte +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - { - 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..3c2c45c 100644 --- a/web/src/routes/OpenDiffDialog.svelte +++ b/web/src/routes/OpenDiffDialog.svelte @@ -280,13 +280,14 @@ {/snippet} - Open new diff - + -
+
Open New Diff 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} +
+
+
+
+ +