Skip to content

Commit

Permalink
feat(gnoweb): add breadcrumb args (#3465)
Browse files Browse the repository at this point in the history
  • Loading branch information
gfanton authored Jan 9, 2025
1 parent 68aff64 commit 60acdf1
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 23 deletions.
5 changes: 3 additions & 2 deletions gno.land/pkg/gnoweb/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ input_css := frontend/css/input.css
output_css := $(PUBLIC_DIR)/styles.css
tw_version := 3.4.14
tw_config_path := frontend/css/tx.config.js
templates_files := $(shell find . -iname '*.gohtml')

# static config
src_dir_static := frontend/static
Expand Down Expand Up @@ -42,8 +43,8 @@ all: generate
generate: css ts static

css: $(output_css)
$(output_css): $(input_css)
npx -y tailwindcss@$(tw_version) -c $(tw_config_path) -i $< -o $@ --minify # tailwind
$(output_css): $(input_css) $(templates_files)
npx -y tailwindcss@$(tw_version) -c $(tw_config_path) -i $(input_css) -o $@ --minify # tailwind
touch $@

ts: $(output_js)
Expand Down
3 changes: 2 additions & 1 deletion gno.land/pkg/gnoweb/components/breadcrumb.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import (

type BreadcrumbPart struct {
Name string
Path string
URL string
}

type BreadcrumbData struct {
Parts []BreadcrumbPart
Args string
}

func RenderBreadcrumpComponent(w io.Writer, data BreadcrumbData) error {
Expand Down
10 changes: 8 additions & 2 deletions gno.land/pkg/gnoweb/components/breadcrumb.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
{{- else }}
<li>
{{- end }}
<a class="hover:bg-green-600 hover:text-light bg-light inline-block rounded-sm px-1 py-px" href="{{ $part.Path }}">{{ $part.Name }}</a></li>
<a class="hover:bg-green-600 hover:text-light bg-light inline-block rounded-sm px-1 py-px" href="{{ $part.URL }}">{{ $part.Name }}</a>
</li>
{{- end }}
{{- if .Args }}
<li class="flex before:content-[':'] before:px-[0.18rem] before:text-gray-300">
<a class="text-light bg-gray-300 inline-block rounded-sm px-1 py-px">{{ .Args }}</a>
</li>
{{- end }}
</ol>
{{ end }}
{{ end }}
20 changes: 12 additions & 8 deletions gno.land/pkg/gnoweb/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ func (h *WebHandler) Get(w http.ResponseWriter, r *http.Request) {
indexData.HeadData.Title = "gno.land - " + gnourl.Path

// Header
indexData.HeaderData.RealmPath = gnourl.Path
indexData.HeaderData.Breadcrumb.Parts = generateBreadcrumbPaths(gnourl.Path)
indexData.HeaderData.RealmPath = gnourl.Encode(EncodePath | EncodeArgs | EncodeQuery | EncodeNoEscape)
indexData.HeaderData.Breadcrumb = generateBreadcrumbPaths(gnourl)
indexData.HeaderData.WebQuery = gnourl.WebQuery

// Render
Expand Down Expand Up @@ -339,21 +339,25 @@ func (h *WebHandler) highlightSource(fileName string, src []byte) ([]byte, error
return buff.Bytes(), nil
}

func generateBreadcrumbPaths(path string) []components.BreadcrumbPart {
split := strings.Split(path, "/")
parts := []components.BreadcrumbPart{}
func generateBreadcrumbPaths(url *GnoURL) components.BreadcrumbData {
split := strings.Split(url.Path, "/")

var data components.BreadcrumbData
var name string
for i := range split {
if name = split[i]; name == "" {
continue
}

parts = append(parts, components.BreadcrumbPart{
data.Parts = append(data.Parts, components.BreadcrumbPart{
Name: name,
Path: strings.Join(split[:i+1], "/"),
URL: strings.Join(split[:i+1], "/"),
})
}

return parts
if args := url.EncodeArgs(); args != "" {
data.Args = args
}

return data
}
2 changes: 1 addition & 1 deletion gno.land/pkg/gnoweb/public/styles.css

Large diffs are not rendered by default.

59 changes: 50 additions & 9 deletions gno.land/pkg/gnoweb/url.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/url"
"path/filepath"
"regexp"
"slices"
"strings"
)

Expand All @@ -31,7 +32,8 @@ type GnoURL struct {
type EncodeFlag int

const (
EncodePath EncodeFlag = 1 << iota // Encode the path component
EncodeDomain EncodeFlag = 1 << iota // Encode the domain component
EncodePath // Encode the path component
EncodeArgs // Encode the arguments component
EncodeWebQuery // Encode the web query component
EncodeQuery // Encode the query component
Expand Down Expand Up @@ -62,13 +64,15 @@ const (
func (gnoURL GnoURL) Encode(encodeFlags EncodeFlag) string {
var urlstr strings.Builder

noEscape := encodeFlags.Has(EncodeNoEscape)

if encodeFlags.Has(EncodeDomain) {
urlstr.WriteString(gnoURL.Domain)
}

if encodeFlags.Has(EncodePath) {
path := gnoURL.Path
if !encodeFlags.Has(EncodeNoEscape) {
path = url.PathEscape(path)
}

urlstr.WriteString(gnoURL.Path)
urlstr.WriteString(path)
}

if len(gnoURL.File) > 0 {
Expand All @@ -84,7 +88,7 @@ func (gnoURL GnoURL) Encode(encodeFlags EncodeFlag) string {
// XXX: Arguments should ideally always be escaped,
// but this may require changes in some realms.
args := gnoURL.Args
if !encodeFlags.Has(EncodeNoEscape) {
if !noEscape {
args = escapeDollarSign(url.PathEscape(args))
}

Expand All @@ -93,12 +97,20 @@ func (gnoURL GnoURL) Encode(encodeFlags EncodeFlag) string {

if encodeFlags.Has(EncodeWebQuery) && len(gnoURL.WebQuery) > 0 {
urlstr.WriteRune('$')
urlstr.WriteString(gnoURL.WebQuery.Encode())
if noEscape {
urlstr.WriteString(NoEscapeQuery(gnoURL.WebQuery))
} else {
urlstr.WriteString(gnoURL.WebQuery.Encode())
}
}

if encodeFlags.Has(EncodeQuery) && len(gnoURL.Query) > 0 {
urlstr.WriteRune('?')
urlstr.WriteString(gnoURL.Query.Encode())
if noEscape {
urlstr.WriteString(NoEscapeQuery(gnoURL.Query))
} else {
urlstr.WriteString(gnoURL.Query.Encode())
}
}

return urlstr.String()
Expand Down Expand Up @@ -213,3 +225,32 @@ func ParseGnoURL(u *url.URL) (*GnoURL, error) {
File: file,
}, nil
}

// NoEscapeQuery generates a URL-encoded query string from the given url.Values,
// without escaping the keys and values. The query parameters are sorted by key.
func NoEscapeQuery(v url.Values) string {
// Encode encodes the values into “URL encoded” form
// ("bar=baz&foo=quux") sorted by key.
if len(v) == 0 {
return ""
}
var buf strings.Builder
keys := make([]string, 0, len(v))
for k := range v {
keys = append(keys, k)
}
slices.Sort(keys)
for _, k := range keys {
vs := v[k]
keyEscaped := k
for _, v := range vs {
if buf.Len() > 0 {
buf.WriteByte('&')
}
buf.WriteString(keyEscaped)
buf.WriteByte('=')
buf.WriteString(v)
}
}
return buf.String()
}
34 changes: 34 additions & 0 deletions gno.land/pkg/gnoweb/url_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,40 @@ func TestEncode(t *testing.T) {
EncodeFlags EncodeFlag
Expected string
}{
{
Name: "encode domain",
GnoURL: GnoURL{
Domain: "gno.land",
Path: "/r/demo/foo",
},
EncodeFlags: EncodeDomain,
Expected: "gno.land",
},

{
Name: "encode web query without escape",
GnoURL: GnoURL{
Domain: "gno.land",
Path: "/r/demo/foo",
WebQuery: url.Values{
"help": []string{""},
"fun$c": []string{"B$ ar"},
},
},
EncodeFlags: EncodeWebQuery | EncodeNoEscape,
Expected: "$fun$c=B$ ar&help=",
},

{
Name: "encode domain and path",
GnoURL: GnoURL{
Domain: "gno.land",
Path: "/r/demo/foo",
},
EncodeFlags: EncodeDomain | EncodePath,
Expected: "gno.land/r/demo/foo",
},

{
Name: "Encode Path Only",
GnoURL: GnoURL{
Expand Down

0 comments on commit 60acdf1

Please sign in to comment.