gites: colorful git tree viewer
Tauri 2 + Vue app that draws the commit graph of the repository given on the command line. Branches can be hidden one by one or by patterns, runs of plain commits fold into a dotted line, and everything is reachable from the keyboard.
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
<script setup lang="ts">
|
||||
import { isRefVisible } from "../lib/filter";
|
||||
import { refsUnder, type TreeNode } from "../lib/tree";
|
||||
import type { FilterState, Ref, RefKind } from "../lib/types";
|
||||
import EyeButton from "./EyeButton.vue";
|
||||
import RefIcon from "./RefIcon.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
nodes: TreeNode[];
|
||||
kind: RefKind;
|
||||
depth: number;
|
||||
filters: FilterState;
|
||||
headRef: string | null;
|
||||
/** Paths of folders the user closed. Shared by the whole tree. */
|
||||
closed: Set<string>;
|
||||
}>();
|
||||
const emit = defineEmits<{ jump: [ref: Ref]; toggle: [names: string[]] }>();
|
||||
|
||||
const allHidden = (n: TreeNode) => refsUnder(n).every((r) => props.filters.hidden.includes(r.name));
|
||||
const anyVisible = (n: TreeNode) => refsUnder(n).some((r) => isRefVisible(r.name, props.filters));
|
||||
|
||||
function toggleFolder(path: string) {
|
||||
if (!props.closed.delete(path)) props.closed.add(path);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-for="n in nodes" :key="n.path">
|
||||
<div
|
||||
class="item"
|
||||
:class="{ off: !anyVisible(n), head: n.ref?.name === headRef, folder: n.children.length }"
|
||||
:style="{ paddingLeft: 10 + depth * 14 + 'px' }"
|
||||
@click="n.ref ? emit('jump', n.ref) : toggleFolder(n.path)"
|
||||
>
|
||||
<span
|
||||
v-if="n.children.length"
|
||||
class="arrow"
|
||||
:class="{ open: !closed.has(n.path) }"
|
||||
@click.stop="toggleFolder(n.path)"
|
||||
>▸</span>
|
||||
<span v-else class="arrow" />
|
||||
<RefIcon v-if="n.ref" :kind="kind" />
|
||||
<svg v-else class="folder-icon" viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<path d="M1.5 4a1 1 0 0 1 1-1h3.5l1.5 1.5h6a1 1 0 0 1 1 1V12a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1z" />
|
||||
</svg>
|
||||
<span class="name" :title="n.ref?.name ?? n.path">{{ n.label }}</span>
|
||||
<EyeButton :shut="allHidden(n)" @click.stop="emit('toggle', refsUnder(n).map((r) => r.name))" />
|
||||
</div>
|
||||
<RefTree
|
||||
v-if="n.children.length && !closed.has(n.path)"
|
||||
:nodes="n.children"
|
||||
:kind="kind"
|
||||
:depth="depth + 1"
|
||||
:filters="filters"
|
||||
:head-ref="headRef"
|
||||
:closed="closed"
|
||||
@jump="emit('jump', $event)"
|
||||
@toggle="emit('toggle', $event)"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 2px 8px 2px 10px;
|
||||
cursor: pointer;
|
||||
color: var(--text);
|
||||
}
|
||||
.item:hover { background: var(--bg-hover); }
|
||||
.item.off { color: var(--text-dim); opacity: 0.55; }
|
||||
.item.head .name { font-weight: 600; color: #fff; }
|
||||
.item.folder { color: #c5cae0; }
|
||||
.folder-icon { flex: none; color: #ffd43b; opacity: 0.8; }
|
||||
.arrow { width: 10px; flex: none; color: var(--text-dim); font-size: 10px; transition: transform 0.12s; }
|
||||
.arrow.open { transform: rotate(90deg); }
|
||||
.name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.item:hover :deep(.eye) { opacity: 1; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user