ATProto Browser

ATProto Browser

Experimental browser for the Atmosphere

Record data

{
  "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"
  }
}