+
{{template "base/footer" .}}
diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl
index 0911d02e1f..e6b9c55770 100644
--- a/templates/repo/editor/edit.tmpl
+++ b/templates/repo/editor/edit.tmpl
@@ -1,53 +1,59 @@
{{template "base/head" .}}
{{template "base/alert" .}}
-
+
+ {{template "repo/view_file_tree" .}}
+
+
+
+
{{template "repo/header" .}}
-
+
{{template "base/footer" .}}
diff --git a/templates/repo/editor/upload.tmpl b/templates/repo/editor/upload.tmpl
index 3e36c77b3b..847d6df88d 100644
--- a/templates/repo/editor/upload.tmpl
+++ b/templates/repo/editor/upload.tmpl
@@ -1,19 +1,25 @@
{{template "base/head" .}}
{{template "base/alert" .}}
-
{{template "repo/header" .}}
-
+
{{template "base/alert" .}}
-
- {{.CsrfTokenHtml}}
- {{template "repo/editor/common_top" .}}
-
{{template "base/footer" .}}
diff --git a/templates/repo/find/files.tmpl b/templates/repo/find/files.tmpl
deleted file mode 100644
index ce242796be..0000000000
--- a/templates/repo/find/files.tmpl
+++ /dev/null
@@ -1,21 +0,0 @@
-{{template "base/head" .}}
-
- {{template "repo/editor/common_breadcrumb" .}}
+
+ {{template "repo/view_file_tree" .}}
+
+
+ {{.CsrfTokenHtml}}
+ {{template "repo/editor/common_top" .}}
+
-
+ {{template "repo/view_file_tree_toggle_button" .}}
+ {{template "repo/editor/common_breadcrumb" .}}
+
+
+ {{template "repo/upload" .}}
+
+ {{template "repo/editor/commit_form" .}}
+
- {{template "repo/upload" .}}
-
- {{template "repo/editor/commit_form" .}}
-
+
- {{template "repo/header" .}}
-
-
-{{template "base/footer" .}}
diff --git a/templates/repo/view.tmpl b/templates/repo/view.tmpl
index f99fe2f57a..99f2a7da7e 100644
--- a/templates/repo/view.tmpl
+++ b/templates/repo/view.tmpl
@@ -17,9 +17,7 @@
{{template "repo/code/recently_pushed_new_branches" dict "RecentBranchesPromptData" .RecentBranchesPromptData}}
-
- {{template "repo/view_file_tree" .}}
-
+ {{template "repo/view_file_tree" .}}
{{template "repo/view_content" .}}
diff --git a/templates/repo/view_content.tmpl b/templates/repo/view_content.tmpl
index 66e4fffcb9..b31648fbbe 100644
--- a/templates/repo/view_content.tmpl
+++ b/templates/repo/view_content.tmpl
@@ -5,11 +5,7 @@
{{if not $isTreePathRoot}}
-
+ {{template "repo/view_file_tree_toggle_button" .}}
{{end}}
{{template "repo/branch_dropdown" dict
@@ -37,31 +33,6 @@
{{end}}
-
- {{if $isTreePathRoot}}
- {{ctx.Locale.Tr "repo.find_file.go_to_file"}}
- {{end}}
-
- {{if and .RefFullName.IsBranch (not .IsViewFile)}}
-
- {{end}}
-
{{if and $isTreePathRoot .Repository.IsTemplate}}
{{ctx.Locale.Tr "repo.use_template"}}
@@ -86,12 +57,65 @@
+
+
+ {{if .RefFullName.IsBranch}}
+ {{$addFilePath := .TreePath}}
+ {{if .IsViewFile}}
+ {{if gt (len .TreeNames) 1}}
+ {{$addFilePath = StringUtils.Join (slice .TreeNames 0 (Eval (len .TreeNames) "-" 1)) "/"}}
+ {{else}}
+ {{$addFilePath = ""}}
+ {{end}}
+ {{end}}
+
+
+ {{if and (not .IsViewFile) (not $isTreePathRoot)}}
+
+ {{end}}
+ {{end}}
{{if $isTreePathRoot}}
{{template "repo/clone_panel" .}}
{{end}}
{{if and (not $isTreePathRoot) (not .IsViewFile) (not .IsBlame)}}{{/* IsViewDirectory (not home), TODO: split the templates, avoid using "if" tricks */}}
-
+
{{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}}
{{end}}
diff --git a/templates/repo/view_file_tree.tmpl b/templates/repo/view_file_tree.tmpl
index 8aed05f346..f79fcc22aa 100644
--- a/templates/repo/view_file_tree.tmpl
+++ b/templates/repo/view_file_tree.tmpl
@@ -1,15 +1,17 @@
-
-
-
- {{ctx.Locale.Tr "files"}}
-
+
+
diff --git a/templates/repo/view_file_tree_toggle_button.tmpl b/templates/repo/view_file_tree_toggle_button.tmpl
new file mode 100644
index 0000000000..3d6ea928ed
--- /dev/null
+++ b/templates/repo/view_file_tree_toggle_button.tmpl
@@ -0,0 +1,6 @@
+
diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl
index 145494aa1a..61443ac465 100644
--- a/templates/repo/view_list.tmpl
+++ b/templates/repo/view_list.tmpl
@@ -47,7 +47,7 @@
{{end}}
{{end}}
+
+ {{ctx.Locale.Tr "files"}}
+
-{{/* TODO: Dynamically move components such as refSelector and createPR here */}}
-
+ {{/* TODO: Dynamically move components such as refSelector and createPR here */}}
+
+
+
{{if $commit}}
{{$commitLink := printf "%s/commit/%s" $.RepoLink (PathEscape $commit.ID.String)}}
{{ctx.RenderUtils.RenderCommitMessageLinkSubject $commit.Message $commitLink $.Repository}}
diff --git a/tests/integration/api_repo_file_helpers.go b/tests/integration/api_repo_file_helpers.go
index f8d6fc803f..9a4c448664 100644
--- a/tests/integration/api_repo_file_helpers.go
+++ b/tests/integration/api_repo_file_helpers.go
@@ -5,6 +5,7 @@ package integration
import (
"context"
+ "errors"
"strings"
"testing"
@@ -72,7 +73,7 @@ func deleteFileInBranch(user *user_model.User, repo *repo_model.Repository, tree
func createOrReplaceFileInBranch(user *user_model.User, repo *repo_model.Repository, treePath, branchName, content string) error {
_, err := deleteFileInBranch(user, repo, treePath, branchName)
- if err != nil && !files_service.IsErrRepoFileDoesNotExist(err) {
+ if err != nil && !errors.Is(err, util.ErrNotExist) {
return err
}
diff --git a/tests/integration/repofiles_change_test.go b/tests/integration/repofiles_change_test.go
index 6821f8bf61..6fd42401c5 100644
--- a/tests/integration/repofiles_change_test.go
+++ b/tests/integration/repofiles_change_test.go
@@ -5,6 +5,7 @@ package integration
import (
"fmt"
+ "net/http"
"net/url"
"path"
"strings"
@@ -12,7 +13,6 @@ import (
"time"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
@@ -22,6 +22,7 @@ import (
files_service "code.gitea.io/gitea/services/repository/files"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func getCreateRepoFilesOptions(repo *repo_model.Repository) *files_service.ChangeRepoFilesOptions {
@@ -93,55 +94,6 @@ func getUpdateRepoFilesRenameOptions(repo *repo_model.Repository) *files_service
}
}
-func getDeleteRepoFilesOptions(repo *repo_model.Repository) *files_service.ChangeRepoFilesOptions {
- return &files_service.ChangeRepoFilesOptions{
- Files: []*files_service.ChangeRepoFile{
- {
- Operation: "delete",
- TreePath: "README.md",
- SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
- },
- },
- LastCommitID: "",
- OldBranch: repo.DefaultBranch,
- NewBranch: repo.DefaultBranch,
- Message: "Deletes README.md",
- Author: &files_service.IdentityOptions{
- GitUserName: "Bob Smith",
- GitUserEmail: "bob@smith.com",
- },
- Committer: nil,
- }
-}
-
-func getExpectedFileResponseForRepoFilesDelete() *api.FileResponse {
- // Just returns fields that don't change, i.e. fields with commit SHAs and dates can't be determined
- return &api.FileResponse{
- Content: nil,
- Commit: &api.FileCommitResponse{
- Author: &api.CommitUser{
- Identity: api.Identity{
- Name: "Bob Smith",
- Email: "bob@smith.com",
- },
- },
- Committer: &api.CommitUser{
- Identity: api.Identity{
- Name: "Bob Smith",
- Email: "bob@smith.com",
- },
- },
- Message: "Deletes README.md\n",
- },
- Verification: &api.PayloadCommitVerification{
- Verified: false,
- Reason: "gpg.error.not_signed_commit",
- Signature: "",
- Payload: "",
- },
- }
-}
-
func getExpectedFileResponseForRepoFilesCreate(commitID string, lastCommit *git.Commit) *api.FileResponse {
treePath := "new/file.txt"
encoding := "base64"
@@ -578,75 +530,88 @@ func TestChangeRepoFilesWithoutBranchNames(t *testing.T) {
}
func TestChangeRepoFilesForDelete(t *testing.T) {
- onGiteaRun(t, testDeleteRepoFiles)
-}
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ ctx, _ := contexttest.MockContext(t, "user2/repo1")
+ ctx.SetPathParam("id", "1")
+ contexttest.LoadRepo(t, ctx, 1)
+ contexttest.LoadRepoCommit(t, ctx)
+ contexttest.LoadUser(t, ctx, 2)
+ contexttest.LoadGitRepo(t, ctx)
+ defer ctx.Repo.GitRepo.Close()
+ repo := ctx.Repo.Repository
+ doer := ctx.Doer
-func testDeleteRepoFiles(t *testing.T, u *url.URL) {
- // setup
- unittest.PrepareTestEnv(t)
- ctx, _ := contexttest.MockContext(t, "user2/repo1")
- ctx.SetPathParam("id", "1")
- contexttest.LoadRepo(t, ctx, 1)
- contexttest.LoadRepoCommit(t, ctx)
- contexttest.LoadUser(t, ctx, 2)
- contexttest.LoadGitRepo(t, ctx)
- defer ctx.Repo.GitRepo.Close()
- repo := ctx.Repo.Repository
- doer := ctx.Doer
- opts := getDeleteRepoFilesOptions(repo)
+ t.Run("Delete README.md by commit", func(t *testing.T) {
+ urlRaw := "/user2/repo1/raw/branch/branch2/README.md"
+ MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusOK)
+ opts := &files_service.ChangeRepoFilesOptions{
+ OldBranch: "branch2",
+ LastCommitID: "985f0301dba5e7b34be866819cd15ad3d8f508ee",
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "delete",
+ TreePath: "README.md",
+ },
+ },
+ Message: "test message",
+ }
+ filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
+ require.NoError(t, err)
+ assert.NotNil(t, filesResponse)
+ assert.Nil(t, filesResponse.Files[0])
+ assert.Equal(t, "test message\n", filesResponse.Commit.Message)
+ MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusNotFound)
+ })
- t.Run("Delete README.md file", func(t *testing.T) {
- filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
- assert.NoError(t, err)
- expectedFileResponse := getExpectedFileResponseForRepoFilesDelete()
- assert.NotNil(t, filesResponse)
- assert.Nil(t, filesResponse.Files[0])
- assert.Equal(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message)
- assert.Equal(t, expectedFileResponse.Commit.Author.Identity, filesResponse.Commit.Author.Identity)
- assert.Equal(t, expectedFileResponse.Commit.Committer.Identity, filesResponse.Commit.Committer.Identity)
- assert.Equal(t, expectedFileResponse.Verification, filesResponse.Verification)
- })
+ t.Run("Delete README.md with options", func(t *testing.T) {
+ urlRaw := "/user2/repo1/raw/branch/master/README.md"
+ MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusOK)
+ opts := &files_service.ChangeRepoFilesOptions{
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "delete",
+ TreePath: "README.md",
+ SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
+ },
+ },
+ OldBranch: repo.DefaultBranch,
+ NewBranch: repo.DefaultBranch,
+ Message: "Message for deleting README.md",
+ Author: &files_service.IdentityOptions{GitUserName: "Bob Smith", GitUserEmail: "bob@smith.com"},
+ }
+ filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
+ require.NoError(t, err)
+ require.NotNil(t, filesResponse)
+ assert.Nil(t, filesResponse.Files[0])
+ assert.Equal(t, "Message for deleting README.md\n", filesResponse.Commit.Message)
+ assert.Equal(t, api.Identity{Name: "Bob Smith", Email: "bob@smith.com"}, filesResponse.Commit.Author.Identity)
+ assert.Equal(t, api.Identity{Name: "Bob Smith", Email: "bob@smith.com"}, filesResponse.Commit.Committer.Identity)
+ assert.Equal(t, &api.PayloadCommitVerification{Reason: "gpg.error.not_signed_commit"}, filesResponse.Verification)
+ MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusNotFound)
+ })
- t.Run("Verify README.md has been deleted", func(t *testing.T) {
- filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
- assert.Nil(t, filesResponse)
- expectedError := "repository file does not exist [path: " + opts.Files[0].TreePath + "]"
- assert.EqualError(t, err, expectedError)
- })
-}
-
-// Test opts with branch names removed, same results
-func TestChangeRepoFilesForDeleteWithoutBranchNames(t *testing.T) {
- onGiteaRun(t, testDeleteRepoFilesWithoutBranchNames)
-}
-
-func testDeleteRepoFilesWithoutBranchNames(t *testing.T, u *url.URL) {
- // setup
- unittest.PrepareTestEnv(t)
- ctx, _ := contexttest.MockContext(t, "user2/repo1")
- ctx.SetPathParam("id", "1")
- contexttest.LoadRepo(t, ctx, 1)
- contexttest.LoadRepoCommit(t, ctx)
- contexttest.LoadUser(t, ctx, 2)
- contexttest.LoadGitRepo(t, ctx)
- defer ctx.Repo.GitRepo.Close()
-
- repo := ctx.Repo.Repository
- doer := ctx.Doer
- opts := getDeleteRepoFilesOptions(repo)
- opts.OldBranch = ""
- opts.NewBranch = ""
-
- t.Run("Delete README.md without Branch Name", func(t *testing.T) {
- filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
- assert.NoError(t, err)
- expectedFileResponse := getExpectedFileResponseForRepoFilesDelete()
- assert.NotNil(t, filesResponse)
- assert.Nil(t, filesResponse.Files[0])
- assert.Equal(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message)
- assert.Equal(t, expectedFileResponse.Commit.Author.Identity, filesResponse.Commit.Author.Identity)
- assert.Equal(t, expectedFileResponse.Commit.Committer.Identity, filesResponse.Commit.Committer.Identity)
- assert.Equal(t, expectedFileResponse.Verification, filesResponse.Verification)
+ t.Run("Delete directory", func(t *testing.T) {
+ urlRaw := "/user2/repo1/raw/branch/sub-home-md-img-check/docs/README.md"
+ MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusOK)
+ opts := &files_service.ChangeRepoFilesOptions{
+ OldBranch: "sub-home-md-img-check",
+ LastCommitID: "4649299398e4d39a5c09eb4f534df6f1e1eb87cc",
+ Files: []*files_service.ChangeRepoFile{
+ {
+ Operation: "delete",
+ TreePath: "docs",
+ DeleteRecursively: true,
+ },
+ },
+ Message: "test message",
+ }
+ filesResponse, err := files_service.ChangeRepoFiles(t.Context(), repo, doer, opts)
+ require.NoError(t, err)
+ assert.NotNil(t, filesResponse)
+ assert.Nil(t, filesResponse.Files[0])
+ assert.Equal(t, "test message\n", filesResponse.Commit.Message)
+ MakeRequest(t, NewRequest(t, "GET", urlRaw), http.StatusNotFound)
+ })
})
}
diff --git a/web_src/css/editor/fileeditor.css b/web_src/css/editor/fileeditor.css
index 698efffc99..12ae97a109 100644
--- a/web_src/css/editor/fileeditor.css
+++ b/web_src/css/editor/fileeditor.css
@@ -1,23 +1,3 @@
-.repository.file.editor .tab[data-tab="write"] {
- padding: 0 !important;
-}
-
-.repository.file.editor .tab[data-tab="write"] .editor-toolbar {
- border: 0 !important;
-}
-
-.repository.file.editor .tab[data-tab="write"] .CodeMirror {
- border-left: 0;
- border-right: 0;
- border-bottom: 0;
-}
-
-.repo-editor-header {
- display: flex;
- margin: 1rem 0;
- padding: 3px 0;
-}
-
.editor-toolbar {
border-color: var(--color-secondary);
}
diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css
index 779339c46b..aedf53569a 100644
--- a/web_src/css/modules/animations.css
+++ b/web_src/css/modules/animations.css
@@ -28,7 +28,7 @@
aspect-ratio: 1;
transform: translate(-50%, -50%);
animation: isloadingspin 1000ms infinite linear;
- border-width: 4px;
+ border-width: 3px;
border-style: solid;
border-color: var(--color-secondary) var(--color-secondary) var(--color-secondary-dark-8) var(--color-secondary-dark-8);
border-radius: var(--border-radius-full);
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 9b70e0e6db..0bf37ca083 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -150,63 +150,68 @@ td .commit-summary {
}
}
-.repository.file.list .non-diff-file-content .header .icon {
+.non-diff-file-content .header .icon {
font-size: 1em;
}
-.repository.file.list .non-diff-file-content .header .small.icon {
+.non-diff-file-content .header .small.icon {
font-size: 0.75em;
}
-.repository.file.list .non-diff-file-content .header .tiny.icon {
+.non-diff-file-content .header .tiny.icon {
font-size: 0.5em;
}
-.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon {
+.non-diff-file-content .header .file-actions .btn-octicon {
line-height: var(--line-height-default);
padding: 8px;
vertical-align: middle;
color: var(--color-text);
}
-.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon:hover {
+.non-diff-file-content .header .file-actions .btn-octicon:hover {
color: var(--color-primary);
}
-.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon-danger:hover {
+.non-diff-file-content .header .file-actions .btn-octicon-danger:hover {
color: var(--color-red);
}
-.repository.file.list .non-diff-file-content .header .file-actions .btn-octicon.disabled {
+.non-diff-file-content .header .file-actions .btn-octicon.disabled {
color: inherit;
opacity: var(--opacity-disabled);
cursor: default;
}
-.repository.file.list .non-diff-file-content .plain-text {
+.non-diff-file-content .plain-text {
padding: 1em 2em;
}
-.repository.file.list .non-diff-file-content .plain-text pre {
+.non-diff-file-content .plain-text pre {
overflow-wrap: anywhere;
white-space: pre-wrap;
}
-.repository.file.list .non-diff-file-content .csv {
+.non-diff-file-content .csv {
overflow-x: auto;
padding: 0 !important;
}
-.repository.file.list .non-diff-file-content pre {
+.non-diff-file-content pre {
overflow: auto;
}
-.repository.file.list .non-diff-file-content .asciicast {
+.non-diff-file-content .asciicast {
padding: 0 !important;
}
.repo-editor-header {
+ display: flex;
+ margin: 1rem 0;
+ padding: 3px 0;
width: 100%;
+ gap: 0.5em;
+ align-items: center;
}
.repo-editor-header input {
@@ -216,17 +221,13 @@ td .commit-summary {
margin-right: 5px !important;
}
-.repository.file.editor .tabular.menu .svg {
- margin-right: 5px;
-}
-
.repository.file.editor .commit-form-wrapper {
- padding-left: 48px;
+ padding-left: 58px;
}
.repository.file.editor .commit-form-wrapper .commit-avatar {
float: left;
- margin-left: -48px;
+ margin-left: -58px;
}
.repository.file.editor .commit-form-wrapper .commit-form {
@@ -1409,12 +1410,25 @@ td .commit-summary {
flex-grow: 1;
}
-.repo-button-row .ui.button {
+.repo-button-row .ui.button,
+.repo-view-container .ui.button.repo-view-file-tree-toggle {
flex-shrink: 0;
margin: 0;
min-height: 30px;
}
+.repo-view-container .ui.button.repo-view-file-tree-toggle {
+ padding: 0 6px;
+}
+
+.repo-button-row .repo-file-search-container .ui.input {
+ height: 30px;
+}
+
+.repo-button-row .ui.dropdown > .menu {
+ margin-top: 4px;
+}
+
tbody.commit-list {
vertical-align: baseline;
}
@@ -1483,6 +1497,12 @@ tbody.commit-list {
line-height: initial;
}
+.commit-body a.commit code,
+.commit-summary a.commit code {
+ /* these links are generated by the render: ... */
+ background: inherit;
+}
+
.git-notes.top {
text-align: left;
}
diff --git a/web_src/css/repo/home.css b/web_src/css/repo/home.css
index ee371f1b1c..60bf1f17f9 100644
--- a/web_src/css/repo/home.css
+++ b/web_src/css/repo/home.css
@@ -54,7 +54,9 @@
gap: var(--page-spacing);
}
-.repo-view-container .repo-view-file-tree-container {
+.repo-view-file-tree-container {
+ display: flex;
+ flex-direction: column;
flex: 0 0 15%;
min-width: 0;
max-height: 100vh;
@@ -65,6 +67,12 @@
overflow-y: hidden;
}
+@media (max-width: 767.98px) {
+ .repo-view-file-tree-container {
+ display: none;
+ }
+}
+
.repo-view-content {
flex: 1;
min-width: 0;
diff --git a/web_src/css/themes/theme-gitea-dark.css b/web_src/css/themes/theme-gitea-dark.css
index 1718a1f06b..f89752dc79 100644
--- a/web_src/css/themes/theme-gitea-dark.css
+++ b/web_src/css/themes/theme-gitea-dark.css
@@ -244,6 +244,7 @@ gitea-theme-meta-info {
--color-highlight-fg: #87651e;
--color-highlight-bg: #352c1c;
--color-overlay-backdrop: #080808c0;
+ --color-danger: var(--color-red);
accent-color: var(--color-accent);
color-scheme: dark;
}
diff --git a/web_src/css/themes/theme-gitea-light.css b/web_src/css/themes/theme-gitea-light.css
index db54f5e5fb..1261ef8be0 100644
--- a/web_src/css/themes/theme-gitea-light.css
+++ b/web_src/css/themes/theme-gitea-light.css
@@ -244,6 +244,7 @@ gitea-theme-meta-info {
--color-highlight-fg: #eed200;
--color-highlight-bg: #fffbdd;
--color-overlay-backdrop: #080808c0;
+ --color-danger: var(--color-red);
accent-color: var(--color-accent);
color-scheme: light;
}
diff --git a/web_src/js/components/RepoFileSearch.vue b/web_src/js/components/RepoFileSearch.vue
new file mode 100644
index 0000000000..cbc1d50656
--- /dev/null
+++ b/web_src/js/components/RepoFileSearch.vue
@@ -0,0 +1,230 @@
+
+
+
+ = [];
-let repoFindFileInput: HTMLInputElement;
-let repoFindFileTableBody: HTMLElement;
-let repoFindFileNoResult: HTMLElement;
// return the case-insensitive sub-match result as an array: [unmatched, matched, unmatched, matched, ...]
// res[even] is unmatched, res[odd] is matched, see unit tests for examples
@@ -73,48 +68,14 @@ export function filterRepoFilesWeighted(files: Array, filter: string) {
return filterResult;
}
-function filterRepoFiles(filter: string) {
- const treeLink = repoFindFileInput.getAttribute('data-url-tree-link');
- repoFindFileTableBody.innerHTML = '';
-
- const filterResult = filterRepoFilesWeighted(files, filter);
-
- toggleElem(repoFindFileNoResult, !filterResult.length);
- for (const r of filterResult) {
- const row = document.createElement('tr');
- const cell = document.createElement('td');
- const a = document.createElement('a');
- a.setAttribute('href', `${treeLink}/${pathEscapeSegments(r.matchResult.join(''))}`);
- a.innerHTML = svg('octicon-file', 16, 'tw-mr-2');
- row.append(cell);
- cell.append(a);
- for (const [index, part] of r.matchResult.entries()) {
- const span = document.createElement('span');
- // safely escape by using textContent
- span.textContent = part;
- span.title = span.textContent;
- // if the target file path is "abc/xyz", to search "bx", then the matchResult is ['a', 'b', 'c/', 'x', 'yz']
- // the matchResult[odd] is matched and highlighted to red.
- if (index % 2 === 1) span.classList.add('ui', 'text', 'red');
- a.append(span);
- }
- repoFindFileTableBody.append(row);
- }
-}
-
-async function loadRepoFiles() {
- const response = await GET(repoFindFileInput.getAttribute('data-url-data-link'));
- files = await response.json();
- filterRepoFiles(repoFindFileInput.value);
-}
-
-export function initFindFileInRepo() {
- repoFindFileInput = document.querySelector('#repo-file-find-input');
- if (!repoFindFileInput) return;
-
- repoFindFileTableBody = document.querySelector('#repo-find-file-table tbody');
- repoFindFileNoResult = document.querySelector('#repo-find-file-no-result');
- repoFindFileInput.addEventListener('input', () => filterRepoFiles(repoFindFileInput.value));
-
- loadRepoFiles();
+export function initRepoFileSearch() {
+ registerGlobalInitFunc('initRepoFileSearch', (el) => {
+ createApp(RepoFileSearch, {
+ repoLink: el.getAttribute('data-repo-link'),
+ currentRefNameSubURL: el.getAttribute('data-current-ref-name-sub-url'),
+ treeListUrl: el.getAttribute('data-tree-list-url'),
+ noResultsText: el.getAttribute('data-no-results-text'),
+ placeholder: el.getAttribute('data-placeholder'),
+ }).mount(el);
+ });
}
diff --git a/web_src/js/features/repo-view-file-tree.ts b/web_src/js/features/repo-view-file-tree.ts
index f52b64cc51..98ffdb8a86 100644
--- a/web_src/js/features/repo-view-file-tree.ts
+++ b/web_src/js/features/repo-view-file-tree.ts
@@ -6,8 +6,12 @@ import {registerGlobalEventFunc} from '../modules/observer.ts';
const {appSubUrl} = window.config;
+function isUserSignedIn() {
+ return Boolean(document.querySelector('#navbar .user-menu'));
+}
+
async function toggleSidebar(btn: HTMLElement) {
- const elToggleShow = document.querySelector('.repo-view-file-tree-toggle-show');
+ const elToggleShow = document.querySelector('.repo-view-file-tree-toggle[data-toggle-action="show"]');
const elFileTreeContainer = document.querySelector('.repo-view-file-tree-container');
const shouldShow = btn.getAttribute('data-toggle-action') === 'show';
toggleElem(elFileTreeContainer, shouldShow);
@@ -15,7 +19,7 @@ async function toggleSidebar(btn: HTMLElement) {
// FIXME: need to remove "full height" style from parent element
- if (!elFileTreeContainer.hasAttribute('data-user-is-signed-in')) return;
+ if (!isUserSignedIn()) return;
await POST(`${appSubUrl}/user/settings/update_preferences`, {
data: {codeViewShowFileTree: shouldShow},
});
diff --git a/web_src/js/index-domready.ts b/web_src/js/index-domready.ts
index df56c85c86..8f5d4ecb15 100644
--- a/web_src/js/index-domready.ts
+++ b/web_src/js/index-domready.ts
@@ -16,7 +16,7 @@ import {initMarkupAnchors} from './markup/anchors.ts';
import {initNotificationCount} from './features/notification.ts';
import {initRepoIssueContentHistory} from './features/repo-issue-content.ts';
import {initStopwatch} from './features/stopwatch.ts';
-import {initFindFileInRepo} from './features/repo-findfile.ts';
+import {initRepoFileSearch} from './features/repo-findfile.ts';
import {initMarkupContent} from './markup/content.ts';
import {initRepoFileView} from './features/file-view.ts';
import {initUserAuthOauth2, initUserCheckAppUrl} from './features/user-auth.ts';
@@ -101,7 +101,7 @@ const initPerformanceTracer = callInitFunctions([
initSshKeyFormParser,
initStopwatch,
initTableSort,
- initFindFileInRepo,
+ initRepoFileSearch,
initCopyContent,
initAdminCommon,
+
+
+
+
+
+
diff --git a/web_src/js/features/repo-findfile.ts b/web_src/js/features/repo-findfile.ts
index 59c827126f..7a35a3c7ff 100644
--- a/web_src/js/features/repo-findfile.ts
+++ b/web_src/js/features/repo-findfile.ts
@@ -1,13 +1,8 @@
-import {svg} from '../svg.ts';
-import {toggleElem} from '../utils/dom.ts';
-import {pathEscapeSegments} from '../utils/url.ts';
-import {GET} from '../modules/fetch.ts';
+import {createApp} from 'vue';
+import RepoFileSearch from '../components/RepoFileSearch.vue';
+import {registerGlobalInitFunc} from '../modules/observer.ts';
const threshold = 50;
-let files: Array
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ part }}
+
+
+
+
+
+
+ {{ props.noResultsText }}
+
+
+