diff --git a/.gitignore b/.gitignore
index c9e25237..1df590cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,5 @@ appview/pages/static/*
result
!.gitkeep
out/
+./camo/node_modules/*
+
diff --git a/appview/pages/markup/markdown.go b/appview/pages/markup/markdown.go
index 6b19bad6..809d418c 100644
--- a/appview/pages/markup/markdown.go
+++ b/appview/pages/markup/markdown.go
@@ -3,6 +3,7 @@ package markup
import (
"bytes"
+ "net/url"
"path"
"github.com/yuin/goldmark"
@@ -11,6 +12,7 @@ import (
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
+ "tangled.sh/tangled.sh/core/appview/pages/repoinfo"
)
// RendererType defines the type of renderer to use based on context
@@ -24,8 +26,8 @@ const (
// RenderContext holds the contextual data for rendering markdown.
// It can be initialized empty, and that'll skip any transformations.
type RenderContext struct {
- Ref string
- FullRepoName string
+ repoinfo.RepoInfo
+ IsDev bool
RendererType RendererType
}
@@ -66,8 +68,11 @@ func (a *MarkdownTransformer) Transform(node *ast.Document, reader text.Reader,
switch a.rctx.RendererType {
case RendererTypeRepoMarkdown:
- if v, ok := n.(*ast.Link); ok {
- a.rctx.relativeLinkTransformer(v)
+ switch n.(type) {
+ case *ast.Link:
+ a.rctx.relativeLinkTransformer(n.(*ast.Link))
+ case *ast.Image:
+ a.rctx.imageFromKnotTransformer(n.(*ast.Image))
}
// more types here like RendererTypeIssue/Pull etc.
}
@@ -79,12 +84,48 @@ func (a *MarkdownTransformer) Transform(node *ast.Document, reader text.Reader,
func (rctx *RenderContext) relativeLinkTransformer(link *ast.Link) {
dst := string(link.Destination)
- if len(dst) == 0 || dst[0] == '#' ||
- bytes.Contains(link.Destination, []byte("://")) ||
- bytes.HasPrefix(link.Destination, []byte("mailto:")) {
+ if isAbsoluteUrl(dst) {
return
}
- newPath := path.Join("/", rctx.FullRepoName, "tree", rctx.Ref, dst)
+ newPath := path.Join("/", rctx.RepoInfo.FullName(), "tree", rctx.RepoInfo.Ref, dst)
link.Destination = []byte(newPath)
}
+
+func (rctx *RenderContext) imageFromKnotTransformer(img *ast.Image) {
+ dst := string(img.Destination)
+
+ if isAbsoluteUrl(dst) {
+ return
+ }
+
+ // strip leading './'
+ if len(dst) >= 2 && dst[0:2] == "./" {
+ dst = dst[2:]
+ }
+
+ scheme := "https"
+ if rctx.IsDev {
+ scheme = "http"
+ }
+ parsedURL := &url.URL{
+ Scheme: scheme,
+ Host: rctx.Knot,
+ Path: path.Join("/",
+ rctx.RepoInfo.OwnerDid,
+ rctx.RepoInfo.Name,
+ "raw",
+ url.PathEscape(rctx.RepoInfo.Ref),
+ dst),
+ }
+ newPath := parsedURL.String()
+ img.Destination = []byte(newPath)
+}
+
+func isAbsoluteUrl(link string) bool {
+ parsed, err := url.Parse(link)
+ if err != nil {
+ return false
+ }
+ return parsed.IsAbs()
+}
diff --git a/appview/pages/pages.go b/appview/pages/pages.go
index 1d0375d3..23fb520b 100644
--- a/appview/pages/pages.go
+++ b/appview/pages/pages.go
@@ -12,16 +12,14 @@ import (
"log"
"net/http"
"os"
- "path"
"path/filepath"
- "slices"
"strings"
"tangled.sh/tangled.sh/core/appview/auth"
"tangled.sh/tangled.sh/core/appview/db"
"tangled.sh/tangled.sh/core/appview/pages/markup"
+ "tangled.sh/tangled.sh/core/appview/pages/repoinfo"
"tangled.sh/tangled.sh/core/appview/pagination"
- "tangled.sh/tangled.sh/core/appview/state/userutil"
"tangled.sh/tangled.sh/core/patchutil"
"tangled.sh/tangled.sh/core/types"
@@ -42,13 +40,20 @@ type Pages struct {
dev bool
embedFS embed.FS
templateDir string // Path to templates on disk for dev mode
+ rctx *markup.RenderContext
}
func NewPages(dev bool) *Pages {
+ // initialized with safe defaults, can be overriden per use
+ rctx := &markup.RenderContext{
+ IsDev: dev,
+ }
+
p := &Pages{
t: make(map[string]*template.Template),
dev: dev,
embedFS: Files,
+ rctx: rctx,
templateDir: "appview/pages",
}
@@ -293,7 +298,7 @@ func (p *Pages) NewRepo(w io.Writer, params NewRepoParams) error {
type ForkRepoParams struct {
LoggedInUser *auth.User
Knots []string
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
}
func (p *Pages) ForkRepo(w io.Writer, params ForkRepoParams) error {
@@ -343,7 +348,7 @@ func (p *Pages) RepoActionsFragment(w io.Writer, params RepoActionsFragmentParam
}
type RepoDescriptionParams struct {
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
}
func (p *Pages) EditRepoDescriptionFragment(w io.Writer, params RepoDescriptionParams) error {
@@ -354,114 +359,9 @@ func (p *Pages) RepoDescriptionFragment(w io.Writer, params RepoDescriptionParam
return p.executePlain("repo/fragments/repoDescription", w, params)
}
-type RepoInfo struct {
- Name string
- OwnerDid string
- OwnerHandle string
- Description string
- Knot string
- RepoAt syntax.ATURI
- IsStarred bool
- Stats db.RepoStats
- Roles RolesInRepo
- Source *db.Repo
- SourceHandle string
- Ref string
- DisableFork bool
-}
-
-type RolesInRepo struct {
- Roles []string
-}
-
-func (r RolesInRepo) SettingsAllowed() bool {
- return slices.Contains(r.Roles, "repo:settings")
-}
-
-func (r RolesInRepo) CollaboratorInviteAllowed() bool {
- return slices.Contains(r.Roles, "repo:invite")
-}
-
-func (r RolesInRepo) RepoDeleteAllowed() bool {
- return slices.Contains(r.Roles, "repo:delete")
-}
-
-func (r RolesInRepo) IsOwner() bool {
- return slices.Contains(r.Roles, "repo:owner")
-}
-
-func (r RolesInRepo) IsCollaborator() bool {
- return slices.Contains(r.Roles, "repo:collaborator")
-}
-
-func (r RolesInRepo) IsPushAllowed() bool {
- return slices.Contains(r.Roles, "repo:push")
-}
-
-func (r RepoInfo) OwnerWithAt() string {
- if r.OwnerHandle != "" {
- return fmt.Sprintf("@%s", r.OwnerHandle)
- } else {
- return r.OwnerDid
- }
-}
-
-func (r RepoInfo) FullName() string {
- return path.Join(r.OwnerWithAt(), r.Name)
-}
-
-func (r RepoInfo) OwnerWithoutAt() string {
- if strings.HasPrefix(r.OwnerWithAt(), "@") {
- return strings.TrimPrefix(r.OwnerWithAt(), "@")
- } else {
- return userutil.FlattenDid(r.OwnerDid)
- }
-}
-
-func (r RepoInfo) FullNameWithoutAt() string {
- return path.Join(r.OwnerWithoutAt(), r.Name)
-}
-
-func (r RepoInfo) GetTabs() [][]string {
- tabs := [][]string{
- {"overview", "/", "square-chart-gantt"},
- {"issues", "/issues", "circle-dot"},
- {"pulls", "/pulls", "git-pull-request"},
- }
-
- if r.Roles.SettingsAllowed() {
- tabs = append(tabs, []string{"settings", "/settings", "cog"})
- }
-
- return tabs
-}
-
-// each tab on a repo could have some metadata:
-//
-// issues -> number of open issues etc.
-// settings -> a warning icon to setup branch protection? idk
-//
-// we gather these bits of info here, because go templates
-// are difficult to program in
-func (r RepoInfo) TabMetadata() map[string]any {
- meta := make(map[string]any)
-
- if r.Stats.PullCount.Open > 0 {
- meta["pulls"] = r.Stats.PullCount.Open
- }
-
- if r.Stats.IssueCount.Open > 0 {
- meta["issues"] = r.Stats.IssueCount.Open
- }
-
- // more stuff?
-
- return meta
-}
-
type RepoIndexParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Active string
TagMap map[string][]string
CommitsTrunc []*object.Commit
@@ -479,9 +379,10 @@ func (p *Pages) RepoIndexPage(w io.Writer, params RepoIndexParams) error {
return p.executeRepo("repo/empty", w, params)
}
- rctx := markup.RenderContext{
- Ref: params.RepoInfo.Ref,
- FullRepoName: params.RepoInfo.FullName(),
+ p.rctx = &markup.RenderContext{
+ RepoInfo: params.RepoInfo,
+ IsDev: p.dev,
+ RendererType: markup.RendererTypeRepoMarkdown,
}
if params.ReadmeFileName != "" {
@@ -489,7 +390,7 @@ func (p *Pages) RepoIndexPage(w io.Writer, params RepoIndexParams) error {
ext := filepath.Ext(params.ReadmeFileName)
switch ext {
case ".md", ".markdown", ".mdown", ".mkdn", ".mkd":
- htmlString = rctx.RenderMarkdown(params.Readme)
+ htmlString = p.rctx.RenderMarkdown(params.Readme)
params.Raw = false
params.HTMLReadme = template.HTML(bluemonday.UGCPolicy().Sanitize(htmlString))
default:
@@ -504,7 +405,7 @@ func (p *Pages) RepoIndexPage(w io.Writer, params RepoIndexParams) error {
type RepoLogParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
TagMap map[string][]string
types.RepoLogResponse
Active string
@@ -518,7 +419,7 @@ func (p *Pages) RepoLog(w io.Writer, params RepoLogParams) error {
type RepoCommitParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Active string
EmailToDidOrHandle map[string]string
@@ -532,7 +433,7 @@ func (p *Pages) RepoCommit(w io.Writer, params RepoCommitParams) error {
type RepoTreeParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Active string
BreadCrumbs [][]string
BaseTreeLink string
@@ -568,7 +469,7 @@ func (p *Pages) RepoTree(w io.Writer, params RepoTreeParams) error {
type RepoBranchesParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Active string
types.RepoBranchesResponse
}
@@ -580,7 +481,7 @@ func (p *Pages) RepoBranches(w io.Writer, params RepoBranchesParams) error {
type RepoTagsParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Active string
types.RepoTagsResponse
}
@@ -592,7 +493,7 @@ func (p *Pages) RepoTags(w io.Writer, params RepoTagsParams) error {
type RepoBlobParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Active string
BreadCrumbs [][]string
ShowRendered bool
@@ -607,12 +508,12 @@ func (p *Pages) RepoBlob(w io.Writer, params RepoBlobParams) error {
if params.ShowRendered {
switch markup.GetFormat(params.Path) {
case markup.FormatMarkdown:
- rctx := markup.RenderContext{
- Ref: params.RepoInfo.Ref,
- FullRepoName: params.RepoInfo.FullName(),
+ p.rctx = &markup.RenderContext{
+ RepoInfo: params.RepoInfo,
+ IsDev: p.dev,
RendererType: markup.RendererTypeRepoMarkdown,
}
- params.RenderedContents = template.HTML(rctx.RenderMarkdown(params.Contents))
+ params.RenderedContents = template.HTML(p.rctx.RenderMarkdown(params.Contents))
}
}
@@ -657,7 +558,7 @@ type Collaborator struct {
type RepoSettingsParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Collaborators []Collaborator
Active string
Branches []string
@@ -673,7 +574,7 @@ func (p *Pages) RepoSettings(w io.Writer, params RepoSettingsParams) error {
type RepoIssuesParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Active string
Issues []db.Issue
DidHandleMap map[string]string
@@ -688,7 +589,7 @@ func (p *Pages) RepoIssues(w io.Writer, params RepoIssuesParams) error {
type RepoSingleIssueParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Active string
Issue db.Issue
Comments []db.Comment
@@ -710,7 +611,7 @@ func (p *Pages) RepoSingleIssue(w io.Writer, params RepoSingleIssueParams) error
type RepoNewIssueParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Active string
}
@@ -721,7 +622,7 @@ func (p *Pages) RepoNewIssue(w io.Writer, params RepoNewIssueParams) error {
type EditIssueCommentParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Issue *db.Issue
Comment *db.Comment
}
@@ -733,7 +634,7 @@ func (p *Pages) EditIssueCommentFragment(w io.Writer, params EditIssueCommentPar
type SingleIssueCommentParams struct {
LoggedInUser *auth.User
DidHandleMap map[string]string
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Issue *db.Issue
Comment *db.Comment
}
@@ -744,7 +645,7 @@ func (p *Pages) SingleIssueCommentFragment(w io.Writer, params SingleIssueCommen
type RepoNewPullParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Branches []types.Branch
Active string
}
@@ -756,7 +657,7 @@ func (p *Pages) RepoNewPull(w io.Writer, params RepoNewPullParams) error {
type RepoPullsParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Pulls []*db.Pull
Active string
DidHandleMap map[string]string
@@ -788,7 +689,7 @@ func (r ResubmitResult) Unknown() bool {
type RepoSinglePullParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Active string
DidHandleMap map[string]string
Pull *db.Pull
@@ -804,7 +705,7 @@ func (p *Pages) RepoSinglePull(w io.Writer, params RepoSinglePullParams) error {
type RepoPullPatchParams struct {
LoggedInUser *auth.User
DidHandleMap map[string]string
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Pull *db.Pull
Diff *types.NiceDiff
Round int
@@ -819,7 +720,7 @@ func (p *Pages) RepoPullPatchPage(w io.Writer, params RepoPullPatchParams) error
type RepoPullInterdiffParams struct {
LoggedInUser *auth.User
DidHandleMap map[string]string
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Pull *db.Pull
Round int
Interdiff *patchutil.InterdiffResult
@@ -831,7 +732,7 @@ func (p *Pages) RepoPullInterdiffPage(w io.Writer, params RepoPullInterdiffParam
}
type PullPatchUploadParams struct {
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
}
func (p *Pages) PullPatchUploadFragment(w io.Writer, params PullPatchUploadParams) error {
@@ -839,7 +740,7 @@ func (p *Pages) PullPatchUploadFragment(w io.Writer, params PullPatchUploadParam
}
type PullCompareBranchesParams struct {
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Branches []types.Branch
}
@@ -848,7 +749,7 @@ func (p *Pages) PullCompareBranchesFragment(w io.Writer, params PullCompareBranc
}
type PullCompareForkParams struct {
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Forks []db.Repo
}
@@ -857,7 +758,7 @@ func (p *Pages) PullCompareForkFragment(w io.Writer, params PullCompareForkParam
}
type PullCompareForkBranchesParams struct {
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
SourceBranches []types.Branch
TargetBranches []types.Branch
}
@@ -868,7 +769,7 @@ func (p *Pages) PullCompareForkBranchesFragment(w io.Writer, params PullCompareF
type PullResubmitParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Pull *db.Pull
SubmissionId int
}
@@ -879,7 +780,7 @@ func (p *Pages) PullResubmitFragment(w io.Writer, params PullResubmitParams) err
type PullActionsParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Pull *db.Pull
RoundNumber int
MergeCheck types.MergeCheckResponse
@@ -892,7 +793,7 @@ func (p *Pages) PullActionsFragment(w io.Writer, params PullActionsParams) error
type PullNewCommentParams struct {
LoggedInUser *auth.User
- RepoInfo RepoInfo
+ RepoInfo repoinfo.RepoInfo
Pull *db.Pull
RoundNumber int
}
diff --git a/appview/pages/templates/repo/blob.html b/appview/pages/templates/repo/blob.html
index d4c7643a..87e20f83 100644
--- a/appview/pages/templates/repo/blob.html
+++ b/appview/pages/templates/repo/blob.html
@@ -42,7 +42,7 @@
{{ byteFmt .SizeHint }}
- view raw
+ view raw
{{ if .RenderToggle }}