+2
-122
api/tangled/cbor_gen.go
+2
-122
api/tangled/cbor_gen.go
···
5512
5512
}
5513
5513
5514
5514
cw := cbg.NewCborWriter(w)
5515
-
fieldCount := 7
5515
+
fieldCount := 6
5516
5516
5517
5517
if t.Body == nil {
5518
5518
fieldCount--
···
5642
5642
return err
5643
5643
}
5644
5644
5645
-
// t.IssueId (int64) (int64)
5646
-
if len("issueId") > 1000000 {
5647
-
return xerrors.Errorf("Value in field \"issueId\" was too long")
5648
-
}
5649
-
5650
-
if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("issueId"))); err != nil {
5651
-
return err
5652
-
}
5653
-
if _, err := cw.WriteString(string("issueId")); err != nil {
5654
-
return err
5655
-
}
5656
-
5657
-
if t.IssueId >= 0 {
5658
-
if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.IssueId)); err != nil {
5659
-
return err
5660
-
}
5661
-
} else {
5662
-
if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-t.IssueId-1)); err != nil {
5663
-
return err
5664
-
}
5665
-
}
5666
-
5667
5645
// t.CreatedAt (string) (string)
5668
5646
if len("createdAt") > 1000000 {
5669
5647
return xerrors.Errorf("Value in field \"createdAt\" was too long")
···
5795
5773
5796
5774
t.Title = string(sval)
5797
5775
}
5798
-
// t.IssueId (int64) (int64)
5799
-
case "issueId":
5800
-
{
5801
-
maj, extra, err := cr.ReadHeader()
5802
-
if err != nil {
5803
-
return err
5804
-
}
5805
-
var extraI int64
5806
-
switch maj {
5807
-
case cbg.MajUnsignedInt:
5808
-
extraI = int64(extra)
5809
-
if extraI < 0 {
5810
-
return fmt.Errorf("int64 positive overflow")
5811
-
}
5812
-
case cbg.MajNegativeInt:
5813
-
extraI = int64(extra)
5814
-
if extraI < 0 {
5815
-
return fmt.Errorf("int64 negative overflow")
5816
-
}
5817
-
extraI = -1 - extraI
5818
-
default:
5819
-
return fmt.Errorf("wrong type for int64 field: %d", maj)
5820
-
}
5821
-
5822
-
t.IssueId = int64(extraI)
5823
-
}
5824
5776
// t.CreatedAt (string) (string)
5825
5777
case "createdAt":
5826
5778
···
5850
5802
}
5851
5803
5852
5804
cw := cbg.NewCborWriter(w)
5853
-
fieldCount := 7
5854
-
5855
-
if t.CommentId == nil {
5856
-
fieldCount--
5857
-
}
5805
+
fieldCount := 6
5858
5806
5859
5807
if t.Owner == nil {
5860
5808
fieldCount--
···
5997
5945
}
5998
5946
}
5999
5947
6000
-
// t.CommentId (int64) (int64)
6001
-
if t.CommentId != nil {
6002
-
6003
-
if len("commentId") > 1000000 {
6004
-
return xerrors.Errorf("Value in field \"commentId\" was too long")
6005
-
}
6006
-
6007
-
if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("commentId"))); err != nil {
6008
-
return err
6009
-
}
6010
-
if _, err := cw.WriteString(string("commentId")); err != nil {
6011
-
return err
6012
-
}
6013
-
6014
-
if t.CommentId == nil {
6015
-
if _, err := cw.Write(cbg.CborNull); err != nil {
6016
-
return err
6017
-
}
6018
-
} else {
6019
-
if *t.CommentId >= 0 {
6020
-
if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(*t.CommentId)); err != nil {
6021
-
return err
6022
-
}
6023
-
} else {
6024
-
if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-*t.CommentId-1)); err != nil {
6025
-
return err
6026
-
}
6027
-
}
6028
-
}
6029
-
6030
-
}
6031
-
6032
5948
// t.CreatedAt (string) (string)
6033
5949
if len("createdAt") > 1000000 {
6034
5950
return xerrors.Errorf("Value in field \"createdAt\" was too long")
···
6168
6084
}
6169
6085
6170
6086
t.Owner = (*string)(&sval)
6171
-
}
6172
-
}
6173
-
// t.CommentId (int64) (int64)
6174
-
case "commentId":
6175
-
{
6176
-
6177
-
b, err := cr.ReadByte()
6178
-
if err != nil {
6179
-
return err
6180
-
}
6181
-
if b != cbg.CborNull[0] {
6182
-
if err := cr.UnreadByte(); err != nil {
6183
-
return err
6184
-
}
6185
-
maj, extra, err := cr.ReadHeader()
6186
-
if err != nil {
6187
-
return err
6188
-
}
6189
-
var extraI int64
6190
-
switch maj {
6191
-
case cbg.MajUnsignedInt:
6192
-
extraI = int64(extra)
6193
-
if extraI < 0 {
6194
-
return fmt.Errorf("int64 positive overflow")
6195
-
}
6196
-
case cbg.MajNegativeInt:
6197
-
extraI = int64(extra)
6198
-
if extraI < 0 {
6199
-
return fmt.Errorf("int64 negative overflow")
6200
-
}
6201
-
extraI = -1 - extraI
6202
-
default:
6203
-
return fmt.Errorf("wrong type for int64 field: %d", maj)
6204
-
}
6205
-
6206
-
t.CommentId = (*int64)(&extraI)
6207
6087
}
6208
6088
}
6209
6089
// t.CreatedAt (string) (string)
-1
api/tangled/issuecomment.go
-1
api/tangled/issuecomment.go
···
19
19
type RepoIssueComment struct {
20
20
LexiconTypeID string `json:"$type,const=sh.tangled.repo.issue.comment" cborgen:"$type,const=sh.tangled.repo.issue.comment"`
21
21
Body string `json:"body" cborgen:"body"`
22
-
CommentId *int64 `json:"commentId,omitempty" cborgen:"commentId,omitempty"`
23
22
CreatedAt string `json:"createdAt" cborgen:"createdAt"`
24
23
Issue string `json:"issue" cborgen:"issue"`
25
24
Owner *string `json:"owner,omitempty" cborgen:"owner,omitempty"`
-1
api/tangled/repoissue.go
-1
api/tangled/repoissue.go
···
20
20
LexiconTypeID string `json:"$type,const=sh.tangled.repo.issue" cborgen:"$type,const=sh.tangled.repo.issue"`
21
21
Body *string `json:"body,omitempty" cborgen:"body,omitempty"`
22
22
CreatedAt string `json:"createdAt" cborgen:"createdAt"`
23
-
IssueId int64 `json:"issueId" cborgen:"issueId"`
24
23
Owner string `json:"owner" cborgen:"owner"`
25
24
Repo string `json:"repo" cborgen:"repo"`
26
25
Title string `json:"title" cborgen:"title"`
+105
appview/db/issues.go
+105
appview/db/issues.go
···
3
3
import (
4
4
"database/sql"
5
5
"fmt"
6
+
mathrand "math/rand/v2"
6
7
"strings"
7
8
"time"
8
9
···
47
48
48
49
func (i *Issue) AtUri() syntax.ATURI {
49
50
return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", i.OwnerDid, tangled.RepoIssueNSID, i.Rkey))
51
+
}
52
+
53
+
func IssueFromRecord(did, rkey string, record tangled.RepoIssue) Issue {
54
+
created, err := time.Parse(time.RFC3339, record.CreatedAt)
55
+
if err != nil {
56
+
created = time.Now()
57
+
}
58
+
59
+
body := ""
60
+
if record.Body != nil {
61
+
body = *record.Body
62
+
}
63
+
64
+
return Issue{
65
+
RepoAt: syntax.ATURI(record.Repo),
66
+
OwnerDid: record.Owner,
67
+
Rkey: rkey,
68
+
Created: created,
69
+
Title: record.Title,
70
+
Body: body,
71
+
Open: true, // new issues are open by default
72
+
}
73
+
}
74
+
75
+
func ResolveIssueFromAtUri(e Execer, issueUri syntax.ATURI) (syntax.ATURI, int, error) {
76
+
ownerDid := issueUri.Authority().String()
77
+
issueRkey := issueUri.RecordKey().String()
78
+
79
+
var repoAt string
80
+
var issueId int
81
+
82
+
query := `select repo_at, issue_id from issues where owner_did = ? and rkey = ?`
83
+
err := e.QueryRow(query, ownerDid, issueRkey).Scan(&repoAt, &issueId)
84
+
if err != nil {
85
+
return "", 0, err
86
+
}
87
+
88
+
return syntax.ATURI(repoAt), issueId, nil
89
+
}
90
+
91
+
func IssueCommentFromRecord(e Execer, did, rkey string, record tangled.RepoIssueComment) (Comment, error) {
92
+
created, err := time.Parse(time.RFC3339, record.CreatedAt)
93
+
if err != nil {
94
+
created = time.Now()
95
+
}
96
+
97
+
ownerDid := did
98
+
if record.Owner != nil {
99
+
ownerDid = *record.Owner
100
+
}
101
+
102
+
issueUri, err := syntax.ParseATURI(record.Issue)
103
+
if err != nil {
104
+
return Comment{}, err
105
+
}
106
+
107
+
repoAt, issueId, err := ResolveIssueFromAtUri(e, issueUri)
108
+
if err != nil {
109
+
return Comment{}, err
110
+
}
111
+
112
+
comment := Comment{
113
+
OwnerDid: ownerDid,
114
+
RepoAt: repoAt,
115
+
Rkey: rkey,
116
+
Body: record.Body,
117
+
Issue: issueId,
118
+
CommentId: mathrand.IntN(1000000),
119
+
Created: &created,
120
+
}
121
+
122
+
return comment, nil
50
123
}
51
124
52
125
func NewIssue(tx *sql.Tx, issue *Issue) error {
···
550
623
deleted = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
551
624
where repo_at = ? and issue_id = ? and comment_id = ?
552
625
`, repoAt, issueId, commentId)
626
+
return err
627
+
}
628
+
629
+
func UpdateCommentByRkey(e Execer, ownerDid, rkey, newBody string) error {
630
+
_, err := e.Exec(
631
+
`
632
+
update comments
633
+
set body = ?,
634
+
edited = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
635
+
where owner_did = ? and rkey = ?
636
+
`, newBody, ownerDid, rkey)
637
+
return err
638
+
}
639
+
640
+
func DeleteCommentByRkey(e Execer, ownerDid, rkey string) error {
641
+
_, err := e.Exec(
642
+
`
643
+
update comments
644
+
set body = "",
645
+
deleted = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
646
+
where owner_did = ? and rkey = ?
647
+
`, ownerDid, rkey)
648
+
return err
649
+
}
650
+
651
+
func UpdateIssueByRkey(e Execer, ownerDid, rkey, title, body string) error {
652
+
_, err := e.Exec(`update issues set title = ?, body = ? where owner_did = ? and rkey = ?`, title, body, ownerDid, rkey)
653
+
return err
654
+
}
655
+
656
+
func DeleteIssueByRkey(e Execer, ownerDid, rkey string) error {
657
+
_, err := e.Exec(`delete from issues where owner_did = ? and rkey = ?`, ownerDid, rkey)
553
658
return err
554
659
}
555
660
+179
-6
appview/ingester.go
+179
-6
appview/ingester.go
···
5
5
"encoding/json"
6
6
"fmt"
7
7
"log/slog"
8
+
"strings"
8
9
"time"
9
10
10
11
"github.com/bluesky-social/indigo/atproto/syntax"
···
14
15
"tangled.sh/tangled.sh/core/api/tangled"
15
16
"tangled.sh/tangled.sh/core/appview/config"
16
17
"tangled.sh/tangled.sh/core/appview/db"
18
+
"tangled.sh/tangled.sh/core/appview/pages/markup"
17
19
"tangled.sh/tangled.sh/core/appview/spindleverify"
18
20
"tangled.sh/tangled.sh/core/idresolver"
19
21
"tangled.sh/tangled.sh/core/rbac"
···
61
63
case tangled.ActorProfileNSID:
62
64
err = i.ingestProfile(e)
63
65
case tangled.SpindleMemberNSID:
64
-
err = i.ingestSpindleMember(e)
66
+
err = i.ingestSpindleMember(ctx, e)
65
67
case tangled.SpindleNSID:
66
-
err = i.ingestSpindle(e)
68
+
err = i.ingestSpindle(ctx, e)
67
69
case tangled.StringNSID:
68
70
err = i.ingestString(e)
71
+
case tangled.RepoIssueNSID:
72
+
err = i.ingestIssue(ctx, e)
73
+
case tangled.RepoIssueCommentNSID:
74
+
err = i.ingestIssueComment(e)
69
75
}
70
76
l = i.Logger.With("nsid", e.Commit.Collection)
71
77
}
···
336
342
return nil
337
343
}
338
344
339
-
func (i *Ingester) ingestSpindleMember(e *models.Event) error {
345
+
func (i *Ingester) ingestSpindleMember(ctx context.Context, e *models.Event) error {
340
346
did := e.Did
341
347
var err error
342
348
···
359
365
return fmt.Errorf("failed to enforce permissions: %w", err)
360
366
}
361
367
362
-
memberId, err := i.IdResolver.ResolveIdent(context.Background(), record.Subject)
368
+
memberId, err := i.IdResolver.ResolveIdent(ctx, record.Subject)
363
369
if err != nil {
364
370
return err
365
371
}
···
442
448
return nil
443
449
}
444
450
445
-
func (i *Ingester) ingestSpindle(e *models.Event) error {
451
+
func (i *Ingester) ingestSpindle(ctx context.Context, e *models.Event) error {
446
452
did := e.Did
447
453
var err error
448
454
···
475
481
return err
476
482
}
477
483
478
-
err = spindleverify.RunVerification(context.Background(), instance, did, i.Config.Core.Dev)
484
+
err = spindleverify.RunVerification(ctx, instance, did, i.Config.Core.Dev)
479
485
if err != nil {
480
486
l.Error("failed to add spindle to db", "err", err, "instance", instance)
481
487
return err
···
609
615
610
616
return nil
611
617
}
618
+
619
+
func (i *Ingester) ingestIssue(ctx context.Context, e *models.Event) error {
620
+
did := e.Did
621
+
rkey := e.Commit.RKey
622
+
623
+
var err error
624
+
625
+
l := i.Logger.With("handler", "ingestIssue", "nsid", e.Commit.Collection, "did", did, "rkey", rkey)
626
+
l.Info("ingesting record")
627
+
628
+
ddb, ok := i.Db.Execer.(*db.DB)
629
+
if !ok {
630
+
return fmt.Errorf("failed to index issue record, invalid db cast")
631
+
}
632
+
633
+
switch e.Commit.Operation {
634
+
case models.CommitOperationCreate:
635
+
raw := json.RawMessage(e.Commit.Record)
636
+
record := tangled.RepoIssue{}
637
+
err = json.Unmarshal(raw, &record)
638
+
if err != nil {
639
+
l.Error("invalid record", "err", err)
640
+
return err
641
+
}
642
+
643
+
issue := db.IssueFromRecord(did, rkey, record)
644
+
645
+
sanitizer := markup.NewSanitizer()
646
+
if st := strings.TrimSpace(sanitizer.SanitizeDescription(issue.Title)); st == "" {
647
+
return fmt.Errorf("title is empty after HTML sanitization")
648
+
}
649
+
if sb := strings.TrimSpace(sanitizer.SanitizeDefault(issue.Body)); sb == "" {
650
+
return fmt.Errorf("body is empty after HTML sanitization")
651
+
}
652
+
653
+
tx, err := ddb.BeginTx(ctx, nil)
654
+
if err != nil {
655
+
l.Error("failed to begin transaction", "err", err)
656
+
return err
657
+
}
658
+
659
+
err = db.NewIssue(tx, &issue)
660
+
if err != nil {
661
+
l.Error("failed to create issue", "err", err)
662
+
return err
663
+
}
664
+
665
+
return nil
666
+
667
+
case models.CommitOperationUpdate:
668
+
raw := json.RawMessage(e.Commit.Record)
669
+
record := tangled.RepoIssue{}
670
+
err = json.Unmarshal(raw, &record)
671
+
if err != nil {
672
+
l.Error("invalid record", "err", err)
673
+
return err
674
+
}
675
+
676
+
body := ""
677
+
if record.Body != nil {
678
+
body = *record.Body
679
+
}
680
+
681
+
sanitizer := markup.NewSanitizer()
682
+
if st := strings.TrimSpace(sanitizer.SanitizeDescription(record.Title)); st == "" {
683
+
return fmt.Errorf("title is empty after HTML sanitization")
684
+
}
685
+
if sb := strings.TrimSpace(sanitizer.SanitizeDefault(body)); sb == "" {
686
+
return fmt.Errorf("body is empty after HTML sanitization")
687
+
}
688
+
689
+
err = db.UpdateIssueByRkey(ddb, did, rkey, record.Title, body)
690
+
if err != nil {
691
+
l.Error("failed to update issue", "err", err)
692
+
return err
693
+
}
694
+
695
+
return nil
696
+
697
+
case models.CommitOperationDelete:
698
+
if err := db.DeleteIssueByRkey(ddb, did, rkey); err != nil {
699
+
l.Error("failed to delete", "err", err)
700
+
return fmt.Errorf("failed to delete issue record: %w", err)
701
+
}
702
+
703
+
return nil
704
+
}
705
+
706
+
return fmt.Errorf("unknown operation: %s", e.Commit.Operation)
707
+
}
708
+
709
+
func (i *Ingester) ingestIssueComment(e *models.Event) error {
710
+
did := e.Did
711
+
rkey := e.Commit.RKey
712
+
713
+
var err error
714
+
715
+
l := i.Logger.With("handler", "ingestIssueComment", "nsid", e.Commit.Collection, "did", did, "rkey", rkey)
716
+
l.Info("ingesting record")
717
+
718
+
ddb, ok := i.Db.Execer.(*db.DB)
719
+
if !ok {
720
+
return fmt.Errorf("failed to index issue comment record, invalid db cast")
721
+
}
722
+
723
+
switch e.Commit.Operation {
724
+
case models.CommitOperationCreate:
725
+
raw := json.RawMessage(e.Commit.Record)
726
+
record := tangled.RepoIssueComment{}
727
+
err = json.Unmarshal(raw, &record)
728
+
if err != nil {
729
+
l.Error("invalid record", "err", err)
730
+
return err
731
+
}
732
+
733
+
comment, err := db.IssueCommentFromRecord(ddb, did, rkey, record)
734
+
if err != nil {
735
+
l.Error("failed to parse comment from record", "err", err)
736
+
return err
737
+
}
738
+
739
+
sanitizer := markup.NewSanitizer()
740
+
if sb := strings.TrimSpace(sanitizer.SanitizeDefault(comment.Body)); sb == "" {
741
+
return fmt.Errorf("body is empty after HTML sanitization")
742
+
}
743
+
744
+
err = db.NewIssueComment(ddb, &comment)
745
+
if err != nil {
746
+
l.Error("failed to create issue comment", "err", err)
747
+
return err
748
+
}
749
+
750
+
return nil
751
+
752
+
case models.CommitOperationUpdate:
753
+
raw := json.RawMessage(e.Commit.Record)
754
+
record := tangled.RepoIssueComment{}
755
+
err = json.Unmarshal(raw, &record)
756
+
if err != nil {
757
+
l.Error("invalid record", "err", err)
758
+
return err
759
+
}
760
+
761
+
sanitizer := markup.NewSanitizer()
762
+
if sb := strings.TrimSpace(sanitizer.SanitizeDefault(record.Body)); sb == "" {
763
+
return fmt.Errorf("body is empty after HTML sanitization")
764
+
}
765
+
766
+
err = db.UpdateCommentByRkey(ddb, did, rkey, record.Body)
767
+
if err != nil {
768
+
l.Error("failed to update issue comment", "err", err)
769
+
return err
770
+
}
771
+
772
+
return nil
773
+
774
+
case models.CommitOperationDelete:
775
+
if err := db.DeleteCommentByRkey(ddb, did, rkey); err != nil {
776
+
l.Error("failed to delete", "err", err)
777
+
return fmt.Errorf("failed to delete issue comment record: %w", err)
778
+
}
779
+
780
+
return nil
781
+
}
782
+
783
+
return fmt.Errorf("unknown operation: %s", e.Commit.Operation)
784
+
}
+4
-9
appview/issues/issues.go
+4
-9
appview/issues/issues.go
···
278
278
}
279
279
280
280
createdAt := time.Now().Format(time.RFC3339)
281
-
commentIdInt64 := int64(commentId)
282
281
ownerDid := user.Did
283
282
issueAt, err := db.GetIssueAt(rp.db, f.RepoAt(), issueIdInt)
284
283
if err != nil {
···
302
301
Val: &tangled.RepoIssueComment{
303
302
Repo: &atUri,
304
303
Issue: issueAt,
305
-
CommentId: &commentIdInt64,
306
304
Owner: &ownerDid,
307
305
Body: body,
308
306
CreatedAt: createdAt,
···
451
449
repoAt := record["repo"].(string)
452
450
issueAt := record["issue"].(string)
453
451
createdAt := record["createdAt"].(string)
454
-
commentIdInt64 := int64(commentIdInt)
455
452
456
453
_, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{
457
454
Collection: tangled.RepoIssueCommentNSID,
···
462
459
Val: &tangled.RepoIssueComment{
463
460
Repo: &repoAt,
464
461
Issue: issueAt,
465
-
CommentId: &commentIdInt64,
466
462
Owner: &comment.OwnerDid,
467
463
Body: newBody,
468
464
CreatedAt: createdAt,
···
687
683
Rkey: issue.Rkey,
688
684
Record: &lexutil.LexiconTypeDecoder{
689
685
Val: &tangled.RepoIssue{
690
-
Repo: atUri,
691
-
Title: title,
692
-
Body: &body,
693
-
Owner: user.Did,
694
-
IssueId: int64(issue.IssueId),
686
+
Repo: atUri,
687
+
Title: title,
688
+
Body: &body,
689
+
Owner: user.Did,
695
690
},
696
691
},
697
692
})
+14
-22
appview/pages/templates/repo/index.html
+14
-22
appview/pages/templates/repo/index.html
···
356
356
357
357
{{ define "repoAfter" }}
358
358
{{- if or .HTMLReadme .Readme -}}
359
-
<div class="mt-4 rounded bg-white dark:bg-gray-800 drop-shadow-sm w-full mx-auto overflow-hidden">
360
-
{{- if .ReadmeFileName -}}
361
-
<div class="px-4 py-2 bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600 flex items-center gap-2">
362
-
{{ i "file-text" "w-4 h-4" "text-gray-600 dark:text-gray-400" }}
363
-
<span class="font-mono text-sm text-gray-800 dark:text-gray-200">{{ .ReadmeFileName }}</span>
364
-
</div>
365
-
{{- end -}}
366
-
<section
367
-
class="p-6 overflow-auto {{ if not .Raw }}
368
-
prose dark:prose-invert dark:[&_pre]:bg-gray-900
369
-
dark:[&_code]:text-gray-300 dark:[&_pre_code]:bg-gray-900
370
-
dark:[&_pre]:border dark:[&_pre]:border-gray-700
371
-
{{ end }}"
372
-
>
373
-
<article class="{{ if .Raw }}whitespace-pre{{ end }}">{{- if .Raw -}}<pre class="dark:bg-gray-800 dark:text-white overflow-x-auto">
374
-
{{- .Readme -}}
375
-
</pre>
376
-
{{- else -}}
377
-
{{ .HTMLReadme }}
378
-
{{- end -}}</article>
379
-
</section>
380
-
</div>
359
+
<section
360
+
class="p-6 mt-4 rounded-br rounded-bl bg-white dark:bg-gray-800 dark:text-white drop-shadow-sm w-full mx-auto overflow-auto {{ if not .Raw }}
361
+
prose dark:prose-invert dark:[&_pre]:bg-gray-900
362
+
dark:[&_code]:text-gray-300 dark:[&_pre_code]:bg-gray-900
363
+
dark:[&_pre]:border dark:[&_pre]:border-gray-700
364
+
{{ end }}"
365
+
>
366
+
<article class="{{ if .Raw }}whitespace-pre{{ end }}">{{- if .Raw -}}<pre class="dark:bg-gray-800 dark:text-white overflow-x-auto">
367
+
{{- .Readme -}}
368
+
</pre>
369
+
{{- else -}}
370
+
{{ .HTMLReadme }}
371
+
{{- end -}}</article>
372
+
</section>
381
373
{{- end -}}
382
374
{{ end }}
+2
appview/state/state.go
+2
appview/state/state.go
+1
-8
lexicons/issue/comment.json
+1
-8
lexicons/issue/comment.json
···
9
9
"key": "tid",
10
10
"record": {
11
11
"type": "object",
12
-
"required": [
13
-
"issue",
14
-
"body",
15
-
"createdAt"
16
-
],
12
+
"required": ["issue", "body", "createdAt"],
17
13
"properties": {
18
14
"issue": {
19
15
"type": "string",
···
22
18
"repo": {
23
19
"type": "string",
24
20
"format": "at-uri"
25
-
},
26
-
"commentId": {
27
-
"type": "integer"
28
21
},
29
22
"owner": {
30
23
"type": "string",
+1
-10
lexicons/issue/issue.json
+1
-10
lexicons/issue/issue.json
···
9
9
"key": "tid",
10
10
"record": {
11
11
"type": "object",
12
-
"required": [
13
-
"repo",
14
-
"issueId",
15
-
"owner",
16
-
"title",
17
-
"createdAt"
18
-
],
12
+
"required": ["repo", "owner", "title", "createdAt"],
19
13
"properties": {
20
14
"repo": {
21
15
"type": "string",
22
16
"format": "at-uri"
23
-
},
24
-
"issueId": {
25
-
"type": "integer"
26
17
},
27
18
"owner": {
28
19
"type": "string",