Files
radicle-explorer/src/components/FileDiff.svelte
T
Rūdolfs Ošiņš 010cac0377 Redesign app with new UI kit
Redesign the entire app with a new UI kit. Typography, colors, icons,
avatars, and border radii have all been updated for a more polished,
consistent appearance that matches our other products like
radicle-explorer.

- Replace blockies-based avatars with a new DID-based avatar system
- Move Settings and Create Issue into fullscreen modals
- Change the default theme to follow the system theme
- Remove window decorations on macOS for a cleaner, more native feel
- Update the app icon
- Fix popovers and tooltips

Navigation:

- Rewrite app navigation and layouts
- Add a sidebar that gives you access to all your repos
- Change the startup screen to open directly to the notification inbox
- Add a getting started guide shown to new users on first launch

Settings:

- Add a setting to disable the notification badge
- Add a code font setting

Bug fixes:

- Fix reactions and edits incorrectly targeting the root comment
  instead of the intended reply
- Restrict issue/patch state, labels and assignees to delegates only
2026-03-20 17:48:38 -06:00

151 lines
4.1 KiB
Svelte

<script lang="ts">
import type { CodeComments } from "@app/components/Diff.svelte";
import type { FileDiff } from "@bindings/diff/FileDiff";
import Diff from "@app/components/Diff.svelte";
import FileBlock from "@app/components/FileBlock.svelte";
import Icon from "@app/components/Icon.svelte";
import Path from "@app/components/Path.svelte";
interface Props {
expanded: boolean;
file: FileDiff;
head: string;
codeComments?: CodeComments;
}
const { expanded, file, head, codeComments }: Props = $props();
// Pass down only the comments that apply to the given diff.
function filterThreadsByFilePath() {
if (codeComments) {
if (
file.status === "added" ||
file.status === "deleted" ||
file.status === "modified"
) {
return {
...codeComments,
threads:
codeComments.threads.filter(t => {
return t.root.location?.path === file.path;
}) ?? [],
};
} else {
return { ...codeComments, threads: [] };
}
} else {
return undefined;
}
}
const commentsOfThisFile = $derived(filterThreadsByFilePath());
const resolvedThreads = $derived(
commentsOfThisFile
? commentsOfThisFile.threads.filter(t => {
return t.root.resolved === true;
}).length
: 0,
);
const unresolvedThreads = $derived(
commentsOfThisFile
? commentsOfThisFile.threads.filter(t => {
return t.root.resolved === false;
}).length
: 0,
);
</script>
<style>
.added {
color: var(--color-feedback-success-text);
background-color: var(--color-feedback-success-bg);
}
.deleted {
color: var(--color-feedback-error-text);
background-color: var(--color-feedback-error-bg);
}
.moved,
.copied {
color: var(--color-text-secondary);
background: var(--color-surface-subtle);
}
.stats {
font: var(--txt-code-regular);
}
</style>
{#snippet emptyPlaceholder()}
<div class="global-flex" style:margin="1rem 0" style:justify-content="center">
Empty file
</div>
{/snippet}
<FileBlock {expanded}>
{#snippet leftHeader()}
{#if file.status === "moved" || file.status === "copied"}
<span style="display: flex; align-items: center; flex-wrap: wrap;">
<Path fullPath={file.oldPath} />
<span style:padding="0 0.5rem">→</span>
<Path fullPath={file.newPath} />
</span>
{:else}
<Path fullPath={file.path} />
{/if}
{#if file.status === "added"}
<span class="global-chip added">Added</span>
{:else if file.status === "deleted"}
<span class="global-chip deleted">Deleted</span>
{:else if file.status === "moved"}
<span class="global-chip moved">Moved</span>
{:else if file.status === "copied"}
<span class="global-chip copied">Copied</span>
{/if}
{/snippet}
{#snippet rightHeader()}
{#if file.diff.type === "plain" && file.diff.hunks.length > 0}
<div class="stats">
<span style:color="var(--color-feedback-success-text)">
+{file.diff.stats.additions}
</span>
<span style:color="var(--color-feedback-error-text)">
-{file.diff.stats.deletions}
</span>
</div>
{/if}
{#if commentsOfThisFile && commentsOfThisFile.threads.length > 0}
{#if unresolvedThreads > 0}
<div class="global-flex">
<Icon name="comment-cross" />
{unresolvedThreads}
</div>
{/if}
{#if resolvedThreads > 0}
<div class="global-flex">
<Icon name="comment-checkmark" />
{resolvedThreads}
</div>
{/if}
{/if}
{/snippet}
{#if file.diff.type === "plain"}
{#if file.diff.hunks.length > 0}
<Diff {file} {head} codeComments={commentsOfThisFile} />
{:else}
{@render emptyPlaceholder()}
{/if}
{:else if file.diff.type === "binary"}
<div
class="global-flex"
style:margin="1rem 0"
style:justify-content="center">
<Icon name="binary" />Binary file
</div>
{:else}
{@render emptyPlaceholder()}
{/if}
</FileBlock>