pico
created pr with
pico/102.1
added pico/102.2
1: e265f5e ! 1: ff673f6 feat(prose): lists are back bby
added pico/102.3
1: ff673f6 ! 1: c86f5a4 feat(prose): lists are back bby
cmds
checkout latest patchset:
ssh pr.pico.sh pull pico:102 | git am -3checkout specific patchset revision:
ssh pr.pico.sh pull pico:102 [rev] | git am -3add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh pico:102add comment to patch request:
ssh pr.pico.sh comment pico:102 "lgtm!"
Patchset
pico/102.1
feat(prose): lists are back bby
Eric Bower
2026-01-04T04:35:04ZSemantic diff summary
2 added,
7 modified,
0 signature changed,
1 removed
across 5 analyzed files
(2 files skipped: unsupported file type)
+43
-15
pkg/apps/prose/api.go
#
| ... | ... | @@ -427,22 +428,47 @@ func postHandler(w http.ResponseWriter, r *http.Request) { | |
| 427 | 428 | post, err := dbpool.FindPostWithSlug(slug, user.ID, cfg.Space) | |
| 428 | 429 | if err == nil { | |
| 429 | 430 | logger.Info("post found", "id", post.ID, "filename", post.FileSize) | |
| 430 | - | parsedText, err := shared.ParseText(post.Text) | |
| 431 | - | if err != nil { | |
| 432 | - | logger.Error("find post with slug", "err", err.Error()) | |
| 433 | - | } | |
| 431 | + | ext := filepath.Ext(post.Filename) | |
| 432 | + | contents := template.HTML("") | |
| 433 | + | tags := []string{} | |
| 434 | + | unlisted := false | |
| 435 | + | var list *shared.ListParsedText | |
| 436 | + | ||
| 437 | + | switch ext { | |
| 438 | + | case ".lxt": | |
| 439 | + | list = shared.ListParseText(post.Text) | |
| 440 | + | ||
| 441 | + | // if parsedText.Image != "" { | |
| 442 | + | // ogImage = parsedText.Image | |
| 443 | + | // } | |
| 444 | + | // | |
| 445 | + | // if parsedText.ImageCard != "" { | |
| 446 | + | // ogImageCard = parsedText.ImageCard | |
| 447 | + | // } | |
| 448 | + | tags = list.Tags | |
| 449 | + | ||
| 450 | + | if post.Hidden || post.PublishAt.After(time.Now()) { | |
| 451 | + | unlisted = true | |
| 452 | + | } | |
| 453 | + | case ".md": | |
| 454 | + | parsedText, err := shared.ParseText(post.Text) | |
| 455 | + | if err != nil { | |
| 456 | + | logger.Error("could not parse md text", "err", err.Error()) | |
| 457 | + | } | |
| 434 | 458 | ||
| 435 | - | if parsedText.Image != "" { | |
| 436 | - | ogImage = parsedText.Image | |
| 437 | - | } | |
| 459 | + | if parsedText.Image != "" { | |
| 460 | + | ogImage = parsedText.Image | |
| 461 | + | } | |
| 438 | 462 | ||
| 439 | - | if parsedText.ImageCard != "" { | |
| 440 | - | ogImageCard = parsedText.ImageCard | |
| 441 | - | } | |
| 463 | + | if parsedText.ImageCard != "" { | |
| 464 | + | ogImageCard = parsedText.ImageCard | |
| 465 | + | } | |
| 466 | + | tags = parsedText.Tags | |
| 442 | 467 | ||
| 443 | - | unlisted := false | |
| 444 | - | if post.Hidden || post.PublishAt.After(time.Now()) { | |
| 445 | - | unlisted = true | |
| 468 | + | if post.Hidden || post.PublishAt.After(time.Now()) { | |
| 469 | + | unlisted = true | |
| 470 | + | } | |
| 471 | + | contents = template.HTML(parsedText.Html) | |
| 446 | 472 | } | |
| 447 | 473 | ||
| 448 | 474 | data = PostPageData{ |
| ... | ... | @@ -459,10 +485,10 @@ func postHandler(w http.ResponseWriter, r *http.Request) { | |
| 459 | 485 | UpdatedAtISO: post.UpdatedAt.Format(time.RFC3339), | |
| 460 | 486 | Username: username, | |
| 461 | 487 | BlogName: blogName, | |
| 462 | - | Contents: template.HTML(parsedText.Html), | |
| 488 | + | Contents: contents, | |
| 463 | 489 | HasCSS: hasCSS, | |
| 464 | 490 | CssURL: template.URL(cfg.CssURL(username)), | |
| 465 | - | Tags: parsedText.Tags, | |
| 491 | + | Tags: tags, | |
| 466 | 492 | Image: template.URL(ogImage), | |
| 467 | 493 | ImageCard: ogImageCard, | |
| 468 | 494 | Favicon: template.URL(favicon), |
+1
-0
pkg/apps/prose/config.go
#
+51
-0
pkg/apps/prose/html/list.partial.tmpl
#
| ... | ... | @@ -0,0 +1,51 @@ | |
| 1 | + | {{define "list"}} | |
| 2 | + | {{$indent := 0}} | |
| 3 | + | {{$mod := 0}} | |
| 4 | + | <ul style="list-style-type: {{.ListType}};"> | |
| 5 | + | {{range .Items}} | |
| 6 | + | {{if lt $indent .Indent}} | |
| 7 | + | <ul style="list-style-type: {{$.ListType}};"> | |
| 8 | + | {{else if gt $indent .Indent}} | |
| 9 | + | ||
| 10 | + | {{$mod = minus $indent .Indent}} | |
| 11 | + | {{range $y := intRange 1 $mod}} | |
| 12 | + | </li></ul> | |
| 13 | + | {{end}} | |
| 14 | + | ||
| 15 | + | {{else}} | |
| 16 | + | </li> | |
| 17 | + | {{end}} | |
| 18 | + | {{$indent = .Indent}} | |
| 19 | + | ||
| 20 | + | {{if .IsText}} | |
| 21 | + | {{if .Value}} | |
| 22 | + | <li>{{.Value}} | |
| 23 | + | {{end}} | |
| 24 | + | {{end}} | |
| 25 | + | ||
| 26 | + | {{if .IsURL}} | |
| 27 | + | <li><a href="{{.URL}}">{{.Value}}</a> | |
| 28 | + | {{end}} | |
| 29 | + | ||
| 30 | + | {{if .IsImg}} | |
| 31 | + | <li><img src="{{.URL}}" alt="{{.Value}}" /> | |
| 32 | + | {{end}} | |
| 33 | + | ||
| 34 | + | {{if .IsBlock}} | |
| 35 | + | <li><blockquote>{{.Value}}</blockquote> | |
| 36 | + | {{end}} | |
| 37 | + | ||
| 38 | + | {{if .IsHeaderOne}} | |
| 39 | + | </ul><h2 class="text-xl font-bold">{{.Value}}</h2><ul style="list-style-type: {{$.ListType}};"> | |
| 40 | + | {{end}} | |
| 41 | + | ||
| 42 | + | {{if .IsHeaderTwo}} | |
| 43 | + | </ul><h3 class="text-lg font-bold">{{.Value}}</h3><ul style="list-style-type: {{$.ListType}};"> | |
| 44 | + | {{end}} | |
| 45 | + | ||
| 46 | + | {{if .IsPre}} | |
| 47 | + | <li><pre>{{.Value}}</pre> | |
| 48 | + | {{end}} | |
| 49 | + | {{end}} | |
| 50 | + | </ul> | |
| 51 | + | {{end}} |
+5
-1
pkg/apps/prose/html/post.page.tmpl
#
+43
-1
pkg/apps/prose/scp_hooks.go
#
| ... | ... | @@ -62,7 +63,29 @@ func (p *MarkdownHooks) FileValidate(s *pssh.SSHServerConnSession, data *filehan | |
| 62 | 63 | return true, nil | |
| 63 | 64 | } | |
| 64 | 65 | ||
| 65 | - | func (p *MarkdownHooks) FileMeta(s *pssh.SSHServerConnSession, data *filehandlers.PostMetaData) error { | |
| 66 | + | func (p *MarkdownHooks) metaLxt(data *filehandlers.PostMetaData) error { | |
| 67 | + | parsedText := shared.ListParseText(data.Text) | |
| 68 | + | ||
| 69 | + | if parsedText.Title == "" { | |
| 70 | + | data.Title = utils.ToUpper(data.Slug) | |
| 71 | + | } else { | |
| 72 | + | data.Title = parsedText.Title | |
| 73 | + | } | |
| 74 | + | ||
| 75 | + | data.Aliases = parsedText.Aliases | |
| 76 | + | data.Tags = parsedText.Tags | |
| 77 | + | data.Description = parsedText.Description | |
| 78 | + | ||
| 79 | + | if parsedText.PublishAt != nil && !parsedText.PublishAt.IsZero() { | |
| 80 | + | data.PublishAt = parsedText.PublishAt | |
| 81 | + | } | |
| 82 | + | ||
| 83 | + | isHiddenFilename := slices.Contains(p.Cfg.HiddenPosts, data.Filename) | |
| 84 | + | data.Hidden = parsedText.Hidden || isHiddenFilename | |
| 85 | + | return nil | |
| 86 | + | } | |
| 87 | + | ||
| 88 | + | func (p *MarkdownHooks) metaMd(data *filehandlers.PostMetaData) error { | |
| 66 | 89 | parsedText, err := shared.ParseText(data.Text) | |
| 67 | 90 | if err != nil { | |
| 68 | 91 | return fmt.Errorf("%s: %w", data.Filename, err) |
| ... | ... | @@ -84,6 +107,25 @@ func (p *MarkdownHooks) FileMeta(s *pssh.SSHServerConnSession, data *filehandler | |
| 84 | 107 | ||
| 85 | 108 | isHiddenFilename := slices.Contains(p.Cfg.HiddenPosts, data.Filename) | |
| 86 | 109 | data.Hidden = parsedText.Hidden || isHiddenFilename | |
| 110 | + | return nil | |
| 111 | + | } | |
| 112 | + | ||
| 113 | + | func (p *MarkdownHooks) FileMeta(s *pssh.SSHServerConnSession, data *filehandlers.PostMetaData) error { | |
| 114 | + | ext := filepath.Ext(data.Filename) | |
| 115 | + | switch ext { | |
| 116 | + | case ".lxt": | |
| 117 | + | err := p.metaLxt(data) | |
| 118 | + | if err != nil { | |
| 119 | + | return fmt.Errorf("%s: %w", data.Filename, err) | |
| 120 | + | } | |
| 121 | + | case ".md": | |
| 122 | + | err := p.metaMd(data) | |
| 123 | + | if err != nil { | |
| 124 | + | return fmt.Errorf("%s: %w", data.Filename, err) | |
| 125 | + | } | |
| 126 | + | default: | |
| 127 | + | return fmt.Errorf("%s: invalid filetype", data.Filename) | |
| 128 | + | } | |
| 87 | 129 | ||
| 88 | 130 | return nil | |
| 89 | 131 | } |
+1
-0
pkg/apps/prose/ssh.go
#
| ... | ... | @@ -55,6 +55,7 @@ func StartSshServer() { | |
| 55 | 55 | ".md": filehandlers.NewScpPostHandler(dbh, cfg, hooks), | |
| 56 | 56 | ".txt": filehandlers.NewScpPostHandler(dbh, cfg, hooks), | |
| 57 | 57 | ".css": filehandlers.NewScpPostHandler(dbh, cfg, hooks), | |
| 58 | + | ".lxt": filehandlers.NewScpPostHandler(dbh, cfg, hooks), | |
| 58 | 59 | "fallback": uploadimgs.NewUploadImgHandler(dbh, cfg, st), | |
| 59 | 60 | } | |
| 60 | 61 | handler := filehandlers.NewFileHandlerRouter(cfg, dbh, fileMap) |