+1
-1
.air/knotserver.toml
+1
-1
.air/knotserver.toml
+246
-45
api/tangled/cbor_gen.go
+246
-45
api/tangled/cbor_gen.go
···············-if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.CreatedAt))); err != nil {-if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sourceRepo"))); err != nil {-if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.SourceRepo))); err != nil {+if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.CreatedAt))); err != nil {············
+15
-9
api/tangled/repopull.go
+15
-9
api/tangled/repopull.go
···-LexiconTypeID string `json:"$type,const=sh.tangled.repo.pull" cborgen:"$type,const=sh.tangled.repo.pull"`+LexiconTypeID string `json:"$type,const=sh.tangled.repo.pull" cborgen:"$type,const=sh.tangled.repo.pull"`
+2
api/tangled/tangledrepo.go
+2
api/tangled/tangledrepo.go
+8
-2
appview/auth/auth.go
+8
-2
appview/auth/auth.go
······
+6
-4
appview/config.go
+6
-4
appview/config.go
···+JetstreamEndpoint string `env:"TANGLED_JETSTREAM_ENDPOINT, default=wss://jetstream1.us-east.bsky.network/subscribe"`
+45
-1
appview/db/db.go
+45
-1
appview/db/db.go
·········
+287
appview/db/email.go
+287
appview/db/email.go
···+err := e.QueryRow(query, did).Scan(&email.ID, &email.Did, &email.Address, &email.Verified, &email.Primary, &email.VerificationCode, &lastSent, &createdStr)+err := e.QueryRow(query, did, em).Scan(&email.ID, &email.Did, &email.Address, &email.Verified, &email.Primary, &email.VerificationCode, &lastSent, &createdStr)+func CheckValidVerificationCode(e Execer, did string, email string, code string) (bool, error) {+_, err = e.Exec(query, email.Did, email.Address, email.Verified, email.Primary, email.VerificationCode)+err := rows.Scan(&email.Did, &email.Address, &email.Verified, &email.Primary, &email.VerificationCode, &lastSent, &createdStr)
+209
-11
appview/db/issues.go
+209
-11
appview/db/issues.go
············query := `select owner_did, created, title, body, open from issues where repo_at = ? and issue_id = ?`·········-query := `insert into comments (owner_did, repo_at, comment_at, issue_id, comment_id, body) values (?, ?, ?, ?, ?, ?)`+query := `insert into comments (owner_did, repo_at, rkey, issue_id, comment_id, body) values (?, ?, ?, ?, ?, ?)`···-rows, err := e.Query(`select owner_did, issue_id, comment_id, comment_at, body, created from comments where repo_at = ? and issue_id = ? order by created asc`, repoAt, issueId)···-err := rows.Scan(&comment.OwnerDid, &comment.Issue, &comment.CommentId, &comment.CommentAt, &comment.Body, &createdAt)+err := rows.Scan(&comment.OwnerDid, &comment.Issue, &comment.CommentId, &rkey, &comment.Body, &createdAt, &editedAt, &deletedAt)······+func EditComment(e Execer, repoAt syntax.ATURI, issueId, commentId int, newBody string) error {
+6
-10
appview/db/jetstream.go
+6
-10
appview/db/jetstream.go
···
+164
appview/db/profile.go
+164
appview/db/profile.go
···
+203
-21
appview/db/pulls.go
+203
-21
appview/db/pulls.go
·········+SourceRev string // include the rev that was used to create this submission: only for branch PRs······-`, pull.RepoAt, pull.OwnerDid, pull.PullId, pull.Title, pull.TargetBranch, pull.Body, pull.Rkey, pull.State)+insert into pulls (repo_at, owner_did, pull_id, title, target_branch, body, rkey, state, source_branch, source_repo_at)·······································query := `insert into pull_comments (owner_did, repo_at, submission_id, comment_at, pull_id, body) values (?, ?, ?, ?, ?, ?)`···
+129
-15
appview/db/repos.go
+129
-15
appview/db/repos.go
·········+rows, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &repo.Description, &repo.Created, &repo.Source,·········-err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repoStats.StarCount)+err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repoStats.StarCount, &nullableSource)···-_, err := e.Exec(`delete from repos where did = ? and name = ? and rkey = ?`, did, name, rkey)+err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repo.AtUri, &nullableSource)+err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repo.AtUri, &nullableSource)func AddCollaborator(e Execer, collaborator, repoOwnerDid, repoName, repoKnot string) error {···-func scanRepo(rows *sql.Rows, did, name, knot, rkey, description *string, created *time.Time) error {+func scanRepo(rows *sql.Rows, did, name, knot, rkey, description *string, created *time.Time, source *string) error {+if err := rows.Scan(did, name, knot, rkey, &nullableDescription, &createdAt, &nullableSource); err != nil {···
+13
appview/db/timeline.go
+13
appview/db/timeline.go
······
+63
appview/email/email.go
+63
appview/email/email.go
···+pattern := `^[a-zA-Z0-9.!#$%&'*+/=?^_\x60{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$`
+40
appview/pages/chroma.go
+40
appview/pages/chroma.go
···
+82
-1
appview/pages/funcmap.go
+82
-1
appview/pages/funcmap.go
···············
-23
appview/pages/markdown.go
-23
appview/pages/markdown.go
···
+24
appview/pages/markup/markdown.go
+24
appview/pages/markup/markdown.go
···
+26
appview/pages/markup/readme.go
+26
appview/pages/markup/readme.go
···
+299
-72
appview/pages/pages.go
+299
-72
appview/pages/pages.go
···························func (p *Pages) EditRepoDescriptionFragment(w io.Writer, params RepoDescriptionParams) error {······························+func (p *Pages) SingleIssueCommentFragment(w io.Writer, params SingleIssueCommentParams) error {······+func (p *Pages) PullCompareBranchesFragment(w io.Writer, params PullCompareBranchesParams) error {+func (p *Pages) PullCompareForkBranchesFragment(w io.Writer, params PullCompareForkBranchesParams) error {······
appview/pages/static/.gitkeep
appview/pages/static/.gitkeep
This is a binary file and will not be displayed.
-112
appview/pages/templates/fragments/diff.html
-112
appview/pages/templates/fragments/diff.html
···-<a title="top of file" href="#file-{{ .Name.New }}" class="{{ $iconstyle }}"><i class="w-4 h-4" data-lucide="arrow-up-to-line"></i></a>-<a title="previous file" href="#file-{{ $prev.Name.New }}" class="{{ $iconstyle }}"><i class="w-4 h-4" data-lucide="arrow-up"></i></a>-<a title="next file" href="#file-{{ $next.Name.New }}" class="{{ $iconstyle }}"><i class="w-4 h-4" data-lucide="arrow-down"></i></a>-<div class="bg-green-100 text-green-700 p-1"><span class="select-none mx-2">{{ .Op.String }}</span><span>{{ .Line }}</span></div>-<div class="bg-red-100 text-red-700 p-1"><span class="select-none mx-2">{{ .Op.String }}</span><span>{{ .Line }}</span></div>-<div class="bg-white text-gray-500 px"><span class="select-none mx-2">{{ .Op.String }}</span><span>{{ .Line }}</span></div>
-11
appview/pages/templates/fragments/editRepoDescription.html
-11
appview/pages/templates/fragments/editRepoDescription.html
···-<button type="submit" class="bg-green-100 text-green-700 rounded p-1 mr-1 hover:bg-green-200 font-mono uppercase text-sm">-<button type="button" class="bg-red-100 text-red-700 rounded p-1 hover:bg-red-200 font-mono uppercase text-sm" hx-get="/{{ .RepoInfo.FullName }}/description" >
-17
appview/pages/templates/fragments/follow.html
-17
appview/pages/templates/fragments/follow.html
···
-15
appview/pages/templates/fragments/repoDescription.html
-15
appview/pages/templates/fragments/repoDescription.html
···-<button class="bg-gray-200 uppercase rounded p-1 ml-1 hover:bg-gray-400 font-mono text-sm" hx-get="/{{ .RepoInfo.FullName }}/description/edit">
-33
appview/pages/templates/fragments/star.html
-33
appview/pages/templates/fragments/star.html
···
+92
-34
appview/pages/templates/knot.html
+92
-34
appview/pages/templates/knot.html
···+<section class="rounded bg-white dark:bg-gray-800 drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">+{{ index $.DidHandleMap .Registration.ByDid }} <span class="text-gray-500 dark:text-gray-400 font-mono">{{ .Registration.ByDid }}</span>+<span class="text-xs bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 px-2 py-1 rounded ml-2">you</span>+<dd class="text-yellow-800 dark:text-yellow-200 bg-yellow-100 dark:bg-yellow-900 rounded px-2 py-1 inline-block">+<section class="rounded bg-white dark:bg-gray-800 drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">+<a href="/{{index $.DidHandleMap .}}" class="text-gray-900 dark:text-white">{{index $.DidHandleMap .}}+<p class="text-gray-500 dark:text-gray-400">Members can be added after registration is complete.</p>+<section class="rounded bg-white dark:bg-gray-800 drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">+class="w-full dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:placeholder-gray-400"/>+<button class="btn dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600" type="submit">add member</button>
+79
-84
appview/pages/templates/knots.html
+79
-84
appview/pages/templates/knots.html
···+<section class="rounded bg-white dark:bg-gray-800 drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">+<section class="rounded bg-white dark:bg-gray-800 drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">+<h2 class="text-sm font-bold py-2 px-6 uppercase dark:text-gray-300">pending registrations</h2>+<section class="rounded bg-white dark:bg-gray-800 drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">+<span class="text-xs bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 px-2 py-1 rounded">
+6
-9
appview/pages/templates/layouts/base.html
+6
-9
appview/pages/templates/layouts/base.html
·········
+34
-19
appview/pages/templates/layouts/repobase.html
+34
-19
appview/pages/templates/layouts/repobase.html
···+<a class="ml-1 underline" href="/{{ $sourceOwner }}/{{ .RepoInfo.Source.Name }}">{{ $sourceOwner }}/{{ .RepoInfo.Source.Name }}</a>···+class="px-4 py-1 mr-1 text-black dark:text-white min-w-[80px] text-center relative rounded-t whitespace-nowrap+class="bg-white dark:bg-gray-800 p-6 rounded relative z-20 w-full mx-auto drop-shadow-sm dark:text-white"
+9
-4
appview/pages/templates/layouts/topbar.html
+9
-4
appview/pages/templates/layouts/topbar.html
···+<nav class="space-x-4 mb-4 px-6 py-2 rounded bg-white dark:bg-gray-800 dark:text-white drop-shadow-sm">······+class="absolute flex flex-col right-0 mt-4 p-4 rounded w-48 bg-white dark:bg-gray-800 dark:text-white border border-gray-200 dark:border-gray-700"
+38
-23
appview/pages/templates/repo/blob.html
+38
-23
appview/pages/templates/repo/blob.html
···+<meta name="forge:file" content="https://tangled.sh/{{ .RepoInfo.FullName }}/blob/{ref}/{path}">+<meta name="forge:line" content="https://tangled.sh/{{ .RepoInfo.FullName }}/blob/{ref}/{path}#L{line}">+<meta name="go-import" content="tangled.sh/{{ .RepoInfo.FullNameWithoutAt }} git https://tangled.sh/{{ .RepoInfo.FullName }}">-{{ $code_number_style := "text-gray-400 left-0 bg-white text-right mr-6 select-none inline-block w-12" }}+{{ $code_number_style := "text-gray-400 dark:text-gray-500 left-0 bg-white dark:bg-gray-800 text-right mr-6 select-none inline-block w-12" }}+<div id="breadcrumbs" class="overflow-x-auto whitespace-nowrap text-gray-400 dark:text-gray-500">+<div id="file-info" class="text-gray-500 dark:text-gray-400 text-xs md:text-sm flex flex-wrap items-center gap-1 md:gap-0">+<div id="blob-contents" class="whitespace-pre peer-target:bg-yellow-200 dark:peer-target:bg-yellow-900">{{ $.Contents | escapeHtml }}</div>
+16
-27
appview/pages/templates/repo/commit.html
+16
-27
appview/pages/templates/repo/commit.html
···-<a href="mailto:{{ $commit.Author.Email }}" class="no-underline hover:underline text-gray-500">{{ $commit.Author.Name }}</a>+<a href="/{{ $didOrHandle }}" class="no-underline hover:underline text-gray-500 dark:text-gray-300">{{ $didOrHandle }}</a>+<a href="mailto:{{ $commit.Author.Email }}" class="no-underline hover:underline text-gray-500 dark:text-gray-300">{{ $commit.Author.Name }}</a>-<span>{{ $stat.FilesChanged }}</span> files <span class="font-mono">(+{{ $stat.Insertions }}, -{{ $stat.Deletions }})</span>-<a href="/{{ $repo }}/commit/{{ $commit.This }}" class="no-underline hover:underline text-gray-500">{{ slice $commit.This 0 8 }}</a>+<a href="/{{ $repo }}/commit/{{ $commit.This }}" class="no-underline hover:underline text-gray-500 dark:text-gray-300">{{ slice $commit.This 0 8 }}</a>-<a href="/{{ $repo }}/commit/{{ $commit.Parent }}" class="no-underline hover:underline text-gray-500">{{ slice $commit.Parent 0 8 }}</a>+<a href="/{{ $repo }}/commit/{{ $commit.Parent }}" class="no-underline hover:underline text-gray-500 dark:text-gray-300">{{ slice $commit.Parent 0 8 }}</a>
+2
-21
appview/pages/templates/repo/empty.html
+2
-21
appview/pages/templates/repo/empty.html
···-<code>git remote add origin git@{{.RepoInfo.Knot}}:{{ .RepoInfo.OwnerHandle }}/{{ .RepoInfo.Name }}</code>-<span class="bg-gray-100 p-1 mr-1 font-mono text-sm rounded select-none">SSH</span><code>git clone git@{{.RepoInfo.Knot}}:{{ .RepoInfo.OwnerHandle }}/{{ .RepoInfo.Name }}</code>-<p class="py-2 text-gray-500">Note that for self-hosted knots, clone URLs may be different based on your setup.</p>
+38
appview/pages/templates/repo/fork.html
+38
appview/pages/templates/repo/fork.html
···+<p class="text-sm text-gray-500 dark:text-gray-400">A knot hosts repository data. <a href="/knots" class="underline">Learn how to register your own knot.</a></p>
+51
appview/pages/templates/repo/fragments/cloneInstructions.html
+51
appview/pages/templates/repo/fragments/cloneInstructions.html
···+class="mt-4 p-6 rounded bg-white dark:bg-gray-800 dark:text-white w-full mx-auto overflow-auto flex flex-col gap-4"+class="bg-gray-100 dark:bg-gray-700 p-1 mr-1 font-mono text-sm rounded select-none dark:text-white"+class="bg-gray-100 dark:bg-gray-700 p-1 mr-1 font-mono text-sm rounded select-none dark:text-white"
+175
appview/pages/templates/repo/fragments/diff.html
+175
appview/pages/templates/repo/fragments/diff.html
···+<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">+<section class="mt-6 border border-gray-200 dark:border-gray-700 w-full mx-auto rounded bg-white dark:bg-gray-800 drop-shadow-sm">+<div id="diff-file-header" class="rounded cursor-pointer bg-white dark:bg-gray-800 flex justify-between">+<div id="left-side-items" class="p-2 flex gap-2 items-center overflow-x-auto" style="direction: rtl;">+<span class="bg-green-100 text-green-700 dark:bg-green-800/50 dark:text-green-400 {{ $markerstyle }}">ADDED</span>+<span class="bg-red-100 text-red-700 dark:bg-red-800/50 dark:text-red-400 {{ $markerstyle }}">DELETED</span>+<span class="bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 {{ $markerstyle }}">COPIED</span>+<span class="bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 {{ $markerstyle }}">RENAMED</span>+<span class="bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300 {{ $markerstyle }}">MODIFIED</span>+<a class="dark:text-white whitespace-nowrap overflow-x-auto" {{if $this }}href="/{{ $repo }}/blob/{{ $this }}/{{ .Name.Old }}"{{end}}>+<a class="dark:text-white whitespace-nowrap overflow-x-auto" {{if $parent}}href="/{{ $repo }}/blob/{{ $parent }}/{{ .Name.Old }}"{{end}}>+<a class="dark:text-white whitespace-nowrap overflow-x-auto" {{if $this}}href="/{{ $repo }}/blob/{{ $this }}/{{ .Name.New }}"{{end}}>+<a class="dark:text-white whitespace-nowrap overflow-x-auto" {{if $this}}href="/{{ $repo }}/blob/{{ $this }}/{{ .Name.New }}"{{end}}>+<a title="top of file" href="#file-{{ .Name.New }}" class="{{ $iconstyle }}">{{ i "arrow-up-to-line" "w-4 h-4" }}</a>+<a title="previous file" href="#file-{{ $prev.Name.New }}" class="{{ $iconstyle }}">{{ i "arrow-up" "w-4 h-4" }}</a>+<a title="next file" href="#file-{{ $next.Name.New }}" class="{{ $iconstyle }}">{{ i "arrow-down" "w-4 h-4" }}</a>+<pre class="overflow-x-auto"><div class="overflow-x-auto"><div class="min-w-full inline-block">{{- range .TextFragments -}}<div class="bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 select-none text-center">···</div>+{{- $lineNrStyle := "min-w-[3.5rem] flex-shrink-0 select-none text-right bg-white dark:bg-gray-800 scroll-mt-10 target:border target:border-amber-500 target:rounded " -}}+<div class="bg-green-100 dark:bg-green-800/30 text-green-700 dark:text-green-400 flex min-w-full items-center">+<div class="{{$lineNrStyle}} {{$lineNrSepStyle1}}"><span aria-hidden="true" class="invisible">{{$newStart}}</span></div>+<div class="{{$lineNrStyle}} {{$lineNrSepStyle2}}" id="{{$name}}-N{{$newStart}}"><a class="{{$linkStyle}}" href="#{{$name}}-N{{$newStart}}">{{ $newStart }}</a></div>+<div class="bg-red-100 dark:bg-red-800/30 text-red-700 dark:text-red-400 flex min-w-full items-center">+<div class="{{$lineNrStyle}} {{$lineNrSepStyle1}}" id="{{$name}}-O{{$oldStart}}"><a class="{{$linkStyle}}" href="#{{$name}}-O{{$oldStart}}">{{ $oldStart }}</a></div>+<div class="{{$lineNrStyle}} {{$lineNrSepStyle2}}"><span aria-hidden="true" class="invisible">{{$oldStart}}</span></div>+<div class="bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400 flex min-w-full items-center">+<div class="{{$lineNrStyle}} {{$lineNrSepStyle1}}" id="{{$name}}-O{{$oldStart}}"><a class="{{$linkStyle}}" href="#{{$name}}-O{{$oldStart}}">{{ $oldStart }}</a></div>+<div class="{{$lineNrStyle}} {{$lineNrSepStyle2}}" id="{{$name}}-N{{$newStart}}"><a class="{{$linkStyle}}" href="#{{$name}}-N{{$newStart}}">{{ $newStart }}</a></div>+<span class="rounded-l p-1 select-none bg-green-100 text-green-700 dark:bg-green-800/50 dark:text-green-400">+{{ .Insertions }}</span>+<span class="rounded-r p-1 select-none bg-red-100 text-red-700 dark:bg-red-800/50 dark:text-red-400">-{{ .Deletions }}</span>+<span class="rounded p-1 select-none bg-green-100 text-green-700 dark:bg-green-800/50 dark:text-green-400">+{{ .Insertions }}</span>+<span class="rounded p-1 select-none bg-red-100 text-red-700 dark:bg-red-800/50 dark:text-red-400">-{{ .Deletions }}</span>
+11
appview/pages/templates/repo/fragments/editRepoDescription.html
+11
appview/pages/templates/repo/fragments/editRepoDescription.html
···+<form hx-put="/{{ .RepoInfo.FullName }}/description" hx-target="this" hx-swap="outerHTML" class="flex flex-wrap gap-2">+<button type="button" class="btn p-1 flex items-center gap-2 no-underline text-sm" hx-get="/{{ .RepoInfo.FullName }}/description" >
+47
appview/pages/templates/repo/fragments/repoActions.html
+47
appview/pages/templates/repo/fragments/repoActions.html
···+class="btn text-sm no-underline hover:no-underline flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
+15
appview/pages/templates/repo/fragments/repoDescription.html
+15
appview/pages/templates/repo/fragments/repoDescription.html
···+<span id="repo-description" class="flex flex-wrap items-center gap-2 text-sm" hx-target="this" hx-swap="outerHTML">+<button class="flex items-center gap-2 no-underline text-sm" hx-get="/{{ .RepoInfo.FullName }}/description/edit">
+204
-198
appview/pages/templates/repo/index.html
+204
-198
appview/pages/templates/repo/index.html
···+content="tangled.sh/{{ .RepoInfo.FullNameWithoutAt }} git https://tangled.sh/{{ .RepoInfo.FullName }}"-onchange="window.location.href = '/{{ .RepoInfo.FullName }}/tree/' + encodeURIComponent(this.value)"+onchange="window.location.href = '/{{ .RepoInfo.FullName }}/tree/' + encodeURIComponent(this.value)"+class="p-1 border border-gray-200 bg-white dark:bg-gray-800 dark:text-white dark:border-gray-700"+class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 rounded dark:bg-gray-700 dark:hover:bg-gray-600"+class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center"-<section class="mt-4 p-6 rounded bg-white w-full mx-auto overflow-auto {{ if not .Raw }} prose {{ end }}">+class="mt-4 p-6 rounded bg-white dark:bg-gray-800 dark:text-white w-full mx-auto overflow-auto {{ if not .Raw }}+class="dark:bg-gray-900 dark:text-gray-200 dark:border dark:border-gray-700 dark:p-4 dark:rounded"···-<code>git remote add origin git@{{.RepoInfo.Knot}}:{{ .RepoInfo.OwnerHandle }}/{{ .RepoInfo.Name }}</code>-<code>git clone git@{{.RepoInfo.Knot}}:{{ .RepoInfo.OwnerHandle }}/{{ .RepoInfo.Name }}</code>-<p class="py-2 text-gray-500">Note that for self-hosted knots, clone URLs may be different based on your setup.</p>
+52
appview/pages/templates/repo/issues/fragments/editIssueComment.html
+52
appview/pages/templates/repo/issues/fragments/editIssueComment.html
···
+59
appview/pages/templates/repo/issues/fragments/issueComment.html
+59
appview/pages/templates/repo/issues/fragments/issueComment.html
···+class="text-gray-500 dark:text-gray-400 hover:text-gray-500 dark:hover:text-gray-400 hover:underline no-underline"+<span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center">
+124
-76
appview/pages/templates/repo/issues/issue.html
+124
-76
appview/pages/templates/repo/issues/issue.html
···-{{ define "title" }}{{ .Issue.Title }} · issue #{{ .Issue.IssueId }} ·{{ .RepoInfo.FullName }}{{ end }}+{{ define "title" }}{{ .Issue.Title }} · issue #{{ .Issue.IssueId }} · {{ .RepoInfo.FullName }}{{ end }}···+class="bg-white dark:bg-gray-800 rounded drop-shadow-sm py-2 px-4 relative w-full md:max-w-3/5 md:w-fit">+{{ template "repo/issues/fragments/issueComment" (dict "RepoInfo" $.RepoInfo "LoggedInUser" $.LoggedInUser "DidHandleMap" $.DidHandleMap "Issue" $.Issue "Comment" .)}}-<div class="bg-white rounded drop-shadow-sm py-4 px-6 relative w-full flex flex-col gap-2 mt-8">+<div class="bg-white dark:bg-gray-800 rounded drop-shadow-sm py-4 px-4 relative w-full md:w-3/5">+hx-vals="js:{body: document.getElementById('comment-textarea').value.trim() !== '' ? document.getElementById('comment-textarea').value : ''}"
+10
-10
appview/pages/templates/repo/issues/issues.html
+10
-10
appview/pages/templates/repo/issues/issues.html
···-<select class="border px-1 bg-white border-gray-200" onchange="window.location.href = '/{{ .RepoInfo.FullName }}/issues?state=' + this.value">+<select class="border p-1 bg-white border-gray-200 dark:bg-gray-800 dark:border-gray-700" onchange="window.location.href = '/{{ .RepoInfo.FullName }}/issues?state=' + this.value"><option value="open" {{ if .FilteringByOpen }}selected{{ end }}>open ({{ .RepoInfo.Stats.IssueCount.Open }})</option><option value="closed" {{ if not .FilteringByOpen }}selected{{ end }}>closed ({{ .RepoInfo.Stats.IssueCount.Closed }})</option>············-<a href="/{{ $.RepoInfo.FullName }}/issues/{{ .IssueId }}" class="text-gray-500">{{ .Metadata.CommentCount }} comment{{$s}}</a>+<a href="/{{ $.RepoInfo.FullName }}/issues/{{ .IssueId }}" class="text-gray-500 dark:text-gray-400">{{ .Metadata.CommentCount }} comment{{$s}}</a>
+1
-1
appview/pages/templates/repo/issues/new.html
+1
-1
appview/pages/templates/repo/issues/new.html
+36
-21
appview/pages/templates/repo/log.html
+36
-21
appview/pages/templates/repo/log.html
············+class="py-1/2 px-1 bg-gray-200 hover:bg-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 rounded"······+class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700"···+class="btn flex items-center gap-2 no-underline hover:no-underline dark:text-white dark:hover:bg-gray-700"
+13
-13
appview/pages/templates/repo/new.html
+13
-13
appview/pages/templates/repo/new.html
······-<p class="text-sm text-gray-500">A knot hosts repository data. <a href="/knots" class="underline">Learn how to register your own knot.</a></p>+<p class="text-sm text-gray-500 dark:text-gray-400">A knot hosts repository data. <a href="/knots" class="underline">Learn how to register your own knot.</a></p>
+90
appview/pages/templates/repo/pulls/fragments/pullActions.html
+90
appview/pages/templates/repo/pulls/fragments/pullActions.html
···+hx-confirm="Are you sure you want to merge pull #{{ .Pull.PullId }} into the `{{ .Pull.TargetBranch }}` branch?"+class="btn p-2 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed" {{ $disabled }}
+20
appview/pages/templates/repo/pulls/fragments/pullCompareBranches.html
+20
appview/pages/templates/repo/pulls/fragments/pullCompareBranches.html
···+class="p-1 border border-gray-200 bg-white dark:bg-gray-700 dark:text-white dark:border-gray-600"
+42
appview/pages/templates/repo/pulls/fragments/pullCompareForks.html
+42
appview/pages/templates/repo/pulls/fragments/pullCompareForks.html
···+class="p-1 border border-gray-200 bg-white dark:bg-gray-700 dark:text-white dark:border-gray-600"
+15
appview/pages/templates/repo/pulls/fragments/pullCompareForksBranches.html
+15
appview/pages/templates/repo/pulls/fragments/pullCompareForksBranches.html
···+class="p-1 border border-gray-200 bg-white dark:bg-gray-700 dark:text-white dark:border-gray-600"
+32
appview/pages/templates/repo/pulls/fragments/pullNewComment.html
+32
appview/pages/templates/repo/pulls/fragments/pullNewComment.html
···+class="bg-white dark:bg-gray-800 rounded drop-shadow-sm p-4 relative w-full flex flex-col gap-2">
+14
appview/pages/templates/repo/pulls/fragments/pullPatchUpload.html
+14
appview/pages/templates/repo/pulls/fragments/pullPatchUpload.html
···
+52
appview/pages/templates/repo/pulls/fragments/pullResubmit.html
+52
appview/pages/templates/repo/pulls/fragments/pullResubmit.html
···+class="rounded relative border bg-amber-50 dark:bg-amber-900 border-amber-200 dark:border-amber-500 px-6 py-2">+hx-get="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/round/{{ .Pull.LastRoundNumber }}/actions"
+90
-53
appview/pages/templates/repo/pulls/new.html
+90
-53
appview/pages/templates/repo/pulls/new.html
···-<li class="leading-relaxed">Grab the diff using <code class="bg-gray-100 px-1 py-0.5 rounded text-gray-800 font-mono text-sm">git diff</code>.</li>+<input type="text" name="title" id="title" class="w-full dark:bg-gray-700 dark:text-white dark:border-gray-600" />+class="p-1 border border-gray-200 bg-white dark:bg-gray-700 dark:text-white dark:border-gray-600"
+15
-29
appview/pages/templates/repo/pulls/patch.html
+15
-29
appview/pages/templates/repo/pulls/patch.html
···-patch of {{ .Pull.Title }} · round #{{ $oneIndexedRound }} · pull #{{ .Pull.PullId }} · {{ .RepoInfo.FullName }}+patch of {{ .Pull.Title }} · round #{{ .Round }} · pull #{{ .Pull.PullId }} · {{ .RepoInfo.FullName }}<a href="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/" class="flex items-center gap-2 font-medium">······-<span class="text-xs rounded bg-gray-100 text-black font-mono px-2 mx-1/2 inline-flex items-center">+<span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center">···
+128
-225
appview/pages/templates/repo/pulls/pull.html
+128
-225
appview/pages/templates/repo/pulls/pull.html
······-<span class="text-xs rounded bg-gray-100 text-black font-mono px-2 mx-1/2 inline-flex items-center">+<span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center">+<a href="/{{ .RepoInfo.FullName }}/tree/{{ .Pull.TargetBranch }}" class="no-underline hover:underline">{{ .Pull.TargetBranch }}</a>+<a href="/{{ $owner }}/{{ .PullSourceRepo.Name }}" class="no-underline hover:underline">{{ $owner }}/{{ .PullSourceRepo.Name }}</a>+<span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center">+<a href="/{{ $fullRepo }}/tree/{{ .Pull.PullSource.Branch }}" class="no-underline hover:underline">{{ .Pull.PullSource.Branch }}</a>······+<div class="rounded drop-shadow-sm bg-white dark:bg-gray-800 p-2 text-gray-500 dark:text-gray-400">+<a class="text-gray-500 dark:text-gray-400 hover:text-gray-500" href="#round-#{{ .RoundNumber }}"><time>{{ .Created | shortTimeFmt }}</time></a>-<a href="/{{ $.RepoInfo.FullName }}/pulls/{{ $.Pull.PullId }}/round/{{.RoundNumber}}">view patch</a>+<div id="comment-{{.ID}}" class="bg-white dark:bg-gray-800 rounded drop-shadow-sm py-2 px-4 relative w-full md:max-w-3/5 md:w-fit">+<a class="text-gray-500 dark:text-gray-400 hover:text-gray-500 dark:hover:text-gray-300" href="#comment-{{.ID}}"><time>{{ .Created | shortTimeFmt }}</time></a>+{{ template "repo/pulls/fragments/pullActions" (dict "LoggedInUser" $.LoggedInUser "Pull" $.Pull "RepoInfo" $.RepoInfo "RoundNumber" .RoundNumber "MergeCheck" $.MergeCheck "ResubmitCheck" $.ResubmitCheck) }}+<div class="bg-white dark:bg-gray-800 rounded drop-shadow-sm px-6 py-4 w-fit dark:text-white">+<div class="bg-gray-50 dark:bg-gray-700 border border-black dark:border-gray-500 rounded drop-shadow-sm px-6 py-2 relative w-fit">+<div class="bg-purple-50 dark:bg-purple-900 border border-purple-500 rounded drop-shadow-sm px-6 py-2 relative w-fit">+<div class="bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative w-fit">+<div class="bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-6 py-2 relative w-fit">+<div class="bg-green-50 dark:bg-green-900 border border-green-500 rounded drop-shadow-sm px-6 py-2 relative w-fit">-<form hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/resubmit" class="w-full" hx-swap="none">+<div class="bg-amber-50 dark:bg-amber-900 border border-amber-500 rounded drop-shadow-sm px-6 py-2 relative w-fit">
+31
-19
appview/pages/templates/repo/pulls/pulls.html
+31
-19
appview/pages/templates/repo/pulls/pulls.html
···+class="border p-1 bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-600 dark:text-white"············-<span class="text-xs rounded bg-gray-100 text-black font-mono px-2 mx-1/2 inline-flex items-center">+<span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center">+<a href="/{{ $owner }}/{{ .PullSource.Repo.Name }}" class="no-underline hover:underline">{{ $owner }}/{{ .PullSource.Repo.Name }}</a>+<span class="text-xs rounded bg-gray-100 dark:bg-gray-700 text-black dark:text-white font-mono px-2 mx-1/2 inline-flex items-center">
+52
-8
appview/pages/templates/repo/settings.html
+52
-8
appview/pages/templates/repo/settings.html
······+<select id="branch" name="branch" required class="p-1 border border-gray-200 bg-white dark:bg-gray-800 dark:text-white dark:border-gray-700">+<form hx-confirm="Are you sure you want to delete this repository?" hx-delete="/{{ $.RepoInfo.FullName }}/settings/delete" class="mt-6">
+37
-24
appview/pages/templates/repo/tree.html
+37
-24
appview/pages/templates/repo/tree.html
···{{ define "title"}}{{ range .BreadCrumbs }}{{ index . 0}}/{{ end }} at {{ .Ref }} · {{ .RepoInfo.FullName }}{{ end }}+<meta name="forge:file" content="https://tangled.sh/{{ .RepoInfo.FullName }}/blob/{ref}/{path}">+<meta name="forge:line" content="https://tangled.sh/{{ .RepoInfo.FullName }}/blob/{ref}/{path}#L{line}">+<meta name="go-import" content="tangled.sh/{{ .RepoInfo.FullNameWithoutAt }} git https://tangled.sh/{{ .RepoInfo.FullName }}">+<div id="breadcrumbs" class="overflow-x-auto whitespace-nowrap text-gray-400 dark:text-gray-500">-<a href="{{ index . 1}}" class="text-bold text-gray-500 {{ $linkstyle }}">{{ index . 0 }}</a> /+<a href="{{ index . 1}}" class="text-bold text-gray-500 dark:text-gray-400 {{ $linkstyle }}">{{ index . 0 }}</a> /+<div id="dir-info" class="text-gray-500 dark:text-gray-400 text-xs md:text-sm flex flex-wrap items-center gap-1 md:gap-0">······
+109
-23
appview/pages/templates/settings.html
+109
-23
appview/pages/templates/settings.html
···+<section class="rounded bg-white dark:bg-gray-800 drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">···+<section class="rounded bg-white dark:bg-gray-800 drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">+<p class="mb-8 dark:text-gray-300">SSH public keys added here will be broadcasted to knots that you are a member of, <br> allowing you to push to repositories there.</p>hx-delete="/settings/keys?name={{urlquery .Name}}&rkey={{urlquery .Rkey}}&key={{urlquery .Key}}"···+class="w-full dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:placeholder-gray-400"/>+class="w-full dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:placeholder-gray-400"/>+<button class="btn dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600" type="submit">add key</button>+<section class="rounded bg-white dark:bg-gray-800 drop-shadow-sm px-6 py-4 mb-6 w-full lg:w-fit">+<p class="mb-8 dark:text-gray-300">Commits authored using emails listed here will be associated with your Tangled profile.</p>+<span class="text-xs bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200 px-2 py-1 rounded">verified</span>+<span class="text-xs bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 px-2 py-1 rounded">unverified</span>+<span class="text-xs bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 px-2 py-1 rounded">primary</span>+<form hx-delete="/settings/emails" hx-confirm="Are you sure you wish to delete the email '{{ .Address }}'?">+class="w-full dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:placeholder-gray-400"/>+<button class="btn dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600" type="submit">add email</button>
+23
-15
appview/pages/templates/timeline.html
+23
-15
appview/pages/templates/timeline.html
···-<div class="flex flex-col items-center justify-center text-center rounded drop-shadow bg-white text-black py-4 px-10">+<div class="flex flex-col items-center justify-center text-center rounded drop-shadow bg-white dark:bg-gray-800 text-black dark:text-white py-4 px-10">-tightly-knit social coding, <a href="/login" class="underline inline-flex gap-1 items-center">join now <i data-lucide="arrow-right" class="w-4 h-4"></i></a>-<p class="pt-5 px-10 text-sm text-gray-500">Join our IRC channel: <a href="https://web.libera.chat/#tangled"><code>#tangled</code> on Libera Chat</a>.+tightly-knit social coding, <a href="/login" class="underline inline-flex gap-1 items-center">join now {{ i "arrow-right" "w-4 h-4" }}</a>+<p class="pt-5 px-10 text-sm text-gray-500 dark:text-gray-400">Join our IRC channel: <a href="https://web.libera.chat/#tangled"><code>#tangled</code> on Libera Chat</a>.···<a href="/{{ $userHandle }}" class="no-underline hover:underline">{{ $userHandle | truncateAt30 }}</a>-<a href="/{{ $userHandle }}/{{ .Repo.Name }}" class="no-underline hover:underline">{{ .Repo.Name }}</a>+<a href="/{{ index $.DidHandleMap .Source.Did }}/{{ .Source.Name }}" class="no-underline hover:underline">+<a href="/{{ $userHandle }}/{{ .Repo.Name }}" class="no-underline hover:underline">{{ .Repo.Name }}</a>+<a href="/{{ $userHandle }}/{{ .Repo.Name }}" class="no-underline hover:underline">{{ .Repo.Name }}</a><a href="/{{ $userHandle }}" class="no-underline hover:underline">{{ $userHandle | truncateAt30 }}</a><a href="/{{ $subjectHandle }}" class="no-underline hover:underline">{{ $subjectHandle | truncateAt30 }}</a>+<time class="text-gray-700 dark:text-gray-400 text-xs">{{ .Follow.FollowedAt | timeFmt }}</time><a href="/{{ $starrerHandle }}" class="no-underline hover:underline">{{ $starrerHandle | truncateAt30 }}</a><a href="/{{ $repoOwnerHandle }}/{{ .Star.Repo.Name }}" class="no-underline hover:underline">{{ $repoOwnerHandle | truncateAt30 }}/{{ .Star.Repo.Name }}</a>···
+17
appview/pages/templates/user/fragments/follow.html
+17
appview/pages/templates/user/fragments/follow.html
···
+14
-6
appview/pages/templates/user/login.html
+14
-6
appview/pages/templates/user/login.html
···············
+242
-37
appview/pages/templates/user/profile.html
+242
-37
appview/pages/templates/user/profile.html
···+<a href="/{{ index $handleMap .Repo.Did }}/{{ .Repo.Name }}" class="no-underline hover:underline">+<span class="px-2 py-1/2 text-sm rounded text-black dark:text-white bg-gray-50 dark:bg-gray-700 ">+class="text-lg font-bold text-center dark:text-white overflow-hidden text-ellipsis whitespace-nowrap max-w-full"···
+3
-3
appview/state/follow.go
+3
-3
appview/state/follow.go
···
+3
-3
appview/state/jetstream.go
+3
-3
appview/state/jetstream.go
······
+3
-3
appview/state/middleware.go
+3
-3
appview/state/middleware.go
···
+102
appview/state/profile.go
+102
appview/state/profile.go
···
+1004
-154
appview/state/pull.go
+1004
-154
appview/state/pull.go
······-resp, err := ksClient.MergeCheck([]byte(pull.LatestPatch()), pull.OwnerDid, f.RepoName, pull.TargetBranch)+resp, err := ksClient.MergeCheck([]byte(pull.LatestPatch()), f.OwnerDid(), f.RepoName, pull.TargetBranch)··················+s.pages.Notice(w, "pull", "This knot doesn't support branch-based pull requests. Try another way?")+s.pages.Notice(w, "pull", "This knot doesn't support fork-based pull requests. Try another way?")+s.pages.Notice(w, "pull", "This knot doesn't support patch-based pull requests. Send your patch over email.")+func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, sourceBranch string) {+diffTreeResponse, err := ksClient.Compare(f.OwnerDid(), f.RepoName, targetBranch, sourceBranch)+s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource)+func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, patch string) {+func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, forkRepo string, title, body, targetBranch, sourceBranch string) {+// the targetBranch on the target repository. This code is a bit confusing, but here's an example:+s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, &db.PullSource{+func (s *State) createPullRequest(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, patch, sourceRev string, pullSource *db.PullSource, recordPullSource *tangled.RepoPull_Source) {···+ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)+s.pages.Notice(w, "resubmit-error", "Failed to update pull request on the PDS. Try again later.")+diffTreeResponse, err := ksClient.Compare(f.OwnerDid(), f.RepoName, pull.TargetBranch, pull.PullSource.Branch)+s.pages.Notice(w, "resubmit-error", "This branch has not changed since the last submission.")-ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)+ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)-s.pages.Notice(w, "resubmit-error", "Failed to update pull request on the PDS. Try again later.")+s.pages.Notice(w, "resubmit-error", "Failed to update pull request on the PDS. Try again later.")+resp, err := signedClient.NewHiddenRef(forkRepo.Did, forkRepo.Name, pull.PullSource.Branch, pull.TargetBranch)+hiddenRef := url.QueryEscape(fmt.Sprintf("hidden/%s/%s", pull.PullSource.Branch, pull.TargetBranch))+diffTreeResponse, err := ksClient.Compare(forkRepo.Did, forkRepo.Name, hiddenRef, pull.PullSource.Branch)+s.pages.Notice(w, "resubmit-error", "This branch has not changed since the last submission.")+ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey)+s.pages.Notice(w, "resubmit-error", "Failed to update pull request on the PDS. Try again later.")······-resp, err := ksClient.Merge([]byte(pull.LatestPatch()), f.OwnerDid(), f.RepoName, pull.TargetBranch, pull.Title, pull.Body, "", "")+resp, err := ksClient.Merge([]byte(pull.LatestPatch()), f.OwnerDid(), f.RepoName, pull.TargetBranch, pull.Title, pull.Body, ident.Handle.String(), email.Address)
+779
-58
appview/state/repo.go
+779
-58
appview/state/repo.go
···············resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/commit/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref))······+resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath))···+_, err = comatproto.RepoDeleteRecord(r.Context(), xrpcClient, &comatproto.RepoDeleteRecord_Input{+repoCollaborators, err := s.enforcer.E.GetImplicitUsersForResourceByDomain(f.OwnerSlashRepo(), f.Knot)·····················+ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, rkey)+s.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "Failed to update description, no record found on PDS.")+_, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{···+atresp, err := comatproto.RepoPutRecord(r.Context(), xrpcClient, &comatproto.RepoPutRecord_Input{
+128
appview/state/repo_util.go
+128
appview/state/repo_util.go
···+emailToDid, err := db.GetEmailToDid(s.db, emails, true) // only get verified emails for mapping
+66
-6
appview/state/router.go
+66
-6
appview/state/router.go
···············r.With(RepoPermissionMiddleware(s, "repo:settings")).Route("/settings", func(r chi.Router) {······
+282
-4
appview/state/settings.go
+282
-4
appview/state/settings.go
······+Text: `Click the link below (or copy and paste it into your browser) to verify your email address.+Html: `<p>Click the link (or copy and paste it into your browser) to verify your email address.</p>+func (s *State) sendVerificationEmail(w http.ResponseWriter, did, emailAddr, code string, errorContext string) error {+s.pages.Notice(w, "settings-emails-error", fmt.Sprintf("Unable to send verification email at this moment, try again later. %s", errorContext))+s.pages.Notice(w, "settings-emails-error", "Unable to add email at this moment, try again later.")+s.pages.Notice(w, "settings-emails-error", "This email is already added but not verified. Check your inbox for the verification link.")+s.pages.Notice(w, "settings-emails-error", "Unable to add email at this moment, try again later.")+s.pages.Notice(w, "settings-emails-error", "Unable to add email at this moment, try again later.")+s.pages.Notice(w, "settings-emails-error", "Unable to add email at this moment, try again later.")+s.pages.Notice(w, "settings-emails-success", "Click the link in the email we sent you to verify your email address.")+s.pages.Notice(w, "settings-emails-error", "Unable to delete email at this moment, try again later.")+s.pages.Notice(w, "settings-emails-error", "Unable to delete email at this moment, try again later.")+s.pages.Notice(w, "settings-emails-error", "Unable to delete email at this moment, try again later.")+return fmt.Sprintf("%s/settings/emails/verify?did=%s&email=%s&code=%s", appUrl, url.QueryEscape(did), url.QueryEscape(email), url.QueryEscape(code))+s.pages.Notice(w, "settings-emails-error", "Invalid verification code. Please request a new verification email.")+s.pages.Notice(w, "settings-emails-error", "Error updating email verification status. Please try again later.")+s.pages.Notice(w, "settings-emails-error", "Unable to resend verification email at this moment, try again later.")+s.pages.Notice(w, "settings-emails-error", fmt.Sprintf("Please wait %d minutes before requesting another verification email.", int(waitTime.Minutes()+1)))+s.pages.Notice(w, "settings-emails-error", "Unable to resend verification email at this moment, try again later.")+s.pages.Notice(w, "settings-emails-error", "Unable to resend verification email at this moment, try again later.")+s.pages.Notice(w, "settings-emails-error", "Unable to resend verification email at this moment, try again later.")+s.pages.Notice(w, "settings-emails-success", "Verification email resent. Click the link in the email we sent you to verify your email address.")+s.pages.Notice(w, "settings-emails-error", "Error setting primary email. Please try again later.")
+158
-8
appview/state/signer.go
+158
-8
appview/state/signer.go
·········+func (s *SignedClient) SetDefaultBranch(ownerDid, repoName, branch string) (*http.Response, error) {······+func (s *SignedClient) NewHiddenRef(ownerDid, targetRepo, forkBranch, remoteBranch string) (*http.Response, error) {+endpoint := fmt.Sprintf("/%s/%s/hidden-ref/%s/%s", ownerDid, targetRepo, forkBranch, remoteBranch)···+func (us *UnsignedClient) Compare(ownerDid, repoName, rev1, rev2 string) (*types.RepoDiffTreeResponse, error) {+endpoint := fmt.Sprintf("/%s/%s/compare/%s/%s", ownerDid, repoName, url.PathEscape(rev1), url.PathEscape(rev2))
+5
-5
appview/state/star.go
+5
-5
appview/state/star.go
·········
+39
-122
appview/state/state.go
+39
-122
appview/state/state.go
·········-jc, err := jetstream.NewJetstreamClient("appview", []string{tangled.GraphFollowNSID}, nil, slog.Default(), wrapper, false)············-recordURL := fmt.Sprintf("%s/xrpc/com.atproto.repo.getRecord?repo=%s&collection=app.bsky.actor.profile&rkey=self", pds, did)
+51
appview/state/userutil/userutil.go
+51
appview/state/userutil/userutil.go
···+re := regexp.MustCompile(`^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$`)
+114
appview/state/userutil/userutil_test.go
+114
appview/state/userutil/userutil_test.go
···+"did-key-z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
+2
-2
cmd/appview/main.go
+2
-2
cmd/appview/main.go
+2
-1
cmd/gen.go
+2
-1
cmd/gen.go
······
+3
-3
cmd/keyfetch/format.go
+3
-3
cmd/keyfetch/format.go
···-func formatKeyData(repoguardPath, gitDir, logPath string, data []map[string]interface{}) string {+func formatKeyData(repoguardPath, gitDir, logPath, endpoint string, data []map[string]interface{}) string {-`command="%s -base-dir %s -user %s -log-path %s",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s`+"\n",+`command="%s -base-dir %s -user %s -log-path %s -internal-api %s",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s`+"\n",
+1
-1
cmd/keyfetch/main.go
+1
-1
cmd/keyfetch/main.go
+8
-8
cmd/knotserver/main.go
+8
-8
cmd/knotserver/main.go
······
+5
-4
cmd/repoguard/main.go
+5
-4
cmd/repoguard/main.go
······
-23
config.yaml
-23
config.yaml
···
+52
docker/Dockerfile
+52
docker/Dockerfile
···+LABEL org.opencontainers.image.description="Tangled is a decentralized and open code collaboration platform, built on atproto."
+17
docker/docker-compose.yml
+17
docker/docker-compose.yml
···
+1
docker/rootfs/etc/s6-overlay/s6-rc.d/create-sshd-host-keys/type
+1
docker/rootfs/etc/s6-overlay/s6-rc.d/create-sshd-host-keys/type
···
+1
docker/rootfs/etc/s6-overlay/s6-rc.d/create-sshd-host-keys/up
+1
docker/rootfs/etc/s6-overlay/s6-rc.d/create-sshd-host-keys/up
···
docker/rootfs/etc/s6-overlay/s6-rc.d/knotserver/dependencies.d/base
docker/rootfs/etc/s6-overlay/s6-rc.d/knotserver/dependencies.d/base
This is a binary file and will not be displayed.
+3
docker/rootfs/etc/s6-overlay/s6-rc.d/knotserver/run
+3
docker/rootfs/etc/s6-overlay/s6-rc.d/knotserver/run
+1
docker/rootfs/etc/s6-overlay/s6-rc.d/knotserver/type
+1
docker/rootfs/etc/s6-overlay/s6-rc.d/knotserver/type
···
docker/rootfs/etc/s6-overlay/s6-rc.d/sshd/dependencies.d/base
docker/rootfs/etc/s6-overlay/s6-rc.d/sshd/dependencies.d/base
This is a binary file and will not be displayed.
docker/rootfs/etc/s6-overlay/s6-rc.d/sshd/dependencies.d/create-sshd-host-keys
docker/rootfs/etc/s6-overlay/s6-rc.d/sshd/dependencies.d/create-sshd-host-keys
This is a binary file and will not be displayed.
+3
docker/rootfs/etc/s6-overlay/s6-rc.d/sshd/run
+3
docker/rootfs/etc/s6-overlay/s6-rc.d/sshd/run
+1
docker/rootfs/etc/s6-overlay/s6-rc.d/sshd/type
+1
docker/rootfs/etc/s6-overlay/s6-rc.d/sshd/type
···
docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/knotserver
docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/knotserver
This is a binary file and will not be displayed.
docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/sshd
docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/sshd
This is a binary file and will not be displayed.
+21
docker/rootfs/etc/s6-overlay/scripts/create-sshd-host-keys
+21
docker/rootfs/etc/s6-overlay/scripts/create-sshd-host-keys
···
+9
docker/rootfs/etc/ssh/sshd_config.d/tangled_sshd.conf
+9
docker/rootfs/etc/ssh/sshd_config.d/tangled_sshd.conf
···
+74
docs/contributing.md
+74
docs/contributing.md
···
+108
docs/knot-hosting.md
+108
docs/knot-hosting.md
···+1. A server of some kind (a VPS, a Raspberry Pi, etc.). Preferably running a Linux of some kind.+[`knotserver.service`](https://tangled.sh/did:plc:wshs7t2adsemcrrd4snkeqli/core/blob/master/systemd/knotserver.service)
+32
-19
flake.lock
+32
-19
flake.lock
······+"url": "https://github.com/lucide-icons/lucide/releases/download/0.483.0/lucide-icons-0.483.0.zip"+"url": "https://github.com/lucide-icons/lucide/releases/download/0.483.0/lucide-icons-0.483.0.zip"···
+83
-45
flake.nix
+83
-45
flake.nix
······+url = "https://github.com/lucide-icons/lucide/releases/download/0.483.0/lucide-icons-0.483.0.zip";+url = "https://github.com/IBM/plex/releases/download/%40ibm%2Fplex-mono%401.1.0/ibm-plex-mono.zip";············+cp -f ${ibm-plex-mono-src}/fonts/complete/woff2/IBMPlexMono-Regular.woff2 appview/pages/static/fonts/·········+cp -f ${ibm-plex-mono-src}/fonts/complete/woff2/IBMPlexMono-Regular.woff2 appview/pages/static/fonts/··················-"KNOT_SERVER_INTERNAL_LISTEN_ADDR=${config.services.tangled-knotserver.server.internalListenAddr}"···+"f+ /var/lib/knotserver/secret 0660 ${u} ${g} - KNOT_SERVER_SECRET=5b42390da4c6659f34c9a545adebd8af82c4a19960d735f651e3d582623ba9f2"
+4
-3
go.mod
+4
-3
go.mod
············
+2
go.sum
+2
go.sum
···github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+29
-92
input.css
+29
-92
input.css
···+@apply bg-yellow-400 text-black bg-opacity-30 dark:bg-yellow-600 dark:bg-opacity-50 dark:text-white;+@apply no-underline text-black hover:underline hover:text-gray-800 dark:text-white dark:hover:text-gray-300;+@apply bg-white border border-gray-400 rounded-sm focus:ring-black p-3 dark:bg-gray-800 dark:border-gray-600 dark:text-white dark:focus:ring-gray-400;+@apply bg-white border border-gray-400 rounded-sm focus:ring-black p-3 dark:bg-gray-800 dark:border-gray-600 dark:text-white dark:focus:ring-gray-400;······
+94
-27
jetstream/jetstream.go
+94
-27
jetstream/jetstream.go
·········-func NewJetstreamClient(ident string, collections []string, cfg *client.ClientConfig, logger *slog.Logger, db DB, waitForDid bool) (*JetstreamClient, error) {+func NewJetstreamClient(endpoint, ident string, collections []string, cfg *client.ClientConfig, logger *slog.Logger, db DB, waitForDid bool) (*JetstreamClient, error) {// StartJetstream starts the jetstream client and processes events using the provided processFunc.-// The caller is responsible for saving the last time_us to the database (just use your db.SaveLastTimeUs).+// The caller is responsible for saving the last time_us to the database (just use your db.UpdateLastTimeUs).func (j *JetstreamClient) StartJetstream(ctx context.Context, processFunc func(context.Context, *models.Event) error) error {············
+1
knotserver/config/config.go
+1
knotserver/config/config.go
···+JetstreamEndpoint string `env:"JETSTREAM_ENDPOINT, default=wss://jetstream1.us-west.bsky.network/subscribe"`
+6
-10
knotserver/db/jetstream.go
+6
-10
knotserver/db/jetstream.go
···
+3
-3
knotserver/db/pubkeys.go
+3
-3
knotserver/db/pubkeys.go
······
+1
-1
knotserver/file.go
+1
-1
knotserver/file.go
+82
-11
knotserver/git/diff.go
+82
-11
knotserver/git/diff.go
·········
+50
knotserver/git/fork.go
+50
knotserver/git/fork.go
···+config.RefSpec(fmt.Sprintf("+refs/heads/%s:refs/hidden/%s/%s", remoteRef, forkRef, remoteRef)),
+30
-2
knotserver/git/git.go
+30
-2
knotserver/git/git.go
············+cmd := exec.Command("git", "-C", g.path, "log", g.h.String(), "-1", "--format=%H %ct", "--", path)
+31
-24
knotserver/git/service/service.go
+31
-24
knotserver/git/service/service.go
······
+9
-2
knotserver/git/tree.go
+9
-2
knotserver/git/tree.go
······
+23
-18
knotserver/git.go
+23
-18
knotserver/git.go
······
+56
-7
knotserver/handler.go
+56
-7
knotserver/handler.go
···············
+2
-2
knotserver/internal.go
+2
-2
knotserver/internal.go
···
+5
-5
knotserver/jetstream.go
+5
-5
knotserver/jetstream.go
···func (h *Handle) processPublicKey(ctx context.Context, did string, record tangled.PublicKey) error {······
+230
-6
knotserver/routes.go
+230
-6
knotserver/routes.go
························
+1
-1
lexicon-build-config.json
+1
-1
lexicon-build-config.json
+24
-5
lexicons/pulls/pull.json
+24
-5
lexicons/pulls/pull.json
·········
+5
lexicons/repo.json
+5
lexicons/repo.json
+55
-34
rbac/rbac.go
+55
-34
rbac/rbac.go
·····················
+8
-89
readme.md
+8
-89
readme.md
···+guide](https://tangled.sh/@tangled.sh/core/blob/master/docs/contributing.md)—**read this before opening a PR!**-1. A server of some kind (a VPS, a Raspberry Pi, etc.). Preferably running a Linux of some kind.-[`knotserver.service`](https://tangled.sh/did:plc:wshs7t2adsemcrrd4snkeqli/core/blob/master/systemd/knotserver.service)
+43
-9
tailwind.config.js
+43
-9
tailwind.config.js
······-mono: ["iA Writer Mono S", "ui-monospace", "SFMono-Regular", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", "monospace"],+"@apply font-normal text-black bg-gray-100 dark:bg-gray-900 dark:text-gray-300 dark:border-gray-700 dark:border": {},+"@apply font-normal font-mono p-1 rounded text-black bg-gray-100 dark:bg-gray-900 dark:text-gray-300 dark:border-gray-700": {},
+9
types/capabilities.go
+9
types/capabilities.go
+21
types/diff.go
+21
types/diff.go
······
+12
types/repo.go
+12
types/repo.go
······