feat: improve repository history navigation

This commit is contained in:
Georg Bauer
2026-07-30 17:40:34 +02:00
parent c7de716b8e
commit 8c383e3672
6 changed files with 767 additions and 59 deletions

View File

@@ -1,5 +1,5 @@
slint::slint! {
import { Button, LineEdit, ListView, ScrollView, Spinner } from "std-widgets.slint";
import { Button, ComboBox, LineEdit, ListView, ScrollView, Spinner } from "std-widgets.slint";
export struct ServerRow { name: string, url: string }
export struct RepositoryRow {
@@ -14,7 +14,10 @@ slint::slint! {
}
export struct MarkdownBlock { text: styled-text, kind: string }
export struct CommentRow { author: string, body: [MarkdownBlock], meta: string }
export struct CommitRow { sha: string, title: string, meta: string }
export struct GraphLane { color: color, top: bool, bottom: bool, node: bool, connect_left: bool }
export struct CommitRow {
sha: string, title: string, meta: string, refs: string, lanes: [GraphLane],
}
export struct FileRow { path: string, status: string }
export struct ActivityRow {
icon: string, title: string, detail: string, meta: string,
@@ -113,6 +116,19 @@ slint::slint! {
}
}
component FileCard inherits Rectangle {
in property <FileRow> file;
callback clicked();
height: 66px;
background: #ffffff;
border-color: #dedee3;
border-width: 1px;
border-radius: 10px;
TouchArea { clicked => { root.clicked(); } }
Text { x: 13px; y: 10px; width: parent.width - 26px; text: root.file.path; font-size: 14px; font-weight: 600; color: #232327; overflow: elide; }
Text { x: 13px; y: 38px; width: parent.width - 26px; text: root.file.status; font-size: 12px; color: #74747a; }
}
component TabButton inherits Rectangle {
in property <string> icon;
in property <string> label;
@@ -175,11 +191,14 @@ slint::slint! {
in-out property <[MarkdownBlock]> issue_body;
in-out property <string> detail_title;
in-out property <string> detail_meta;
in-out property <string> detail_files_ref;
in-out property <[MarkdownBlock]> detail_body;
in-out property <[DiffLine]> diff_lines;
in-out property <int> diff_columns;
in-out property <string> issue_filter: "open";
in-out property <string> pull_filter: "open";
in-out property <[string]> branch_choices;
in-out property <int> branch_index;
in-out property <bool> filter_open;
in-out property <bool> loading;
in-out property <bool> home_loading;
@@ -211,7 +230,9 @@ slint::slint! {
callback select_issue(int);
callback select_pull(string, string, int);
callback select_commit(string);
callback select_branch(string);
callback select_file(string);
callback select_pull_file(string);
callback select_activity(string, string, string, int, string);
callback choose_filter(string);
callback back();
@@ -535,6 +556,8 @@ slint::slint! {
Text { text: root.detail_meta; font-size: 13px; color: #707077; wrap: word-wrap; }
Rectangle { height: 1px; background: #d8d8dd; }
MarkdownContent { width: parent.width; blocks: root.detail_body; }
if root.files.length > 0: Text { text: root.detail_files_ref; font-size: 17px; font-weight: 600; color: #252529; }
for file in root.files: FileCard { width: parent.width; file: file; clicked => { root.select_pull_file(file.path); } }
if root.comments.length > 0: Text { text: "Comments"; font-size: 17px; font-weight: 600; color: #252529; }
for comment in root.comments: CommentCard { width: parent.width; comment: comment; }
}
@@ -549,6 +572,11 @@ slint::slint! {
height: root.height - root.safe-area-insets.top - root.safe-area-insets.bottom - 58px;
background: #f2f2f7;
Header { x: 0; y: 0; title: root.repository_title; can_back: true; back => { root.back(); } }
ComboBox {
x: 16px; y: 66px; width: parent.width - 32px; height: 42px;
model: root.branch_choices; current-index: root.branch_index;
selected(value) => { root.select_branch(value); }
}
if root.refreshing: Spinner { x: parent.width - 42px; y: 17px; width: 24px; height: 24px; }
if !root.loading && root.commits.length == 0: EmptyState {
x: 34px; width: parent.width - 68px; y: 220px; height: 160px;
@@ -556,18 +584,59 @@ slint::slint! {
detail: "This repository has no commit history.";
}
PullToRefreshGesture {
x: 16px; y: 70px; width: parent.width - 32px; height: parent.height - 82px;
x: 16px; y: 116px; width: parent.width - 32px; height: parent.height - 128px;
at_top: root.repositories_at_top; refreshing: root.refreshing;
refresh => { root.refresh_commits(); }
ListView {
width: 100%; height: 100%; mouse-drag-pan-enabled: true;
scrolled => { root.repositories_at_top = self.viewport-y >= 0px; }
for commit in root.commits: Rectangle {
height: 86px; background: #ffffff; border-color: #dedee3; border-width: 1px; border-radius: 12px;
for commit in root.commits: commit_row := Rectangle {
property <length> graph-width: commit.lanes.length > 0 ? min(72px, commit.lanes.length * 10px + 8px) : 0px;
property <length> lane-spacing: (self.graph-width - 8px) / max(1, commit.lanes.length);
height: 86px; background: transparent;
Rectangle { width: 100%; height: 100%; background: #ffffff; border-color: #dedee3; border-width: 1px; border-radius: 12px; }
TouchArea { clicked => { root.select_commit(commit.sha); } }
Text { x: 14px; y: 11px; width: parent.width - 28px; text: commit.title; font-size: 15px; font-weight: 600; color: #17171a; overflow: elide; }
Text { x: 14px; y: 40px; width: parent.width - 28px; text: commit.meta; font-size: 12px; color: #707077; overflow: elide; }
Text { x: 14px; y: 61px; width: parent.width - 28px; text: commit.sha; font-size: 11px; color: #919198; overflow: elide; }
for lane[index] in commit.lanes: Rectangle {
x: 4px + index * commit_row.lane-spacing;
width: 3px; height: 100%; background: transparent;
if lane.top: Rectangle {
y: -1px; width: 3px;
height: lane.connect_left && !lane.bottom ? 37px : 42px;
background: lane.color;
}
if lane.bottom: Rectangle {
y: lane.connect_left && !lane.top ? 45px : 40px;
width: 3px;
height: parent.height - (lane.connect_left && !lane.top ? 44px : 39px);
background: lane.color;
}
if lane.connect_left && lane.top == lane.bottom: Rectangle {
x: -commit_row.lane-spacing; y: 39px;
width: commit_row.lane-spacing + 3px; height: 3px;
background: lane.color;
}
if lane.connect_left && lane.bottom && !lane.top: Path {
x: 1.5px - commit_row.lane-spacing; y: 35px;
width: commit_row.lane-spacing; height: 11px;
viewbox-width: 10; viewbox-height: 11;
commands: "M 0 5.5 L 5 5.5 Q 10 5.5 10 10.5";
fill: transparent; stroke: lane.color; stroke-width: 3px; stroke-line-cap: round;
}
if lane.connect_left && lane.top && !lane.bottom: Path {
x: 1.5px - commit_row.lane-spacing; y: 35px;
width: commit_row.lane-spacing; height: 11px;
viewbox-width: 10; viewbox-height: 11;
commands: "M 10 0.5 Q 10 5.5 5 5.5 L 0 5.5";
fill: transparent; stroke: lane.color; stroke-width: 3px; stroke-line-cap: round;
}
if lane.node: Rectangle {
x: -3px; y: 36px; width: 9px; height: 9px; border-radius: 5px;
background: lane.color; border-color: #ffffff; border-width: 2px;
}
}
Text { x: parent.graph-width + 14px; y: 8px; width: parent.width - self.x - 14px; text: commit.title; font-size: 15px; font-weight: 600; color: #17171a; overflow: elide; }
Text { x: parent.graph-width + 14px; y: 33px; width: parent.width - self.x - 14px; text: commit.refs != "" ? commit.refs : commit.meta; font-size: 12px; color: commit.refs != "" ? #0879e1 : #707077; overflow: elide; }
Text { x: parent.graph-width + 14px; y: 53px; width: parent.width - self.x - 14px; text: commit.refs != "" ? commit.meta + " · " + commit.sha : commit.sha; font-size: 11px; color: #919198; overflow: elide; }
}
}
}
@@ -587,17 +656,12 @@ slint::slint! {
}
ListView {
x: 16px; y: 70px; width: parent.width - 32px; height: parent.height - 82px; mouse-drag-pan-enabled: true;
for file in root.files: Rectangle {
height: 66px; background: #ffffff; border-color: #dedee3; border-width: 1px; border-radius: 10px;
TouchArea { clicked => { root.select_file(file.path); } }
Text { x: 13px; y: 10px; width: parent.width - 26px; text: file.path; font-size: 14px; font-weight: 600; color: #232327; overflow: elide; }
Text { x: 13px; y: 38px; width: parent.width - 26px; text: file.status; font-size: 12px; color: #74747a; }
}
for file in root.files: FileCard { file: file; clicked => { root.select_file(file.path); } }
}
EdgeBack { x: 0; y: 0; back => { root.back(); } }
}
if tab == "repositories" && page == "diff": Rectangle {
if (tab == "repositories" || tab == "pulls") && page == "diff": Rectangle {
x: root.safe-area-insets.left; y: root.safe-area-insets.top;
width: root.width - root.safe-area-insets.left - root.safe-area-insets.right;
height: root.height - root.safe-area-insets.top - root.safe-area-insets.bottom - 58px;