Yōten: A social platform for tracking the essential points of your language learning yoten.app
0

Configure Feed

Select the types of activity you want to include in your feed.

feat: update comment lexicon to support facet

Signed-off-by: brookjeynes <me@brookjeynes.dev>

author
brookjeynes
date (May 21, 2026, 3:23 PM +1000) commit 2f9f0ef5 parent 327a9fa0 change-id kkvxslkx
+198 -3
+83 -1
api/yoten/cbor_gen.go
··· 1788 1788 } 1789 1789 1790 1790 cw := cbg.NewCborWriter(w) 1791 - fieldCount := 5 1791 + fieldCount := 6 1792 + 1793 + if t.Facets == nil { 1794 + fieldCount-- 1795 + } 1792 1796 1793 1797 if t.Reply == nil { 1794 1798 fieldCount-- ··· 1856 1860 1857 1861 if err := t.Reply.MarshalCBOR(cw); err != nil { 1858 1862 return err 1863 + } 1864 + } 1865 + 1866 + // t.Facets ([]*yoten.RichtextFacet) (slice) 1867 + if t.Facets != nil { 1868 + 1869 + if len("facets") > 1000000 { 1870 + return xerrors.Errorf("Value in field \"facets\" was too long") 1871 + } 1872 + 1873 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("facets"))); err != nil { 1874 + return err 1875 + } 1876 + if _, err := cw.WriteString(string("facets")); err != nil { 1877 + return err 1878 + } 1879 + 1880 + if len(t.Facets) > 8192 { 1881 + return xerrors.Errorf("Slice value in field t.Facets was too long") 1882 + } 1883 + 1884 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Facets))); err != nil { 1885 + return err 1886 + } 1887 + for _, v := range t.Facets { 1888 + if err := v.MarshalCBOR(cw); err != nil { 1889 + return err 1890 + } 1891 + 1859 1892 } 1860 1893 } 1861 1894 ··· 1989 2022 } 1990 2023 } 1991 2024 2025 + } 2026 + // t.Facets ([]*yoten.RichtextFacet) (slice) 2027 + case "facets": 2028 + 2029 + maj, extra, err = cr.ReadHeader() 2030 + if err != nil { 2031 + return err 2032 + } 2033 + 2034 + if extra > 8192 { 2035 + return fmt.Errorf("t.Facets: array too large (%d)", extra) 2036 + } 2037 + 2038 + if maj != cbg.MajArray { 2039 + return fmt.Errorf("expected cbor array") 2040 + } 2041 + 2042 + if extra > 0 { 2043 + t.Facets = make([]*RichtextFacet, extra) 2044 + } 2045 + 2046 + for i := 0; i < int(extra); i++ { 2047 + { 2048 + var maj byte 2049 + var extra uint64 2050 + var err error 2051 + _ = maj 2052 + _ = extra 2053 + _ = err 2054 + 2055 + { 2056 + 2057 + b, err := cr.ReadByte() 2058 + if err != nil { 2059 + return err 2060 + } 2061 + if b != cbg.CborNull[0] { 2062 + if err := cr.UnreadByte(); err != nil { 2063 + return err 2064 + } 2065 + t.Facets[i] = new(RichtextFacet) 2066 + if err := t.Facets[i].UnmarshalCBOR(cr); err != nil { 2067 + return xerrors.Errorf("unmarshaling t.Facets[i] pointer: %w", err) 2068 + } 2069 + } 2070 + 2071 + } 2072 + 2073 + } 1992 2074 } 1993 2075 // t.Subject (string) (string) 1994 2076 case "subject":
+2
api/yoten/feedcomment.go
··· 20 20 LexiconTypeID string `json:"$type,const=app.yoten.feed.comment" cborgen:"$type,const=app.yoten.feed.comment"` 21 21 Body string `json:"body" cborgen:"body"` 22 22 CreatedAt string `json:"createdAt" cborgen:"createdAt"` 23 + // facets: Annotations of text (mentions, URLs, hashtags, etc) 24 + Facets []*RichtextFacet `json:"facets,omitempty" cborgen:"facets,omitempty"` 23 25 // reply: Indicates that this comment is a reply to another comment. 24 26 Reply *FeedComment_Reply `json:"reply,omitempty" cborgen:"reply,omitempty"` 25 27 // subject: A reference to the study session being commented on.
+69
api/yoten/richtextfacet.go
··· 1 + // Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT. 2 + 3 + package yoten 4 + 5 + // schema: app.yoten.richtext.facet 6 + 7 + import ( 8 + "encoding/json" 9 + "fmt" 10 + 11 + "github.com/bluesky-social/indigo/lex/util" 12 + ) 13 + 14 + const ( 15 + RichtextFacetNSID = "app.yoten.richtext.facet" 16 + ) 17 + 18 + // RichtextFacet is a "main" in the app.yoten.richtext.facet schema. 19 + // 20 + // Annotation of a sub-string within rich text. 21 + type RichtextFacet struct { 22 + Features []*RichtextFacet_Features_Elem `json:"features" cborgen:"features"` 23 + Index *RichtextFacet_ByteSlice `json:"index" cborgen:"index"` 24 + } 25 + 26 + // RichtextFacet_ByteSlice is a "byteSlice" in the app.yoten.richtext.facet schema. 27 + // 28 + // Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets. 29 + type RichtextFacet_ByteSlice struct { 30 + ByteEnd int64 `json:"byteEnd" cborgen:"byteEnd"` 31 + ByteStart int64 `json:"byteStart" cborgen:"byteStart"` 32 + } 33 + 34 + type RichtextFacet_Features_Elem struct { 35 + RichtextFacet_Mention *RichtextFacet_Mention 36 + } 37 + 38 + func (t *RichtextFacet_Features_Elem) MarshalJSON() ([]byte, error) { 39 + if t.RichtextFacet_Mention != nil { 40 + t.RichtextFacet_Mention.LexiconTypeID = "app.yoten.richtext.facet#mention" 41 + return json.Marshal(t.RichtextFacet_Mention) 42 + } 43 + return nil, fmt.Errorf("cannot marshal empty enum") 44 + } 45 + func (t *RichtextFacet_Features_Elem) UnmarshalJSON(b []byte) error { 46 + typ, err := util.TypeExtract(b) 47 + if err != nil { 48 + return err 49 + } 50 + 51 + switch typ { 52 + case "app.yoten.richtext.facet#mention": 53 + t.RichtextFacet_Mention = new(RichtextFacet_Mention) 54 + return json.Unmarshal(b, t.RichtextFacet_Mention) 55 + 56 + default: 57 + return nil 58 + } 59 + } 60 + 61 + // RichtextFacet_Mention is a "mention" in the app.yoten.richtext.facet schema. 62 + // 63 + // Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID. 64 + // 65 + // RECORDTYPE: RichtextFacet_Mention 66 + type RichtextFacet_Mention struct { 67 + LexiconTypeID string `json:"$type,const=app.yoten.richtext.facet#mention" cborgen:"$type,const=app.yoten.richtext.facet#mention"` 68 + Did string `json:"did" cborgen:"did"` 69 + }
+2 -2
generate-lexicons.sh
··· 3 3 # https://tangled.org/oppi.li/indigo 4 4 # generates NSID consts for types 5 5 6 - rm -f api/yoten/* 6 + rm -rf api/yoten/* 7 7 8 8 /usr/bin/lexgen \ 9 9 --build-file lexicon-build-config.json \ ··· 13 13 14 14 # lexgen generates incomplete Marshaler/Unmarshaler for union types 15 15 find api/yoten/*.go -not -name "cbor_gen.go" -exec \ 16 - sed -i '/^func.*\(MarshalCBOR\|MarshalJSON\|UnmarshalJSON\|UnmarshalCBOR\)/,/^}/ s/^/\/\/ /' {} + 16 + sed -i '/^func.*\(MarshalCBOR\|UnmarshalCBOR\)/,/^}/ s/^/\/\/ /' {} + 17 17 18 18 goimports -w api/yoten/* 19 19
+5
lexicons/feed/comment.json
··· 39 39 } 40 40 } 41 41 }, 42 + "facets": { 43 + "type": "array", 44 + "description": "Annotations of text (mentions, URLs, hashtags, etc)", 45 + "items": { "type": "ref", "ref": "app.yoten.richtext.facet" } 46 + }, 42 47 "createdAt": { 43 48 "type": "string", 44 49 "format": "datetime"
+37
lexicons/richtext/facet.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.yoten.richtext.facet", 4 + "needsCbor": true, 5 + "needsType": true, 6 + "defs": { 7 + "main": { 8 + "type": "object", 9 + "description": "Annotation of a sub-string within rich text.", 10 + "required": ["index", "features"], 11 + "properties": { 12 + "index": { "type": "ref", "ref": "#byteSlice" }, 13 + "features": { 14 + "type": "array", 15 + "items": { "type": "union", "refs": ["#mention"] } 16 + } 17 + } 18 + }, 19 + "mention": { 20 + "type": "object", 21 + "description": "Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID.", 22 + "required": ["did"], 23 + "properties": { 24 + "did": { "type": "string", "format": "did" } 25 + } 26 + }, 27 + "byteSlice": { 28 + "type": "object", 29 + "description": "Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets.", 30 + "required": ["byteStart", "byteEnd"], 31 + "properties": { 32 + "byteStart": { "type": "integer", "minimum": 0 }, 33 + "byteEnd": { "type": "integer", "minimum": 0 } 34 + } 35 + } 36 + } 37 + }