mirror of
https://github.com/go-gitea/gitea.git
synced 2025-08-17 17:07:27 +00:00
Merge branch 'main' into feat/add-oauth-management-to-api-for-iac-tooling
This commit is contained in:
commit
cf3d746afc
@ -81,6 +81,10 @@ var microcmdUserCreate = &cli.Command{
|
|||||||
Name: "restricted",
|
Name: "restricted",
|
||||||
Usage: "Make a restricted user account",
|
Usage: "Make a restricted user account",
|
||||||
},
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "fullname",
|
||||||
|
Usage: `The full, human-readable name of the user`,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,6 +195,7 @@ func runCreateUser(c *cli.Context) error {
|
|||||||
Passwd: password,
|
Passwd: password,
|
||||||
MustChangePassword: mustChangePassword,
|
MustChangePassword: mustChangePassword,
|
||||||
Visibility: visibility,
|
Visibility: visibility,
|
||||||
|
FullName: c.String("fullname"),
|
||||||
}
|
}
|
||||||
|
|
||||||
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||||
|
@ -50,17 +50,17 @@ func TestAdminUserCreate(t *testing.T) {
|
|||||||
assert.Equal(t, check{IsAdmin: false, MustChangePassword: false}, createCheck("u5", "--must-change-password=false"))
|
assert.Equal(t, check{IsAdmin: false, MustChangePassword: false}, createCheck("u5", "--must-change-password=false"))
|
||||||
})
|
})
|
||||||
|
|
||||||
createUser := func(name, args string) error {
|
createUser := func(name string, args ...string) error {
|
||||||
return app.Run(strings.Fields(fmt.Sprintf("./gitea admin user create --username %s --email %s@gitea.local %s", name, name, args)))
|
return app.Run(append([]string{"./gitea", "admin", "user", "create", "--username", name, "--email", name + "@gitea.local"}, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("UserType", func(t *testing.T) {
|
t.Run("UserType", func(t *testing.T) {
|
||||||
reset()
|
reset()
|
||||||
assert.ErrorContains(t, createUser("u", "--user-type invalid"), "invalid user type")
|
assert.ErrorContains(t, createUser("u", "--user-type", "invalid"), "invalid user type")
|
||||||
assert.ErrorContains(t, createUser("u", "--user-type bot --password 123"), "can only be set for individual users")
|
assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--password", "123"), "can only be set for individual users")
|
||||||
assert.ErrorContains(t, createUser("u", "--user-type bot --must-change-password"), "can only be set for individual users")
|
assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--must-change-password"), "can only be set for individual users")
|
||||||
|
|
||||||
assert.NoError(t, createUser("u", "--user-type bot"))
|
assert.NoError(t, createUser("u", "--user-type", "bot"))
|
||||||
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u"})
|
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u"})
|
||||||
assert.Equal(t, user_model.UserTypeBot, u.Type)
|
assert.Equal(t, user_model.UserTypeBot, u.Type)
|
||||||
assert.Empty(t, u.Passwd)
|
assert.Empty(t, u.Passwd)
|
||||||
@ -75,7 +75,7 @@ func TestAdminUserCreate(t *testing.T) {
|
|||||||
|
|
||||||
// using "--access-token" only means "all" access
|
// using "--access-token" only means "all" access
|
||||||
reset()
|
reset()
|
||||||
assert.NoError(t, createUser("u", "--random-password --access-token"))
|
assert.NoError(t, createUser("u", "--random-password", "--access-token"))
|
||||||
assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
|
assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
|
||||||
assert.Equal(t, 1, unittest.GetCount(t, &auth_model.AccessToken{}))
|
assert.Equal(t, 1, unittest.GetCount(t, &auth_model.AccessToken{}))
|
||||||
accessToken := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{Name: "gitea-admin"})
|
accessToken := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{Name: "gitea-admin"})
|
||||||
@ -85,7 +85,7 @@ func TestAdminUserCreate(t *testing.T) {
|
|||||||
|
|
||||||
// using "--access-token" with name & scopes
|
// using "--access-token" with name & scopes
|
||||||
reset()
|
reset()
|
||||||
assert.NoError(t, createUser("u", "--random-password --access-token --access-token-name new-token-name --access-token-scopes read:issue,read:user"))
|
assert.NoError(t, createUser("u", "--random-password", "--access-token", "--access-token-name", "new-token-name", "--access-token-scopes", "read:issue,read:user"))
|
||||||
assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
|
assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
|
||||||
assert.Equal(t, 1, unittest.GetCount(t, &auth_model.AccessToken{}))
|
assert.Equal(t, 1, unittest.GetCount(t, &auth_model.AccessToken{}))
|
||||||
accessToken = unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{Name: "new-token-name"})
|
accessToken = unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{Name: "new-token-name"})
|
||||||
@ -98,23 +98,38 @@ func TestAdminUserCreate(t *testing.T) {
|
|||||||
|
|
||||||
// using "--access-token-name" without "--access-token"
|
// using "--access-token-name" without "--access-token"
|
||||||
reset()
|
reset()
|
||||||
err = createUser("u", "--random-password --access-token-name new-token-name")
|
err = createUser("u", "--random-password", "--access-token-name", "new-token-name")
|
||||||
assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
|
assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
|
||||||
assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
|
assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
|
||||||
assert.ErrorContains(t, err, "access-token-name and access-token-scopes flags are only valid when access-token flag is set")
|
assert.ErrorContains(t, err, "access-token-name and access-token-scopes flags are only valid when access-token flag is set")
|
||||||
|
|
||||||
// using "--access-token-scopes" without "--access-token"
|
// using "--access-token-scopes" without "--access-token"
|
||||||
reset()
|
reset()
|
||||||
err = createUser("u", "--random-password --access-token-scopes read:issue")
|
err = createUser("u", "--random-password", "--access-token-scopes", "read:issue")
|
||||||
assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
|
assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
|
||||||
assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
|
assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
|
||||||
assert.ErrorContains(t, err, "access-token-name and access-token-scopes flags are only valid when access-token flag is set")
|
assert.ErrorContains(t, err, "access-token-name and access-token-scopes flags are only valid when access-token flag is set")
|
||||||
|
|
||||||
// empty permission
|
// empty permission
|
||||||
reset()
|
reset()
|
||||||
err = createUser("u", "--random-password --access-token --access-token-scopes public-only")
|
err = createUser("u", "--random-password", "--access-token", "--access-token-scopes", "public-only")
|
||||||
assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
|
assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
|
||||||
assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
|
assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
|
||||||
assert.ErrorContains(t, err, "access token does not have any permission")
|
assert.ErrorContains(t, err, "access token does not have any permission")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("UserFields", func(t *testing.T) {
|
||||||
|
reset()
|
||||||
|
assert.NoError(t, createUser("u-FullNameWithSpace", "--random-password", "--fullname", "First O'Middle Last"))
|
||||||
|
unittest.AssertExistsAndLoadBean(t, &user_model.User{
|
||||||
|
Name: "u-FullNameWithSpace",
|
||||||
|
LowerName: "u-fullnamewithspace",
|
||||||
|
FullName: "First O'Middle Last",
|
||||||
|
Email: "u-FullNameWithSpace@gitea.local",
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, createUser("u-FullNameEmpty", "--random-password", "--fullname", ""))
|
||||||
|
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u-fullnameempty"})
|
||||||
|
assert.Empty(t, u.FullName)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -86,20 +86,15 @@ func (r *GlodmarkRender) highlightingRenderer(w util.BufWriter, c highlighting.C
|
|||||||
preClasses += " is-loading"
|
preClasses += " is-loading"
|
||||||
}
|
}
|
||||||
|
|
||||||
err := r.ctx.RenderInternal.FormatWithSafeAttrs(w, `<pre class="%s">`, preClasses)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// include language-x class as part of commonmark spec, "chroma" class is used to highlight the code
|
// include language-x class as part of commonmark spec, "chroma" class is used to highlight the code
|
||||||
// the "display" class is used by "js/markup/math.ts" to render the code element as a block
|
// the "display" class is used by "js/markup/math.ts" to render the code element as a block
|
||||||
// the "math.ts" strictly depends on the structure: <pre class="code-block is-loading"><code class="language-math display">...</code></pre>
|
// the "math.ts" strictly depends on the structure: <pre class="code-block is-loading"><code class="language-math display">...</code></pre>
|
||||||
err = r.ctx.RenderInternal.FormatWithSafeAttrs(w, `<code class="chroma language-%s display">`, languageStr)
|
err := r.ctx.RenderInternal.FormatWithSafeAttrs(w, `<div class="code-block-container code-overflow-scroll"><pre class="%s"><code class="chroma language-%s display">`, preClasses, languageStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err := w.WriteString("</code></pre>")
|
_, err := w.WriteString("</code></pre></div>")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -117,6 +117,7 @@ files=ファイル
|
|||||||
|
|
||||||
error=エラー
|
error=エラー
|
||||||
error404=アクセスしようとしたページは<strong>存在しない</strong>か、閲覧が<strong>許可されていません</strong>。
|
error404=アクセスしようとしたページは<strong>存在しない</strong>か、閲覧が<strong>許可されていません</strong>。
|
||||||
|
error503=サーバーはリクエストを完了できませんでした。 後でもう一度お試しください。
|
||||||
go_back=戻る
|
go_back=戻る
|
||||||
invalid_data=無効なデータ: %v
|
invalid_data=無効なデータ: %v
|
||||||
|
|
||||||
@ -730,6 +731,8 @@ public_profile=公開プロフィール
|
|||||||
biography_placeholder=自己紹介してください!(Markdownを使うことができます)
|
biography_placeholder=自己紹介してください!(Markdownを使うことができます)
|
||||||
location_placeholder=おおよその場所を他の人と共有
|
location_placeholder=おおよその場所を他の人と共有
|
||||||
profile_desc=あなたのプロフィールが他のユーザーにどのように表示されるかを制御します。あなたのプライマリメールアドレスは、通知、パスワードの回復、WebベースのGit操作に使用されます。
|
profile_desc=あなたのプロフィールが他のユーザーにどのように表示されるかを制御します。あなたのプライマリメールアドレスは、通知、パスワードの回復、WebベースのGit操作に使用されます。
|
||||||
|
password_username_disabled=ユーザー名の変更は許可されていません。詳細はサイト管理者にお問い合わせください。
|
||||||
|
password_full_name_disabled=フルネームの変更は許可されていません。詳細はサイト管理者にお問い合わせください。
|
||||||
full_name=フルネーム
|
full_name=フルネーム
|
||||||
website=Webサイト
|
website=Webサイト
|
||||||
location=場所
|
location=場所
|
||||||
@ -924,6 +927,9 @@ permission_not_set=設定なし
|
|||||||
permission_no_access=アクセス不可
|
permission_no_access=アクセス不可
|
||||||
permission_read=読み取り
|
permission_read=読み取り
|
||||||
permission_write=読み取りと書き込み
|
permission_write=読み取りと書き込み
|
||||||
|
permission_anonymous_read=匿名の読み込み
|
||||||
|
permission_everyone_read=全員の読み込み
|
||||||
|
permission_everyone_write=全員の書き込み
|
||||||
access_token_desc=選択したトークン権限に応じて、関連する<a %s>API</a>ルートのみに許可が制限されます。 詳細は<a %s>ドキュメント</a>を参照してください。
|
access_token_desc=選択したトークン権限に応じて、関連する<a %s>API</a>ルートのみに許可が制限されます。 詳細は<a %s>ドキュメント</a>を参照してください。
|
||||||
at_least_one_permission=トークンを作成するには、少なくともひとつの許可を選択する必要があります
|
at_least_one_permission=トークンを作成するには、少なくともひとつの許可を選択する必要があります
|
||||||
permissions_list=許可:
|
permissions_list=許可:
|
||||||
@ -1136,6 +1142,7 @@ transfer.no_permission_to_reject=この移転を拒否する権限がありま
|
|||||||
|
|
||||||
desc.private=プライベート
|
desc.private=プライベート
|
||||||
desc.public=公開
|
desc.public=公開
|
||||||
|
desc.public_access=公開アクセス
|
||||||
desc.template=テンプレート
|
desc.template=テンプレート
|
||||||
desc.internal=内部
|
desc.internal=内部
|
||||||
desc.archived=アーカイブ
|
desc.archived=アーカイブ
|
||||||
@ -1544,6 +1551,7 @@ issues.filter_project=プロジェクト
|
|||||||
issues.filter_project_all=すべてのプロジェクト
|
issues.filter_project_all=すべてのプロジェクト
|
||||||
issues.filter_project_none=プロジェクトなし
|
issues.filter_project_none=プロジェクトなし
|
||||||
issues.filter_assignee=担当者
|
issues.filter_assignee=担当者
|
||||||
|
issues.filter_assignee_no_assignee=担当者なし
|
||||||
issues.filter_assignee_any_assignee=担当者あり
|
issues.filter_assignee_any_assignee=担当者あり
|
||||||
issues.filter_poster=作成者
|
issues.filter_poster=作成者
|
||||||
issues.filter_user_placeholder=ユーザーを検索
|
issues.filter_user_placeholder=ユーザーを検索
|
||||||
@ -1647,6 +1655,8 @@ issues.label_archived_filter=アーカイブされたラベルを表示
|
|||||||
issues.label_archive_tooltip=アーカイブされたラベルは、ラベルによる検索時のサジェストからデフォルトで除外されます。
|
issues.label_archive_tooltip=アーカイブされたラベルは、ラベルによる検索時のサジェストからデフォルトで除外されます。
|
||||||
issues.label_exclusive_desc=ラベル名を <code>スコープ/アイテム</code> の形にすることで、他の <code>スコープ/</code> ラベルと排他的になります。
|
issues.label_exclusive_desc=ラベル名を <code>スコープ/アイテム</code> の形にすることで、他の <code>スコープ/</code> ラベルと排他的になります。
|
||||||
issues.label_exclusive_warning=イシューやプルリクエストのラベル編集では、競合するスコープ付きラベルは解除されます。
|
issues.label_exclusive_warning=イシューやプルリクエストのラベル編集では、競合するスコープ付きラベルは解除されます。
|
||||||
|
issues.label_exclusive_order=ソート順
|
||||||
|
issues.label_exclusive_order_tooltip=同じスコープ内の排他的なラベルは、この数値順にソートされます。
|
||||||
issues.label_count=ラベル %d件
|
issues.label_count=ラベル %d件
|
||||||
issues.label_open_issues=オープン中のイシュー %d件
|
issues.label_open_issues=オープン中のイシュー %d件
|
||||||
issues.label_edit=編集
|
issues.label_edit=編集
|
||||||
@ -2129,6 +2139,12 @@ contributors.contribution_type.deletions=削除
|
|||||||
settings=設定
|
settings=設定
|
||||||
settings.desc=設定では、リポジトリの設定を管理することができます。
|
settings.desc=設定では、リポジトリの設定を管理することができます。
|
||||||
settings.options=リポジトリ
|
settings.options=リポジトリ
|
||||||
|
settings.public_access=公開アクセス
|
||||||
|
settings.public_access_desc=外部からの訪問者のアクセス権限について、このリポジトリのデフォルト設定を上書きします。
|
||||||
|
settings.public_access.docs.not_set=設定なし: 公開アクセス権限はありません。訪問者の権限は、リポジトリの公開範囲とメンバーの権限に従います。
|
||||||
|
settings.public_access.docs.anonymous_read=匿名の読み込み: ログインしていないユーザーは読み取り権限でユニットにアクセスできます。
|
||||||
|
settings.public_access.docs.everyone_read=全員の読み込み: すべてのログインユーザーは読み取り権限でユニットにアクセスできます。イシュー/プルリクエストユニットの読み取り権限は、ユーザーが新しいイシュー/プルリクエストを作成できることを意味します。
|
||||||
|
settings.public_access.docs.everyone_write=全員の書き込み: すべてのログインユーザーに書き込み権限があります。Wikiユニットのみがこの権限をサポートします。
|
||||||
settings.collaboration=共同作業者
|
settings.collaboration=共同作業者
|
||||||
settings.collaboration.admin=管理者
|
settings.collaboration.admin=管理者
|
||||||
settings.collaboration.write=書き込み
|
settings.collaboration.write=書き込み
|
||||||
@ -2719,6 +2735,7 @@ branch.restore_success=ブランチ "%s" を復元しました。
|
|||||||
branch.restore_failed=ブランチ "%s" の復元に失敗しました。
|
branch.restore_failed=ブランチ "%s" の復元に失敗しました。
|
||||||
branch.protected_deletion_failed=ブランチ "%s" は保護されています。 削除できません。
|
branch.protected_deletion_failed=ブランチ "%s" は保護されています。 削除できません。
|
||||||
branch.default_deletion_failed=ブランチ "%s" はデフォルトブランチです。 削除できません。
|
branch.default_deletion_failed=ブランチ "%s" はデフォルトブランチです。 削除できません。
|
||||||
|
branch.default_branch_not_exist=デフォルトブランチ "%s" がありません。
|
||||||
branch.restore=ブランチ "%s" の復元
|
branch.restore=ブランチ "%s" の復元
|
||||||
branch.download=ブランチ "%s" をダウンロード
|
branch.download=ブランチ "%s" をダウンロード
|
||||||
branch.rename=ブランチ名 "%s" を変更
|
branch.rename=ブランチ名 "%s" を変更
|
||||||
|
@ -193,7 +193,7 @@ func getWikiPage(ctx *context.APIContext, wikiName wiki_service.WebPath) *api.Wi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get commit count - wiki revisions
|
// get commit count - wiki revisions
|
||||||
commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
|
commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
|
||||||
|
|
||||||
// Get last change information.
|
// Get last change information.
|
||||||
lastCommit, err := wikiRepo.GetCommitByPath(pageFilename)
|
lastCommit, err := wikiRepo.GetCommitByPath(pageFilename)
|
||||||
@ -432,7 +432,7 @@ func ListPageRevisions(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get commit count - wiki revisions
|
// get commit count - wiki revisions
|
||||||
commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
|
commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename)
|
||||||
|
|
||||||
page := ctx.FormInt("page")
|
page := ctx.FormInt("page")
|
||||||
if page <= 1 {
|
if page <= 1 {
|
||||||
@ -442,7 +442,7 @@ func ListPageRevisions(ctx *context.APIContext) {
|
|||||||
// get Commit Count
|
// get Commit Count
|
||||||
commitsHistory, err := wikiRepo.CommitsByFileAndRange(
|
commitsHistory, err := wikiRepo.CommitsByFileAndRange(
|
||||||
git.CommitsByFileAndRangeOptions{
|
git.CommitsByFileAndRangeOptions{
|
||||||
Revision: "master",
|
Revision: ctx.Repo.Repository.DefaultWikiBranch,
|
||||||
File: pageFilename,
|
File: pageFilename,
|
||||||
Page: page,
|
Page: page,
|
||||||
})
|
})
|
||||||
@ -486,7 +486,7 @@ func findWikiRepoCommit(ctx *context.APIContext) (*git.Repository, *git.Commit)
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
commit, err := wikiRepo.GetBranchCommit("master")
|
commit, err := wikiRepo.GetBranchCommit(ctx.Repo.Repository.DefaultWikiBranch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if git.IsErrNotExist(err) {
|
if git.IsErrNotExist(err) {
|
||||||
ctx.APIErrorNotFound(err)
|
ctx.APIErrorNotFound(err)
|
||||||
|
71
templates/devtest/markup-render.tmpl
Normal file
71
templates/devtest/markup-render.tmpl
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
{{template "devtest/devtest-header"}}
|
||||||
|
<div class="page-content devtest ui container">
|
||||||
|
{{$longCode := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}}
|
||||||
|
<div class="tw-flex">
|
||||||
|
<div class="tw-w-[50%] tw-p-4">
|
||||||
|
<div class="markup render-content">
|
||||||
|
Inline <code>code</code> content
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
<div class="markup render-content">
|
||||||
|
<p>content before</p>
|
||||||
|
<pre><code>Very long line with no code block or container: {{$longCode}}</code></pre>
|
||||||
|
<p>content after</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
<div class="markup render-content">
|
||||||
|
<p>content before</p>
|
||||||
|
<div class="code-block-container code-overflow-wrap">
|
||||||
|
<pre class="code-block"><code>Very long line with wrap: {{$longCode}}</code></pre>
|
||||||
|
</div>
|
||||||
|
<p>content after</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
<div class="markup render-content">
|
||||||
|
<p>content before</p>
|
||||||
|
<div class="code-block-container code-overflow-scroll">
|
||||||
|
<pre class="code-block"><code>Short line in scroll container</code></pre>
|
||||||
|
</div>
|
||||||
|
<div class="code-block-container code-overflow-scroll">
|
||||||
|
<pre class="code-block"><code>Very long line with scroll: {{$longCode}}</code></pre>
|
||||||
|
</div>
|
||||||
|
<p>content after</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tw-w-[50%] tw-p-4">
|
||||||
|
<div class="markup render-content">
|
||||||
|
<p>content before</p>
|
||||||
|
<div class="code-block-container">
|
||||||
|
<pre class="code-block"><code class="language-math">
|
||||||
|
\lim\limits_{n\rightarrow\infty}{\left(1+\frac{1}{n}\right)^n}
|
||||||
|
</code></pre>
|
||||||
|
</div>
|
||||||
|
<p>content after</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
<div class="markup render-content">
|
||||||
|
<p>content before</p>
|
||||||
|
<div class="code-block-container">
|
||||||
|
<pre class="code-block"><code class="language-mermaid is-loading">
|
||||||
|
graph LR
|
||||||
|
A[Square Rect] -- Link text --> B((Circle))
|
||||||
|
A --> C(Round Rect)
|
||||||
|
B --> D{Rhombus}
|
||||||
|
C --> D
|
||||||
|
</code></pre>
|
||||||
|
</div>
|
||||||
|
<p>content after</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{template "devtest/devtest-footer"}}
|
@ -8,9 +8,9 @@
|
|||||||
{{$referenceUrl := printf "%s#%s" $.Issue.Link $comment.HashTag}}
|
{{$referenceUrl := printf "%s#%s" $.Issue.Link $comment.HashTag}}
|
||||||
<div class="conversation-holder" data-path="{{$comment.TreePath}}" data-side="{{if lt $comment.Line 0}}left{{else}}right{{end}}" data-idx="{{$comment.UnsignedLine}}">
|
<div class="conversation-holder" data-path="{{$comment.TreePath}}" data-side="{{if lt $comment.Line 0}}left{{else}}right{{end}}" data-idx="{{$comment.UnsignedLine}}">
|
||||||
{{if $resolved}}
|
{{if $resolved}}
|
||||||
<div class="ui attached header resolved-placeholder tw-flex tw-items-center tw-justify-between">
|
<div class="resolved-placeholder">
|
||||||
<div class="ui grey text tw-flex tw-items-center tw-flex-wrap tw-gap-1">
|
<div class="flex-text-block tw-flex-wrap grey text">
|
||||||
{{svg "octicon-check" 16 "icon tw-mr-1"}}
|
{{svg "octicon-check"}}
|
||||||
<b>{{$resolveDoer.Name}}</b> {{ctx.Locale.Tr "repo.issues.review.resolved_by"}}
|
<b>{{$resolveDoer.Name}}</b> {{ctx.Locale.Tr "repo.issues.review.resolved_by"}}
|
||||||
{{if $invalid}}
|
{{if $invalid}}
|
||||||
<!--
|
<!--
|
||||||
@ -22,14 +22,12 @@
|
|||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="tw-flex tw-items-center tw-gap-2">
|
<div class="flex-text-block">
|
||||||
<button id="show-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="ui tiny labeled button show-outdated tw-flex tw-items-center">
|
<button id="show-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="btn tiny show-outdated">
|
||||||
{{svg "octicon-unfold" 16 "tw-mr-2"}}
|
{{svg "octicon-unfold" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.issues.review.show_resolved"}}
|
||||||
{{ctx.Locale.Tr "repo.issues.review.show_resolved"}}
|
|
||||||
</button>
|
</button>
|
||||||
<button id="hide-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="ui tiny labeled button hide-outdated tw-flex tw-items-center tw-hidden">
|
<button id="hide-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="btn tiny hide-outdated tw-hidden">
|
||||||
{{svg "octicon-fold" 16 "tw-mr-2"}}
|
{{svg "octicon-fold" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.issues.review.hide_resolved"}}
|
||||||
{{ctx.Locale.Tr "repo.issues.review.hide_resolved"}}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{{if or $invalid $resolved}}
|
{{if or $invalid $resolved}}
|
||||||
<button id="show-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="{{if not $resolved}}tw-hidden {{end}}ui compact labeled button show-outdated tw-flex tw-items-center">
|
<button id="show-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="{{if not $resolved}}tw-hidden{{end}} btn tiny show-outdated">
|
||||||
{{svg "octicon-unfold" 16 "tw-mr-2"}}
|
{{svg "octicon-unfold" 16 "tw-mr-2"}}
|
||||||
{{if $resolved}}
|
{{if $resolved}}
|
||||||
{{ctx.Locale.Tr "repo.issues.review.show_resolved"}}
|
{{ctx.Locale.Tr "repo.issues.review.show_resolved"}}
|
||||||
@ -25,7 +25,7 @@
|
|||||||
{{ctx.Locale.Tr "repo.issues.review.show_outdated"}}
|
{{ctx.Locale.Tr "repo.issues.review.show_outdated"}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</button>
|
</button>
|
||||||
<button id="hide-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="{{if $resolved}}tw-hidden {{end}}ui compact labeled button hide-outdated tw-flex tw-items-center">
|
<button id="hide-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="{{if $resolved}}tw-hidden {{end}} btn tiny hide-outdated">
|
||||||
{{svg "octicon-fold" 16 "tw-mr-2"}}
|
{{svg "octicon-fold" 16 "tw-mr-2"}}
|
||||||
{{if $resolved}}
|
{{if $resolved}}
|
||||||
{{ctx.Locale.Tr "repo.issues.review.hide_resolved"}}
|
{{ctx.Locale.Tr "repo.issues.review.hide_resolved"}}
|
||||||
|
@ -3,18 +3,18 @@
|
|||||||
{{template "repo/header" .}}
|
{{template "repo/header" .}}
|
||||||
{{$title := .title}}
|
{{$title := .title}}
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui stackable grid">
|
<div class="ui dividing header flex-text-block tw-flex-wrap tw-justify-between">
|
||||||
<div class="ui eight wide column">
|
<div class="flex-text-block">
|
||||||
<div class="ui header">
|
<a class="ui basic button tw-px-3" title="{{ctx.Locale.Tr "repo.wiki.back_to_wiki"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}">{{svg "octicon-home"}}</a>
|
||||||
<a class="file-revisions-btn ui basic button" title="{{ctx.Locale.Tr "repo.wiki.back_to_wiki"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}">{{if .revision}}<span>{{.revision}}</span> {{end}}{{svg "octicon-home"}}</a>
|
<div class="tw-flex-1 gt-ellipsis">
|
||||||
{{$title}}
|
{{$title}}
|
||||||
<div class="ui sub header tw-break-anywhere">
|
<div class="ui sub header gt-ellipsis">
|
||||||
{{$timeSince := DateUtils.TimeSince .Author.When}}
|
{{$timeSince := DateUtils.TimeSince .Author.When}}
|
||||||
{{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince}}
|
{{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui eight wide column tw-text-right">
|
<div>
|
||||||
{{template "repo/clone_panel" .}}
|
{{template "repo/clone_panel" .}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
<div class="ui dividing header">
|
<div class="ui dividing header">
|
||||||
<div class="flex-text-block tw-flex-wrap tw-justify-end">
|
<div class="flex-text-block tw-flex-wrap tw-justify-end">
|
||||||
<div class="flex-text-block tw-flex-1 tw-min-w-[300px]">
|
<div class="flex-text-block tw-flex-1 tw-min-w-[300px]">
|
||||||
<a class="file-revisions-btn ui basic button" title="{{ctx.Locale.Tr "repo.wiki.file_revision"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}?action=_revision" >{{if .CommitCount}}<span>{{.CommitCount}}</span> {{end}}{{svg "octicon-history"}}</a>
|
<a class="ui basic button tw-px-3 tw-gap-3" title="{{ctx.Locale.Tr "repo.wiki.file_revision"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}?action=_revision" >{{if .CommitCount}}<span>{{.CommitCount}}</span> {{end}}{{svg "octicon-history"}}</a>
|
||||||
<div class="tw-flex-1 gt-ellipsis">
|
<div class="tw-flex-1 gt-ellipsis">
|
||||||
{{$title}}
|
{{$title}}
|
||||||
<div class="ui sub header gt-ellipsis">
|
<div class="ui sub header gt-ellipsis">
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{{- /* we do not need to set for/id here, global aria init code will add them automatically */ -}}
|
{{- /* we do not need to set for/id here, global aria init code will add them automatically */ -}}
|
||||||
<label>{{.LabelText}}</label>
|
<label>{{.LabelText}}</label>
|
||||||
<input class="avatar-file-with-cropper" name="avatar" type="file" accept="image/png,image/jpeg,image/gif,image/webp">
|
<input class="avatar-file-with-cropper" name="avatar" type="file" accept="image/png,image/jpeg,image/gif,image/webp" data-global-init="initAvatarUploader">
|
||||||
{{- /* the cropper-panel must be next sibling of the input "avatar" */ -}}
|
{{- /* the cropper-panel must be next sibling of the input "avatar" */ -}}
|
||||||
<div class="cropper-panel tw-hidden">
|
<div class="cropper-panel tw-hidden">
|
||||||
<div class="tw-my-2">{{ctx.Locale.Tr "settings.cropper_prompt"}}</div>
|
<div class="tw-my-2">{{ctx.Locale.Tr "settings.cropper_prompt"}}</div>
|
||||||
|
@ -224,6 +224,7 @@ progress::-moz-progress-bar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.unselectable,
|
.unselectable,
|
||||||
|
.btn,
|
||||||
.button,
|
.button,
|
||||||
.lines-num,
|
.lines-num,
|
||||||
.lines-commit,
|
.lines-commit,
|
||||||
|
@ -1,8 +1,3 @@
|
|||||||
.markup .code-block,
|
|
||||||
.markup .mermaid-block {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markup .code-copy {
|
.markup .code-copy {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 8px;
|
top: 8px;
|
||||||
@ -28,8 +23,8 @@
|
|||||||
background: var(--color-secondary-dark-1) !important;
|
background: var(--color-secondary-dark-1) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markup .code-block:hover .code-copy,
|
.markup .code-block-container:hover .code-copy,
|
||||||
.markup .mermaid-block:hover .code-copy {
|
.markup .code-block:hover .code-copy {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
animation: fadein 0.2s both;
|
animation: fadein 0.2s both;
|
||||||
}
|
}
|
||||||
|
@ -443,13 +443,25 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.markup pre > code {
|
.markup pre > code {
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markup .code-block,
|
||||||
|
.markup .code-block-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markup .code-block-container.code-overflow-wrap pre > code {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
overflow-wrap: anywhere;
|
}
|
||||||
background: transparent;
|
|
||||||
border: 0;
|
.markup .code-block-container.code-overflow-scroll pre {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markup .code-block-container.code-overflow-scroll pre > code {
|
||||||
|
white-space: pre;
|
||||||
|
overflow-wrap: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markup .highlight {
|
.markup .highlight {
|
||||||
@ -470,16 +482,11 @@
|
|||||||
word-break: normal;
|
word-break: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markup pre {
|
|
||||||
word-wrap: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markup pre code,
|
.markup pre code,
|
||||||
.markup pre tt {
|
.markup pre tt {
|
||||||
display: inline;
|
display: inline;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
word-wrap: normal;
|
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
@ -522,20 +529,6 @@
|
|||||||
margin: 0 0.25em;
|
margin: 0 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-revisions-btn {
|
|
||||||
display: block;
|
|
||||||
float: left;
|
|
||||||
margin-bottom: 2px !important;
|
|
||||||
padding: 11px !important;
|
|
||||||
margin-right: 10px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-revisions-btn i {
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markup-content-iframe {
|
.markup-content-iframe {
|
||||||
display: block;
|
display: block;
|
||||||
border: none;
|
border: none;
|
||||||
|
@ -352,6 +352,14 @@ a.btn:hover {
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn.tiny {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.small {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
/* By default, Fomantic UI doesn't support "bordered" buttons group, but Gitea would like to use it.
|
/* By default, Fomantic UI doesn't support "bordered" buttons group, but Gitea would like to use it.
|
||||||
And the default buttons always have borders now (not the same as Fomantic UI's default buttons, see above).
|
And the default buttons always have borders now (not the same as Fomantic UI's default buttons, see above).
|
||||||
It needs some tricks to tweak the left/right borders with active state */
|
It needs some tricks to tweak the left/right borders with active state */
|
||||||
|
@ -92,6 +92,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tippy-box[data-theme="menu"] .item:focus {
|
.tippy-box[data-theme="menu"] .item:focus {
|
||||||
|
background: var(--color-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box[data-theme="menu"] .item.active {
|
||||||
background: var(--color-active);
|
background: var(--color-active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1784,12 +1784,12 @@ tbody.commit-list {
|
|||||||
.resolved-placeholder {
|
.resolved-placeholder {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 14px !important;
|
justify-content: space-between;
|
||||||
padding: 8px !important;
|
margin: 4px;
|
||||||
font-weight: var(--font-weight-normal) !important;
|
padding: 8px;
|
||||||
border: 1px solid var(--color-secondary) !important;
|
border: 1px solid var(--color-secondary);
|
||||||
border-radius: var(--border-radius) !important;
|
border-radius: var(--border-radius);
|
||||||
margin: 4px !important;
|
background: var(--color-box-header);
|
||||||
}
|
}
|
||||||
|
|
||||||
.resolved-placeholder + .comment-code-cloud {
|
.resolved-placeholder + .comment-code-cloud {
|
||||||
|
@ -1,11 +1,3 @@
|
|||||||
.show-outdated,
|
|
||||||
.hide-outdated {
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
margin-right: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui.button.add-code-comment {
|
.ui.button.add-code-comment {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -59,11 +51,6 @@
|
|||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.show-outdated:hover,
|
|
||||||
.hide-outdated:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-code-cloud {
|
.comment-code-cloud {
|
||||||
padding: 0.5rem 1rem !important;
|
padding: 0.5rem 1rem !important;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import {checkAppUrl} from '../common-page.ts';
|
import {checkAppUrl} from '../common-page.ts';
|
||||||
import {hideElem, queryElems, showElem, toggleElem} from '../../utils/dom.ts';
|
import {hideElem, queryElems, showElem, toggleElem} from '../../utils/dom.ts';
|
||||||
import {POST} from '../../modules/fetch.ts';
|
import {POST} from '../../modules/fetch.ts';
|
||||||
import {initAvatarUploaderWithCropper} from '../comp/Cropper.ts';
|
|
||||||
import {fomanticQuery} from '../../modules/fomantic/base.ts';
|
import {fomanticQuery} from '../../modules/fomantic/base.ts';
|
||||||
|
|
||||||
const {appSubUrl} = window.config;
|
const {appSubUrl} = window.config;
|
||||||
@ -23,8 +22,6 @@ export function initAdminCommon(): void {
|
|||||||
initAdminUser();
|
initAdminUser();
|
||||||
initAdminAuthentication();
|
initAdminAuthentication();
|
||||||
initAdminNotice();
|
initAdminNotice();
|
||||||
|
|
||||||
queryElems(document, '.avatar-file-with-cropper', initAvatarUploaderWithCropper);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function initAdminUser() {
|
function initAdminUser() {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import {initCompLabelEdit} from './comp/LabelEdit.ts';
|
import {initCompLabelEdit} from './comp/LabelEdit.ts';
|
||||||
import {queryElems, toggleElem} from '../utils/dom.ts';
|
import {toggleElem} from '../utils/dom.ts';
|
||||||
import {initAvatarUploaderWithCropper} from './comp/Cropper.ts';
|
|
||||||
|
|
||||||
export function initCommonOrganization() {
|
export function initCommonOrganization() {
|
||||||
if (!document.querySelectorAll('.organization').length) {
|
if (!document.querySelectorAll('.organization').length) {
|
||||||
@ -14,6 +13,4 @@ export function initCommonOrganization() {
|
|||||||
|
|
||||||
// Labels
|
// Labels
|
||||||
initCompLabelEdit('.page-content.organization.settings.labels');
|
initCompLabelEdit('.page-content.organization.settings.labels');
|
||||||
|
|
||||||
queryElems(document, '.avatar-file-with-cropper', initAvatarUploaderWithCropper);
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import {showGlobalErrorMessage} from '../bootstrap.ts';
|
|||||||
import {fomanticQuery} from '../modules/fomantic/base.ts';
|
import {fomanticQuery} from '../modules/fomantic/base.ts';
|
||||||
import {queryElems} from '../utils/dom.ts';
|
import {queryElems} from '../utils/dom.ts';
|
||||||
import {registerGlobalInitFunc, registerGlobalSelectorFunc} from '../modules/observer.ts';
|
import {registerGlobalInitFunc, registerGlobalSelectorFunc} from '../modules/observer.ts';
|
||||||
|
import {initAvatarUploaderWithCropper} from './comp/Cropper.ts';
|
||||||
|
|
||||||
const {appUrl} = window.config;
|
const {appUrl} = window.config;
|
||||||
|
|
||||||
@ -80,6 +81,10 @@ export function initGlobalTabularMenu() {
|
|||||||
fomanticQuery('.ui.menu.tabular:not(.custom) .item').tab();
|
fomanticQuery('.ui.menu.tabular:not(.custom) .item').tab();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function initGlobalAvatarUploader() {
|
||||||
|
registerGlobalInitFunc('initAvatarUploader', initAvatarUploaderWithCropper);
|
||||||
|
}
|
||||||
|
|
||||||
// for performance considerations, it only uses performant syntax
|
// for performance considerations, it only uses performant syntax
|
||||||
function attachInputDirAuto(el: Partial<HTMLInputElement | HTMLTextAreaElement>) {
|
function attachInputDirAuto(el: Partial<HTMLInputElement | HTMLTextAreaElement>) {
|
||||||
if (el.type !== 'hidden' &&
|
if (el.type !== 'hidden' &&
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import {svg} from '../svg.ts';
|
import {svg} from '../svg.ts';
|
||||||
import {createTippy} from '../modules/tippy.ts';
|
import {createTippy} from '../modules/tippy.ts';
|
||||||
import {clippie} from 'clippie';
|
|
||||||
import {toAbsoluteUrl} from '../utils.ts';
|
import {toAbsoluteUrl} from '../utils.ts';
|
||||||
import {addDelegatedEventListener} from '../utils/dom.ts';
|
import {addDelegatedEventListener} from '../utils/dom.ts';
|
||||||
|
|
||||||
@ -43,7 +42,8 @@ function selectRange(range: string): Element {
|
|||||||
if (!copyPermalink) return;
|
if (!copyPermalink) return;
|
||||||
let link = copyPermalink.getAttribute('data-url');
|
let link = copyPermalink.getAttribute('data-url');
|
||||||
link = `${link.replace(/#L\d+$|#L\d+-L\d+$/, '')}#${anchor}`;
|
link = `${link.replace(/#L\d+$|#L\d+-L\d+$/, '')}#${anchor}`;
|
||||||
copyPermalink.setAttribute('data-url', link);
|
copyPermalink.setAttribute('data-clipboard-text', link);
|
||||||
|
copyPermalink.setAttribute('data-clipboard-text-type', 'url');
|
||||||
};
|
};
|
||||||
|
|
||||||
const rangeFields = range ? range.split('-') : [];
|
const rangeFields = range ? range.split('-') : [];
|
||||||
@ -138,8 +138,4 @@ export function initRepoCodeView() {
|
|||||||
};
|
};
|
||||||
onHashChange();
|
onHashChange();
|
||||||
window.addEventListener('hashchange', onHashChange);
|
window.addEventListener('hashchange', onHashChange);
|
||||||
|
|
||||||
addDelegatedEventListener(document, 'click', '.copy-line-permalink', (el) => {
|
|
||||||
clippie(toAbsoluteUrl(el.getAttribute('data-url')));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ import {minimatch} from 'minimatch';
|
|||||||
import {createMonaco} from './codeeditor.ts';
|
import {createMonaco} from './codeeditor.ts';
|
||||||
import {onInputDebounce, queryElems, toggleClass, toggleElem} from '../utils/dom.ts';
|
import {onInputDebounce, queryElems, toggleClass, toggleElem} from '../utils/dom.ts';
|
||||||
import {POST} from '../modules/fetch.ts';
|
import {POST} from '../modules/fetch.ts';
|
||||||
import {initAvatarUploaderWithCropper} from './comp/Cropper.ts';
|
|
||||||
import {initRepoSettingsBranchesDrag} from './repo-settings-branches.ts';
|
import {initRepoSettingsBranchesDrag} from './repo-settings-branches.ts';
|
||||||
import {fomanticQuery} from '../modules/fomantic/base.ts';
|
import {fomanticQuery} from '../modules/fomantic/base.ts';
|
||||||
|
|
||||||
@ -149,6 +148,4 @@ export function initRepoSettings() {
|
|||||||
initRepoSettingsSearchTeamBox();
|
initRepoSettingsSearchTeamBox();
|
||||||
initRepoSettingsGitHook();
|
initRepoSettingsGitHook();
|
||||||
initRepoSettingsBranchesDrag();
|
initRepoSettingsBranchesDrag();
|
||||||
|
|
||||||
queryElems(document, '.avatar-file-with-cropper', initAvatarUploaderWithCropper);
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
import {hideElem, queryElems, showElem} from '../utils/dom.ts';
|
import {hideElem, showElem} from '../utils/dom.ts';
|
||||||
import {initAvatarUploaderWithCropper} from './comp/Cropper.ts';
|
|
||||||
|
|
||||||
export function initUserSettings() {
|
export function initUserSettings() {
|
||||||
if (!document.querySelector('.user.settings.profile')) return;
|
if (!document.querySelector('.user.settings.profile')) return;
|
||||||
|
|
||||||
queryElems(document, '.avatar-file-with-cropper', initAvatarUploaderWithCropper);
|
|
||||||
|
|
||||||
const usernameInput = document.querySelector<HTMLInputElement>('#username');
|
const usernameInput = document.querySelector<HTMLInputElement>('#username');
|
||||||
if (!usernameInput) return;
|
if (!usernameInput) return;
|
||||||
usernameInput.addEventListener('input', function () {
|
usernameInput.addEventListener('input', function () {
|
||||||
|
@ -60,7 +60,7 @@ import {initColorPickers} from './features/colorpicker.ts';
|
|||||||
import {initAdminSelfCheck} from './features/admin/selfcheck.ts';
|
import {initAdminSelfCheck} from './features/admin/selfcheck.ts';
|
||||||
import {initOAuth2SettingsDisableCheckbox} from './features/oauth2-settings.ts';
|
import {initOAuth2SettingsDisableCheckbox} from './features/oauth2-settings.ts';
|
||||||
import {initGlobalFetchAction} from './features/common-fetch-action.ts';
|
import {initGlobalFetchAction} from './features/common-fetch-action.ts';
|
||||||
import {initFootLanguageMenu, initGlobalDropdown, initGlobalInput, initGlobalTabularMenu, initHeadNavbarContentToggle} from './features/common-page.ts';
|
import {initFootLanguageMenu, initGlobalAvatarUploader, initGlobalDropdown, initGlobalInput, initGlobalTabularMenu, initHeadNavbarContentToggle} from './features/common-page.ts';
|
||||||
import {initGlobalButtonClickOnEnter, initGlobalButtons, initGlobalDeleteButton} from './features/common-button.ts';
|
import {initGlobalButtonClickOnEnter, initGlobalButtons, initGlobalDeleteButton} from './features/common-button.ts';
|
||||||
import {initGlobalComboMarkdownEditor, initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm} from './features/common-form.ts';
|
import {initGlobalComboMarkdownEditor, initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm} from './features/common-form.ts';
|
||||||
import {callInitFunctions} from './modules/init.ts';
|
import {callInitFunctions} from './modules/init.ts';
|
||||||
@ -72,6 +72,7 @@ initSubmitEventPolyfill();
|
|||||||
onDomReady(() => {
|
onDomReady(() => {
|
||||||
const initStartTime = performance.now();
|
const initStartTime = performance.now();
|
||||||
const initPerformanceTracer = callInitFunctions([
|
const initPerformanceTracer = callInitFunctions([
|
||||||
|
initGlobalAvatarUploader,
|
||||||
initGlobalDropdown,
|
initGlobalDropdown,
|
||||||
initGlobalTabularMenu,
|
initGlobalTabularMenu,
|
||||||
initGlobalFetchAction,
|
initGlobalFetchAction,
|
||||||
|
@ -15,6 +15,8 @@ export function initMarkupCodeCopy(elMarkup: HTMLElement): void {
|
|||||||
const btn = makeCodeCopyButton();
|
const btn = makeCodeCopyButton();
|
||||||
// remove final trailing newline introduced during HTML rendering
|
// remove final trailing newline introduced during HTML rendering
|
||||||
btn.setAttribute('data-clipboard-text', el.textContent.replace(/\r?\n$/, ''));
|
btn.setAttribute('data-clipboard-text', el.textContent.replace(/\r?\n$/, ''));
|
||||||
el.after(btn);
|
// we only want to use `.code-block-container` if it exists, no matter `.code-block` exists or not.
|
||||||
|
const btnContainer = el.closest('.code-block-container') ?? el.closest('.code-block');
|
||||||
|
btnContainer.append(btn);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ export function queryElemChildren<T extends Element>(parent: Element | ParentNod
|
|||||||
}
|
}
|
||||||
|
|
||||||
// it works like parent.querySelectorAll: all descendants are selected
|
// it works like parent.querySelectorAll: all descendants are selected
|
||||||
// in the future, all "queryElems(document, ...)" should be refactored to use a more specific parent
|
// in the future, all "queryElems(document, ...)" should be refactored to use a more specific parent if the targets are not for page-level components.
|
||||||
export function queryElems<T extends HTMLElement>(parent: Element | ParentNode, selector: string, fn?: ElementsCallback<T>): ArrayLikeIterable<T> {
|
export function queryElems<T extends HTMLElement>(parent: Element | ParentNode, selector: string, fn?: ElementsCallback<T>): ArrayLikeIterable<T> {
|
||||||
return applyElemsCallback<T>(parent.querySelectorAll(selector), fn);
|
return applyElemsCallback<T>(parent.querySelectorAll(selector), fn);
|
||||||
}
|
}
|
||||||
@ -360,7 +360,11 @@ export function querySingleVisibleElem<T extends HTMLElement>(parent: Element, s
|
|||||||
export function addDelegatedEventListener<T extends HTMLElement, E extends Event>(parent: Node, type: string, selector: string, listener: (elem: T, e: E) => Promisable<void>, options?: boolean | AddEventListenerOptions) {
|
export function addDelegatedEventListener<T extends HTMLElement, E extends Event>(parent: Node, type: string, selector: string, listener: (elem: T, e: E) => Promisable<void>, options?: boolean | AddEventListenerOptions) {
|
||||||
parent.addEventListener(type, (e: Event) => {
|
parent.addEventListener(type, (e: Event) => {
|
||||||
const elem = (e.target as HTMLElement).closest(selector);
|
const elem = (e.target as HTMLElement).closest(selector);
|
||||||
if (!elem || !parent.contains(elem)) return;
|
// It strictly checks "parent contains the target elem" to avoid side effects of selector running on outside the parent.
|
||||||
|
// Keep in mind that the elem could have been removed from parent by other event handlers before this event handler is called.
|
||||||
|
// For example: tippy popup item, the tippy popup could be hidden and removed from DOM before this.
|
||||||
|
// It is caller's responsibility make sure the elem is still in parent's DOM when this event handler is called.
|
||||||
|
if (!elem || (parent !== document && !parent.contains(elem))) return;
|
||||||
listener(elem as T, e as E);
|
listener(elem as T, e as E);
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user