Experimental browser for the Atmosphere
{ "uri": "at://did:plc:qfpnj4og54vl56wngdriaxug/sh.tangled.repo.pull/3lnlnvrujof22", "cid": "bafyreidbxzldns2s7mpwbbrlttxekbxpv4hdpqhh5si4vublkgxxw7ifrq", "value": { "$type": "sh.tangled.repo.pull", "patch": "From 3f5c3e1dc421cb7fbaecd3cc4a50b1c97d9487a0 Mon Sep 17 00:00:00 2001\nFrom: Akshay <nerdy@peppe.rs>\nDate: Sun, 20 Apr 2025 18:45:15 +0100\nSubject: [PATCH] appview: diff: organize changed-files into file-tree\n\nintroduces the filetree package.\n\neventually we will have a sticky side-panel style layout for any page\ndisplaying diffs (probably makes most sense when we have split diffs),\nand this file-tree will move there.\n---\n appview/filetree/filetree.go | 62 +++++++++++++++++++\n appview/pages/funcmap.go | 2 +\n appview/pages/pages.go | 11 ++--\n .../pages/templates/repo/fragments/diff.html | 21 ++-----\n .../templates/repo/fragments/filetree.html | 27 ++++++++\n .../templates/repo/fragments/interdiff.html | 9 +--\n appview/state/pull.go | 4 +-\n patchutil/interdiff.go | 8 +++\n types/diff.go | 14 +++++\n 9 files changed, 128 insertions(+), 30 deletions(-)\n create mode 100644 appview/filetree/filetree.go\n create mode 100644 appview/pages/templates/repo/fragments/filetree.html\n\ndiff --git a/appview/filetree/filetree.go b/appview/filetree/filetree.go\nnew file mode 100644\nindex 0000000..01fb56b\n--- /dev/null\n+++ b/appview/filetree/filetree.go\n@@ -0,0 +1,62 @@\n+package filetree\n+\n+import (\n+\t\"path/filepath\"\n+\t\"sort\"\n+\t\"strings\"\n+)\n+\n+type FileTreeNode struct {\n+\tName string\n+\tPath string\n+\tIsDirectory bool\n+\tChildren map[string]*FileTreeNode\n+}\n+\n+// NewNode creates a new node\n+func newNode(name, path string, isDir bool) *FileTreeNode {\n+\treturn &FileTreeNode{\n+\t\tName: name,\n+\t\tPath: path,\n+\t\tIsDirectory: isDir,\n+\t\tChildren: make(map[string]*FileTreeNode),\n+\t}\n+}\n+\n+func FileTree(files []string) *FileTreeNode {\n+\trootNode := newNode(\"\", \"\", true)\n+\n+\tsort.Strings(files)\n+\n+\tfor _, file := range files {\n+\t\tif file == \"\" {\n+\t\t\tcontinue\n+\t\t}\n+\n+\t\tparts := strings.Split(filepath.Clean(file), \"/\")\n+\t\tif len(parts) == 0 {\n+\t\t\tcontinue\n+\t\t}\n+\n+\t\tcurrentNode := rootNode\n+\t\tcurrentPath := \"\"\n+\n+\t\tfor i, part := range parts {\n+\t\t\tif currentPath == \"\" {\n+\t\t\t\tcurrentPath = part\n+\t\t\t} else {\n+\t\t\t\tcurrentPath = filepath.Join(currentPath, part)\n+\t\t\t}\n+\n+\t\t\tisDir := i < len(parts)-1\n+\n+\t\t\tif _, exists := currentNode.Children[part]; !exists {\n+\t\t\t\tcurrentNode.Children[part] = newNode(part, currentPath, isDir)\n+\t\t\t}\n+\n+\t\t\tcurrentNode = currentNode.Children[part]\n+\t\t}\n+\t}\n+\n+\treturn rootNode\n+}\ndiff --git a/appview/pages/funcmap.go b/appview/pages/funcmap.go\nindex ed2399a..490b98a 100644\n--- a/appview/pages/funcmap.go\n+++ b/appview/pages/funcmap.go\n@@ -13,6 +13,7 @@ import (\n \t\"time\"\n \n \t\"github.com/dustin/go-humanize\"\n+\t\"tangled.sh/tangled.sh/core/appview/filetree\"\n \t\"tangled.sh/tangled.sh/core/appview/pages/markup\"\n )\n \n@@ -174,6 +175,7 @@ func funcMap() template.FuncMap {\n \t\t\treturn template.HTML(data)\n \t\t},\n \t\t\"cssContentHash\": CssContentHash,\n+\t\t\"fileTree\": filetree.FileTree,\n \t}\n }\n \ndiff --git a/appview/pages/pages.go b/appview/pages/pages.go\nindex 3f32e0a..190d8cd 100644\n--- a/appview/pages/pages.go\n+++ b/appview/pages/pages.go\n@@ -505,11 +505,12 @@ func (p *Pages) RepoLog(w io.Writer, params RepoLogParams) error {\n }\n \n type RepoCommitParams struct {\n-\tLoggedInUser *auth.User\n-\tRepoInfo RepoInfo\n-\tActive string\n-\ttypes.RepoCommitResponse\n+\tLoggedInUser *auth.User\n+\tRepoInfo RepoInfo\n+\tActive string\n \tEmailToDidOrHandle map[string]string\n+\n+\ttypes.RepoCommitResponse\n }\n \n func (p *Pages) RepoCommit(w io.Writer, params RepoCommitParams) error {\n@@ -784,7 +785,7 @@ type RepoPullPatchParams struct {\n \tDidHandleMap map[string]string\n \tRepoInfo RepoInfo\n \tPull *db.Pull\n-\tDiff types.NiceDiff\n+\tDiff *types.NiceDiff\n \tRound int\n \tSubmission *db.PullSubmission\n }\ndiff --git a/appview/pages/templates/repo/fragments/diff.html b/appview/pages/templates/repo/fragments/diff.html\nindex e237f56..605c247 100644\n--- a/appview/pages/templates/repo/fragments/diff.html\n+++ b/appview/pages/templates/repo/fragments/diff.html\n@@ -3,6 +3,7 @@\n {{ $diff := index . 1 }}\n {{ $commit := $diff.Commit }}\n {{ $stat := $diff.Stat }}\n+{{ $fileTree := fileTree $diff.ChangedFiles }}\n {{ $diff := $diff.Diff }}\n \n {{ $this := $commit.This }}\n@@ -14,17 +15,7 @@\n <strong class=\"text-sm uppercase dark:text-gray-200\">Changed files</strong>\n {{ block \"statPill\" $stat }} {{ end }}\n </div>\n- <div class=\"overflow-x-auto\">\n- {{ range $diff }}\n- <ul class=\"dark:text-gray-200\">\n- {{ if .IsDelete }}\n- <li><a href=\"#file-{{ .Name.Old }}\" class=\"dark:hover:text-gray-300\">{{ .Name.Old }}</a></li>\n- {{ else }}\n- <li><a href=\"#file-{{ .Name.New }}\" class=\"dark:hover:text-gray-300\">{{ .Name.New }}</a></li>\n- {{ end }}\n- </ul>\n- {{ end }}\n- </div>\n+ {{ block \"fileTree\" $fileTree }} {{ end }}\n </div>\n </section>\n \n@@ -38,7 +29,7 @@\n <summary class=\"list-none cursor-pointer sticky top-0\">\n <div id=\"diff-file-header\" class=\"rounded cursor-pointer bg-white dark:bg-gray-800 flex justify-between\">\n <div id=\"left-side-items\" class=\"p-2 flex gap-2 items-center overflow-x-auto\">\n- <div class=\"flex gap-1 items-center\" style=\"direction: ltr;\">\n+ <div class=\"flex gap-1 items-center\">\n {{ $markerstyle := \"diff-type p-1 mr-1 font-mono text-sm rounded select-none\" }}\n {{ if .IsNew }}\n <span class=\"bg-green-100 text-green-700 dark:bg-green-800/50 dark:text-green-400 {{ $markerstyle }}\">ADDED</span>\n@@ -55,7 +46,7 @@\n {{ block \"statPill\" .Stats }} {{ end }}\n </div>\n \n- <div class=\"flex gap-2 items-center overflow-x-auto\" style=\"direction: rtl;\">\n+ <div class=\"flex gap-2 items-center overflow-x-auto\">\n {{ if .IsDelete }}\n <a class=\"dark:text-white whitespace-nowrap overflow-x-auto\" {{if $this }}href=\"/{{ $repo }}/blob/{{ $this }}/{{ .Name.Old }}\"{{end}}>\n {{ .Name.Old }}\n@@ -102,10 +93,6 @@\n <p class=\"text-center text-gray-400 dark:text-gray-500 p-4\">\n This file has been copied.\n </p>\n- {{ else if .IsRename }}\n- <p class=\"text-center text-gray-400 dark:text-gray-500 p-4\">\n- This file has been renamed.\n- </p>\n {{ else if .IsBinary }}\n <p class=\"text-center text-gray-400 dark:text-gray-500 p-4\">\n This is a binary file and will not be displayed.\ndiff --git a/appview/pages/templates/repo/fragments/filetree.html b/appview/pages/templates/repo/fragments/filetree.html\nnew file mode 100644\nindex 0000000..9268610\n--- /dev/null\n+++ b/appview/pages/templates/repo/fragments/filetree.html\n@@ -0,0 +1,27 @@\n+{{ define \"fileTree\" }}\n+ {{ if and .Name .IsDirectory }}\n+ <details open>\n+ <summary class=\"cursor-pointer list-none pt-1\">\n+ <span class=\"inline-flex items-center gap-2 \">\n+ {{ i \"folder\" \"w-3 h-3 fill-current\" }}\n+ <span class=\"text-black dark:text-white\">{{ .Name }}</span>\n+ </span>\n+ </summary>\n+ <div class=\"ml-1 pl-4 border-l border-gray-200 dark:border-gray-700\">\n+ {{ range $child := .Children }}\n+ {{ block \"fileTree\" $child }} {{ end }}\n+ {{ end }}\n+ </div>\n+ </details>\n+ {{ else if .Name }}\n+ <div class=\"flex items-center gap-2 pt-1\">\n+ {{ i \"file\" \"w-3 h-3\" }}\n+ <a href=\"#file-{{ .Path }}\" class=\"text-black dark:text-white no-underline hover:underline\">{{ .Name }}</a>\n+ </div>\n+ {{ else }}\n+ {{ range $child := .Children }}\n+ {{ block \"fileTree\" $child }} {{ end }}\n+ {{ end }}\n+ {{ end }}\n+{{ end }}\n+\ndiff --git a/appview/pages/templates/repo/fragments/interdiff.html b/appview/pages/templates/repo/fragments/interdiff.html\nindex f9fd6e5..90959f4 100644\n--- a/appview/pages/templates/repo/fragments/interdiff.html\n+++ b/appview/pages/templates/repo/fragments/interdiff.html\n@@ -1,6 +1,7 @@\n {{ define \"repo/fragments/interdiff\" }}\n {{ $repo := index . 0 }}\n {{ $x := index . 1 }}\n+{{ $fileTree := fileTree $x.AffectedFiles }}\n {{ $diff := $x.Files }}\n \n <section class=\"mt-6 p-6 border border-gray-200 dark:border-gray-700 w-full mx-auto rounded bg-white dark:bg-gray-800 drop-shadow-sm\">\n@@ -8,13 +9,7 @@\n <div class=\"flex gap-2 items-center\">\n <strong class=\"text-sm uppercase dark:text-gray-200\">files</strong>\n </div>\n- <div class=\"overflow-x-auto\">\n- <ul class=\"dark:text-gray-200\">\n- {{ range $diff }}\n- <li><a href=\"#file-{{ .Name }}\" class=\"dark:hover:text-gray-300\">{{ .Name }}</a></li>\n- {{ end }}\n- </ul>\n- </div>\n+ {{ block \"fileTree\" $fileTree }} {{ end }}\n </div>\n </section>\n \ndiff --git a/appview/state/pull.go b/appview/state/pull.go\nindex 064f707..790d8a8 100644\n--- a/appview/state/pull.go\n+++ b/appview/state/pull.go\n@@ -284,6 +284,8 @@ func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) {\n \t\t}\n \t}\n \n+\tdiff := pull.Submissions[roundIdInt].AsNiceDiff(pull.TargetBranch)\n+\n \ts.pages.RepoPullPatchPage(w, pages.RepoPullPatchParams{\n \t\tLoggedInUser: user,\n \t\tDidHandleMap: didHandleMap,\n@@ -291,7 +293,7 @@ func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) {\n \t\tPull: pull,\n \t\tRound: roundIdInt,\n \t\tSubmission: pull.Submissions[roundIdInt],\n-\t\tDiff: pull.Submissions[roundIdInt].AsNiceDiff(pull.TargetBranch),\n+\t\tDiff: &diff,\n \t})\n \n }\ndiff --git a/patchutil/interdiff.go b/patchutil/interdiff.go\nindex 6f4072b..716a9a3 100644\n--- a/patchutil/interdiff.go\n+++ b/patchutil/interdiff.go\n@@ -11,6 +11,14 @@ type InterdiffResult struct {\n \tFiles []*InterdiffFile\n }\n \n+func (i *InterdiffResult) AffectedFiles() []string {\n+\tfiles := make([]string, len(i.Files))\n+\tfor _, f := range i.Files {\n+\t\tfiles = append(files, f.Name)\n+\t}\n+\treturn files\n+}\n+\n func (i *InterdiffResult) String() string {\n \tvar b strings.Builder\n \tfor _, f := range i.Files {\ndiff --git a/types/diff.go b/types/diff.go\nindex e45d68d..a9a809b 100644\n--- a/types/diff.go\n+++ b/types/diff.go\n@@ -59,3 +59,17 @@ type DiffTree struct {\n \tPatch string `json:\"patch\"`\n \tDiff []*gitdiff.File `json:\"diff\"`\n }\n+\n+func (d *NiceDiff) ChangedFiles() []string {\n+\tfiles := make([]string, len(d.Diff))\n+\n+\tfor i, f := range d.Diff {\n+\t\tif f.IsDelete {\n+\t\t\tfiles[i] = f.Name.Old\n+\t\t} else {\n+\t\t\tfiles[i] = f.Name.New\n+\t\t}\n+\t}\n+\n+\treturn files\n+}\n-- \n2.43.0\n\n", "title": "appview: diff: organize changed-files into file-tree", "pullId": 64, "source": { "branch": "file-tree" }, "targetRepo": "at://did:plc:wshs7t2adsemcrrd4snkeqli/sh.tangled.repo/3liuighjy2h22", "targetBranch": "master" } }