diff --git a/go.mod b/go.mod index 7285b2939..9f86a0b0e 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/spf13/cobra v1.10.1 github.com/spf13/viper v1.20.1 github.com/stretchr/testify v1.10.0 - github.com/usememos/gomark v0.0.0-20251021153759-00d1ea6c86f0 + github.com/yuin/goldmark v1.7.13 golang.org/x/crypto v0.42.0 golang.org/x/mod v0.28.0 golang.org/x/net v0.43.0 diff --git a/go.sum b/go.sum index 2c63c9f29..d2a9b2c8d 100644 --- a/go.sum +++ b/go.sum @@ -433,8 +433,6 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/usememos/gomark v0.0.0-20251021153759-00d1ea6c86f0 h1:hN+LjlPdqd/6OLYWs5mYYwJ6WUQBKBUreCt1Kg8u5jk= -github.com/usememos/gomark v0.0.0-20251021153759-00d1ea6c86f0/go.mod h1:7CZRoYFQyyljzplOTeyODFR26O+wr0BbnpTWVLGfKJA= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= @@ -442,6 +440,8 @@ github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= +github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= diff --git a/plugin/markdown/ast/tag.go b/plugin/markdown/ast/tag.go new file mode 100644 index 000000000..75b0e3e0b --- /dev/null +++ b/plugin/markdown/ast/tag.go @@ -0,0 +1,28 @@ +package ast + +import ( + gast "github.com/yuin/goldmark/ast" +) + +// TagNode represents a #tag in the markdown AST. +type TagNode struct { + gast.BaseInline + + // Tag name without the # prefix + Tag []byte +} + +// KindTag is the NodeKind for TagNode. +var KindTag = gast.NewNodeKind("Tag") + +// Kind returns KindTag. +func (*TagNode) Kind() gast.NodeKind { + return KindTag +} + +// Dump implements Node.Dump for debugging. +func (n *TagNode) Dump(source []byte, level int) { + gast.DumpHelper(n, source, level, map[string]string{ + "Tag": string(n.Tag), + }, nil) +} diff --git a/plugin/markdown/ast/wikilink.go b/plugin/markdown/ast/wikilink.go new file mode 100644 index 000000000..3cbead1ec --- /dev/null +++ b/plugin/markdown/ast/wikilink.go @@ -0,0 +1,32 @@ +package ast + +import ( + gast "github.com/yuin/goldmark/ast" +) + +// WikilinkNode represents [[target]] or [[target?params]] syntax. +type WikilinkNode struct { + gast.BaseInline + + // Target is the link destination (e.g., "memos/1", "Hello world", "resources/101") + Target []byte + + // Params are optional parameters (e.g., "align=center" from [[target?align=center]]) + Params []byte +} + +// KindWikilink is the NodeKind for WikilinkNode. +var KindWikilink = gast.NewNodeKind("Wikilink") + +// Kind returns KindWikilink. +func (*WikilinkNode) Kind() gast.NodeKind { + return KindWikilink +} + +// Dump implements Node.Dump for debugging. +func (n *WikilinkNode) Dump(source []byte, level int) { + gast.DumpHelper(n, source, level, map[string]string{ + "Target": string(n.Target), + "Params": string(n.Params), + }, nil) +} diff --git a/plugin/markdown/extensions/tag.go b/plugin/markdown/extensions/tag.go new file mode 100644 index 000000000..572f1ce31 --- /dev/null +++ b/plugin/markdown/extensions/tag.go @@ -0,0 +1,24 @@ +package extensions + +import ( + "github.com/yuin/goldmark" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/util" + + mparser "github.com/usememos/memos/plugin/markdown/parser" +) + +type tagExtension struct{} + +// TagExtension is a goldmark extension for #tag syntax +var TagExtension = &tagExtension{} + +// Extend extends the goldmark parser with tag support. +func (*tagExtension) Extend(m goldmark.Markdown) { + m.Parser().AddOptions( + parser.WithInlineParsers( + // Priority 200 - run before standard link parser (500) + util.Prioritized(mparser.NewTagParser(), 200), + ), + ) +} diff --git a/plugin/markdown/extensions/wikilink.go b/plugin/markdown/extensions/wikilink.go new file mode 100644 index 000000000..8d7bb046c --- /dev/null +++ b/plugin/markdown/extensions/wikilink.go @@ -0,0 +1,24 @@ +package extensions + +import ( + "github.com/yuin/goldmark" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/util" + + mparser "github.com/usememos/memos/plugin/markdown/parser" +) + +type wikilinkExtension struct{} + +// WikilinkExtension is a goldmark extension for [[...]] wikilink syntax +var WikilinkExtension = &wikilinkExtension{} + +// Extend extends the goldmark parser with wikilink support. +func (*wikilinkExtension) Extend(m goldmark.Markdown) { + m.Parser().AddOptions( + parser.WithInlineParsers( + // Priority 199 - run before standard link parser (500) but after tags (200) + util.Prioritized(mparser.NewWikilinkParser(), 199), + ), + ) +} diff --git a/plugin/markdown/markdown.go b/plugin/markdown/markdown.go new file mode 100644 index 000000000..ab4f95c36 --- /dev/null +++ b/plugin/markdown/markdown.go @@ -0,0 +1,460 @@ +package markdown + +import ( + "bytes" + "strings" + + "github.com/yuin/goldmark" + gast "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/extension" + east "github.com/yuin/goldmark/extension/ast" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/text" + + mast "github.com/usememos/memos/plugin/markdown/ast" + "github.com/usememos/memos/plugin/markdown/extensions" + "github.com/usememos/memos/plugin/markdown/renderer" + storepb "github.com/usememos/memos/proto/gen/store" +) + +// ExtractedData contains all metadata extracted from markdown in a single pass +type ExtractedData struct { + Tags []string + Property *storepb.MemoPayload_Property + References []string +} + +// Service handles markdown metadata extraction. +// It uses goldmark to parse markdown and extract tags, properties, and snippets. +// HTML rendering is primarily done on frontend using markdown-it, but backend provides +// RenderHTML for RSS feeds and other server-side rendering needs. +type Service interface { + // ExtractAll extracts tags, properties, and references in a single parse (most efficient) + ExtractAll(content []byte) (*ExtractedData, error) + + // ExtractTags returns all #tags found in content + ExtractTags(content []byte) ([]string, error) + + // ExtractProperties computes boolean properties + ExtractProperties(content []byte) (*storepb.MemoPayload_Property, error) + + // ExtractReferences returns all wikilink references ([[...]]) found in content + ExtractReferences(content []byte) ([]string, error) + + // RenderMarkdown renders goldmark AST back to markdown text + RenderMarkdown(content []byte) (string, error) + + // RenderHTML renders markdown content to HTML + RenderHTML(content []byte) (string, error) + + // GenerateSnippet creates plain text summary + GenerateSnippet(content []byte, maxLength int) (string, error) + + // ValidateContent checks for syntax errors + ValidateContent(content []byte) error + + // RenameTag renames all occurrences of oldTag to newTag in content + RenameTag(content []byte, oldTag, newTag string) (string, error) +} + +// service implements the Service interface +type service struct { + md goldmark.Markdown +} + +// Option configures the markdown service +type Option func(*config) + +type config struct { + enableTags bool + enableWikilink bool +} + +// WithTagExtension enables #tag parsing +func WithTagExtension() Option { + return func(c *config) { + c.enableTags = true + } +} + +// WithWikilinkExtension enables [[wikilink]] parsing +func WithWikilinkExtension() Option { + return func(c *config) { + c.enableWikilink = true + } +} + +// NewService creates a new markdown service with the given options +func NewService(opts ...Option) Service { + cfg := &config{} + for _, opt := range opts { + opt(cfg) + } + + exts := []goldmark.Extender{ + extension.GFM, // GitHub Flavored Markdown (tables, strikethrough, task lists, autolinks) + } + + // Add custom extensions based on config + if cfg.enableTags { + exts = append(exts, extensions.TagExtension) + } + if cfg.enableWikilink { + exts = append(exts, extensions.WikilinkExtension) + } + + md := goldmark.New( + goldmark.WithExtensions(exts...), + goldmark.WithParserOptions( + parser.WithAutoHeadingID(), // Generate heading IDs + ), + ) + + return &service{ + md: md, + } +} + +// parse is an internal helper to parse content into AST +func (s *service) parse(content []byte) (gast.Node, error) { + reader := text.NewReader(content) + doc := s.md.Parser().Parse(reader) + return doc, nil +} + +// ExtractTags returns all #tags found in content +func (s *service) ExtractTags(content []byte) ([]string, error) { + root, err := s.parse(content) + if err != nil { + return nil, err + } + + var tags []string + + // Walk the AST to find tag nodes + err = gast.Walk(root, func(n gast.Node, entering bool) (gast.WalkStatus, error) { + if !entering { + return gast.WalkContinue, nil + } + + // Check for custom TagNode + if tagNode, ok := n.(*mast.TagNode); ok { + tags = append(tags, string(tagNode.Tag)) + } + + return gast.WalkContinue, nil + }) + + if err != nil { + return nil, err + } + + // Deduplicate and normalize tags + return uniqueLowercase(tags), nil +} + +// ExtractProperties computes boolean properties about the content +func (s *service) ExtractProperties(content []byte) (*storepb.MemoPayload_Property, error) { + root, err := s.parse(content) + if err != nil { + return nil, err + } + + prop := &storepb.MemoPayload_Property{} + + err = gast.Walk(root, func(n gast.Node, entering bool) (gast.WalkStatus, error) { + if !entering { + return gast.WalkContinue, nil + } + + switch n.Kind() { + case gast.KindLink, mast.KindWikilink: + prop.HasLink = true + + case mast.KindWikilink: + prop.HasLink = true + + case gast.KindCodeBlock, gast.KindFencedCodeBlock, gast.KindCodeSpan: + prop.HasCode = true + + case gast.KindCodeSpan: + prop.HasCode = true + + case east.KindTaskCheckBox: + prop.HasTaskList = true + if checkBox, ok := n.(*east.TaskCheckBox); ok { + if !checkBox.IsChecked { + prop.HasIncompleteTasks = true + } + } + } + + return gast.WalkContinue, nil + }) + + if err != nil { + return nil, err + } + + return prop, nil +} + +// ExtractReferences returns all wikilink references found in content +func (s *service) ExtractReferences(content []byte) ([]string, error) { + root, err := s.parse(content) + if err != nil { + return nil, err + } + + references := []string{} // Initialize to empty slice, not nil + + // Walk the AST to find wikilink nodes + err = gast.Walk(root, func(n gast.Node, entering bool) (gast.WalkStatus, error) { + if !entering { + return gast.WalkContinue, nil + } + + // Check for custom WikilinkNode + if wikilinkNode, ok := n.(*mast.WikilinkNode); ok { + references = append(references, string(wikilinkNode.Target)) + } + + return gast.WalkContinue, nil + }) + + if err != nil { + return nil, err + } + + return references, nil +} + +// RenderMarkdown renders goldmark AST back to markdown text +func (s *service) RenderMarkdown(content []byte) (string, error) { + root, err := s.parse(content) + if err != nil { + return "", err + } + + mdRenderer := renderer.NewMarkdownRenderer() + return mdRenderer.Render(root, content), nil +} + +// RenderHTML renders markdown content to HTML using goldmark's built-in HTML renderer +func (s *service) RenderHTML(content []byte) (string, error) { + var buf bytes.Buffer + if err := s.md.Convert(content, &buf); err != nil { + return "", err + } + return buf.String(), nil +} + +// GenerateSnippet creates a plain text summary from markdown content +func (s *service) GenerateSnippet(content []byte, maxLength int) (string, error) { + root, err := s.parse(content) + if err != nil { + return "", err + } + + var buf strings.Builder + var lastNodeWasBlock bool + + err = gast.Walk(root, func(n gast.Node, entering bool) (gast.WalkStatus, error) { + if entering { + // Skip code blocks and code spans entirely + switch n.Kind() { + case gast.KindCodeBlock, gast.KindFencedCodeBlock, gast.KindCodeSpan: + return gast.WalkSkipChildren, nil + } + + // Add space before block elements (except first) + switch n.Kind() { + case gast.KindParagraph, gast.KindHeading, gast.KindListItem: + if buf.Len() > 0 && lastNodeWasBlock { + buf.WriteByte(' ') + } + } + } + + if !entering { + // Mark that we just exited a block element + switch n.Kind() { + case gast.KindParagraph, gast.KindHeading, gast.KindListItem: + lastNodeWasBlock = true + } + return gast.WalkContinue, nil + } + + lastNodeWasBlock = false + + // Only extract plain text nodes + if textNode, ok := n.(*gast.Text); ok { + segment := textNode.Segment + buf.Write(segment.Value(content)) + + // Add space if this is a soft line break + if textNode.SoftLineBreak() { + buf.WriteByte(' ') + } + } + + // Stop walking if we've exceeded double the max length + // (we'll truncate precisely later) + if buf.Len() > maxLength*2 { + return gast.WalkStop, nil + } + + return gast.WalkContinue, nil + }) + + if err != nil { + return "", err + } + + snippet := buf.String() + + // Truncate at word boundary if needed + if len(snippet) > maxLength { + snippet = truncateAtWord(snippet, maxLength) + } + + return strings.TrimSpace(snippet), nil +} + +// ValidateContent checks if the markdown content is valid +func (s *service) ValidateContent(content []byte) error { + // Try to parse the content + _, err := s.parse(content) + return err +} + +// ExtractAll extracts tags, properties, and references in a single parse for efficiency +func (s *service) ExtractAll(content []byte) (*ExtractedData, error) { + root, err := s.parse(content) + if err != nil { + return nil, err + } + + data := &ExtractedData{ + Tags: []string{}, + Property: &storepb.MemoPayload_Property{}, + References: []string{}, + } + + // Single walk to collect all data + err = gast.Walk(root, func(n gast.Node, entering bool) (gast.WalkStatus, error) { + if !entering { + return gast.WalkContinue, nil + } + + // Extract tags + if tagNode, ok := n.(*mast.TagNode); ok { + data.Tags = append(data.Tags, string(tagNode.Tag)) + } + + // Extract references (wikilinks) + if wikilinkNode, ok := n.(*mast.WikilinkNode); ok { + data.References = append(data.References, string(wikilinkNode.Target)) + } + + // Extract properties based on node kind + switch n.Kind() { + case gast.KindLink, mast.KindWikilink: + data.Property.HasLink = true + + case mast.KindWikilink: + data.Property.HasLink = true + + case gast.KindCodeBlock, gast.KindFencedCodeBlock, gast.KindCodeSpan: + data.Property.HasCode = true + + case gast.KindCodeSpan: + data.Property.HasCode = true + + case east.KindTaskCheckBox: + data.Property.HasTaskList = true + if checkBox, ok := n.(*east.TaskCheckBox); ok { + if !checkBox.IsChecked { + data.Property.HasIncompleteTasks = true + } + } + } + + return gast.WalkContinue, nil + }) + + if err != nil { + return nil, err + } + + // Deduplicate and normalize tags + data.Tags = uniqueLowercase(data.Tags) + + return data, nil +} + +// RenameTag renames all occurrences of oldTag to newTag in content +func (s *service) RenameTag(content []byte, oldTag, newTag string) (string, error) { + root, err := s.parse(content) + if err != nil { + return "", err + } + + // Walk the AST to find and rename tag nodes + err = gast.Walk(root, func(n gast.Node, entering bool) (gast.WalkStatus, error) { + if !entering { + return gast.WalkContinue, nil + } + + // Check for custom TagNode and rename if it matches + if tagNode, ok := n.(*mast.TagNode); ok { + if string(tagNode.Tag) == oldTag { + tagNode.Tag = []byte(newTag) + } + } + + return gast.WalkContinue, nil + }) + + if err != nil { + return "", err + } + + // Render back to markdown using the already-parsed AST + mdRenderer := renderer.NewMarkdownRenderer() + return mdRenderer.Render(root, content), nil +} + +// uniqueLowercase returns unique lowercase strings from input +func uniqueLowercase(strs []string) []string { + seen := make(map[string]bool) + var result []string + + for _, s := range strs { + lower := strings.ToLower(s) + if !seen[lower] { + seen[lower] = true + result = append(result, lower) + } + } + + return result +} + +// truncateAtWord truncates a string at the last word boundary before maxLength +func truncateAtWord(s string, maxLength int) string { + if len(s) <= maxLength { + return s + } + + // Truncate to max length + truncated := s[:maxLength] + + // Find last space + lastSpace := strings.LastIndexAny(truncated, " \t\n\r") + if lastSpace > 0 { + truncated = truncated[:lastSpace] + } + + return truncated + " ..." +} diff --git a/plugin/markdown/markdown_test.go b/plugin/markdown/markdown_test.go new file mode 100644 index 000000000..9768d2f2a --- /dev/null +++ b/plugin/markdown/markdown_test.go @@ -0,0 +1,490 @@ +package markdown + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewService(t *testing.T) { + svc := NewService() + assert.NotNil(t, svc) +} + +func TestValidateContent(t *testing.T) { + svc := NewService() + + tests := []struct { + name string + content string + wantErr bool + }{ + { + name: "valid markdown", + content: "# Hello\n\nThis is **bold** text.", + wantErr: false, + }, + { + name: "empty content", + content: "", + wantErr: false, + }, + { + name: "complex markdown", + content: "# Title\n\n- List item 1\n- List item 2\n\n```go\ncode block\n```", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := svc.ValidateContent([]byte(tt.content)) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestGenerateSnippet(t *testing.T) { + svc := NewService() + + tests := []struct { + name string + content string + maxLength int + expected string + }{ + { + name: "simple text", + content: "Hello world", + maxLength: 100, + expected: "Hello world", + }, + { + name: "text with formatting", + content: "This is **bold** and *italic* text.", + maxLength: 100, + expected: "This is bold and italic text.", + }, + { + name: "truncate long text", + content: "This is a very long piece of text that should be truncated at a word boundary.", + maxLength: 30, + expected: "This is a very long piece of ...", + }, + { + name: "heading and paragraph", + content: "# My Title\n\nThis is the first paragraph.", + maxLength: 100, + expected: "My Title This is the first paragraph.", + }, + { + name: "code block removed", + content: "Text before\n\n```go\ncode\n```\n\nText after", + maxLength: 100, + expected: "Text before Text after", + }, + { + name: "list items", + content: "- Item 1\n- Item 2\n- Item 3", + maxLength: 100, + expected: "Item 1 Item 2 Item 3", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + snippet, err := svc.GenerateSnippet([]byte(tt.content), tt.maxLength) + require.NoError(t, err) + assert.Equal(t, tt.expected, snippet) + }) + } +} + +func TestExtractProperties(t *testing.T) { + tests := []struct { + name string + content string + withExt bool + hasLink bool + hasCode bool + hasTasks bool + hasInc bool + }{ + { + name: "plain text", + content: "Just plain text", + withExt: false, + hasLink: false, + hasCode: false, + hasTasks: false, + hasInc: false, + }, + { + name: "with link", + content: "Check out [this link](https://example.com)", + withExt: false, + hasLink: true, + hasCode: false, + hasTasks: false, + hasInc: false, + }, + { + name: "with inline code", + content: "Use `console.log()` to debug", + withExt: false, + hasLink: false, + hasCode: true, + hasTasks: false, + hasInc: false, + }, + { + name: "with code block", + content: "```go\nfunc main() {}\n```", + withExt: false, + hasLink: false, + hasCode: true, + hasTasks: false, + hasInc: false, + }, + { + name: "with completed task", + content: "- [x] Completed task", + withExt: false, + hasLink: false, + hasCode: false, + hasTasks: true, + hasInc: false, + }, + { + name: "with incomplete task", + content: "- [ ] Todo item", + withExt: false, + hasLink: false, + hasCode: false, + hasTasks: true, + hasInc: true, + }, + { + name: "mixed tasks", + content: "- [x] Done\n- [ ] Not done", + withExt: false, + hasLink: false, + hasCode: false, + hasTasks: true, + hasInc: true, + }, + { + name: "with referenced content", + content: "See [[memos/1]] for details", + withExt: true, + hasLink: true, + hasCode: false, + hasTasks: false, + hasInc: false, + }, + { + name: "everything", + content: "# Title\n\n[Link](url)\n\n`code`\n\n- [ ] Task", + withExt: false, + hasLink: true, + hasCode: true, + hasTasks: true, + hasInc: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var svc Service + if tt.withExt { + svc = NewService(WithWikilinkExtension()) + } else { + svc = NewService() + } + + props, err := svc.ExtractProperties([]byte(tt.content)) + require.NoError(t, err) + assert.Equal(t, tt.hasLink, props.HasLink, "HasLink") + assert.Equal(t, tt.hasCode, props.HasCode, "HasCode") + assert.Equal(t, tt.hasTasks, props.HasTaskList, "HasTaskList") + assert.Equal(t, tt.hasInc, props.HasIncompleteTasks, "HasIncompleteTasks") + }) + } +} + +func TestExtractTags(t *testing.T) { + tests := []struct { + name string + content string + withExt bool + expected []string + }{ + { + name: "no tags", + content: "Just plain text", + withExt: false, + expected: []string{}, + }, + { + name: "single tag", + content: "Text with #tag", + withExt: true, + expected: []string{"tag"}, + }, + { + name: "multiple tags", + content: "Text with #tag1 and #tag2", + withExt: true, + expected: []string{"tag1", "tag2"}, + }, + { + name: "duplicate tags", + content: "#work is important. #Work #WORK", + withExt: true, + expected: []string{"work"}, // Deduplicated and lowercased + }, + { + name: "tags with hyphens and underscores", + content: "Tags: #work-notes #2024_plans", + withExt: true, + expected: []string{"work-notes", "2024_plans"}, + }, + { + name: "tags at end of sentence", + content: "This is important #urgent.", + withExt: true, + expected: []string{"urgent"}, + }, + { + name: "headings not tags", + content: "## Heading\n\n# Title\n\nText with #realtag", + withExt: true, + expected: []string{"realtag"}, + }, + { + name: "numeric tag", + content: "Issue #123", + withExt: true, + expected: []string{"123"}, + }, + { + name: "tag in list", + content: "- Item 1 #todo\n- Item 2 #done", + withExt: true, + expected: []string{"todo", "done"}, + }, + { + name: "no extension enabled", + content: "Text with #tag", + withExt: false, + expected: []string{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var svc Service + if tt.withExt { + svc = NewService(WithTagExtension()) + } else { + svc = NewService() + } + + tags, err := svc.ExtractTags([]byte(tt.content)) + require.NoError(t, err) + assert.ElementsMatch(t, tt.expected, tags) + }) + } +} + +func TestExtractReferences(t *testing.T) { + tests := []struct { + name string + content string + withExt bool + expected []string + }{ + { + name: "no references", + content: "Just plain text", + withExt: false, + expected: []string{}, + }, + { + name: "single wikilink", + content: "Check this: [[resources/101]]", + withExt: true, + expected: []string{"resources/101"}, + }, + { + name: "multiple wikilinks", + content: "[[resources/101]]\n\nAnd also: [[memos/42]]", + withExt: true, + expected: []string{"resources/101", "memos/42"}, + }, + { + name: "wikilink with params", + content: "[[resources/101?align=center]]", + withExt: true, + expected: []string{"resources/101"}, + }, + { + name: "duplicate wikilinks", + content: "[[resources/101]]\n\n[[resources/101]]", + withExt: true, + expected: []string{"resources/101", "resources/101"}, // Not deduplicated at this layer + }, + { + name: "no extension enabled", + content: "[[resources/101]]", + withExt: false, + expected: []string{}, + }, + { + name: "wikilink in sentence", + content: "Check [[memos/1]] for details", + withExt: true, + expected: []string{"memos/1"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var svc Service + if tt.withExt { + svc = NewService(WithWikilinkExtension()) + } else { + svc = NewService() + } + + references, err := svc.ExtractReferences([]byte(tt.content)) + require.NoError(t, err) + assert.Equal(t, tt.expected, references) + }) + } +} + +func TestUniqueLowercase(t *testing.T) { + tests := []struct { + name string + input []string + expected []string + }{ + { + name: "empty", + input: []string{}, + expected: []string{}, + }, + { + name: "unique items", + input: []string{"tag1", "tag2", "tag3"}, + expected: []string{"tag1", "tag2", "tag3"}, + }, + { + name: "duplicates", + input: []string{"tag", "TAG", "Tag"}, + expected: []string{"tag"}, + }, + { + name: "mixed", + input: []string{"Work", "work", "Important", "work"}, + expected: []string{"work", "important"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := uniqueLowercase(tt.input) + assert.ElementsMatch(t, tt.expected, result) + }) + } +} + +func TestTruncateAtWord(t *testing.T) { + tests := []struct { + name string + input string + maxLength int + expected string + }{ + { + name: "no truncation needed", + input: "short", + maxLength: 10, + expected: "short", + }, + { + name: "exact length", + input: "exactly ten", + maxLength: 11, + expected: "exactly ten", + }, + { + name: "truncate at word", + input: "this is a long sentence", + maxLength: 10, + expected: "this is a ...", + }, + { + name: "truncate very long word", + input: "supercalifragilisticexpialidocious", + maxLength: 10, + expected: "supercalif ...", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := truncateAtWord(tt.input, tt.maxLength) + assert.Equal(t, tt.expected, result) + }) + } +} + +// Benchmark tests +func BenchmarkGenerateSnippet(b *testing.B) { + svc := NewService() + content := []byte(`# Large Document + +This is a large document with multiple paragraphs and formatting. + +## Section 1 + +Here is some **bold** text and *italic* text with [links](https://example.com). + +- List item 1 +- List item 2 +- List item 3 + +## Section 2 + +More content here with ` + "`inline code`" + ` and other elements. + +` + "```go\nfunc example() {\n return true\n}\n```") + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := svc.GenerateSnippet(content, 200) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkExtractProperties(b *testing.B) { + svc := NewService() + content := []byte("# Title\n\n[Link](url)\n\n`code`\n\n- [ ] Task\n- [x] Done") + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := svc.ExtractProperties(content) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/plugin/markdown/parser/tag.go b/plugin/markdown/parser/tag.go new file mode 100644 index 000000000..b5f01f492 --- /dev/null +++ b/plugin/markdown/parser/tag.go @@ -0,0 +1,86 @@ +package parser + +import ( + gast "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/text" + + mast "github.com/usememos/memos/plugin/markdown/ast" +) + +type tagParser struct{} + +// NewTagParser creates a new inline parser for #tag syntax +func NewTagParser() parser.InlineParser { + return &tagParser{} +} + +// Trigger returns the characters that trigger this parser. +func (*tagParser) Trigger() []byte { + return []byte{'#'} +} + +// Parse parses #tag syntax +func (p *tagParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node { + line, _ := block.PeekLine() + + // Must start with # + if len(line) == 0 || line[0] != '#' { + return nil + } + + // Check if it's a heading (## or space after #) + if len(line) > 1 { + if line[1] == '#' { + // It's a heading (##), not a tag + return nil + } + if line[1] == ' ' { + // Space after # - heading or just a hash + return nil + } + } else { + // Just a lone # + return nil + } + + // Scan tag characters + // Valid: alphanumeric, dash, underscore + tagEnd := 1 // Start after # + for tagEnd < len(line) { + c := line[tagEnd] + + isValid := (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '-' || c == '_' + + if !isValid { + break + } + + tagEnd++ + } + + // Must have at least one character after # + if tagEnd == 1 { + return nil + } + + // Extract tag (without #) + tagName := line[1:tagEnd] + + // Make a copy of the tag name + tagCopy := make([]byte, len(tagName)) + copy(tagCopy, tagName) + + // Advance reader + block.Advance(tagEnd) + + // Create node + node := &mast.TagNode{ + Tag: tagCopy, + } + + return node +} diff --git a/plugin/markdown/parser/tag_test.go b/plugin/markdown/parser/tag_test.go new file mode 100644 index 000000000..5a67f49a6 --- /dev/null +++ b/plugin/markdown/parser/tag_test.go @@ -0,0 +1,170 @@ +package parser + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/text" + + mast "github.com/usememos/memos/plugin/markdown/ast" +) + +func TestTagParser(t *testing.T) { + tests := []struct { + name string + input string + expectedTag string + shouldParse bool + }{ + { + name: "basic tag", + input: "#tag", + expectedTag: "tag", + shouldParse: true, + }, + { + name: "tag with hyphen", + input: "#work-notes", + expectedTag: "work-notes", + shouldParse: true, + }, + { + name: "tag with underscore", + input: "#2024_plans", + expectedTag: "2024_plans", + shouldParse: true, + }, + { + name: "numeric tag", + input: "#123", + expectedTag: "123", + shouldParse: true, + }, + { + name: "tag followed by space", + input: "#tag ", + expectedTag: "tag", + shouldParse: true, + }, + { + name: "tag followed by punctuation", + input: "#tag.", + expectedTag: "tag", + shouldParse: true, + }, + { + name: "tag in sentence", + input: "#important task", + expectedTag: "important", + shouldParse: true, + }, + { + name: "heading (##)", + input: "## Heading", + expectedTag: "", + shouldParse: false, + }, + { + name: "space after hash", + input: "# heading", + expectedTag: "", + shouldParse: false, + }, + { + name: "lone hash", + input: "#", + expectedTag: "", + shouldParse: false, + }, + { + name: "hash with space", + input: "# ", + expectedTag: "", + shouldParse: false, + }, + { + name: "special characters", + input: "#tag@special", + expectedTag: "tag", + shouldParse: true, // Stops at @ + }, + { + name: "mixed case", + input: "#WorkNotes", + expectedTag: "WorkNotes", + shouldParse: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := NewTagParser() + reader := text.NewReader([]byte(tt.input)) + ctx := parser.NewContext() + + node := p.Parse(nil, reader, ctx) + + if tt.shouldParse { + require.NotNil(t, node, "Expected tag to be parsed") + require.IsType(t, &mast.TagNode{}, node) + + tagNode := node.(*mast.TagNode) + assert.Equal(t, tt.expectedTag, string(tagNode.Tag)) + } else { + assert.Nil(t, node, "Expected tag NOT to be parsed") + } + }) + } +} + +func TestTagParser_Trigger(t *testing.T) { + p := NewTagParser() + triggers := p.Trigger() + + assert.Equal(t, []byte{'#'}, triggers) +} + +func TestTagParser_MultipleTags(t *testing.T) { + // Test that parser correctly handles multiple tags in sequence + input := "#tag1 #tag2" + + p := NewTagParser() + reader := text.NewReader([]byte(input)) + ctx := parser.NewContext() + + // Parse first tag + node1 := p.Parse(nil, reader, ctx) + require.NotNil(t, node1) + tagNode1 := node1.(*mast.TagNode) + assert.Equal(t, "tag1", string(tagNode1.Tag)) + + // Advance past the space + reader.Advance(1) + + // Parse second tag + node2 := p.Parse(nil, reader, ctx) + require.NotNil(t, node2) + tagNode2 := node2.(*mast.TagNode) + assert.Equal(t, "tag2", string(tagNode2.Tag)) +} + +func TestTagNode_Kind(t *testing.T) { + node := &mast.TagNode{ + Tag: []byte("test"), + } + + assert.Equal(t, mast.KindTag, node.Kind()) +} + +func TestTagNode_Dump(t *testing.T) { + node := &mast.TagNode{ + Tag: []byte("test"), + } + + // Should not panic + assert.NotPanics(t, func() { + node.Dump([]byte("#test"), 0) + }) +} diff --git a/plugin/markdown/parser/wikilink.go b/plugin/markdown/parser/wikilink.go new file mode 100644 index 000000000..9d026e439 --- /dev/null +++ b/plugin/markdown/parser/wikilink.go @@ -0,0 +1,104 @@ +package parser + +import ( + "bytes" + + gast "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/text" + + mast "github.com/usememos/memos/plugin/markdown/ast" +) + +type wikilinkParser struct{} + +// NewWikilinkParser creates a new inline parser for [[...]] wikilink syntax +func NewWikilinkParser() parser.InlineParser { + return &wikilinkParser{} +} + +// Trigger returns the characters that trigger this parser. +func (*wikilinkParser) Trigger() []byte { + return []byte{'['} +} + +// Parse parses [[target]] or [[target?params]] wikilink syntax. +func (*wikilinkParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node { + line, _ := block.PeekLine() + + // Must start with [[ + if len(line) < 2 || line[0] != '[' || line[1] != '[' { + return nil + } + + // Find closing ]] + closePos := findClosingBrackets(line[2:]) + if closePos == -1 { + return nil + } + + // Extract content between [[ and ]] + // closePos is relative to line[2:], so actual position is closePos + 2 + contentStart := 2 + contentEnd := contentStart + closePos + content := line[contentStart:contentEnd] + + // Empty content is not allowed + if len(bytes.TrimSpace(content)) == 0 { + return nil + } + + // Parse target and parameters + target, params := parseTargetAndParams(content) + + // Advance reader position + // +2 for [[, +len(content), +2 for ]] + block.Advance(contentEnd + 2) + + // Create AST node + node := &mast.WikilinkNode{ + Target: target, + Params: params, + } + + return node +} + +// findClosingBrackets finds the position of ]] in the byte slice +// Returns -1 if not found +func findClosingBrackets(data []byte) int { + for i := 0; i < len(data)-1; i++ { + if data[i] == ']' && data[i+1] == ']' { + return i + } + } + return -1 +} + +// parseTargetAndParams splits content on ? to extract target and parameters +func parseTargetAndParams(content []byte) (target []byte, params []byte) { + // Find ? separator + idx := bytes.IndexByte(content, '?') + + if idx == -1 { + // No parameters + target = bytes.TrimSpace(content) + return target, nil + } + + // Split on ? + target = bytes.TrimSpace(content[:idx]) + params = content[idx+1:] // Keep params as-is (don't trim, might have meaningful spaces) + + // Make copies to avoid issues with slice sharing + targetCopy := make([]byte, len(target)) + copy(targetCopy, target) + + var paramsCopy []byte + if len(params) > 0 { + paramsCopy = make([]byte, len(params)) + copy(paramsCopy, params) + } + + return targetCopy, paramsCopy +} diff --git a/plugin/markdown/parser/wikilink_test.go b/plugin/markdown/parser/wikilink_test.go new file mode 100644 index 000000000..2501b6be3 --- /dev/null +++ b/plugin/markdown/parser/wikilink_test.go @@ -0,0 +1,251 @@ +package parser + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/text" + + mast "github.com/usememos/memos/plugin/markdown/ast" +) + +func TestWikilinkParser(t *testing.T) { + tests := []struct { + name string + input string + expectedTarget string + expectedParams string + shouldParse bool + }{ + { + name: "basic wikilink", + input: "[[Hello world]]", + expectedTarget: "Hello world", + expectedParams: "", + shouldParse: true, + }, + { + name: "memo wikilink", + input: "[[memos/1]]", + expectedTarget: "memos/1", + expectedParams: "", + shouldParse: true, + }, + { + name: "resource wikilink", + input: "[[resources/101]]", + expectedTarget: "resources/101", + expectedParams: "", + shouldParse: true, + }, + { + name: "with parameters", + input: "[[resources/101?align=center]]", + expectedTarget: "resources/101", + expectedParams: "align=center", + shouldParse: true, + }, + { + name: "multiple parameters", + input: "[[resources/101?align=center&width=300]]", + expectedTarget: "resources/101", + expectedParams: "align=center&width=300", + shouldParse: true, + }, + { + name: "inline with text after", + input: "[[resources/101]]111", + expectedTarget: "resources/101", + expectedParams: "", + shouldParse: true, + }, + { + name: "whitespace trimmed", + input: "[[ Hello world ]]", + expectedTarget: "Hello world", + expectedParams: "", + shouldParse: true, + }, + { + name: "empty content", + input: "[[]]", + expectedTarget: "", + expectedParams: "", + shouldParse: false, + }, + { + name: "whitespace only", + input: "[[ ]]", + expectedTarget: "", + expectedParams: "", + shouldParse: false, + }, + { + name: "missing closing brackets", + input: "[[Hello world", + expectedTarget: "", + expectedParams: "", + shouldParse: false, + }, + { + name: "single bracket", + input: "[Hello]", + expectedTarget: "", + expectedParams: "", + shouldParse: false, + }, + { + name: "nested brackets", + input: "[[outer [[inner]] ]]", + expectedTarget: "outer [[inner", + expectedParams: "", + shouldParse: true, // Stops at first ]] + }, + { + name: "special characters", + input: "[[Project/2024/Notes]]", + expectedTarget: "Project/2024/Notes", + expectedParams: "", + shouldParse: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := NewWikilinkParser() + reader := text.NewReader([]byte(tt.input)) + ctx := parser.NewContext() + + node := p.Parse(nil, reader, ctx) + + if tt.shouldParse { + require.NotNil(t, node, "Expected wikilink to be parsed") + require.IsType(t, &mast.WikilinkNode{}, node) + + wikilinkNode := node.(*mast.WikilinkNode) + assert.Equal(t, tt.expectedTarget, string(wikilinkNode.Target)) + assert.Equal(t, tt.expectedParams, string(wikilinkNode.Params)) + } else { + assert.Nil(t, node, "Expected wikilink NOT to be parsed") + } + }) + } +} + +func TestWikilinkParser_Trigger(t *testing.T) { + p := NewWikilinkParser() + triggers := p.Trigger() + + assert.Equal(t, []byte{'['}, triggers) +} + +func TestFindClosingBrackets(t *testing.T) { + tests := []struct { + name string + input []byte + expected int + }{ + { + name: "simple case", + input: []byte("hello]]world"), + expected: 5, + }, + { + name: "not found", + input: []byte("hello world"), + expected: -1, + }, + { + name: "at start", + input: []byte("]]hello"), + expected: 0, + }, + { + name: "single bracket", + input: []byte("hello]world"), + expected: -1, + }, + { + name: "empty", + input: []byte(""), + expected: -1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := findClosingBrackets(tt.input) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestParseTargetAndParams(t *testing.T) { + tests := []struct { + name string + input []byte + expectedTarget string + expectedParams string + }{ + { + name: "no params", + input: []byte("target"), + expectedTarget: "target", + expectedParams: "", + }, + { + name: "with params", + input: []byte("target?param=value"), + expectedTarget: "target", + expectedParams: "param=value", + }, + { + name: "multiple params", + input: []byte("target?a=1&b=2"), + expectedTarget: "target", + expectedParams: "a=1&b=2", + }, + { + name: "whitespace trimmed from target", + input: []byte(" target ?param=value"), + expectedTarget: "target", + expectedParams: "param=value", + }, + { + name: "empty params", + input: []byte("target?"), + expectedTarget: "target", + expectedParams: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + target, params := parseTargetAndParams(tt.input) + assert.Equal(t, tt.expectedTarget, string(target)) + assert.Equal(t, tt.expectedParams, string(params)) + }) + } +} + +func TestWikilinkNode_Kind(t *testing.T) { + node := &mast.WikilinkNode{ + Target: []byte("test"), + } + + assert.Equal(t, mast.KindWikilink, node.Kind()) +} + +func TestWikilinkNode_Dump(t *testing.T) { + node := &mast.WikilinkNode{ + Target: []byte("test"), + Params: []byte("param=value"), + } + + // Should not panic + assert.NotPanics(t, func() { + node.Dump([]byte("[[test?param=value]]"), 0) + }) +} diff --git a/plugin/markdown/renderer/markdown_renderer.go b/plugin/markdown/renderer/markdown_renderer.go new file mode 100644 index 000000000..c63e79fa7 --- /dev/null +++ b/plugin/markdown/renderer/markdown_renderer.go @@ -0,0 +1,275 @@ +package renderer + +import ( + "bytes" + "fmt" + "strings" + + gast "github.com/yuin/goldmark/ast" + east "github.com/yuin/goldmark/extension/ast" + + mast "github.com/usememos/memos/plugin/markdown/ast" +) + +// MarkdownRenderer renders goldmark AST back to markdown text. +type MarkdownRenderer struct { + buf *bytes.Buffer +} + +// NewMarkdownRenderer creates a new markdown renderer. +func NewMarkdownRenderer() *MarkdownRenderer { + return &MarkdownRenderer{ + buf: &bytes.Buffer{}, + } +} + +// Render renders the AST node to markdown and returns the result. +func (r *MarkdownRenderer) Render(node gast.Node, source []byte) string { + r.buf.Reset() + r.renderNode(node, source, 0) + return r.buf.String() +} + +// renderNode renders a single node and its children. +func (r *MarkdownRenderer) renderNode(node gast.Node, source []byte, depth int) { + switch n := node.(type) { + case *gast.Document: + r.renderChildren(n, source, depth) + + case *gast.Paragraph: + r.renderChildren(n, source, depth) + if node.NextSibling() != nil { + r.buf.WriteString("\n\n") + } + + case *gast.Text: + // Text nodes store their content as segments in the source + segment := n.Segment + r.buf.Write(segment.Value(source)) + if n.SoftLineBreak() { + r.buf.WriteByte('\n') + } else if n.HardLineBreak() { + r.buf.WriteString(" \n") + } + + case *gast.CodeSpan: + r.buf.WriteByte('`') + r.renderChildren(n, source, depth) + r.buf.WriteByte('`') + + case *gast.Emphasis: + symbol := "*" + if n.Level == 2 { + symbol = "**" + } + r.buf.WriteString(symbol) + r.renderChildren(n, source, depth) + r.buf.WriteString(symbol) + + case *gast.Link: + r.buf.WriteString("[") + r.renderChildren(n, source, depth) + r.buf.WriteString("](") + r.buf.Write(n.Destination) + if len(n.Title) > 0 { + r.buf.WriteString(` "`) + r.buf.Write(n.Title) + r.buf.WriteString(`"`) + } + r.buf.WriteString(")") + + case *gast.AutoLink: + url := n.URL(source) + if n.AutoLinkType == gast.AutoLinkEmail { + r.buf.WriteString("<") + r.buf.Write(url) + r.buf.WriteString(">") + } else { + r.buf.Write(url) + } + + case *gast.Image: + r.buf.WriteString("![") + r.renderChildren(n, source, depth) + r.buf.WriteString("](") + r.buf.Write(n.Destination) + if len(n.Title) > 0 { + r.buf.WriteString(` "`) + r.buf.Write(n.Title) + r.buf.WriteString(`"`) + } + r.buf.WriteString(")") + + case *gast.Heading: + r.buf.WriteString(strings.Repeat("#", n.Level)) + r.buf.WriteByte(' ') + r.renderChildren(n, source, depth) + if node.NextSibling() != nil { + r.buf.WriteString("\n\n") + } + + case *gast.CodeBlock, *gast.FencedCodeBlock: + r.renderCodeBlock(n, source) + + case *gast.Blockquote: + // Render each child line with "> " prefix + r.renderBlockquote(n, source, depth) + if node.NextSibling() != nil { + r.buf.WriteString("\n\n") + } + + case *gast.List: + r.renderChildren(n, source, depth) + if node.NextSibling() != nil { + r.buf.WriteString("\n\n") + } + + case *gast.ListItem: + r.renderListItem(n, source, depth) + + case *gast.ThematicBreak: + r.buf.WriteString("---") + if node.NextSibling() != nil { + r.buf.WriteString("\n\n") + } + + case *east.Strikethrough: + r.buf.WriteString("~~") + r.renderChildren(n, source, depth) + r.buf.WriteString("~~") + + case *east.TaskCheckBox: + if n.IsChecked { + r.buf.WriteString("[x] ") + } else { + r.buf.WriteString("[ ] ") + } + + case *east.Table: + r.renderTable(n, source) + if node.NextSibling() != nil { + r.buf.WriteString("\n\n") + } + + // Custom Memos nodes + case *mast.TagNode: + r.buf.WriteByte('#') + r.buf.Write(n.Tag) + + case *mast.WikilinkNode: + r.buf.WriteString("[[") + r.buf.Write(n.Target) + if len(n.Params) > 0 { + r.buf.WriteByte('?') + r.buf.Write(n.Params) + } + r.buf.WriteString("]]") + + default: + // For unknown nodes, try to render children + r.renderChildren(n, source, depth) + } +} + +// renderChildren renders all children of a node. +func (r *MarkdownRenderer) renderChildren(node gast.Node, source []byte, depth int) { + child := node.FirstChild() + for child != nil { + r.renderNode(child, source, depth+1) + child = child.NextSibling() + } +} + +// renderCodeBlock renders a code block. +func (r *MarkdownRenderer) renderCodeBlock(node gast.Node, source []byte) { + if fenced, ok := node.(*gast.FencedCodeBlock); ok { + // Fenced code block with language + r.buf.WriteString("```") + if lang := fenced.Language(source); len(lang) > 0 { + r.buf.Write(lang) + } + r.buf.WriteByte('\n') + + // Write all lines + lines := fenced.Lines() + for i := 0; i < lines.Len(); i++ { + line := lines.At(i) + r.buf.Write(line.Value(source)) + } + + r.buf.WriteString("```") + if node.NextSibling() != nil { + r.buf.WriteString("\n\n") + } + } else if codeBlock, ok := node.(*gast.CodeBlock); ok { + // Indented code block + lines := codeBlock.Lines() + for i := 0; i < lines.Len(); i++ { + line := lines.At(i) + r.buf.WriteString(" ") + r.buf.Write(line.Value(source)) + } + if node.NextSibling() != nil { + r.buf.WriteString("\n\n") + } + } +} + +// renderBlockquote renders a blockquote with "> " prefix. +func (r *MarkdownRenderer) renderBlockquote(node *gast.Blockquote, source []byte, depth int) { + // Create a temporary buffer for the blockquote content + tempBuf := &bytes.Buffer{} + tempRenderer := &MarkdownRenderer{buf: tempBuf} + tempRenderer.renderChildren(node, source, depth) + + // Add "> " prefix to each line + content := tempBuf.String() + lines := strings.Split(strings.TrimRight(content, "\n"), "\n") + for i, line := range lines { + r.buf.WriteString("> ") + r.buf.WriteString(line) + if i < len(lines)-1 { + r.buf.WriteByte('\n') + } + } +} + +// renderListItem renders a list item with proper indentation and markers. +func (r *MarkdownRenderer) renderListItem(node *gast.ListItem, source []byte, depth int) { + parent := node.Parent() + list, ok := parent.(*gast.List) + if !ok { + r.renderChildren(node, source, depth) + return + } + + // Add indentation only for nested lists + // Document=0, List=1, ListItem=2 (no indent), nested ListItem=3+ (indent) + if depth > 2 { + indent := strings.Repeat(" ", depth-2) + r.buf.WriteString(indent) + } + + // Add list marker + if list.IsOrdered() { + r.buf.WriteString(fmt.Sprintf("%d. ", list.Start)) + list.Start++ // Increment for next item + } else { + r.buf.WriteString("- ") + } + + // Render content + r.renderChildren(node, source, depth) + + // Add newline if there's a next sibling + if node.NextSibling() != nil { + r.buf.WriteByte('\n') + } +} + +// renderTable renders a table in markdown format. +func (r *MarkdownRenderer) renderTable(table *east.Table, source []byte) { + // This is a simplified table renderer + // A full implementation would need to handle alignment, etc. + r.renderChildren(table, source, 0) +} diff --git a/plugin/markdown/renderer/markdown_renderer_test.go b/plugin/markdown/renderer/markdown_renderer_test.go new file mode 100644 index 000000000..a665eb426 --- /dev/null +++ b/plugin/markdown/renderer/markdown_renderer_test.go @@ -0,0 +1,184 @@ +package renderer + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/yuin/goldmark" + "github.com/yuin/goldmark/extension" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/text" + + "github.com/usememos/memos/plugin/markdown/extensions" +) + +func TestMarkdownRenderer(t *testing.T) { + // Create goldmark instance with all extensions + md := goldmark.New( + goldmark.WithExtensions( + extension.GFM, + extensions.TagExtension, + extensions.WikilinkExtension, + ), + goldmark.WithParserOptions( + parser.WithAutoHeadingID(), + ), + ) + + tests := []struct { + name string + input string + expected string + }{ + { + name: "simple text", + input: "Hello world", + expected: "Hello world", + }, + { + name: "paragraph with newlines", + input: "First paragraph\n\nSecond paragraph", + expected: "First paragraph\n\nSecond paragraph", + }, + { + name: "emphasis", + input: "This is *italic* and **bold** text", + expected: "This is *italic* and **bold** text", + }, + { + name: "headings", + input: "# Heading 1\n\n## Heading 2\n\n### Heading 3", + expected: "# Heading 1\n\n## Heading 2\n\n### Heading 3", + }, + { + name: "link", + input: "Check [this link](https://example.com)", + expected: "Check [this link](https://example.com)", + }, + { + name: "image", + input: "![alt text](image.png)", + expected: "![alt text](image.png)", + }, + { + name: "code inline", + input: "This is `inline code` here", + expected: "This is `inline code` here", + }, + { + name: "code block fenced", + input: "```go\nfunc main() {\n}\n```", + expected: "```go\nfunc main() {\n}\n```", + }, + { + name: "unordered list", + input: "- Item 1\n- Item 2\n- Item 3", + expected: "- Item 1\n- Item 2\n- Item 3", + }, + { + name: "ordered list", + input: "1. First\n2. Second\n3. Third", + expected: "1. First\n2. Second\n3. Third", + }, + { + name: "blockquote", + input: "> This is a quote\n> Second line", + expected: "> This is a quote\n> Second line", + }, + { + name: "horizontal rule", + input: "Text before\n\n---\n\nText after", + expected: "Text before\n\n---\n\nText after", + }, + { + name: "strikethrough", + input: "This is ~~deleted~~ text", + expected: "This is ~~deleted~~ text", + }, + { + name: "task list", + input: "- [x] Completed task\n- [ ] Incomplete task", + expected: "- [x] Completed task\n- [ ] Incomplete task", + }, + { + name: "tag", + input: "This has #tag in it", + expected: "This has #tag in it", + }, + { + name: "multiple tags", + input: "#work #important meeting notes", + expected: "#work #important meeting notes", + }, + { + name: "referenced content (wikilink)", + input: "Check [[memos/42]] for details", + expected: "Check [[memos/42]] for details", + }, + { + name: "complex mixed content", + input: "# Meeting Notes\n\n**Date**: 2024-01-01\n\n## Attendees\n- Alice\n- Bob\n\n## Discussion\n\nWe discussed #project status.\n\nSee [[memos/1]] for background.\n\n```python\nprint('hello')\n```", + expected: "# Meeting Notes\n\n**Date**: 2024-01-01\n\n## Attendees\n\n- Alice\n- Bob\n\n## Discussion\n\nWe discussed #project status.\n\nSee [[memos/1]] for background.\n\n```python\nprint('hello')\n```", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Parse the input + source := []byte(tt.input) + reader := text.NewReader(source) + doc := md.Parser().Parse(reader) + require.NotNil(t, doc) + + // Render back to markdown + renderer := NewMarkdownRenderer() + result := renderer.Render(doc, source) + + // For debugging + if result != tt.expected { + t.Logf("Input: %q", tt.input) + t.Logf("Expected: %q", tt.expected) + t.Logf("Got: %q", result) + } + + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestMarkdownRendererPreservesStructure(t *testing.T) { + // Test that parsing and rendering preserves structure + md := goldmark.New( + goldmark.WithExtensions( + extension.GFM, + extensions.TagExtension, + extensions.WikilinkExtension, + ), + ) + + inputs := []string{ + "# Title\n\nParagraph", + "**Bold** and *italic*", + "- List\n- Items", + "#tag #another", + "[[wikilink]]", + "> Quote", + } + + renderer := NewMarkdownRenderer() + + for _, input := range inputs { + t.Run(input, func(t *testing.T) { + source := []byte(input) + reader := text.NewReader(source) + doc := md.Parser().Parse(reader) + + result := renderer.Render(doc, source) + + // The result should be structurally similar + // (may have minor formatting differences) + assert.NotEmpty(t, result) + }) + } +} diff --git a/proto/api/v1/markdown_service.proto b/proto/api/v1/markdown_service.proto deleted file mode 100644 index 8fc7fd526..000000000 --- a/proto/api/v1/markdown_service.proto +++ /dev/null @@ -1,331 +0,0 @@ -syntax = "proto3"; - -package memos.api.v1; - -import "google/api/annotations.proto"; -import "google/api/field_behavior.proto"; - -option go_package = "gen/api/v1"; - -service MarkdownService { - // ParseMarkdown parses the given markdown content and returns a list of nodes. - // This is a utility method that transforms markdown text into structured nodes. - rpc ParseMarkdown(ParseMarkdownRequest) returns (ParseMarkdownResponse) { - option (google.api.http) = { - post: "/api/v1/markdown:parse" - body: "*" - }; - } - - // RestoreMarkdownNodes restores the given nodes to markdown content. - // This is the inverse operation of ParseMarkdown. - rpc RestoreMarkdownNodes(RestoreMarkdownNodesRequest) returns (RestoreMarkdownNodesResponse) { - option (google.api.http) = { - post: "/api/v1/markdown:restore" - body: "*" - }; - } - - // StringifyMarkdownNodes stringify the given nodes to plain text content. - // This removes all markdown formatting and returns plain text. - rpc StringifyMarkdownNodes(StringifyMarkdownNodesRequest) returns (StringifyMarkdownNodesResponse) { - option (google.api.http) = { - post: "/api/v1/markdown:stringify" - body: "*" - }; - } - - // GetLinkMetadata returns metadata for a given link. - // This is useful for generating link previews. - rpc GetLinkMetadata(GetLinkMetadataRequest) returns (LinkMetadata) { - option (google.api.http) = {get: "/api/v1/markdown/links:getMetadata"}; - } -} - -message ParseMarkdownRequest { - // The markdown content to parse. - string markdown = 1 [(google.api.field_behavior) = REQUIRED]; -} - -message ParseMarkdownResponse { - // The parsed markdown nodes. - repeated Node nodes = 1; -} - -message RestoreMarkdownNodesRequest { - // The nodes to restore to markdown content. - repeated Node nodes = 1 [(google.api.field_behavior) = REQUIRED]; -} - -message RestoreMarkdownNodesResponse { - // The restored markdown content. - string markdown = 1; -} - -message StringifyMarkdownNodesRequest { - // The nodes to stringify to plain text. - repeated Node nodes = 1 [(google.api.field_behavior) = REQUIRED]; -} - -message StringifyMarkdownNodesResponse { - // The plain text content. - string plain_text = 1; -} - -message GetLinkMetadataRequest { - // The link URL to get metadata for. - string link = 1 [(google.api.field_behavior) = REQUIRED]; -} - -message LinkMetadata { - // The title of the linked page. - string title = 1; - - // The description of the linked page. - string description = 2; - - // The URL of the preview image for the linked page. - string image = 3; -} - -enum NodeType { - NODE_UNSPECIFIED = 0; - - // Block nodes. - LINE_BREAK = 1; - PARAGRAPH = 2; - CODE_BLOCK = 3; - HEADING = 4; - HORIZONTAL_RULE = 5; - BLOCKQUOTE = 6; - LIST = 7; - ORDERED_LIST_ITEM = 8; - UNORDERED_LIST_ITEM = 9; - TASK_LIST_ITEM = 10; - MATH_BLOCK = 11; - TABLE = 12; - EMBEDDED_CONTENT = 13; - - // Inline nodes. - TEXT = 51; - BOLD = 52; - ITALIC = 53; - BOLD_ITALIC = 54; - CODE = 55; - IMAGE = 56; - LINK = 57; - AUTO_LINK = 58; - TAG = 59; - STRIKETHROUGH = 60; - ESCAPING_CHARACTER = 61; - MATH = 62; - HIGHLIGHT = 63; - SUBSCRIPT = 64; - SUPERSCRIPT = 65; - REFERENCED_CONTENT = 66; - SPOILER = 67; - HTML_ELEMENT = 68; -} - -message Node { - NodeType type = 1; - - oneof node { - // Block nodes. - LineBreakNode line_break_node = 11; - ParagraphNode paragraph_node = 12; - CodeBlockNode code_block_node = 13; - HeadingNode heading_node = 14; - HorizontalRuleNode horizontal_rule_node = 15; - BlockquoteNode blockquote_node = 16; - ListNode list_node = 17; - OrderedListItemNode ordered_list_item_node = 18; - UnorderedListItemNode unordered_list_item_node = 19; - TaskListItemNode task_list_item_node = 20; - MathBlockNode math_block_node = 21; - TableNode table_node = 22; - EmbeddedContentNode embedded_content_node = 23; - - // Inline nodes. - TextNode text_node = 51; - BoldNode bold_node = 52; - ItalicNode italic_node = 53; - BoldItalicNode bold_italic_node = 54; - CodeNode code_node = 55; - ImageNode image_node = 56; - LinkNode link_node = 57; - AutoLinkNode auto_link_node = 58; - TagNode tag_node = 59; - StrikethroughNode strikethrough_node = 60; - EscapingCharacterNode escaping_character_node = 61; - MathNode math_node = 62; - HighlightNode highlight_node = 63; - SubscriptNode subscript_node = 64; - SuperscriptNode superscript_node = 65; - ReferencedContentNode referenced_content_node = 66; - SpoilerNode spoiler_node = 67; - HTMLElementNode html_element_node = 68; - } -} - -message LineBreakNode {} - -message ParagraphNode { - repeated Node children = 1; -} - -message CodeBlockNode { - string language = 1; - string content = 2; -} - -message HeadingNode { - int32 level = 1; - repeated Node children = 2; -} - -message HorizontalRuleNode { - string symbol = 1; -} - -message BlockquoteNode { - repeated Node children = 1; -} - -message ListNode { - enum Kind { - KIND_UNSPECIFIED = 0; - ORDERED = 1; - UNORDERED = 2; - DESCRIPTION = 3; - } - Kind kind = 1; - int32 indent = 2; - repeated Node children = 3; -} - -message OrderedListItemNode { - string number = 1; - int32 indent = 2; - repeated Node children = 3; -} - -message UnorderedListItemNode { - string symbol = 1; - int32 indent = 2; - repeated Node children = 3; -} - -message TaskListItemNode { - string symbol = 1; - int32 indent = 2; - bool complete = 3; - repeated Node children = 4; -} - -message MathBlockNode { - string content = 1; -} - -message TableNode { - repeated Node header = 1; - repeated string delimiter = 2; - - message Row { - repeated Node cells = 1; - } - repeated Row rows = 3; -} - -message EmbeddedContentNode { - // The resource name of the embedded content. - string resource_name = 1; - - // Additional parameters for the embedded content. - string params = 2; -} - -message TextNode { - string content = 1; -} - -message BoldNode { - string symbol = 1; - repeated Node children = 2; -} - -message ItalicNode { - string symbol = 1; - repeated Node children = 2; -} - -message BoldItalicNode { - string symbol = 1; - string content = 2; -} - -message CodeNode { - string content = 1; -} - -message ImageNode { - string alt_text = 1; - string url = 2; -} - -message LinkNode { - repeated Node content = 1; - string url = 2; -} - -message AutoLinkNode { - string url = 1; - bool is_raw_text = 2; -} - -message TagNode { - string content = 1; -} - -message StrikethroughNode { - string content = 1; -} - -message EscapingCharacterNode { - string symbol = 1; -} - -message MathNode { - string content = 1; -} - -message HighlightNode { - string content = 1; -} - -message SubscriptNode { - string content = 1; -} - -message SuperscriptNode { - string content = 1; -} - -message ReferencedContentNode { - // The resource name of the referenced content. - string resource_name = 1; - - // Additional parameters for the referenced content. - string params = 2; -} - -message SpoilerNode { - string content = 1; -} - -message HTMLElementNode { - string tag_name = 1; - map attributes = 2; - repeated Node children = 3; - bool is_self_closing = 4; -} diff --git a/proto/api/v1/memo_service.proto b/proto/api/v1/memo_service.proto index 7ae4b10f1..60407434d 100644 --- a/proto/api/v1/memo_service.proto +++ b/proto/api/v1/memo_service.proto @@ -4,7 +4,6 @@ package memos.api.v1; import "api/v1/attachment_service.proto"; import "api/v1/common.proto"; -import "api/v1/markdown_service.proto"; import "google/api/annotations.proto"; import "google/api/client.proto"; import "google/api/field_behavior.proto"; @@ -202,9 +201,6 @@ message Memo { // Required. The content of the memo in Markdown format. string content = 7 [(google.api.field_behavior) = REQUIRED]; - // Output only. The parsed nodes from the content. - repeated Node nodes = 8 [(google.api.field_behavior) = OUTPUT_ONLY]; - // The visibility of the memo. Visibility visibility = 9 [(google.api.field_behavior) = REQUIRED]; diff --git a/proto/gen/api/v1/markdown_service.pb.go b/proto/gen/api/v1/markdown_service.pb.go deleted file mode 100644 index bdabacf55..000000000 --- a/proto/gen/api/v1/markdown_service.pb.go +++ /dev/null @@ -1,3133 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.10 -// protoc (unknown) -// source: api/v1/markdown_service.proto - -package apiv1 - -import ( - _ "google.golang.org/genproto/googleapis/api/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type NodeType int32 - -const ( - NodeType_NODE_UNSPECIFIED NodeType = 0 - // Block nodes. - NodeType_LINE_BREAK NodeType = 1 - NodeType_PARAGRAPH NodeType = 2 - NodeType_CODE_BLOCK NodeType = 3 - NodeType_HEADING NodeType = 4 - NodeType_HORIZONTAL_RULE NodeType = 5 - NodeType_BLOCKQUOTE NodeType = 6 - NodeType_LIST NodeType = 7 - NodeType_ORDERED_LIST_ITEM NodeType = 8 - NodeType_UNORDERED_LIST_ITEM NodeType = 9 - NodeType_TASK_LIST_ITEM NodeType = 10 - NodeType_MATH_BLOCK NodeType = 11 - NodeType_TABLE NodeType = 12 - NodeType_EMBEDDED_CONTENT NodeType = 13 - // Inline nodes. - NodeType_TEXT NodeType = 51 - NodeType_BOLD NodeType = 52 - NodeType_ITALIC NodeType = 53 - NodeType_BOLD_ITALIC NodeType = 54 - NodeType_CODE NodeType = 55 - NodeType_IMAGE NodeType = 56 - NodeType_LINK NodeType = 57 - NodeType_AUTO_LINK NodeType = 58 - NodeType_TAG NodeType = 59 - NodeType_STRIKETHROUGH NodeType = 60 - NodeType_ESCAPING_CHARACTER NodeType = 61 - NodeType_MATH NodeType = 62 - NodeType_HIGHLIGHT NodeType = 63 - NodeType_SUBSCRIPT NodeType = 64 - NodeType_SUPERSCRIPT NodeType = 65 - NodeType_REFERENCED_CONTENT NodeType = 66 - NodeType_SPOILER NodeType = 67 - NodeType_HTML_ELEMENT NodeType = 68 -) - -// Enum value maps for NodeType. -var ( - NodeType_name = map[int32]string{ - 0: "NODE_UNSPECIFIED", - 1: "LINE_BREAK", - 2: "PARAGRAPH", - 3: "CODE_BLOCK", - 4: "HEADING", - 5: "HORIZONTAL_RULE", - 6: "BLOCKQUOTE", - 7: "LIST", - 8: "ORDERED_LIST_ITEM", - 9: "UNORDERED_LIST_ITEM", - 10: "TASK_LIST_ITEM", - 11: "MATH_BLOCK", - 12: "TABLE", - 13: "EMBEDDED_CONTENT", - 51: "TEXT", - 52: "BOLD", - 53: "ITALIC", - 54: "BOLD_ITALIC", - 55: "CODE", - 56: "IMAGE", - 57: "LINK", - 58: "AUTO_LINK", - 59: "TAG", - 60: "STRIKETHROUGH", - 61: "ESCAPING_CHARACTER", - 62: "MATH", - 63: "HIGHLIGHT", - 64: "SUBSCRIPT", - 65: "SUPERSCRIPT", - 66: "REFERENCED_CONTENT", - 67: "SPOILER", - 68: "HTML_ELEMENT", - } - NodeType_value = map[string]int32{ - "NODE_UNSPECIFIED": 0, - "LINE_BREAK": 1, - "PARAGRAPH": 2, - "CODE_BLOCK": 3, - "HEADING": 4, - "HORIZONTAL_RULE": 5, - "BLOCKQUOTE": 6, - "LIST": 7, - "ORDERED_LIST_ITEM": 8, - "UNORDERED_LIST_ITEM": 9, - "TASK_LIST_ITEM": 10, - "MATH_BLOCK": 11, - "TABLE": 12, - "EMBEDDED_CONTENT": 13, - "TEXT": 51, - "BOLD": 52, - "ITALIC": 53, - "BOLD_ITALIC": 54, - "CODE": 55, - "IMAGE": 56, - "LINK": 57, - "AUTO_LINK": 58, - "TAG": 59, - "STRIKETHROUGH": 60, - "ESCAPING_CHARACTER": 61, - "MATH": 62, - "HIGHLIGHT": 63, - "SUBSCRIPT": 64, - "SUPERSCRIPT": 65, - "REFERENCED_CONTENT": 66, - "SPOILER": 67, - "HTML_ELEMENT": 68, - } -) - -func (x NodeType) Enum() *NodeType { - p := new(NodeType) - *p = x - return p -} - -func (x NodeType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (NodeType) Descriptor() protoreflect.EnumDescriptor { - return file_api_v1_markdown_service_proto_enumTypes[0].Descriptor() -} - -func (NodeType) Type() protoreflect.EnumType { - return &file_api_v1_markdown_service_proto_enumTypes[0] -} - -func (x NodeType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use NodeType.Descriptor instead. -func (NodeType) EnumDescriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{0} -} - -type ListNode_Kind int32 - -const ( - ListNode_KIND_UNSPECIFIED ListNode_Kind = 0 - ListNode_ORDERED ListNode_Kind = 1 - ListNode_UNORDERED ListNode_Kind = 2 - ListNode_DESCRIPTION ListNode_Kind = 3 -) - -// Enum value maps for ListNode_Kind. -var ( - ListNode_Kind_name = map[int32]string{ - 0: "KIND_UNSPECIFIED", - 1: "ORDERED", - 2: "UNORDERED", - 3: "DESCRIPTION", - } - ListNode_Kind_value = map[string]int32{ - "KIND_UNSPECIFIED": 0, - "ORDERED": 1, - "UNORDERED": 2, - "DESCRIPTION": 3, - } -) - -func (x ListNode_Kind) Enum() *ListNode_Kind { - p := new(ListNode_Kind) - *p = x - return p -} - -func (x ListNode_Kind) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ListNode_Kind) Descriptor() protoreflect.EnumDescriptor { - return file_api_v1_markdown_service_proto_enumTypes[1].Descriptor() -} - -func (ListNode_Kind) Type() protoreflect.EnumType { - return &file_api_v1_markdown_service_proto_enumTypes[1] -} - -func (x ListNode_Kind) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ListNode_Kind.Descriptor instead. -func (ListNode_Kind) EnumDescriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{15, 0} -} - -type ParseMarkdownRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The markdown content to parse. - Markdown string `protobuf:"bytes,1,opt,name=markdown,proto3" json:"markdown,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ParseMarkdownRequest) Reset() { - *x = ParseMarkdownRequest{} - mi := &file_api_v1_markdown_service_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ParseMarkdownRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ParseMarkdownRequest) ProtoMessage() {} - -func (x *ParseMarkdownRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ParseMarkdownRequest.ProtoReflect.Descriptor instead. -func (*ParseMarkdownRequest) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{0} -} - -func (x *ParseMarkdownRequest) GetMarkdown() string { - if x != nil { - return x.Markdown - } - return "" -} - -type ParseMarkdownResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The parsed markdown nodes. - Nodes []*Node `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ParseMarkdownResponse) Reset() { - *x = ParseMarkdownResponse{} - mi := &file_api_v1_markdown_service_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ParseMarkdownResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ParseMarkdownResponse) ProtoMessage() {} - -func (x *ParseMarkdownResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ParseMarkdownResponse.ProtoReflect.Descriptor instead. -func (*ParseMarkdownResponse) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{1} -} - -func (x *ParseMarkdownResponse) GetNodes() []*Node { - if x != nil { - return x.Nodes - } - return nil -} - -type RestoreMarkdownNodesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The nodes to restore to markdown content. - Nodes []*Node `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RestoreMarkdownNodesRequest) Reset() { - *x = RestoreMarkdownNodesRequest{} - mi := &file_api_v1_markdown_service_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RestoreMarkdownNodesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RestoreMarkdownNodesRequest) ProtoMessage() {} - -func (x *RestoreMarkdownNodesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RestoreMarkdownNodesRequest.ProtoReflect.Descriptor instead. -func (*RestoreMarkdownNodesRequest) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{2} -} - -func (x *RestoreMarkdownNodesRequest) GetNodes() []*Node { - if x != nil { - return x.Nodes - } - return nil -} - -type RestoreMarkdownNodesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The restored markdown content. - Markdown string `protobuf:"bytes,1,opt,name=markdown,proto3" json:"markdown,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RestoreMarkdownNodesResponse) Reset() { - *x = RestoreMarkdownNodesResponse{} - mi := &file_api_v1_markdown_service_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RestoreMarkdownNodesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RestoreMarkdownNodesResponse) ProtoMessage() {} - -func (x *RestoreMarkdownNodesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[3] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RestoreMarkdownNodesResponse.ProtoReflect.Descriptor instead. -func (*RestoreMarkdownNodesResponse) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{3} -} - -func (x *RestoreMarkdownNodesResponse) GetMarkdown() string { - if x != nil { - return x.Markdown - } - return "" -} - -type StringifyMarkdownNodesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The nodes to stringify to plain text. - Nodes []*Node `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *StringifyMarkdownNodesRequest) Reset() { - *x = StringifyMarkdownNodesRequest{} - mi := &file_api_v1_markdown_service_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StringifyMarkdownNodesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StringifyMarkdownNodesRequest) ProtoMessage() {} - -func (x *StringifyMarkdownNodesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[4] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StringifyMarkdownNodesRequest.ProtoReflect.Descriptor instead. -func (*StringifyMarkdownNodesRequest) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{4} -} - -func (x *StringifyMarkdownNodesRequest) GetNodes() []*Node { - if x != nil { - return x.Nodes - } - return nil -} - -type StringifyMarkdownNodesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The plain text content. - PlainText string `protobuf:"bytes,1,opt,name=plain_text,json=plainText,proto3" json:"plain_text,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *StringifyMarkdownNodesResponse) Reset() { - *x = StringifyMarkdownNodesResponse{} - mi := &file_api_v1_markdown_service_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StringifyMarkdownNodesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StringifyMarkdownNodesResponse) ProtoMessage() {} - -func (x *StringifyMarkdownNodesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[5] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StringifyMarkdownNodesResponse.ProtoReflect.Descriptor instead. -func (*StringifyMarkdownNodesResponse) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{5} -} - -func (x *StringifyMarkdownNodesResponse) GetPlainText() string { - if x != nil { - return x.PlainText - } - return "" -} - -type GetLinkMetadataRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The link URL to get metadata for. - Link string `protobuf:"bytes,1,opt,name=link,proto3" json:"link,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetLinkMetadataRequest) Reset() { - *x = GetLinkMetadataRequest{} - mi := &file_api_v1_markdown_service_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetLinkMetadataRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetLinkMetadataRequest) ProtoMessage() {} - -func (x *GetLinkMetadataRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[6] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetLinkMetadataRequest.ProtoReflect.Descriptor instead. -func (*GetLinkMetadataRequest) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{6} -} - -func (x *GetLinkMetadataRequest) GetLink() string { - if x != nil { - return x.Link - } - return "" -} - -type LinkMetadata struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The title of the linked page. - Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` - // The description of the linked page. - Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` - // The URL of the preview image for the linked page. - Image string `protobuf:"bytes,3,opt,name=image,proto3" json:"image,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *LinkMetadata) Reset() { - *x = LinkMetadata{} - mi := &file_api_v1_markdown_service_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *LinkMetadata) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LinkMetadata) ProtoMessage() {} - -func (x *LinkMetadata) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[7] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LinkMetadata.ProtoReflect.Descriptor instead. -func (*LinkMetadata) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{7} -} - -func (x *LinkMetadata) GetTitle() string { - if x != nil { - return x.Title - } - return "" -} - -func (x *LinkMetadata) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *LinkMetadata) GetImage() string { - if x != nil { - return x.Image - } - return "" -} - -type Node struct { - state protoimpl.MessageState `protogen:"open.v1"` - Type NodeType `protobuf:"varint,1,opt,name=type,proto3,enum=memos.api.v1.NodeType" json:"type,omitempty"` - // Types that are valid to be assigned to Node: - // - // *Node_LineBreakNode - // *Node_ParagraphNode - // *Node_CodeBlockNode - // *Node_HeadingNode - // *Node_HorizontalRuleNode - // *Node_BlockquoteNode - // *Node_ListNode - // *Node_OrderedListItemNode - // *Node_UnorderedListItemNode - // *Node_TaskListItemNode - // *Node_MathBlockNode - // *Node_TableNode - // *Node_EmbeddedContentNode - // *Node_TextNode - // *Node_BoldNode - // *Node_ItalicNode - // *Node_BoldItalicNode - // *Node_CodeNode - // *Node_ImageNode - // *Node_LinkNode - // *Node_AutoLinkNode - // *Node_TagNode - // *Node_StrikethroughNode - // *Node_EscapingCharacterNode - // *Node_MathNode - // *Node_HighlightNode - // *Node_SubscriptNode - // *Node_SuperscriptNode - // *Node_ReferencedContentNode - // *Node_SpoilerNode - // *Node_HtmlElementNode - Node isNode_Node `protobuf_oneof:"node"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *Node) Reset() { - *x = Node{} - mi := &file_api_v1_markdown_service_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Node) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Node) ProtoMessage() {} - -func (x *Node) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[8] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Node.ProtoReflect.Descriptor instead. -func (*Node) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{8} -} - -func (x *Node) GetType() NodeType { - if x != nil { - return x.Type - } - return NodeType_NODE_UNSPECIFIED -} - -func (x *Node) GetNode() isNode_Node { - if x != nil { - return x.Node - } - return nil -} - -func (x *Node) GetLineBreakNode() *LineBreakNode { - if x != nil { - if x, ok := x.Node.(*Node_LineBreakNode); ok { - return x.LineBreakNode - } - } - return nil -} - -func (x *Node) GetParagraphNode() *ParagraphNode { - if x != nil { - if x, ok := x.Node.(*Node_ParagraphNode); ok { - return x.ParagraphNode - } - } - return nil -} - -func (x *Node) GetCodeBlockNode() *CodeBlockNode { - if x != nil { - if x, ok := x.Node.(*Node_CodeBlockNode); ok { - return x.CodeBlockNode - } - } - return nil -} - -func (x *Node) GetHeadingNode() *HeadingNode { - if x != nil { - if x, ok := x.Node.(*Node_HeadingNode); ok { - return x.HeadingNode - } - } - return nil -} - -func (x *Node) GetHorizontalRuleNode() *HorizontalRuleNode { - if x != nil { - if x, ok := x.Node.(*Node_HorizontalRuleNode); ok { - return x.HorizontalRuleNode - } - } - return nil -} - -func (x *Node) GetBlockquoteNode() *BlockquoteNode { - if x != nil { - if x, ok := x.Node.(*Node_BlockquoteNode); ok { - return x.BlockquoteNode - } - } - return nil -} - -func (x *Node) GetListNode() *ListNode { - if x != nil { - if x, ok := x.Node.(*Node_ListNode); ok { - return x.ListNode - } - } - return nil -} - -func (x *Node) GetOrderedListItemNode() *OrderedListItemNode { - if x != nil { - if x, ok := x.Node.(*Node_OrderedListItemNode); ok { - return x.OrderedListItemNode - } - } - return nil -} - -func (x *Node) GetUnorderedListItemNode() *UnorderedListItemNode { - if x != nil { - if x, ok := x.Node.(*Node_UnorderedListItemNode); ok { - return x.UnorderedListItemNode - } - } - return nil -} - -func (x *Node) GetTaskListItemNode() *TaskListItemNode { - if x != nil { - if x, ok := x.Node.(*Node_TaskListItemNode); ok { - return x.TaskListItemNode - } - } - return nil -} - -func (x *Node) GetMathBlockNode() *MathBlockNode { - if x != nil { - if x, ok := x.Node.(*Node_MathBlockNode); ok { - return x.MathBlockNode - } - } - return nil -} - -func (x *Node) GetTableNode() *TableNode { - if x != nil { - if x, ok := x.Node.(*Node_TableNode); ok { - return x.TableNode - } - } - return nil -} - -func (x *Node) GetEmbeddedContentNode() *EmbeddedContentNode { - if x != nil { - if x, ok := x.Node.(*Node_EmbeddedContentNode); ok { - return x.EmbeddedContentNode - } - } - return nil -} - -func (x *Node) GetTextNode() *TextNode { - if x != nil { - if x, ok := x.Node.(*Node_TextNode); ok { - return x.TextNode - } - } - return nil -} - -func (x *Node) GetBoldNode() *BoldNode { - if x != nil { - if x, ok := x.Node.(*Node_BoldNode); ok { - return x.BoldNode - } - } - return nil -} - -func (x *Node) GetItalicNode() *ItalicNode { - if x != nil { - if x, ok := x.Node.(*Node_ItalicNode); ok { - return x.ItalicNode - } - } - return nil -} - -func (x *Node) GetBoldItalicNode() *BoldItalicNode { - if x != nil { - if x, ok := x.Node.(*Node_BoldItalicNode); ok { - return x.BoldItalicNode - } - } - return nil -} - -func (x *Node) GetCodeNode() *CodeNode { - if x != nil { - if x, ok := x.Node.(*Node_CodeNode); ok { - return x.CodeNode - } - } - return nil -} - -func (x *Node) GetImageNode() *ImageNode { - if x != nil { - if x, ok := x.Node.(*Node_ImageNode); ok { - return x.ImageNode - } - } - return nil -} - -func (x *Node) GetLinkNode() *LinkNode { - if x != nil { - if x, ok := x.Node.(*Node_LinkNode); ok { - return x.LinkNode - } - } - return nil -} - -func (x *Node) GetAutoLinkNode() *AutoLinkNode { - if x != nil { - if x, ok := x.Node.(*Node_AutoLinkNode); ok { - return x.AutoLinkNode - } - } - return nil -} - -func (x *Node) GetTagNode() *TagNode { - if x != nil { - if x, ok := x.Node.(*Node_TagNode); ok { - return x.TagNode - } - } - return nil -} - -func (x *Node) GetStrikethroughNode() *StrikethroughNode { - if x != nil { - if x, ok := x.Node.(*Node_StrikethroughNode); ok { - return x.StrikethroughNode - } - } - return nil -} - -func (x *Node) GetEscapingCharacterNode() *EscapingCharacterNode { - if x != nil { - if x, ok := x.Node.(*Node_EscapingCharacterNode); ok { - return x.EscapingCharacterNode - } - } - return nil -} - -func (x *Node) GetMathNode() *MathNode { - if x != nil { - if x, ok := x.Node.(*Node_MathNode); ok { - return x.MathNode - } - } - return nil -} - -func (x *Node) GetHighlightNode() *HighlightNode { - if x != nil { - if x, ok := x.Node.(*Node_HighlightNode); ok { - return x.HighlightNode - } - } - return nil -} - -func (x *Node) GetSubscriptNode() *SubscriptNode { - if x != nil { - if x, ok := x.Node.(*Node_SubscriptNode); ok { - return x.SubscriptNode - } - } - return nil -} - -func (x *Node) GetSuperscriptNode() *SuperscriptNode { - if x != nil { - if x, ok := x.Node.(*Node_SuperscriptNode); ok { - return x.SuperscriptNode - } - } - return nil -} - -func (x *Node) GetReferencedContentNode() *ReferencedContentNode { - if x != nil { - if x, ok := x.Node.(*Node_ReferencedContentNode); ok { - return x.ReferencedContentNode - } - } - return nil -} - -func (x *Node) GetSpoilerNode() *SpoilerNode { - if x != nil { - if x, ok := x.Node.(*Node_SpoilerNode); ok { - return x.SpoilerNode - } - } - return nil -} - -func (x *Node) GetHtmlElementNode() *HTMLElementNode { - if x != nil { - if x, ok := x.Node.(*Node_HtmlElementNode); ok { - return x.HtmlElementNode - } - } - return nil -} - -type isNode_Node interface { - isNode_Node() -} - -type Node_LineBreakNode struct { - // Block nodes. - LineBreakNode *LineBreakNode `protobuf:"bytes,11,opt,name=line_break_node,json=lineBreakNode,proto3,oneof"` -} - -type Node_ParagraphNode struct { - ParagraphNode *ParagraphNode `protobuf:"bytes,12,opt,name=paragraph_node,json=paragraphNode,proto3,oneof"` -} - -type Node_CodeBlockNode struct { - CodeBlockNode *CodeBlockNode `protobuf:"bytes,13,opt,name=code_block_node,json=codeBlockNode,proto3,oneof"` -} - -type Node_HeadingNode struct { - HeadingNode *HeadingNode `protobuf:"bytes,14,opt,name=heading_node,json=headingNode,proto3,oneof"` -} - -type Node_HorizontalRuleNode struct { - HorizontalRuleNode *HorizontalRuleNode `protobuf:"bytes,15,opt,name=horizontal_rule_node,json=horizontalRuleNode,proto3,oneof"` -} - -type Node_BlockquoteNode struct { - BlockquoteNode *BlockquoteNode `protobuf:"bytes,16,opt,name=blockquote_node,json=blockquoteNode,proto3,oneof"` -} - -type Node_ListNode struct { - ListNode *ListNode `protobuf:"bytes,17,opt,name=list_node,json=listNode,proto3,oneof"` -} - -type Node_OrderedListItemNode struct { - OrderedListItemNode *OrderedListItemNode `protobuf:"bytes,18,opt,name=ordered_list_item_node,json=orderedListItemNode,proto3,oneof"` -} - -type Node_UnorderedListItemNode struct { - UnorderedListItemNode *UnorderedListItemNode `protobuf:"bytes,19,opt,name=unordered_list_item_node,json=unorderedListItemNode,proto3,oneof"` -} - -type Node_TaskListItemNode struct { - TaskListItemNode *TaskListItemNode `protobuf:"bytes,20,opt,name=task_list_item_node,json=taskListItemNode,proto3,oneof"` -} - -type Node_MathBlockNode struct { - MathBlockNode *MathBlockNode `protobuf:"bytes,21,opt,name=math_block_node,json=mathBlockNode,proto3,oneof"` -} - -type Node_TableNode struct { - TableNode *TableNode `protobuf:"bytes,22,opt,name=table_node,json=tableNode,proto3,oneof"` -} - -type Node_EmbeddedContentNode struct { - EmbeddedContentNode *EmbeddedContentNode `protobuf:"bytes,23,opt,name=embedded_content_node,json=embeddedContentNode,proto3,oneof"` -} - -type Node_TextNode struct { - // Inline nodes. - TextNode *TextNode `protobuf:"bytes,51,opt,name=text_node,json=textNode,proto3,oneof"` -} - -type Node_BoldNode struct { - BoldNode *BoldNode `protobuf:"bytes,52,opt,name=bold_node,json=boldNode,proto3,oneof"` -} - -type Node_ItalicNode struct { - ItalicNode *ItalicNode `protobuf:"bytes,53,opt,name=italic_node,json=italicNode,proto3,oneof"` -} - -type Node_BoldItalicNode struct { - BoldItalicNode *BoldItalicNode `protobuf:"bytes,54,opt,name=bold_italic_node,json=boldItalicNode,proto3,oneof"` -} - -type Node_CodeNode struct { - CodeNode *CodeNode `protobuf:"bytes,55,opt,name=code_node,json=codeNode,proto3,oneof"` -} - -type Node_ImageNode struct { - ImageNode *ImageNode `protobuf:"bytes,56,opt,name=image_node,json=imageNode,proto3,oneof"` -} - -type Node_LinkNode struct { - LinkNode *LinkNode `protobuf:"bytes,57,opt,name=link_node,json=linkNode,proto3,oneof"` -} - -type Node_AutoLinkNode struct { - AutoLinkNode *AutoLinkNode `protobuf:"bytes,58,opt,name=auto_link_node,json=autoLinkNode,proto3,oneof"` -} - -type Node_TagNode struct { - TagNode *TagNode `protobuf:"bytes,59,opt,name=tag_node,json=tagNode,proto3,oneof"` -} - -type Node_StrikethroughNode struct { - StrikethroughNode *StrikethroughNode `protobuf:"bytes,60,opt,name=strikethrough_node,json=strikethroughNode,proto3,oneof"` -} - -type Node_EscapingCharacterNode struct { - EscapingCharacterNode *EscapingCharacterNode `protobuf:"bytes,61,opt,name=escaping_character_node,json=escapingCharacterNode,proto3,oneof"` -} - -type Node_MathNode struct { - MathNode *MathNode `protobuf:"bytes,62,opt,name=math_node,json=mathNode,proto3,oneof"` -} - -type Node_HighlightNode struct { - HighlightNode *HighlightNode `protobuf:"bytes,63,opt,name=highlight_node,json=highlightNode,proto3,oneof"` -} - -type Node_SubscriptNode struct { - SubscriptNode *SubscriptNode `protobuf:"bytes,64,opt,name=subscript_node,json=subscriptNode,proto3,oneof"` -} - -type Node_SuperscriptNode struct { - SuperscriptNode *SuperscriptNode `protobuf:"bytes,65,opt,name=superscript_node,json=superscriptNode,proto3,oneof"` -} - -type Node_ReferencedContentNode struct { - ReferencedContentNode *ReferencedContentNode `protobuf:"bytes,66,opt,name=referenced_content_node,json=referencedContentNode,proto3,oneof"` -} - -type Node_SpoilerNode struct { - SpoilerNode *SpoilerNode `protobuf:"bytes,67,opt,name=spoiler_node,json=spoilerNode,proto3,oneof"` -} - -type Node_HtmlElementNode struct { - HtmlElementNode *HTMLElementNode `protobuf:"bytes,68,opt,name=html_element_node,json=htmlElementNode,proto3,oneof"` -} - -func (*Node_LineBreakNode) isNode_Node() {} - -func (*Node_ParagraphNode) isNode_Node() {} - -func (*Node_CodeBlockNode) isNode_Node() {} - -func (*Node_HeadingNode) isNode_Node() {} - -func (*Node_HorizontalRuleNode) isNode_Node() {} - -func (*Node_BlockquoteNode) isNode_Node() {} - -func (*Node_ListNode) isNode_Node() {} - -func (*Node_OrderedListItemNode) isNode_Node() {} - -func (*Node_UnorderedListItemNode) isNode_Node() {} - -func (*Node_TaskListItemNode) isNode_Node() {} - -func (*Node_MathBlockNode) isNode_Node() {} - -func (*Node_TableNode) isNode_Node() {} - -func (*Node_EmbeddedContentNode) isNode_Node() {} - -func (*Node_TextNode) isNode_Node() {} - -func (*Node_BoldNode) isNode_Node() {} - -func (*Node_ItalicNode) isNode_Node() {} - -func (*Node_BoldItalicNode) isNode_Node() {} - -func (*Node_CodeNode) isNode_Node() {} - -func (*Node_ImageNode) isNode_Node() {} - -func (*Node_LinkNode) isNode_Node() {} - -func (*Node_AutoLinkNode) isNode_Node() {} - -func (*Node_TagNode) isNode_Node() {} - -func (*Node_StrikethroughNode) isNode_Node() {} - -func (*Node_EscapingCharacterNode) isNode_Node() {} - -func (*Node_MathNode) isNode_Node() {} - -func (*Node_HighlightNode) isNode_Node() {} - -func (*Node_SubscriptNode) isNode_Node() {} - -func (*Node_SuperscriptNode) isNode_Node() {} - -func (*Node_ReferencedContentNode) isNode_Node() {} - -func (*Node_SpoilerNode) isNode_Node() {} - -func (*Node_HtmlElementNode) isNode_Node() {} - -type LineBreakNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *LineBreakNode) Reset() { - *x = LineBreakNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *LineBreakNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LineBreakNode) ProtoMessage() {} - -func (x *LineBreakNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[9] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LineBreakNode.ProtoReflect.Descriptor instead. -func (*LineBreakNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{9} -} - -type ParagraphNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Children []*Node `protobuf:"bytes,1,rep,name=children,proto3" json:"children,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ParagraphNode) Reset() { - *x = ParagraphNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ParagraphNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ParagraphNode) ProtoMessage() {} - -func (x *ParagraphNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[10] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ParagraphNode.ProtoReflect.Descriptor instead. -func (*ParagraphNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{10} -} - -func (x *ParagraphNode) GetChildren() []*Node { - if x != nil { - return x.Children - } - return nil -} - -type CodeBlockNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Language string `protobuf:"bytes,1,opt,name=language,proto3" json:"language,omitempty"` - Content string `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CodeBlockNode) Reset() { - *x = CodeBlockNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CodeBlockNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CodeBlockNode) ProtoMessage() {} - -func (x *CodeBlockNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[11] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CodeBlockNode.ProtoReflect.Descriptor instead. -func (*CodeBlockNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{11} -} - -func (x *CodeBlockNode) GetLanguage() string { - if x != nil { - return x.Language - } - return "" -} - -func (x *CodeBlockNode) GetContent() string { - if x != nil { - return x.Content - } - return "" -} - -type HeadingNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Level int32 `protobuf:"varint,1,opt,name=level,proto3" json:"level,omitempty"` - Children []*Node `protobuf:"bytes,2,rep,name=children,proto3" json:"children,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *HeadingNode) Reset() { - *x = HeadingNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *HeadingNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HeadingNode) ProtoMessage() {} - -func (x *HeadingNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[12] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HeadingNode.ProtoReflect.Descriptor instead. -func (*HeadingNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{12} -} - -func (x *HeadingNode) GetLevel() int32 { - if x != nil { - return x.Level - } - return 0 -} - -func (x *HeadingNode) GetChildren() []*Node { - if x != nil { - return x.Children - } - return nil -} - -type HorizontalRuleNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Symbol string `protobuf:"bytes,1,opt,name=symbol,proto3" json:"symbol,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *HorizontalRuleNode) Reset() { - *x = HorizontalRuleNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *HorizontalRuleNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HorizontalRuleNode) ProtoMessage() {} - -func (x *HorizontalRuleNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[13] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HorizontalRuleNode.ProtoReflect.Descriptor instead. -func (*HorizontalRuleNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{13} -} - -func (x *HorizontalRuleNode) GetSymbol() string { - if x != nil { - return x.Symbol - } - return "" -} - -type BlockquoteNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Children []*Node `protobuf:"bytes,1,rep,name=children,proto3" json:"children,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *BlockquoteNode) Reset() { - *x = BlockquoteNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *BlockquoteNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BlockquoteNode) ProtoMessage() {} - -func (x *BlockquoteNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[14] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BlockquoteNode.ProtoReflect.Descriptor instead. -func (*BlockquoteNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{14} -} - -func (x *BlockquoteNode) GetChildren() []*Node { - if x != nil { - return x.Children - } - return nil -} - -type ListNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Kind ListNode_Kind `protobuf:"varint,1,opt,name=kind,proto3,enum=memos.api.v1.ListNode_Kind" json:"kind,omitempty"` - Indent int32 `protobuf:"varint,2,opt,name=indent,proto3" json:"indent,omitempty"` - Children []*Node `protobuf:"bytes,3,rep,name=children,proto3" json:"children,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListNode) Reset() { - *x = ListNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListNode) ProtoMessage() {} - -func (x *ListNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[15] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListNode.ProtoReflect.Descriptor instead. -func (*ListNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{15} -} - -func (x *ListNode) GetKind() ListNode_Kind { - if x != nil { - return x.Kind - } - return ListNode_KIND_UNSPECIFIED -} - -func (x *ListNode) GetIndent() int32 { - if x != nil { - return x.Indent - } - return 0 -} - -func (x *ListNode) GetChildren() []*Node { - if x != nil { - return x.Children - } - return nil -} - -type OrderedListItemNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Number string `protobuf:"bytes,1,opt,name=number,proto3" json:"number,omitempty"` - Indent int32 `protobuf:"varint,2,opt,name=indent,proto3" json:"indent,omitempty"` - Children []*Node `protobuf:"bytes,3,rep,name=children,proto3" json:"children,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *OrderedListItemNode) Reset() { - *x = OrderedListItemNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *OrderedListItemNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OrderedListItemNode) ProtoMessage() {} - -func (x *OrderedListItemNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[16] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OrderedListItemNode.ProtoReflect.Descriptor instead. -func (*OrderedListItemNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{16} -} - -func (x *OrderedListItemNode) GetNumber() string { - if x != nil { - return x.Number - } - return "" -} - -func (x *OrderedListItemNode) GetIndent() int32 { - if x != nil { - return x.Indent - } - return 0 -} - -func (x *OrderedListItemNode) GetChildren() []*Node { - if x != nil { - return x.Children - } - return nil -} - -type UnorderedListItemNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Symbol string `protobuf:"bytes,1,opt,name=symbol,proto3" json:"symbol,omitempty"` - Indent int32 `protobuf:"varint,2,opt,name=indent,proto3" json:"indent,omitempty"` - Children []*Node `protobuf:"bytes,3,rep,name=children,proto3" json:"children,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *UnorderedListItemNode) Reset() { - *x = UnorderedListItemNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[17] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *UnorderedListItemNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UnorderedListItemNode) ProtoMessage() {} - -func (x *UnorderedListItemNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[17] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UnorderedListItemNode.ProtoReflect.Descriptor instead. -func (*UnorderedListItemNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{17} -} - -func (x *UnorderedListItemNode) GetSymbol() string { - if x != nil { - return x.Symbol - } - return "" -} - -func (x *UnorderedListItemNode) GetIndent() int32 { - if x != nil { - return x.Indent - } - return 0 -} - -func (x *UnorderedListItemNode) GetChildren() []*Node { - if x != nil { - return x.Children - } - return nil -} - -type TaskListItemNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Symbol string `protobuf:"bytes,1,opt,name=symbol,proto3" json:"symbol,omitempty"` - Indent int32 `protobuf:"varint,2,opt,name=indent,proto3" json:"indent,omitempty"` - Complete bool `protobuf:"varint,3,opt,name=complete,proto3" json:"complete,omitempty"` - Children []*Node `protobuf:"bytes,4,rep,name=children,proto3" json:"children,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *TaskListItemNode) Reset() { - *x = TaskListItemNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[18] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *TaskListItemNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TaskListItemNode) ProtoMessage() {} - -func (x *TaskListItemNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[18] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TaskListItemNode.ProtoReflect.Descriptor instead. -func (*TaskListItemNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{18} -} - -func (x *TaskListItemNode) GetSymbol() string { - if x != nil { - return x.Symbol - } - return "" -} - -func (x *TaskListItemNode) GetIndent() int32 { - if x != nil { - return x.Indent - } - return 0 -} - -func (x *TaskListItemNode) GetComplete() bool { - if x != nil { - return x.Complete - } - return false -} - -func (x *TaskListItemNode) GetChildren() []*Node { - if x != nil { - return x.Children - } - return nil -} - -type MathBlockNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *MathBlockNode) Reset() { - *x = MathBlockNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[19] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *MathBlockNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MathBlockNode) ProtoMessage() {} - -func (x *MathBlockNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[19] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use MathBlockNode.ProtoReflect.Descriptor instead. -func (*MathBlockNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{19} -} - -func (x *MathBlockNode) GetContent() string { - if x != nil { - return x.Content - } - return "" -} - -type TableNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Header []*Node `protobuf:"bytes,1,rep,name=header,proto3" json:"header,omitempty"` - Delimiter []string `protobuf:"bytes,2,rep,name=delimiter,proto3" json:"delimiter,omitempty"` - Rows []*TableNode_Row `protobuf:"bytes,3,rep,name=rows,proto3" json:"rows,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *TableNode) Reset() { - *x = TableNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[20] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *TableNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TableNode) ProtoMessage() {} - -func (x *TableNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[20] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TableNode.ProtoReflect.Descriptor instead. -func (*TableNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{20} -} - -func (x *TableNode) GetHeader() []*Node { - if x != nil { - return x.Header - } - return nil -} - -func (x *TableNode) GetDelimiter() []string { - if x != nil { - return x.Delimiter - } - return nil -} - -func (x *TableNode) GetRows() []*TableNode_Row { - if x != nil { - return x.Rows - } - return nil -} - -type EmbeddedContentNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The resource name of the embedded content. - ResourceName string `protobuf:"bytes,1,opt,name=resource_name,json=resourceName,proto3" json:"resource_name,omitempty"` - // Additional parameters for the embedded content. - Params string `protobuf:"bytes,2,opt,name=params,proto3" json:"params,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *EmbeddedContentNode) Reset() { - *x = EmbeddedContentNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[21] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *EmbeddedContentNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EmbeddedContentNode) ProtoMessage() {} - -func (x *EmbeddedContentNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[21] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EmbeddedContentNode.ProtoReflect.Descriptor instead. -func (*EmbeddedContentNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{21} -} - -func (x *EmbeddedContentNode) GetResourceName() string { - if x != nil { - return x.ResourceName - } - return "" -} - -func (x *EmbeddedContentNode) GetParams() string { - if x != nil { - return x.Params - } - return "" -} - -type TextNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *TextNode) Reset() { - *x = TextNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[22] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *TextNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TextNode) ProtoMessage() {} - -func (x *TextNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[22] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TextNode.ProtoReflect.Descriptor instead. -func (*TextNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{22} -} - -func (x *TextNode) GetContent() string { - if x != nil { - return x.Content - } - return "" -} - -type BoldNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Symbol string `protobuf:"bytes,1,opt,name=symbol,proto3" json:"symbol,omitempty"` - Children []*Node `protobuf:"bytes,2,rep,name=children,proto3" json:"children,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *BoldNode) Reset() { - *x = BoldNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[23] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *BoldNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BoldNode) ProtoMessage() {} - -func (x *BoldNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[23] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BoldNode.ProtoReflect.Descriptor instead. -func (*BoldNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{23} -} - -func (x *BoldNode) GetSymbol() string { - if x != nil { - return x.Symbol - } - return "" -} - -func (x *BoldNode) GetChildren() []*Node { - if x != nil { - return x.Children - } - return nil -} - -type ItalicNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Symbol string `protobuf:"bytes,1,opt,name=symbol,proto3" json:"symbol,omitempty"` - Children []*Node `protobuf:"bytes,2,rep,name=children,proto3" json:"children,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ItalicNode) Reset() { - *x = ItalicNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[24] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ItalicNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItalicNode) ProtoMessage() {} - -func (x *ItalicNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[24] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ItalicNode.ProtoReflect.Descriptor instead. -func (*ItalicNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{24} -} - -func (x *ItalicNode) GetSymbol() string { - if x != nil { - return x.Symbol - } - return "" -} - -func (x *ItalicNode) GetChildren() []*Node { - if x != nil { - return x.Children - } - return nil -} - -type BoldItalicNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Symbol string `protobuf:"bytes,1,opt,name=symbol,proto3" json:"symbol,omitempty"` - Content string `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *BoldItalicNode) Reset() { - *x = BoldItalicNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[25] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *BoldItalicNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BoldItalicNode) ProtoMessage() {} - -func (x *BoldItalicNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[25] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BoldItalicNode.ProtoReflect.Descriptor instead. -func (*BoldItalicNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{25} -} - -func (x *BoldItalicNode) GetSymbol() string { - if x != nil { - return x.Symbol - } - return "" -} - -func (x *BoldItalicNode) GetContent() string { - if x != nil { - return x.Content - } - return "" -} - -type CodeNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CodeNode) Reset() { - *x = CodeNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[26] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CodeNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CodeNode) ProtoMessage() {} - -func (x *CodeNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[26] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CodeNode.ProtoReflect.Descriptor instead. -func (*CodeNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{26} -} - -func (x *CodeNode) GetContent() string { - if x != nil { - return x.Content - } - return "" -} - -type ImageNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - AltText string `protobuf:"bytes,1,opt,name=alt_text,json=altText,proto3" json:"alt_text,omitempty"` - Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ImageNode) Reset() { - *x = ImageNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[27] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ImageNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ImageNode) ProtoMessage() {} - -func (x *ImageNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[27] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ImageNode.ProtoReflect.Descriptor instead. -func (*ImageNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{27} -} - -func (x *ImageNode) GetAltText() string { - if x != nil { - return x.AltText - } - return "" -} - -func (x *ImageNode) GetUrl() string { - if x != nil { - return x.Url - } - return "" -} - -type LinkNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Content []*Node `protobuf:"bytes,1,rep,name=content,proto3" json:"content,omitempty"` - Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *LinkNode) Reset() { - *x = LinkNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[28] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *LinkNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LinkNode) ProtoMessage() {} - -func (x *LinkNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[28] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LinkNode.ProtoReflect.Descriptor instead. -func (*LinkNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{28} -} - -func (x *LinkNode) GetContent() []*Node { - if x != nil { - return x.Content - } - return nil -} - -func (x *LinkNode) GetUrl() string { - if x != nil { - return x.Url - } - return "" -} - -type AutoLinkNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` - IsRawText bool `protobuf:"varint,2,opt,name=is_raw_text,json=isRawText,proto3" json:"is_raw_text,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AutoLinkNode) Reset() { - *x = AutoLinkNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[29] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AutoLinkNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AutoLinkNode) ProtoMessage() {} - -func (x *AutoLinkNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[29] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AutoLinkNode.ProtoReflect.Descriptor instead. -func (*AutoLinkNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{29} -} - -func (x *AutoLinkNode) GetUrl() string { - if x != nil { - return x.Url - } - return "" -} - -func (x *AutoLinkNode) GetIsRawText() bool { - if x != nil { - return x.IsRawText - } - return false -} - -type TagNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *TagNode) Reset() { - *x = TagNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[30] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *TagNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TagNode) ProtoMessage() {} - -func (x *TagNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[30] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TagNode.ProtoReflect.Descriptor instead. -func (*TagNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{30} -} - -func (x *TagNode) GetContent() string { - if x != nil { - return x.Content - } - return "" -} - -type StrikethroughNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *StrikethroughNode) Reset() { - *x = StrikethroughNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[31] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StrikethroughNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StrikethroughNode) ProtoMessage() {} - -func (x *StrikethroughNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[31] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StrikethroughNode.ProtoReflect.Descriptor instead. -func (*StrikethroughNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{31} -} - -func (x *StrikethroughNode) GetContent() string { - if x != nil { - return x.Content - } - return "" -} - -type EscapingCharacterNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Symbol string `protobuf:"bytes,1,opt,name=symbol,proto3" json:"symbol,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *EscapingCharacterNode) Reset() { - *x = EscapingCharacterNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[32] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *EscapingCharacterNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EscapingCharacterNode) ProtoMessage() {} - -func (x *EscapingCharacterNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[32] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EscapingCharacterNode.ProtoReflect.Descriptor instead. -func (*EscapingCharacterNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{32} -} - -func (x *EscapingCharacterNode) GetSymbol() string { - if x != nil { - return x.Symbol - } - return "" -} - -type MathNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *MathNode) Reset() { - *x = MathNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[33] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *MathNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MathNode) ProtoMessage() {} - -func (x *MathNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[33] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use MathNode.ProtoReflect.Descriptor instead. -func (*MathNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{33} -} - -func (x *MathNode) GetContent() string { - if x != nil { - return x.Content - } - return "" -} - -type HighlightNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *HighlightNode) Reset() { - *x = HighlightNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[34] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *HighlightNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HighlightNode) ProtoMessage() {} - -func (x *HighlightNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[34] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HighlightNode.ProtoReflect.Descriptor instead. -func (*HighlightNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{34} -} - -func (x *HighlightNode) GetContent() string { - if x != nil { - return x.Content - } - return "" -} - -type SubscriptNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SubscriptNode) Reset() { - *x = SubscriptNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[35] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SubscriptNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubscriptNode) ProtoMessage() {} - -func (x *SubscriptNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[35] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SubscriptNode.ProtoReflect.Descriptor instead. -func (*SubscriptNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{35} -} - -func (x *SubscriptNode) GetContent() string { - if x != nil { - return x.Content - } - return "" -} - -type SuperscriptNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SuperscriptNode) Reset() { - *x = SuperscriptNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[36] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SuperscriptNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SuperscriptNode) ProtoMessage() {} - -func (x *SuperscriptNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[36] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SuperscriptNode.ProtoReflect.Descriptor instead. -func (*SuperscriptNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{36} -} - -func (x *SuperscriptNode) GetContent() string { - if x != nil { - return x.Content - } - return "" -} - -type ReferencedContentNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The resource name of the referenced content. - ResourceName string `protobuf:"bytes,1,opt,name=resource_name,json=resourceName,proto3" json:"resource_name,omitempty"` - // Additional parameters for the referenced content. - Params string `protobuf:"bytes,2,opt,name=params,proto3" json:"params,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ReferencedContentNode) Reset() { - *x = ReferencedContentNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[37] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ReferencedContentNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReferencedContentNode) ProtoMessage() {} - -func (x *ReferencedContentNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[37] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReferencedContentNode.ProtoReflect.Descriptor instead. -func (*ReferencedContentNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{37} -} - -func (x *ReferencedContentNode) GetResourceName() string { - if x != nil { - return x.ResourceName - } - return "" -} - -func (x *ReferencedContentNode) GetParams() string { - if x != nil { - return x.Params - } - return "" -} - -type SpoilerNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SpoilerNode) Reset() { - *x = SpoilerNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[38] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SpoilerNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SpoilerNode) ProtoMessage() {} - -func (x *SpoilerNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[38] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SpoilerNode.ProtoReflect.Descriptor instead. -func (*SpoilerNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{38} -} - -func (x *SpoilerNode) GetContent() string { - if x != nil { - return x.Content - } - return "" -} - -type HTMLElementNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - TagName string `protobuf:"bytes,1,opt,name=tag_name,json=tagName,proto3" json:"tag_name,omitempty"` - Attributes map[string]string `protobuf:"bytes,2,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - Children []*Node `protobuf:"bytes,3,rep,name=children,proto3" json:"children,omitempty"` - IsSelfClosing bool `protobuf:"varint,4,opt,name=is_self_closing,json=isSelfClosing,proto3" json:"is_self_closing,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *HTMLElementNode) Reset() { - *x = HTMLElementNode{} - mi := &file_api_v1_markdown_service_proto_msgTypes[39] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *HTMLElementNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HTMLElementNode) ProtoMessage() {} - -func (x *HTMLElementNode) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[39] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HTMLElementNode.ProtoReflect.Descriptor instead. -func (*HTMLElementNode) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{39} -} - -func (x *HTMLElementNode) GetTagName() string { - if x != nil { - return x.TagName - } - return "" -} - -func (x *HTMLElementNode) GetAttributes() map[string]string { - if x != nil { - return x.Attributes - } - return nil -} - -func (x *HTMLElementNode) GetChildren() []*Node { - if x != nil { - return x.Children - } - return nil -} - -func (x *HTMLElementNode) GetIsSelfClosing() bool { - if x != nil { - return x.IsSelfClosing - } - return false -} - -type TableNode_Row struct { - state protoimpl.MessageState `protogen:"open.v1"` - Cells []*Node `protobuf:"bytes,1,rep,name=cells,proto3" json:"cells,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *TableNode_Row) Reset() { - *x = TableNode_Row{} - mi := &file_api_v1_markdown_service_proto_msgTypes[40] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *TableNode_Row) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TableNode_Row) ProtoMessage() {} - -func (x *TableNode_Row) ProtoReflect() protoreflect.Message { - mi := &file_api_v1_markdown_service_proto_msgTypes[40] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TableNode_Row.ProtoReflect.Descriptor instead. -func (*TableNode_Row) Descriptor() ([]byte, []int) { - return file_api_v1_markdown_service_proto_rawDescGZIP(), []int{20, 0} -} - -func (x *TableNode_Row) GetCells() []*Node { - if x != nil { - return x.Cells - } - return nil -} - -var File_api_v1_markdown_service_proto protoreflect.FileDescriptor - -const file_api_v1_markdown_service_proto_rawDesc = "" + - "\n" + - "\x1dapi/v1/markdown_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x1fgoogle/api/field_behavior.proto\"7\n" + - "\x14ParseMarkdownRequest\x12\x1f\n" + - "\bmarkdown\x18\x01 \x01(\tB\x03\xe0A\x02R\bmarkdown\"A\n" + - "\x15ParseMarkdownResponse\x12(\n" + - "\x05nodes\x18\x01 \x03(\v2\x12.memos.api.v1.NodeR\x05nodes\"L\n" + - "\x1bRestoreMarkdownNodesRequest\x12-\n" + - "\x05nodes\x18\x01 \x03(\v2\x12.memos.api.v1.NodeB\x03\xe0A\x02R\x05nodes\":\n" + - "\x1cRestoreMarkdownNodesResponse\x12\x1a\n" + - "\bmarkdown\x18\x01 \x01(\tR\bmarkdown\"N\n" + - "\x1dStringifyMarkdownNodesRequest\x12-\n" + - "\x05nodes\x18\x01 \x03(\v2\x12.memos.api.v1.NodeB\x03\xe0A\x02R\x05nodes\"?\n" + - "\x1eStringifyMarkdownNodesResponse\x12\x1d\n" + - "\n" + - "plain_text\x18\x01 \x01(\tR\tplainText\"1\n" + - "\x16GetLinkMetadataRequest\x12\x17\n" + - "\x04link\x18\x01 \x01(\tB\x03\xe0A\x02R\x04link\"\\\n" + - "\fLinkMetadata\x12\x14\n" + - "\x05title\x18\x01 \x01(\tR\x05title\x12 \n" + - "\vdescription\x18\x02 \x01(\tR\vdescription\x12\x14\n" + - "\x05image\x18\x03 \x01(\tR\x05image\"\xca\x11\n" + - "\x04Node\x12*\n" + - "\x04type\x18\x01 \x01(\x0e2\x16.memos.api.v1.NodeTypeR\x04type\x12E\n" + - "\x0fline_break_node\x18\v \x01(\v2\x1b.memos.api.v1.LineBreakNodeH\x00R\rlineBreakNode\x12D\n" + - "\x0eparagraph_node\x18\f \x01(\v2\x1b.memos.api.v1.ParagraphNodeH\x00R\rparagraphNode\x12E\n" + - "\x0fcode_block_node\x18\r \x01(\v2\x1b.memos.api.v1.CodeBlockNodeH\x00R\rcodeBlockNode\x12>\n" + - "\fheading_node\x18\x0e \x01(\v2\x19.memos.api.v1.HeadingNodeH\x00R\vheadingNode\x12T\n" + - "\x14horizontal_rule_node\x18\x0f \x01(\v2 .memos.api.v1.HorizontalRuleNodeH\x00R\x12horizontalRuleNode\x12G\n" + - "\x0fblockquote_node\x18\x10 \x01(\v2\x1c.memos.api.v1.BlockquoteNodeH\x00R\x0eblockquoteNode\x125\n" + - "\tlist_node\x18\x11 \x01(\v2\x16.memos.api.v1.ListNodeH\x00R\blistNode\x12X\n" + - "\x16ordered_list_item_node\x18\x12 \x01(\v2!.memos.api.v1.OrderedListItemNodeH\x00R\x13orderedListItemNode\x12^\n" + - "\x18unordered_list_item_node\x18\x13 \x01(\v2#.memos.api.v1.UnorderedListItemNodeH\x00R\x15unorderedListItemNode\x12O\n" + - "\x13task_list_item_node\x18\x14 \x01(\v2\x1e.memos.api.v1.TaskListItemNodeH\x00R\x10taskListItemNode\x12E\n" + - "\x0fmath_block_node\x18\x15 \x01(\v2\x1b.memos.api.v1.MathBlockNodeH\x00R\rmathBlockNode\x128\n" + - "\n" + - "table_node\x18\x16 \x01(\v2\x17.memos.api.v1.TableNodeH\x00R\ttableNode\x12W\n" + - "\x15embedded_content_node\x18\x17 \x01(\v2!.memos.api.v1.EmbeddedContentNodeH\x00R\x13embeddedContentNode\x125\n" + - "\ttext_node\x183 \x01(\v2\x16.memos.api.v1.TextNodeH\x00R\btextNode\x125\n" + - "\tbold_node\x184 \x01(\v2\x16.memos.api.v1.BoldNodeH\x00R\bboldNode\x12;\n" + - "\vitalic_node\x185 \x01(\v2\x18.memos.api.v1.ItalicNodeH\x00R\n" + - "italicNode\x12H\n" + - "\x10bold_italic_node\x186 \x01(\v2\x1c.memos.api.v1.BoldItalicNodeH\x00R\x0eboldItalicNode\x125\n" + - "\tcode_node\x187 \x01(\v2\x16.memos.api.v1.CodeNodeH\x00R\bcodeNode\x128\n" + - "\n" + - "image_node\x188 \x01(\v2\x17.memos.api.v1.ImageNodeH\x00R\timageNode\x125\n" + - "\tlink_node\x189 \x01(\v2\x16.memos.api.v1.LinkNodeH\x00R\blinkNode\x12B\n" + - "\x0eauto_link_node\x18: \x01(\v2\x1a.memos.api.v1.AutoLinkNodeH\x00R\fautoLinkNode\x122\n" + - "\btag_node\x18; \x01(\v2\x15.memos.api.v1.TagNodeH\x00R\atagNode\x12P\n" + - "\x12strikethrough_node\x18< \x01(\v2\x1f.memos.api.v1.StrikethroughNodeH\x00R\x11strikethroughNode\x12]\n" + - "\x17escaping_character_node\x18= \x01(\v2#.memos.api.v1.EscapingCharacterNodeH\x00R\x15escapingCharacterNode\x125\n" + - "\tmath_node\x18> \x01(\v2\x16.memos.api.v1.MathNodeH\x00R\bmathNode\x12D\n" + - "\x0ehighlight_node\x18? \x01(\v2\x1b.memos.api.v1.HighlightNodeH\x00R\rhighlightNode\x12D\n" + - "\x0esubscript_node\x18@ \x01(\v2\x1b.memos.api.v1.SubscriptNodeH\x00R\rsubscriptNode\x12J\n" + - "\x10superscript_node\x18A \x01(\v2\x1d.memos.api.v1.SuperscriptNodeH\x00R\x0fsuperscriptNode\x12]\n" + - "\x17referenced_content_node\x18B \x01(\v2#.memos.api.v1.ReferencedContentNodeH\x00R\x15referencedContentNode\x12>\n" + - "\fspoiler_node\x18C \x01(\v2\x19.memos.api.v1.SpoilerNodeH\x00R\vspoilerNode\x12K\n" + - "\x11html_element_node\x18D \x01(\v2\x1d.memos.api.v1.HTMLElementNodeH\x00R\x0fhtmlElementNodeB\x06\n" + - "\x04node\"\x0f\n" + - "\rLineBreakNode\"?\n" + - "\rParagraphNode\x12.\n" + - "\bchildren\x18\x01 \x03(\v2\x12.memos.api.v1.NodeR\bchildren\"E\n" + - "\rCodeBlockNode\x12\x1a\n" + - "\blanguage\x18\x01 \x01(\tR\blanguage\x12\x18\n" + - "\acontent\x18\x02 \x01(\tR\acontent\"S\n" + - "\vHeadingNode\x12\x14\n" + - "\x05level\x18\x01 \x01(\x05R\x05level\x12.\n" + - "\bchildren\x18\x02 \x03(\v2\x12.memos.api.v1.NodeR\bchildren\",\n" + - "\x12HorizontalRuleNode\x12\x16\n" + - "\x06symbol\x18\x01 \x01(\tR\x06symbol\"@\n" + - "\x0eBlockquoteNode\x12.\n" + - "\bchildren\x18\x01 \x03(\v2\x12.memos.api.v1.NodeR\bchildren\"\xce\x01\n" + - "\bListNode\x12/\n" + - "\x04kind\x18\x01 \x01(\x0e2\x1b.memos.api.v1.ListNode.KindR\x04kind\x12\x16\n" + - "\x06indent\x18\x02 \x01(\x05R\x06indent\x12.\n" + - "\bchildren\x18\x03 \x03(\v2\x12.memos.api.v1.NodeR\bchildren\"I\n" + - "\x04Kind\x12\x14\n" + - "\x10KIND_UNSPECIFIED\x10\x00\x12\v\n" + - "\aORDERED\x10\x01\x12\r\n" + - "\tUNORDERED\x10\x02\x12\x0f\n" + - "\vDESCRIPTION\x10\x03\"u\n" + - "\x13OrderedListItemNode\x12\x16\n" + - "\x06number\x18\x01 \x01(\tR\x06number\x12\x16\n" + - "\x06indent\x18\x02 \x01(\x05R\x06indent\x12.\n" + - "\bchildren\x18\x03 \x03(\v2\x12.memos.api.v1.NodeR\bchildren\"w\n" + - "\x15UnorderedListItemNode\x12\x16\n" + - "\x06symbol\x18\x01 \x01(\tR\x06symbol\x12\x16\n" + - "\x06indent\x18\x02 \x01(\x05R\x06indent\x12.\n" + - "\bchildren\x18\x03 \x03(\v2\x12.memos.api.v1.NodeR\bchildren\"\x8e\x01\n" + - "\x10TaskListItemNode\x12\x16\n" + - "\x06symbol\x18\x01 \x01(\tR\x06symbol\x12\x16\n" + - "\x06indent\x18\x02 \x01(\x05R\x06indent\x12\x1a\n" + - "\bcomplete\x18\x03 \x01(\bR\bcomplete\x12.\n" + - "\bchildren\x18\x04 \x03(\v2\x12.memos.api.v1.NodeR\bchildren\")\n" + - "\rMathBlockNode\x12\x18\n" + - "\acontent\x18\x01 \x01(\tR\acontent\"\xb7\x01\n" + - "\tTableNode\x12*\n" + - "\x06header\x18\x01 \x03(\v2\x12.memos.api.v1.NodeR\x06header\x12\x1c\n" + - "\tdelimiter\x18\x02 \x03(\tR\tdelimiter\x12/\n" + - "\x04rows\x18\x03 \x03(\v2\x1b.memos.api.v1.TableNode.RowR\x04rows\x1a/\n" + - "\x03Row\x12(\n" + - "\x05cells\x18\x01 \x03(\v2\x12.memos.api.v1.NodeR\x05cells\"R\n" + - "\x13EmbeddedContentNode\x12#\n" + - "\rresource_name\x18\x01 \x01(\tR\fresourceName\x12\x16\n" + - "\x06params\x18\x02 \x01(\tR\x06params\"$\n" + - "\bTextNode\x12\x18\n" + - "\acontent\x18\x01 \x01(\tR\acontent\"R\n" + - "\bBoldNode\x12\x16\n" + - "\x06symbol\x18\x01 \x01(\tR\x06symbol\x12.\n" + - "\bchildren\x18\x02 \x03(\v2\x12.memos.api.v1.NodeR\bchildren\"T\n" + - "\n" + - "ItalicNode\x12\x16\n" + - "\x06symbol\x18\x01 \x01(\tR\x06symbol\x12.\n" + - "\bchildren\x18\x02 \x03(\v2\x12.memos.api.v1.NodeR\bchildren\"B\n" + - "\x0eBoldItalicNode\x12\x16\n" + - "\x06symbol\x18\x01 \x01(\tR\x06symbol\x12\x18\n" + - "\acontent\x18\x02 \x01(\tR\acontent\"$\n" + - "\bCodeNode\x12\x18\n" + - "\acontent\x18\x01 \x01(\tR\acontent\"8\n" + - "\tImageNode\x12\x19\n" + - "\balt_text\x18\x01 \x01(\tR\aaltText\x12\x10\n" + - "\x03url\x18\x02 \x01(\tR\x03url\"J\n" + - "\bLinkNode\x12,\n" + - "\acontent\x18\x01 \x03(\v2\x12.memos.api.v1.NodeR\acontent\x12\x10\n" + - "\x03url\x18\x02 \x01(\tR\x03url\"@\n" + - "\fAutoLinkNode\x12\x10\n" + - "\x03url\x18\x01 \x01(\tR\x03url\x12\x1e\n" + - "\vis_raw_text\x18\x02 \x01(\bR\tisRawText\"#\n" + - "\aTagNode\x12\x18\n" + - "\acontent\x18\x01 \x01(\tR\acontent\"-\n" + - "\x11StrikethroughNode\x12\x18\n" + - "\acontent\x18\x01 \x01(\tR\acontent\"/\n" + - "\x15EscapingCharacterNode\x12\x16\n" + - "\x06symbol\x18\x01 \x01(\tR\x06symbol\"$\n" + - "\bMathNode\x12\x18\n" + - "\acontent\x18\x01 \x01(\tR\acontent\")\n" + - "\rHighlightNode\x12\x18\n" + - "\acontent\x18\x01 \x01(\tR\acontent\")\n" + - "\rSubscriptNode\x12\x18\n" + - "\acontent\x18\x01 \x01(\tR\acontent\"+\n" + - "\x0fSuperscriptNode\x12\x18\n" + - "\acontent\x18\x01 \x01(\tR\acontent\"T\n" + - "\x15ReferencedContentNode\x12#\n" + - "\rresource_name\x18\x01 \x01(\tR\fresourceName\x12\x16\n" + - "\x06params\x18\x02 \x01(\tR\x06params\"'\n" + - "\vSpoilerNode\x12\x18\n" + - "\acontent\x18\x01 \x01(\tR\acontent\"\x92\x02\n" + - "\x0fHTMLElementNode\x12\x19\n" + - "\btag_name\x18\x01 \x01(\tR\atagName\x12M\n" + - "\n" + - "attributes\x18\x02 \x03(\v2-.memos.api.v1.HTMLElementNode.AttributesEntryR\n" + - "attributes\x12.\n" + - "\bchildren\x18\x03 \x03(\v2\x12.memos.api.v1.NodeR\bchildren\x12&\n" + - "\x0fis_self_closing\x18\x04 \x01(\bR\risSelfClosing\x1a=\n" + - "\x0fAttributesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01*\x83\x04\n" + - "\bNodeType\x12\x14\n" + - "\x10NODE_UNSPECIFIED\x10\x00\x12\x0e\n" + - "\n" + - "LINE_BREAK\x10\x01\x12\r\n" + - "\tPARAGRAPH\x10\x02\x12\x0e\n" + - "\n" + - "CODE_BLOCK\x10\x03\x12\v\n" + - "\aHEADING\x10\x04\x12\x13\n" + - "\x0fHORIZONTAL_RULE\x10\x05\x12\x0e\n" + - "\n" + - "BLOCKQUOTE\x10\x06\x12\b\n" + - "\x04LIST\x10\a\x12\x15\n" + - "\x11ORDERED_LIST_ITEM\x10\b\x12\x17\n" + - "\x13UNORDERED_LIST_ITEM\x10\t\x12\x12\n" + - "\x0eTASK_LIST_ITEM\x10\n" + - "\x12\x0e\n" + - "\n" + - "MATH_BLOCK\x10\v\x12\t\n" + - "\x05TABLE\x10\f\x12\x14\n" + - "\x10EMBEDDED_CONTENT\x10\r\x12\b\n" + - "\x04TEXT\x103\x12\b\n" + - "\x04BOLD\x104\x12\n" + - "\n" + - "\x06ITALIC\x105\x12\x0f\n" + - "\vBOLD_ITALIC\x106\x12\b\n" + - "\x04CODE\x107\x12\t\n" + - "\x05IMAGE\x108\x12\b\n" + - "\x04LINK\x109\x12\r\n" + - "\tAUTO_LINK\x10:\x12\a\n" + - "\x03TAG\x10;\x12\x11\n" + - "\rSTRIKETHROUGH\x10<\x12\x16\n" + - "\x12ESCAPING_CHARACTER\x10=\x12\b\n" + - "\x04MATH\x10>\x12\r\n" + - "\tHIGHLIGHT\x10?\x12\r\n" + - "\tSUBSCRIPT\x10@\x12\x0f\n" + - "\vSUPERSCRIPT\x10A\x12\x16\n" + - "\x12REFERENCED_CONTENT\x10B\x12\v\n" + - "\aSPOILER\x10C\x12\x10\n" + - "\fHTML_ELEMENT\x10D2\xc1\x04\n" + - "\x0fMarkdownService\x12{\n" + - "\rParseMarkdown\x12\".memos.api.v1.ParseMarkdownRequest\x1a#.memos.api.v1.ParseMarkdownResponse\"!\x82\xd3\xe4\x93\x02\x1b:\x01*\"\x16/api/v1/markdown:parse\x12\x92\x01\n" + - "\x14RestoreMarkdownNodes\x12).memos.api.v1.RestoreMarkdownNodesRequest\x1a*.memos.api.v1.RestoreMarkdownNodesResponse\"#\x82\xd3\xe4\x93\x02\x1d:\x01*\"\x18/api/v1/markdown:restore\x12\x9a\x01\n" + - "\x16StringifyMarkdownNodes\x12+.memos.api.v1.StringifyMarkdownNodesRequest\x1a,.memos.api.v1.StringifyMarkdownNodesResponse\"%\x82\xd3\xe4\x93\x02\x1f:\x01*\"\x1a/api/v1/markdown:stringify\x12\x7f\n" + - "\x0fGetLinkMetadata\x12$.memos.api.v1.GetLinkMetadataRequest\x1a\x1a.memos.api.v1.LinkMetadata\"*\x82\xd3\xe4\x93\x02$\x12\"/api/v1/markdown/links:getMetadataB\xac\x01\n" + - "\x10com.memos.api.v1B\x14MarkdownServiceProtoP\x01Z0github.com/usememos/memos/proto/gen/api/v1;apiv1\xa2\x02\x03MAX\xaa\x02\fMemos.Api.V1\xca\x02\fMemos\\Api\\V1\xe2\x02\x18Memos\\Api\\V1\\GPBMetadata\xea\x02\x0eMemos::Api::V1b\x06proto3" - -var ( - file_api_v1_markdown_service_proto_rawDescOnce sync.Once - file_api_v1_markdown_service_proto_rawDescData []byte -) - -func file_api_v1_markdown_service_proto_rawDescGZIP() []byte { - file_api_v1_markdown_service_proto_rawDescOnce.Do(func() { - file_api_v1_markdown_service_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_api_v1_markdown_service_proto_rawDesc), len(file_api_v1_markdown_service_proto_rawDesc))) - }) - return file_api_v1_markdown_service_proto_rawDescData -} - -var file_api_v1_markdown_service_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_api_v1_markdown_service_proto_msgTypes = make([]protoimpl.MessageInfo, 42) -var file_api_v1_markdown_service_proto_goTypes = []any{ - (NodeType)(0), // 0: memos.api.v1.NodeType - (ListNode_Kind)(0), // 1: memos.api.v1.ListNode.Kind - (*ParseMarkdownRequest)(nil), // 2: memos.api.v1.ParseMarkdownRequest - (*ParseMarkdownResponse)(nil), // 3: memos.api.v1.ParseMarkdownResponse - (*RestoreMarkdownNodesRequest)(nil), // 4: memos.api.v1.RestoreMarkdownNodesRequest - (*RestoreMarkdownNodesResponse)(nil), // 5: memos.api.v1.RestoreMarkdownNodesResponse - (*StringifyMarkdownNodesRequest)(nil), // 6: memos.api.v1.StringifyMarkdownNodesRequest - (*StringifyMarkdownNodesResponse)(nil), // 7: memos.api.v1.StringifyMarkdownNodesResponse - (*GetLinkMetadataRequest)(nil), // 8: memos.api.v1.GetLinkMetadataRequest - (*LinkMetadata)(nil), // 9: memos.api.v1.LinkMetadata - (*Node)(nil), // 10: memos.api.v1.Node - (*LineBreakNode)(nil), // 11: memos.api.v1.LineBreakNode - (*ParagraphNode)(nil), // 12: memos.api.v1.ParagraphNode - (*CodeBlockNode)(nil), // 13: memos.api.v1.CodeBlockNode - (*HeadingNode)(nil), // 14: memos.api.v1.HeadingNode - (*HorizontalRuleNode)(nil), // 15: memos.api.v1.HorizontalRuleNode - (*BlockquoteNode)(nil), // 16: memos.api.v1.BlockquoteNode - (*ListNode)(nil), // 17: memos.api.v1.ListNode - (*OrderedListItemNode)(nil), // 18: memos.api.v1.OrderedListItemNode - (*UnorderedListItemNode)(nil), // 19: memos.api.v1.UnorderedListItemNode - (*TaskListItemNode)(nil), // 20: memos.api.v1.TaskListItemNode - (*MathBlockNode)(nil), // 21: memos.api.v1.MathBlockNode - (*TableNode)(nil), // 22: memos.api.v1.TableNode - (*EmbeddedContentNode)(nil), // 23: memos.api.v1.EmbeddedContentNode - (*TextNode)(nil), // 24: memos.api.v1.TextNode - (*BoldNode)(nil), // 25: memos.api.v1.BoldNode - (*ItalicNode)(nil), // 26: memos.api.v1.ItalicNode - (*BoldItalicNode)(nil), // 27: memos.api.v1.BoldItalicNode - (*CodeNode)(nil), // 28: memos.api.v1.CodeNode - (*ImageNode)(nil), // 29: memos.api.v1.ImageNode - (*LinkNode)(nil), // 30: memos.api.v1.LinkNode - (*AutoLinkNode)(nil), // 31: memos.api.v1.AutoLinkNode - (*TagNode)(nil), // 32: memos.api.v1.TagNode - (*StrikethroughNode)(nil), // 33: memos.api.v1.StrikethroughNode - (*EscapingCharacterNode)(nil), // 34: memos.api.v1.EscapingCharacterNode - (*MathNode)(nil), // 35: memos.api.v1.MathNode - (*HighlightNode)(nil), // 36: memos.api.v1.HighlightNode - (*SubscriptNode)(nil), // 37: memos.api.v1.SubscriptNode - (*SuperscriptNode)(nil), // 38: memos.api.v1.SuperscriptNode - (*ReferencedContentNode)(nil), // 39: memos.api.v1.ReferencedContentNode - (*SpoilerNode)(nil), // 40: memos.api.v1.SpoilerNode - (*HTMLElementNode)(nil), // 41: memos.api.v1.HTMLElementNode - (*TableNode_Row)(nil), // 42: memos.api.v1.TableNode.Row - nil, // 43: memos.api.v1.HTMLElementNode.AttributesEntry -} -var file_api_v1_markdown_service_proto_depIdxs = []int32{ - 10, // 0: memos.api.v1.ParseMarkdownResponse.nodes:type_name -> memos.api.v1.Node - 10, // 1: memos.api.v1.RestoreMarkdownNodesRequest.nodes:type_name -> memos.api.v1.Node - 10, // 2: memos.api.v1.StringifyMarkdownNodesRequest.nodes:type_name -> memos.api.v1.Node - 0, // 3: memos.api.v1.Node.type:type_name -> memos.api.v1.NodeType - 11, // 4: memos.api.v1.Node.line_break_node:type_name -> memos.api.v1.LineBreakNode - 12, // 5: memos.api.v1.Node.paragraph_node:type_name -> memos.api.v1.ParagraphNode - 13, // 6: memos.api.v1.Node.code_block_node:type_name -> memos.api.v1.CodeBlockNode - 14, // 7: memos.api.v1.Node.heading_node:type_name -> memos.api.v1.HeadingNode - 15, // 8: memos.api.v1.Node.horizontal_rule_node:type_name -> memos.api.v1.HorizontalRuleNode - 16, // 9: memos.api.v1.Node.blockquote_node:type_name -> memos.api.v1.BlockquoteNode - 17, // 10: memos.api.v1.Node.list_node:type_name -> memos.api.v1.ListNode - 18, // 11: memos.api.v1.Node.ordered_list_item_node:type_name -> memos.api.v1.OrderedListItemNode - 19, // 12: memos.api.v1.Node.unordered_list_item_node:type_name -> memos.api.v1.UnorderedListItemNode - 20, // 13: memos.api.v1.Node.task_list_item_node:type_name -> memos.api.v1.TaskListItemNode - 21, // 14: memos.api.v1.Node.math_block_node:type_name -> memos.api.v1.MathBlockNode - 22, // 15: memos.api.v1.Node.table_node:type_name -> memos.api.v1.TableNode - 23, // 16: memos.api.v1.Node.embedded_content_node:type_name -> memos.api.v1.EmbeddedContentNode - 24, // 17: memos.api.v1.Node.text_node:type_name -> memos.api.v1.TextNode - 25, // 18: memos.api.v1.Node.bold_node:type_name -> memos.api.v1.BoldNode - 26, // 19: memos.api.v1.Node.italic_node:type_name -> memos.api.v1.ItalicNode - 27, // 20: memos.api.v1.Node.bold_italic_node:type_name -> memos.api.v1.BoldItalicNode - 28, // 21: memos.api.v1.Node.code_node:type_name -> memos.api.v1.CodeNode - 29, // 22: memos.api.v1.Node.image_node:type_name -> memos.api.v1.ImageNode - 30, // 23: memos.api.v1.Node.link_node:type_name -> memos.api.v1.LinkNode - 31, // 24: memos.api.v1.Node.auto_link_node:type_name -> memos.api.v1.AutoLinkNode - 32, // 25: memos.api.v1.Node.tag_node:type_name -> memos.api.v1.TagNode - 33, // 26: memos.api.v1.Node.strikethrough_node:type_name -> memos.api.v1.StrikethroughNode - 34, // 27: memos.api.v1.Node.escaping_character_node:type_name -> memos.api.v1.EscapingCharacterNode - 35, // 28: memos.api.v1.Node.math_node:type_name -> memos.api.v1.MathNode - 36, // 29: memos.api.v1.Node.highlight_node:type_name -> memos.api.v1.HighlightNode - 37, // 30: memos.api.v1.Node.subscript_node:type_name -> memos.api.v1.SubscriptNode - 38, // 31: memos.api.v1.Node.superscript_node:type_name -> memos.api.v1.SuperscriptNode - 39, // 32: memos.api.v1.Node.referenced_content_node:type_name -> memos.api.v1.ReferencedContentNode - 40, // 33: memos.api.v1.Node.spoiler_node:type_name -> memos.api.v1.SpoilerNode - 41, // 34: memos.api.v1.Node.html_element_node:type_name -> memos.api.v1.HTMLElementNode - 10, // 35: memos.api.v1.ParagraphNode.children:type_name -> memos.api.v1.Node - 10, // 36: memos.api.v1.HeadingNode.children:type_name -> memos.api.v1.Node - 10, // 37: memos.api.v1.BlockquoteNode.children:type_name -> memos.api.v1.Node - 1, // 38: memos.api.v1.ListNode.kind:type_name -> memos.api.v1.ListNode.Kind - 10, // 39: memos.api.v1.ListNode.children:type_name -> memos.api.v1.Node - 10, // 40: memos.api.v1.OrderedListItemNode.children:type_name -> memos.api.v1.Node - 10, // 41: memos.api.v1.UnorderedListItemNode.children:type_name -> memos.api.v1.Node - 10, // 42: memos.api.v1.TaskListItemNode.children:type_name -> memos.api.v1.Node - 10, // 43: memos.api.v1.TableNode.header:type_name -> memos.api.v1.Node - 42, // 44: memos.api.v1.TableNode.rows:type_name -> memos.api.v1.TableNode.Row - 10, // 45: memos.api.v1.BoldNode.children:type_name -> memos.api.v1.Node - 10, // 46: memos.api.v1.ItalicNode.children:type_name -> memos.api.v1.Node - 10, // 47: memos.api.v1.LinkNode.content:type_name -> memos.api.v1.Node - 43, // 48: memos.api.v1.HTMLElementNode.attributes:type_name -> memos.api.v1.HTMLElementNode.AttributesEntry - 10, // 49: memos.api.v1.HTMLElementNode.children:type_name -> memos.api.v1.Node - 10, // 50: memos.api.v1.TableNode.Row.cells:type_name -> memos.api.v1.Node - 2, // 51: memos.api.v1.MarkdownService.ParseMarkdown:input_type -> memos.api.v1.ParseMarkdownRequest - 4, // 52: memos.api.v1.MarkdownService.RestoreMarkdownNodes:input_type -> memos.api.v1.RestoreMarkdownNodesRequest - 6, // 53: memos.api.v1.MarkdownService.StringifyMarkdownNodes:input_type -> memos.api.v1.StringifyMarkdownNodesRequest - 8, // 54: memos.api.v1.MarkdownService.GetLinkMetadata:input_type -> memos.api.v1.GetLinkMetadataRequest - 3, // 55: memos.api.v1.MarkdownService.ParseMarkdown:output_type -> memos.api.v1.ParseMarkdownResponse - 5, // 56: memos.api.v1.MarkdownService.RestoreMarkdownNodes:output_type -> memos.api.v1.RestoreMarkdownNodesResponse - 7, // 57: memos.api.v1.MarkdownService.StringifyMarkdownNodes:output_type -> memos.api.v1.StringifyMarkdownNodesResponse - 9, // 58: memos.api.v1.MarkdownService.GetLinkMetadata:output_type -> memos.api.v1.LinkMetadata - 55, // [55:59] is the sub-list for method output_type - 51, // [51:55] is the sub-list for method input_type - 51, // [51:51] is the sub-list for extension type_name - 51, // [51:51] is the sub-list for extension extendee - 0, // [0:51] is the sub-list for field type_name -} - -func init() { file_api_v1_markdown_service_proto_init() } -func file_api_v1_markdown_service_proto_init() { - if File_api_v1_markdown_service_proto != nil { - return - } - file_api_v1_markdown_service_proto_msgTypes[8].OneofWrappers = []any{ - (*Node_LineBreakNode)(nil), - (*Node_ParagraphNode)(nil), - (*Node_CodeBlockNode)(nil), - (*Node_HeadingNode)(nil), - (*Node_HorizontalRuleNode)(nil), - (*Node_BlockquoteNode)(nil), - (*Node_ListNode)(nil), - (*Node_OrderedListItemNode)(nil), - (*Node_UnorderedListItemNode)(nil), - (*Node_TaskListItemNode)(nil), - (*Node_MathBlockNode)(nil), - (*Node_TableNode)(nil), - (*Node_EmbeddedContentNode)(nil), - (*Node_TextNode)(nil), - (*Node_BoldNode)(nil), - (*Node_ItalicNode)(nil), - (*Node_BoldItalicNode)(nil), - (*Node_CodeNode)(nil), - (*Node_ImageNode)(nil), - (*Node_LinkNode)(nil), - (*Node_AutoLinkNode)(nil), - (*Node_TagNode)(nil), - (*Node_StrikethroughNode)(nil), - (*Node_EscapingCharacterNode)(nil), - (*Node_MathNode)(nil), - (*Node_HighlightNode)(nil), - (*Node_SubscriptNode)(nil), - (*Node_SuperscriptNode)(nil), - (*Node_ReferencedContentNode)(nil), - (*Node_SpoilerNode)(nil), - (*Node_HtmlElementNode)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v1_markdown_service_proto_rawDesc), len(file_api_v1_markdown_service_proto_rawDesc)), - NumEnums: 2, - NumMessages: 42, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_api_v1_markdown_service_proto_goTypes, - DependencyIndexes: file_api_v1_markdown_service_proto_depIdxs, - EnumInfos: file_api_v1_markdown_service_proto_enumTypes, - MessageInfos: file_api_v1_markdown_service_proto_msgTypes, - }.Build() - File_api_v1_markdown_service_proto = out.File - file_api_v1_markdown_service_proto_goTypes = nil - file_api_v1_markdown_service_proto_depIdxs = nil -} diff --git a/proto/gen/api/v1/markdown_service.pb.gw.go b/proto/gen/api/v1/markdown_service.pb.gw.go deleted file mode 100644 index 39e162dd7..000000000 --- a/proto/gen/api/v1/markdown_service.pb.gw.go +++ /dev/null @@ -1,363 +0,0 @@ -// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: api/v1/markdown_service.proto - -/* -Package apiv1 is a reverse proxy. - -It translates gRPC into RESTful JSON APIs. -*/ -package apiv1 - -import ( - "context" - "errors" - "io" - "net/http" - - "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" - "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/proto" -) - -// Suppress "imported and not used" errors -var ( - _ codes.Code - _ io.Reader - _ status.Status - _ = errors.New - _ = runtime.String - _ = utilities.NewDoubleArray - _ = metadata.Join -) - -func request_MarkdownService_ParseMarkdown_0(ctx context.Context, marshaler runtime.Marshaler, client MarkdownServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var ( - protoReq ParseMarkdownRequest - metadata runtime.ServerMetadata - ) - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if req.Body != nil { - _, _ = io.Copy(io.Discard, req.Body) - } - msg, err := client.ParseMarkdown(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err -} - -func local_request_MarkdownService_ParseMarkdown_0(ctx context.Context, marshaler runtime.Marshaler, server MarkdownServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var ( - protoReq ParseMarkdownRequest - metadata runtime.ServerMetadata - ) - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - msg, err := server.ParseMarkdown(ctx, &protoReq) - return msg, metadata, err -} - -func request_MarkdownService_RestoreMarkdownNodes_0(ctx context.Context, marshaler runtime.Marshaler, client MarkdownServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var ( - protoReq RestoreMarkdownNodesRequest - metadata runtime.ServerMetadata - ) - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if req.Body != nil { - _, _ = io.Copy(io.Discard, req.Body) - } - msg, err := client.RestoreMarkdownNodes(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err -} - -func local_request_MarkdownService_RestoreMarkdownNodes_0(ctx context.Context, marshaler runtime.Marshaler, server MarkdownServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var ( - protoReq RestoreMarkdownNodesRequest - metadata runtime.ServerMetadata - ) - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - msg, err := server.RestoreMarkdownNodes(ctx, &protoReq) - return msg, metadata, err -} - -func request_MarkdownService_StringifyMarkdownNodes_0(ctx context.Context, marshaler runtime.Marshaler, client MarkdownServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var ( - protoReq StringifyMarkdownNodesRequest - metadata runtime.ServerMetadata - ) - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if req.Body != nil { - _, _ = io.Copy(io.Discard, req.Body) - } - msg, err := client.StringifyMarkdownNodes(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err -} - -func local_request_MarkdownService_StringifyMarkdownNodes_0(ctx context.Context, marshaler runtime.Marshaler, server MarkdownServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var ( - protoReq StringifyMarkdownNodesRequest - metadata runtime.ServerMetadata - ) - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - msg, err := server.StringifyMarkdownNodes(ctx, &protoReq) - return msg, metadata, err -} - -var filter_MarkdownService_GetLinkMetadata_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} - -func request_MarkdownService_GetLinkMetadata_0(ctx context.Context, marshaler runtime.Marshaler, client MarkdownServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var ( - protoReq GetLinkMetadataRequest - metadata runtime.ServerMetadata - ) - if req.Body != nil { - _, _ = io.Copy(io.Discard, req.Body) - } - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_MarkdownService_GetLinkMetadata_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - msg, err := client.GetLinkMetadata(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err -} - -func local_request_MarkdownService_GetLinkMetadata_0(ctx context.Context, marshaler runtime.Marshaler, server MarkdownServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var ( - protoReq GetLinkMetadataRequest - metadata runtime.ServerMetadata - ) - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_MarkdownService_GetLinkMetadata_0); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - msg, err := server.GetLinkMetadata(ctx, &protoReq) - return msg, metadata, err -} - -// RegisterMarkdownServiceHandlerServer registers the http handlers for service MarkdownService to "mux". -// UnaryRPC :call MarkdownServiceServer directly. -// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterMarkdownServiceHandlerFromEndpoint instead. -// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call. -func RegisterMarkdownServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server MarkdownServiceServer) error { - mux.Handle(http.MethodPost, pattern_MarkdownService_ParseMarkdown_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MarkdownService/ParseMarkdown", runtime.WithHTTPPathPattern("/api/v1/markdown:parse")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_MarkdownService_ParseMarkdown_0(annotatedContext, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) - if err != nil { - runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) - return - } - forward_MarkdownService_ParseMarkdown_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - }) - mux.Handle(http.MethodPost, pattern_MarkdownService_RestoreMarkdownNodes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MarkdownService/RestoreMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown:restore")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_MarkdownService_RestoreMarkdownNodes_0(annotatedContext, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) - if err != nil { - runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) - return - } - forward_MarkdownService_RestoreMarkdownNodes_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - }) - mux.Handle(http.MethodPost, pattern_MarkdownService_StringifyMarkdownNodes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MarkdownService/StringifyMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown:stringify")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_MarkdownService_StringifyMarkdownNodes_0(annotatedContext, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) - if err != nil { - runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) - return - } - forward_MarkdownService_StringifyMarkdownNodes_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - }) - mux.Handle(http.MethodGet, pattern_MarkdownService_GetLinkMetadata_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MarkdownService/GetLinkMetadata", runtime.WithHTTPPathPattern("/api/v1/markdown/links:getMetadata")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_MarkdownService_GetLinkMetadata_0(annotatedContext, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) - if err != nil { - runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) - return - } - forward_MarkdownService_GetLinkMetadata_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - }) - - return nil -} - -// RegisterMarkdownServiceHandlerFromEndpoint is same as RegisterMarkdownServiceHandler but -// automatically dials to "endpoint" and closes the connection when "ctx" gets done. -func RegisterMarkdownServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.NewClient(endpoint, opts...) - if err != nil { - return err - } - defer func() { - if err != nil { - if cerr := conn.Close(); cerr != nil { - grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr) - } - return - } - go func() { - <-ctx.Done() - if cerr := conn.Close(); cerr != nil { - grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr) - } - }() - }() - return RegisterMarkdownServiceHandler(ctx, mux, conn) -} - -// RegisterMarkdownServiceHandler registers the http handlers for service MarkdownService to "mux". -// The handlers forward requests to the grpc endpoint over "conn". -func RegisterMarkdownServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { - return RegisterMarkdownServiceHandlerClient(ctx, mux, NewMarkdownServiceClient(conn)) -} - -// RegisterMarkdownServiceHandlerClient registers the http handlers for service MarkdownService -// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "MarkdownServiceClient". -// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "MarkdownServiceClient" -// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in -// "MarkdownServiceClient" to call the correct interceptors. This client ignores the HTTP middlewares. -func RegisterMarkdownServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client MarkdownServiceClient) error { - mux.Handle(http.MethodPost, pattern_MarkdownService_ParseMarkdown_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MarkdownService/ParseMarkdown", runtime.WithHTTPPathPattern("/api/v1/markdown:parse")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_MarkdownService_ParseMarkdown_0(annotatedContext, inboundMarshaler, client, req, pathParams) - annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) - if err != nil { - runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) - return - } - forward_MarkdownService_ParseMarkdown_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - }) - mux.Handle(http.MethodPost, pattern_MarkdownService_RestoreMarkdownNodes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MarkdownService/RestoreMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown:restore")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_MarkdownService_RestoreMarkdownNodes_0(annotatedContext, inboundMarshaler, client, req, pathParams) - annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) - if err != nil { - runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) - return - } - forward_MarkdownService_RestoreMarkdownNodes_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - }) - mux.Handle(http.MethodPost, pattern_MarkdownService_StringifyMarkdownNodes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MarkdownService/StringifyMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown:stringify")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_MarkdownService_StringifyMarkdownNodes_0(annotatedContext, inboundMarshaler, client, req, pathParams) - annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) - if err != nil { - runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) - return - } - forward_MarkdownService_StringifyMarkdownNodes_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - }) - mux.Handle(http.MethodGet, pattern_MarkdownService_GetLinkMetadata_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MarkdownService/GetLinkMetadata", runtime.WithHTTPPathPattern("/api/v1/markdown/links:getMetadata")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_MarkdownService_GetLinkMetadata_0(annotatedContext, inboundMarshaler, client, req, pathParams) - annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) - if err != nil { - runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) - return - } - forward_MarkdownService_GetLinkMetadata_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - }) - return nil -} - -var ( - pattern_MarkdownService_ParseMarkdown_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "markdown"}, "parse")) - pattern_MarkdownService_RestoreMarkdownNodes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "markdown"}, "restore")) - pattern_MarkdownService_StringifyMarkdownNodes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "markdown"}, "stringify")) - pattern_MarkdownService_GetLinkMetadata_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "markdown", "links"}, "getMetadata")) -) - -var ( - forward_MarkdownService_ParseMarkdown_0 = runtime.ForwardResponseMessage - forward_MarkdownService_RestoreMarkdownNodes_0 = runtime.ForwardResponseMessage - forward_MarkdownService_StringifyMarkdownNodes_0 = runtime.ForwardResponseMessage - forward_MarkdownService_GetLinkMetadata_0 = runtime.ForwardResponseMessage -) diff --git a/proto/gen/api/v1/markdown_service_grpc.pb.go b/proto/gen/api/v1/markdown_service_grpc.pb.go deleted file mode 100644 index 569ef62dc..000000000 --- a/proto/gen/api/v1/markdown_service_grpc.pb.go +++ /dev/null @@ -1,251 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc (unknown) -// source: api/v1/markdown_service.proto - -package apiv1 - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 - -const ( - MarkdownService_ParseMarkdown_FullMethodName = "/memos.api.v1.MarkdownService/ParseMarkdown" - MarkdownService_RestoreMarkdownNodes_FullMethodName = "/memos.api.v1.MarkdownService/RestoreMarkdownNodes" - MarkdownService_StringifyMarkdownNodes_FullMethodName = "/memos.api.v1.MarkdownService/StringifyMarkdownNodes" - MarkdownService_GetLinkMetadata_FullMethodName = "/memos.api.v1.MarkdownService/GetLinkMetadata" -) - -// MarkdownServiceClient is the client API for MarkdownService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type MarkdownServiceClient interface { - // ParseMarkdown parses the given markdown content and returns a list of nodes. - // This is a utility method that transforms markdown text into structured nodes. - ParseMarkdown(ctx context.Context, in *ParseMarkdownRequest, opts ...grpc.CallOption) (*ParseMarkdownResponse, error) - // RestoreMarkdownNodes restores the given nodes to markdown content. - // This is the inverse operation of ParseMarkdown. - RestoreMarkdownNodes(ctx context.Context, in *RestoreMarkdownNodesRequest, opts ...grpc.CallOption) (*RestoreMarkdownNodesResponse, error) - // StringifyMarkdownNodes stringify the given nodes to plain text content. - // This removes all markdown formatting and returns plain text. - StringifyMarkdownNodes(ctx context.Context, in *StringifyMarkdownNodesRequest, opts ...grpc.CallOption) (*StringifyMarkdownNodesResponse, error) - // GetLinkMetadata returns metadata for a given link. - // This is useful for generating link previews. - GetLinkMetadata(ctx context.Context, in *GetLinkMetadataRequest, opts ...grpc.CallOption) (*LinkMetadata, error) -} - -type markdownServiceClient struct { - cc grpc.ClientConnInterface -} - -func NewMarkdownServiceClient(cc grpc.ClientConnInterface) MarkdownServiceClient { - return &markdownServiceClient{cc} -} - -func (c *markdownServiceClient) ParseMarkdown(ctx context.Context, in *ParseMarkdownRequest, opts ...grpc.CallOption) (*ParseMarkdownResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ParseMarkdownResponse) - err := c.cc.Invoke(ctx, MarkdownService_ParseMarkdown_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *markdownServiceClient) RestoreMarkdownNodes(ctx context.Context, in *RestoreMarkdownNodesRequest, opts ...grpc.CallOption) (*RestoreMarkdownNodesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(RestoreMarkdownNodesResponse) - err := c.cc.Invoke(ctx, MarkdownService_RestoreMarkdownNodes_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *markdownServiceClient) StringifyMarkdownNodes(ctx context.Context, in *StringifyMarkdownNodesRequest, opts ...grpc.CallOption) (*StringifyMarkdownNodesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(StringifyMarkdownNodesResponse) - err := c.cc.Invoke(ctx, MarkdownService_StringifyMarkdownNodes_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *markdownServiceClient) GetLinkMetadata(ctx context.Context, in *GetLinkMetadataRequest, opts ...grpc.CallOption) (*LinkMetadata, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(LinkMetadata) - err := c.cc.Invoke(ctx, MarkdownService_GetLinkMetadata_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -// MarkdownServiceServer is the server API for MarkdownService service. -// All implementations must embed UnimplementedMarkdownServiceServer -// for forward compatibility. -type MarkdownServiceServer interface { - // ParseMarkdown parses the given markdown content and returns a list of nodes. - // This is a utility method that transforms markdown text into structured nodes. - ParseMarkdown(context.Context, *ParseMarkdownRequest) (*ParseMarkdownResponse, error) - // RestoreMarkdownNodes restores the given nodes to markdown content. - // This is the inverse operation of ParseMarkdown. - RestoreMarkdownNodes(context.Context, *RestoreMarkdownNodesRequest) (*RestoreMarkdownNodesResponse, error) - // StringifyMarkdownNodes stringify the given nodes to plain text content. - // This removes all markdown formatting and returns plain text. - StringifyMarkdownNodes(context.Context, *StringifyMarkdownNodesRequest) (*StringifyMarkdownNodesResponse, error) - // GetLinkMetadata returns metadata for a given link. - // This is useful for generating link previews. - GetLinkMetadata(context.Context, *GetLinkMetadataRequest) (*LinkMetadata, error) - mustEmbedUnimplementedMarkdownServiceServer() -} - -// UnimplementedMarkdownServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedMarkdownServiceServer struct{} - -func (UnimplementedMarkdownServiceServer) ParseMarkdown(context.Context, *ParseMarkdownRequest) (*ParseMarkdownResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ParseMarkdown not implemented") -} -func (UnimplementedMarkdownServiceServer) RestoreMarkdownNodes(context.Context, *RestoreMarkdownNodesRequest) (*RestoreMarkdownNodesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method RestoreMarkdownNodes not implemented") -} -func (UnimplementedMarkdownServiceServer) StringifyMarkdownNodes(context.Context, *StringifyMarkdownNodesRequest) (*StringifyMarkdownNodesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method StringifyMarkdownNodes not implemented") -} -func (UnimplementedMarkdownServiceServer) GetLinkMetadata(context.Context, *GetLinkMetadataRequest) (*LinkMetadata, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetLinkMetadata not implemented") -} -func (UnimplementedMarkdownServiceServer) mustEmbedUnimplementedMarkdownServiceServer() {} -func (UnimplementedMarkdownServiceServer) testEmbeddedByValue() {} - -// UnsafeMarkdownServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to MarkdownServiceServer will -// result in compilation errors. -type UnsafeMarkdownServiceServer interface { - mustEmbedUnimplementedMarkdownServiceServer() -} - -func RegisterMarkdownServiceServer(s grpc.ServiceRegistrar, srv MarkdownServiceServer) { - // If the following call pancis, it indicates UnimplementedMarkdownServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } - s.RegisterService(&MarkdownService_ServiceDesc, srv) -} - -func _MarkdownService_ParseMarkdown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ParseMarkdownRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MarkdownServiceServer).ParseMarkdown(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: MarkdownService_ParseMarkdown_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MarkdownServiceServer).ParseMarkdown(ctx, req.(*ParseMarkdownRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _MarkdownService_RestoreMarkdownNodes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RestoreMarkdownNodesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MarkdownServiceServer).RestoreMarkdownNodes(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: MarkdownService_RestoreMarkdownNodes_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MarkdownServiceServer).RestoreMarkdownNodes(ctx, req.(*RestoreMarkdownNodesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _MarkdownService_StringifyMarkdownNodes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(StringifyMarkdownNodesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MarkdownServiceServer).StringifyMarkdownNodes(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: MarkdownService_StringifyMarkdownNodes_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MarkdownServiceServer).StringifyMarkdownNodes(ctx, req.(*StringifyMarkdownNodesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _MarkdownService_GetLinkMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetLinkMetadataRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MarkdownServiceServer).GetLinkMetadata(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: MarkdownService_GetLinkMetadata_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MarkdownServiceServer).GetLinkMetadata(ctx, req.(*GetLinkMetadataRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// MarkdownService_ServiceDesc is the grpc.ServiceDesc for MarkdownService service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var MarkdownService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "memos.api.v1.MarkdownService", - HandlerType: (*MarkdownServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "ParseMarkdown", - Handler: _MarkdownService_ParseMarkdown_Handler, - }, - { - MethodName: "RestoreMarkdownNodes", - Handler: _MarkdownService_RestoreMarkdownNodes_Handler, - }, - { - MethodName: "StringifyMarkdownNodes", - Handler: _MarkdownService_StringifyMarkdownNodes_Handler, - }, - { - MethodName: "GetLinkMetadata", - Handler: _MarkdownService_GetLinkMetadata_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "api/v1/markdown_service.proto", -} diff --git a/proto/gen/api/v1/memo_service.pb.go b/proto/gen/api/v1/memo_service.pb.go index 6991a6c56..c91a22174 100644 --- a/proto/gen/api/v1/memo_service.pb.go +++ b/proto/gen/api/v1/memo_service.pb.go @@ -230,8 +230,6 @@ type Memo struct { DisplayTime *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=display_time,json=displayTime,proto3" json:"display_time,omitempty"` // Required. The content of the memo in Markdown format. Content string `protobuf:"bytes,7,opt,name=content,proto3" json:"content,omitempty"` - // Output only. The parsed nodes from the content. - Nodes []*Node `protobuf:"bytes,8,rep,name=nodes,proto3" json:"nodes,omitempty"` // The visibility of the memo. Visibility Visibility `protobuf:"varint,9,opt,name=visibility,proto3,enum=memos.api.v1.Visibility" json:"visibility,omitempty"` // Output only. The tags extracted from the content. @@ -336,13 +334,6 @@ func (x *Memo) GetContent() string { return "" } -func (x *Memo) GetNodes() []*Node { - if x != nil { - return x.Nodes - } - return nil -} - func (x *Memo) GetVisibility() Visibility { if x != nil { return x.Visibility @@ -2000,7 +1991,7 @@ var File_api_v1_memo_service_proto protoreflect.FileDescriptor const file_api_v1_memo_service_proto_rawDesc = "" + "\n" + - "\x19api/v1/memo_service.proto\x12\fmemos.api.v1\x1a\x1fapi/v1/attachment_service.proto\x1a\x13api/v1/common.proto\x1a\x1dapi/v1/markdown_service.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xce\x02\n" + + "\x19api/v1/memo_service.proto\x12\fmemos.api.v1\x1a\x1fapi/v1/attachment_service.proto\x1a\x13api/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xce\x02\n" + "\bReaction\x12\x1a\n" + "\x04name\x18\x01 \x01(\tB\x06\xe0A\x03\xe0A\bR\x04name\x123\n" + "\acreator\x18\x02 \x01(\tB\x19\xe0A\x03\xfaA\x13\n" + @@ -2011,7 +2002,7 @@ const file_api_v1_memo_service_proto_rawDesc = "" + "\rreaction_type\x18\x04 \x01(\tB\x03\xe0A\x02R\freactionType\x12@\n" + "\vcreate_time\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampB\x03\xe0A\x03R\n" + "createTime:K\xeaAH\n" + - "\x15memos.api.v1/Reaction\x12\x14reactions/{reaction}\x1a\x04name*\treactions2\breaction\"\x87\t\n" + + "\x15memos.api.v1/Reaction\x12\x14reactions/{reaction}\x1a\x04name*\treactions2\breaction\"\xd8\b\n" + "\x04Memo\x12\x17\n" + "\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x12.\n" + "\x05state\x18\x02 \x01(\x0e2\x13.memos.api.v1.StateB\x03\xe0A\x02R\x05state\x123\n" + @@ -2022,8 +2013,7 @@ const file_api_v1_memo_service_proto_rawDesc = "" + "\vupdate_time\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampB\x03\xe0A\x03R\n" + "updateTime\x12B\n" + "\fdisplay_time\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampB\x03\xe0A\x01R\vdisplayTime\x12\x1d\n" + - "\acontent\x18\a \x01(\tB\x03\xe0A\x02R\acontent\x12-\n" + - "\x05nodes\x18\b \x03(\v2\x12.memos.api.v1.NodeB\x03\xe0A\x03R\x05nodes\x12=\n" + + "\acontent\x18\a \x01(\tB\x03\xe0A\x02R\acontent\x12=\n" + "\n" + "visibility\x18\t \x01(\x0e2\x18.memos.api.v1.VisibilityB\x03\xe0A\x02R\n" + "visibility\x12\x17\n" + @@ -2246,10 +2236,9 @@ var file_api_v1_memo_service_proto_goTypes = []any{ (*MemoRelation_Memo)(nil), // 28: memos.api.v1.MemoRelation.Memo (*timestamppb.Timestamp)(nil), // 29: google.protobuf.Timestamp (State)(0), // 30: memos.api.v1.State - (*Node)(nil), // 31: memos.api.v1.Node - (*Attachment)(nil), // 32: memos.api.v1.Attachment - (*fieldmaskpb.FieldMask)(nil), // 33: google.protobuf.FieldMask - (*emptypb.Empty)(nil), // 34: google.protobuf.Empty + (*Attachment)(nil), // 31: memos.api.v1.Attachment + (*fieldmaskpb.FieldMask)(nil), // 32: google.protobuf.FieldMask + (*emptypb.Empty)(nil), // 33: google.protobuf.Empty } var file_api_v1_memo_service_proto_depIdxs = []int32{ 29, // 0: memos.api.v1.Reaction.create_time:type_name -> google.protobuf.Timestamp @@ -2257,67 +2246,66 @@ var file_api_v1_memo_service_proto_depIdxs = []int32{ 29, // 2: memos.api.v1.Memo.create_time:type_name -> google.protobuf.Timestamp 29, // 3: memos.api.v1.Memo.update_time:type_name -> google.protobuf.Timestamp 29, // 4: memos.api.v1.Memo.display_time:type_name -> google.protobuf.Timestamp - 31, // 5: memos.api.v1.Memo.nodes:type_name -> memos.api.v1.Node - 0, // 6: memos.api.v1.Memo.visibility:type_name -> memos.api.v1.Visibility - 32, // 7: memos.api.v1.Memo.attachments:type_name -> memos.api.v1.Attachment - 16, // 8: memos.api.v1.Memo.relations:type_name -> memos.api.v1.MemoRelation - 2, // 9: memos.api.v1.Memo.reactions:type_name -> memos.api.v1.Reaction - 27, // 10: memos.api.v1.Memo.property:type_name -> memos.api.v1.Memo.Property - 4, // 11: memos.api.v1.Memo.location:type_name -> memos.api.v1.Location - 3, // 12: memos.api.v1.CreateMemoRequest.memo:type_name -> memos.api.v1.Memo - 30, // 13: memos.api.v1.ListMemosRequest.state:type_name -> memos.api.v1.State - 3, // 14: memos.api.v1.ListMemosResponse.memos:type_name -> memos.api.v1.Memo - 33, // 15: memos.api.v1.GetMemoRequest.read_mask:type_name -> google.protobuf.FieldMask - 3, // 16: memos.api.v1.UpdateMemoRequest.memo:type_name -> memos.api.v1.Memo - 33, // 17: memos.api.v1.UpdateMemoRequest.update_mask:type_name -> google.protobuf.FieldMask - 32, // 18: memos.api.v1.SetMemoAttachmentsRequest.attachments:type_name -> memos.api.v1.Attachment - 32, // 19: memos.api.v1.ListMemoAttachmentsResponse.attachments:type_name -> memos.api.v1.Attachment - 28, // 20: memos.api.v1.MemoRelation.memo:type_name -> memos.api.v1.MemoRelation.Memo - 28, // 21: memos.api.v1.MemoRelation.related_memo:type_name -> memos.api.v1.MemoRelation.Memo - 1, // 22: memos.api.v1.MemoRelation.type:type_name -> memos.api.v1.MemoRelation.Type - 16, // 23: memos.api.v1.SetMemoRelationsRequest.relations:type_name -> memos.api.v1.MemoRelation - 16, // 24: memos.api.v1.ListMemoRelationsResponse.relations:type_name -> memos.api.v1.MemoRelation - 3, // 25: memos.api.v1.CreateMemoCommentRequest.comment:type_name -> memos.api.v1.Memo - 3, // 26: memos.api.v1.ListMemoCommentsResponse.memos:type_name -> memos.api.v1.Memo - 2, // 27: memos.api.v1.ListMemoReactionsResponse.reactions:type_name -> memos.api.v1.Reaction - 2, // 28: memos.api.v1.UpsertMemoReactionRequest.reaction:type_name -> memos.api.v1.Reaction - 5, // 29: memos.api.v1.MemoService.CreateMemo:input_type -> memos.api.v1.CreateMemoRequest - 6, // 30: memos.api.v1.MemoService.ListMemos:input_type -> memos.api.v1.ListMemosRequest - 8, // 31: memos.api.v1.MemoService.GetMemo:input_type -> memos.api.v1.GetMemoRequest - 9, // 32: memos.api.v1.MemoService.UpdateMemo:input_type -> memos.api.v1.UpdateMemoRequest - 10, // 33: memos.api.v1.MemoService.DeleteMemo:input_type -> memos.api.v1.DeleteMemoRequest - 11, // 34: memos.api.v1.MemoService.RenameMemoTag:input_type -> memos.api.v1.RenameMemoTagRequest - 12, // 35: memos.api.v1.MemoService.DeleteMemoTag:input_type -> memos.api.v1.DeleteMemoTagRequest - 13, // 36: memos.api.v1.MemoService.SetMemoAttachments:input_type -> memos.api.v1.SetMemoAttachmentsRequest - 14, // 37: memos.api.v1.MemoService.ListMemoAttachments:input_type -> memos.api.v1.ListMemoAttachmentsRequest - 17, // 38: memos.api.v1.MemoService.SetMemoRelations:input_type -> memos.api.v1.SetMemoRelationsRequest - 18, // 39: memos.api.v1.MemoService.ListMemoRelations:input_type -> memos.api.v1.ListMemoRelationsRequest - 20, // 40: memos.api.v1.MemoService.CreateMemoComment:input_type -> memos.api.v1.CreateMemoCommentRequest - 21, // 41: memos.api.v1.MemoService.ListMemoComments:input_type -> memos.api.v1.ListMemoCommentsRequest - 23, // 42: memos.api.v1.MemoService.ListMemoReactions:input_type -> memos.api.v1.ListMemoReactionsRequest - 25, // 43: memos.api.v1.MemoService.UpsertMemoReaction:input_type -> memos.api.v1.UpsertMemoReactionRequest - 26, // 44: memos.api.v1.MemoService.DeleteMemoReaction:input_type -> memos.api.v1.DeleteMemoReactionRequest - 3, // 45: memos.api.v1.MemoService.CreateMemo:output_type -> memos.api.v1.Memo - 7, // 46: memos.api.v1.MemoService.ListMemos:output_type -> memos.api.v1.ListMemosResponse - 3, // 47: memos.api.v1.MemoService.GetMemo:output_type -> memos.api.v1.Memo - 3, // 48: memos.api.v1.MemoService.UpdateMemo:output_type -> memos.api.v1.Memo - 34, // 49: memos.api.v1.MemoService.DeleteMemo:output_type -> google.protobuf.Empty - 34, // 50: memos.api.v1.MemoService.RenameMemoTag:output_type -> google.protobuf.Empty - 34, // 51: memos.api.v1.MemoService.DeleteMemoTag:output_type -> google.protobuf.Empty - 34, // 52: memos.api.v1.MemoService.SetMemoAttachments:output_type -> google.protobuf.Empty - 15, // 53: memos.api.v1.MemoService.ListMemoAttachments:output_type -> memos.api.v1.ListMemoAttachmentsResponse - 34, // 54: memos.api.v1.MemoService.SetMemoRelations:output_type -> google.protobuf.Empty - 19, // 55: memos.api.v1.MemoService.ListMemoRelations:output_type -> memos.api.v1.ListMemoRelationsResponse - 3, // 56: memos.api.v1.MemoService.CreateMemoComment:output_type -> memos.api.v1.Memo - 22, // 57: memos.api.v1.MemoService.ListMemoComments:output_type -> memos.api.v1.ListMemoCommentsResponse - 24, // 58: memos.api.v1.MemoService.ListMemoReactions:output_type -> memos.api.v1.ListMemoReactionsResponse - 2, // 59: memos.api.v1.MemoService.UpsertMemoReaction:output_type -> memos.api.v1.Reaction - 34, // 60: memos.api.v1.MemoService.DeleteMemoReaction:output_type -> google.protobuf.Empty - 45, // [45:61] is the sub-list for method output_type - 29, // [29:45] is the sub-list for method input_type - 29, // [29:29] is the sub-list for extension type_name - 29, // [29:29] is the sub-list for extension extendee - 0, // [0:29] is the sub-list for field type_name + 0, // 5: memos.api.v1.Memo.visibility:type_name -> memos.api.v1.Visibility + 31, // 6: memos.api.v1.Memo.attachments:type_name -> memos.api.v1.Attachment + 16, // 7: memos.api.v1.Memo.relations:type_name -> memos.api.v1.MemoRelation + 2, // 8: memos.api.v1.Memo.reactions:type_name -> memos.api.v1.Reaction + 27, // 9: memos.api.v1.Memo.property:type_name -> memos.api.v1.Memo.Property + 4, // 10: memos.api.v1.Memo.location:type_name -> memos.api.v1.Location + 3, // 11: memos.api.v1.CreateMemoRequest.memo:type_name -> memos.api.v1.Memo + 30, // 12: memos.api.v1.ListMemosRequest.state:type_name -> memos.api.v1.State + 3, // 13: memos.api.v1.ListMemosResponse.memos:type_name -> memos.api.v1.Memo + 32, // 14: memos.api.v1.GetMemoRequest.read_mask:type_name -> google.protobuf.FieldMask + 3, // 15: memos.api.v1.UpdateMemoRequest.memo:type_name -> memos.api.v1.Memo + 32, // 16: memos.api.v1.UpdateMemoRequest.update_mask:type_name -> google.protobuf.FieldMask + 31, // 17: memos.api.v1.SetMemoAttachmentsRequest.attachments:type_name -> memos.api.v1.Attachment + 31, // 18: memos.api.v1.ListMemoAttachmentsResponse.attachments:type_name -> memos.api.v1.Attachment + 28, // 19: memos.api.v1.MemoRelation.memo:type_name -> memos.api.v1.MemoRelation.Memo + 28, // 20: memos.api.v1.MemoRelation.related_memo:type_name -> memos.api.v1.MemoRelation.Memo + 1, // 21: memos.api.v1.MemoRelation.type:type_name -> memos.api.v1.MemoRelation.Type + 16, // 22: memos.api.v1.SetMemoRelationsRequest.relations:type_name -> memos.api.v1.MemoRelation + 16, // 23: memos.api.v1.ListMemoRelationsResponse.relations:type_name -> memos.api.v1.MemoRelation + 3, // 24: memos.api.v1.CreateMemoCommentRequest.comment:type_name -> memos.api.v1.Memo + 3, // 25: memos.api.v1.ListMemoCommentsResponse.memos:type_name -> memos.api.v1.Memo + 2, // 26: memos.api.v1.ListMemoReactionsResponse.reactions:type_name -> memos.api.v1.Reaction + 2, // 27: memos.api.v1.UpsertMemoReactionRequest.reaction:type_name -> memos.api.v1.Reaction + 5, // 28: memos.api.v1.MemoService.CreateMemo:input_type -> memos.api.v1.CreateMemoRequest + 6, // 29: memos.api.v1.MemoService.ListMemos:input_type -> memos.api.v1.ListMemosRequest + 8, // 30: memos.api.v1.MemoService.GetMemo:input_type -> memos.api.v1.GetMemoRequest + 9, // 31: memos.api.v1.MemoService.UpdateMemo:input_type -> memos.api.v1.UpdateMemoRequest + 10, // 32: memos.api.v1.MemoService.DeleteMemo:input_type -> memos.api.v1.DeleteMemoRequest + 11, // 33: memos.api.v1.MemoService.RenameMemoTag:input_type -> memos.api.v1.RenameMemoTagRequest + 12, // 34: memos.api.v1.MemoService.DeleteMemoTag:input_type -> memos.api.v1.DeleteMemoTagRequest + 13, // 35: memos.api.v1.MemoService.SetMemoAttachments:input_type -> memos.api.v1.SetMemoAttachmentsRequest + 14, // 36: memos.api.v1.MemoService.ListMemoAttachments:input_type -> memos.api.v1.ListMemoAttachmentsRequest + 17, // 37: memos.api.v1.MemoService.SetMemoRelations:input_type -> memos.api.v1.SetMemoRelationsRequest + 18, // 38: memos.api.v1.MemoService.ListMemoRelations:input_type -> memos.api.v1.ListMemoRelationsRequest + 20, // 39: memos.api.v1.MemoService.CreateMemoComment:input_type -> memos.api.v1.CreateMemoCommentRequest + 21, // 40: memos.api.v1.MemoService.ListMemoComments:input_type -> memos.api.v1.ListMemoCommentsRequest + 23, // 41: memos.api.v1.MemoService.ListMemoReactions:input_type -> memos.api.v1.ListMemoReactionsRequest + 25, // 42: memos.api.v1.MemoService.UpsertMemoReaction:input_type -> memos.api.v1.UpsertMemoReactionRequest + 26, // 43: memos.api.v1.MemoService.DeleteMemoReaction:input_type -> memos.api.v1.DeleteMemoReactionRequest + 3, // 44: memos.api.v1.MemoService.CreateMemo:output_type -> memos.api.v1.Memo + 7, // 45: memos.api.v1.MemoService.ListMemos:output_type -> memos.api.v1.ListMemosResponse + 3, // 46: memos.api.v1.MemoService.GetMemo:output_type -> memos.api.v1.Memo + 3, // 47: memos.api.v1.MemoService.UpdateMemo:output_type -> memos.api.v1.Memo + 33, // 48: memos.api.v1.MemoService.DeleteMemo:output_type -> google.protobuf.Empty + 33, // 49: memos.api.v1.MemoService.RenameMemoTag:output_type -> google.protobuf.Empty + 33, // 50: memos.api.v1.MemoService.DeleteMemoTag:output_type -> google.protobuf.Empty + 33, // 51: memos.api.v1.MemoService.SetMemoAttachments:output_type -> google.protobuf.Empty + 15, // 52: memos.api.v1.MemoService.ListMemoAttachments:output_type -> memos.api.v1.ListMemoAttachmentsResponse + 33, // 53: memos.api.v1.MemoService.SetMemoRelations:output_type -> google.protobuf.Empty + 19, // 54: memos.api.v1.MemoService.ListMemoRelations:output_type -> memos.api.v1.ListMemoRelationsResponse + 3, // 55: memos.api.v1.MemoService.CreateMemoComment:output_type -> memos.api.v1.Memo + 22, // 56: memos.api.v1.MemoService.ListMemoComments:output_type -> memos.api.v1.ListMemoCommentsResponse + 24, // 57: memos.api.v1.MemoService.ListMemoReactions:output_type -> memos.api.v1.ListMemoReactionsResponse + 2, // 58: memos.api.v1.MemoService.UpsertMemoReaction:output_type -> memos.api.v1.Reaction + 33, // 59: memos.api.v1.MemoService.DeleteMemoReaction:output_type -> google.protobuf.Empty + 44, // [44:60] is the sub-list for method output_type + 28, // [28:44] is the sub-list for method input_type + 28, // [28:28] is the sub-list for extension type_name + 28, // [28:28] is the sub-list for extension extendee + 0, // [0:28] is the sub-list for field type_name } func init() { file_api_v1_memo_service_proto_init() } @@ -2327,7 +2315,6 @@ func file_api_v1_memo_service_proto_init() { } file_api_v1_attachment_service_proto_init() file_api_v1_common_proto_init() - file_api_v1_markdown_service_proto_init() file_api_v1_memo_service_proto_msgTypes[1].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ diff --git a/proto/gen/openapi.yaml b/proto/gen/openapi.yaml index 25ecf23f3..d0454333e 100644 --- a/proto/gen/openapi.yaml +++ b/proto/gen/openapi.yaml @@ -507,114 +507,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' - /api/v1/markdown/links:getMetadata: - get: - tags: - - MarkdownService - description: |- - GetLinkMetadata returns metadata for a given link. - This is useful for generating link previews. - operationId: MarkdownService_GetLinkMetadata - parameters: - - name: link - in: query - description: The link URL to get metadata for. - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/LinkMetadata' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /api/v1/markdown:parse: - post: - tags: - - MarkdownService - description: |- - ParseMarkdown parses the given markdown content and returns a list of nodes. - This is a utility method that transforms markdown text into structured nodes. - operationId: MarkdownService_ParseMarkdown - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ParseMarkdownRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/ParseMarkdownResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /api/v1/markdown:restore: - post: - tags: - - MarkdownService - description: |- - RestoreMarkdownNodes restores the given nodes to markdown content. - This is the inverse operation of ParseMarkdown. - operationId: MarkdownService_RestoreMarkdownNodes - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/RestoreMarkdownNodesRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/RestoreMarkdownNodesResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' - /api/v1/markdown:stringify: - post: - tags: - - MarkdownService - description: |- - StringifyMarkdownNodes stringify the given nodes to plain text content. - This removes all markdown formatting and returns plain text. - operationId: MarkdownService_StringifyMarkdownNodes - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/StringifyMarkdownNodesRequest' - required: true - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/StringifyMarkdownNodesResponse' - default: - description: Default error response - content: - application/json: - schema: - $ref: '#/components/schemas/Status' /api/v1/memos: get: tags: @@ -2312,48 +2204,6 @@ components: description: |- Optional. The related memo. Refer to `Memo.name`. Format: memos/{memo} - AutoLinkNode: - type: object - properties: - url: - type: string - isRawText: - type: boolean - BlockquoteNode: - type: object - properties: - children: - type: array - items: - $ref: '#/components/schemas/Node' - BoldItalicNode: - type: object - properties: - symbol: - type: string - content: - type: string - BoldNode: - type: object - properties: - symbol: - type: string - children: - type: array - items: - $ref: '#/components/schemas/Node' - CodeBlockNode: - type: object - properties: - language: - type: string - content: - type: string - CodeNode: - type: object - properties: - content: - type: string CreateSessionRequest: type: object properties: @@ -2436,20 +2286,6 @@ components: deleteRelatedMemos: type: boolean description: Optional. Whether to delete related memos. - EmbeddedContentNode: - type: object - properties: - resourceName: - type: string - description: The resource name of the embedded content. - params: - type: string - description: Additional parameters for the embedded content. - EscapingCharacterNode: - type: object - properties: - symbol: - type: string FieldMapping: type: object properties: @@ -2492,41 +2328,6 @@ components: description: The type of the serialized message. additionalProperties: true description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. - HTMLElementNode: - type: object - properties: - tagName: - type: string - attributes: - type: object - additionalProperties: - type: string - children: - type: array - items: - $ref: '#/components/schemas/Node' - isSelfClosing: - type: boolean - HeadingNode: - type: object - properties: - level: - type: integer - format: int32 - children: - type: array - items: - $ref: '#/components/schemas/Node' - HighlightNode: - type: object - properties: - content: - type: string - HorizontalRuleNode: - type: object - properties: - symbol: - type: string IdentityProvider: required: - type @@ -2561,13 +2362,6 @@ components: properties: oauth2Config: $ref: '#/components/schemas/OAuth2Config' - ImageNode: - type: object - properties: - altText: - type: string - url: - type: string Inbox: type: object properties: @@ -2614,39 +2408,6 @@ components: type: integer description: Optional. The activity ID associated with this inbox notification. format: int32 - ItalicNode: - type: object - properties: - symbol: - type: string - children: - type: array - items: - $ref: '#/components/schemas/Node' - LineBreakNode: - type: object - properties: {} - LinkMetadata: - type: object - properties: - title: - type: string - description: The title of the linked page. - description: - type: string - description: The description of the linked page. - image: - type: string - description: The URL of the preview image for the linked page. - LinkNode: - type: object - properties: - content: - type: array - items: - $ref: '#/components/schemas/Node' - url: - type: string ListActivitiesResponse: type: object properties: @@ -2788,24 +2549,6 @@ components: type: integer description: The total count of memos (may be approximate). format: int32 - ListNode: - type: object - properties: - kind: - enum: - - KIND_UNSPECIFIED - - ORDERED - - UNORDERED - - DESCRIPTION - type: string - format: enum - indent: - type: integer - format: int32 - children: - type: array - items: - $ref: '#/components/schemas/Node' ListShortcutsResponse: type: object properties: @@ -2894,16 +2637,6 @@ components: type: number description: The longitude of the location. format: double - MathBlockNode: - type: object - properties: - content: - type: string - MathNode: - type: object - properties: - content: - type: string Memo: required: - state @@ -2947,12 +2680,6 @@ components: content: type: string description: Required. The content of the memo in Markdown format. - nodes: - readOnly: true - type: array - items: - $ref: '#/components/schemas/Node' - description: Output only. The parsed nodes from the content. visibility: enum: - VISIBILITY_UNSPECIFIED @@ -3055,111 +2782,6 @@ components: hasIncompleteTasks: type: boolean description: Computed properties of a memo. - Node: - type: object - properties: - type: - enum: - - NODE_UNSPECIFIED - - LINE_BREAK - - PARAGRAPH - - CODE_BLOCK - - HEADING - - HORIZONTAL_RULE - - BLOCKQUOTE - - LIST - - ORDERED_LIST_ITEM - - UNORDERED_LIST_ITEM - - TASK_LIST_ITEM - - MATH_BLOCK - - TABLE - - EMBEDDED_CONTENT - - TEXT - - BOLD - - ITALIC - - BOLD_ITALIC - - CODE - - IMAGE - - LINK - - AUTO_LINK - - TAG - - STRIKETHROUGH - - ESCAPING_CHARACTER - - MATH - - HIGHLIGHT - - SUBSCRIPT - - SUPERSCRIPT - - REFERENCED_CONTENT - - SPOILER - - HTML_ELEMENT - type: string - format: enum - lineBreakNode: - allOf: - - $ref: '#/components/schemas/LineBreakNode' - description: Block nodes. - paragraphNode: - $ref: '#/components/schemas/ParagraphNode' - codeBlockNode: - $ref: '#/components/schemas/CodeBlockNode' - headingNode: - $ref: '#/components/schemas/HeadingNode' - horizontalRuleNode: - $ref: '#/components/schemas/HorizontalRuleNode' - blockquoteNode: - $ref: '#/components/schemas/BlockquoteNode' - listNode: - $ref: '#/components/schemas/ListNode' - orderedListItemNode: - $ref: '#/components/schemas/OrderedListItemNode' - unorderedListItemNode: - $ref: '#/components/schemas/UnorderedListItemNode' - taskListItemNode: - $ref: '#/components/schemas/TaskListItemNode' - mathBlockNode: - $ref: '#/components/schemas/MathBlockNode' - tableNode: - $ref: '#/components/schemas/TableNode' - embeddedContentNode: - $ref: '#/components/schemas/EmbeddedContentNode' - textNode: - allOf: - - $ref: '#/components/schemas/TextNode' - description: Inline nodes. - boldNode: - $ref: '#/components/schemas/BoldNode' - italicNode: - $ref: '#/components/schemas/ItalicNode' - boldItalicNode: - $ref: '#/components/schemas/BoldItalicNode' - codeNode: - $ref: '#/components/schemas/CodeNode' - imageNode: - $ref: '#/components/schemas/ImageNode' - linkNode: - $ref: '#/components/schemas/LinkNode' - autoLinkNode: - $ref: '#/components/schemas/AutoLinkNode' - tagNode: - $ref: '#/components/schemas/TagNode' - strikethroughNode: - $ref: '#/components/schemas/StrikethroughNode' - escapingCharacterNode: - $ref: '#/components/schemas/EscapingCharacterNode' - mathNode: - $ref: '#/components/schemas/MathNode' - highlightNode: - $ref: '#/components/schemas/HighlightNode' - subscriptNode: - $ref: '#/components/schemas/SubscriptNode' - superscriptNode: - $ref: '#/components/schemas/SuperscriptNode' - referencedContentNode: - $ref: '#/components/schemas/ReferencedContentNode' - spoilerNode: - $ref: '#/components/schemas/SpoilerNode' - htmlElementNode: - $ref: '#/components/schemas/HTMLElementNode' OAuth2Config: type: object properties: @@ -3179,41 +2801,6 @@ components: type: string fieldMapping: $ref: '#/components/schemas/FieldMapping' - OrderedListItemNode: - type: object - properties: - number: - type: string - indent: - type: integer - format: int32 - children: - type: array - items: - $ref: '#/components/schemas/Node' - ParagraphNode: - type: object - properties: - children: - type: array - items: - $ref: '#/components/schemas/Node' - ParseMarkdownRequest: - required: - - markdown - type: object - properties: - markdown: - type: string - description: The markdown content to parse. - ParseMarkdownResponse: - type: object - properties: - nodes: - type: array - items: - $ref: '#/components/schemas/Node' - description: The parsed markdown nodes. Reaction: required: - contentId @@ -3246,15 +2833,6 @@ components: type: string description: Output only. The creation timestamp. format: date-time - ReferencedContentNode: - type: object - properties: - resourceName: - type: string - description: The resource name of the referenced content. - params: - type: string - description: Additional parameters for the referenced content. RenameMemoTagRequest: required: - parent @@ -3273,22 +2851,6 @@ components: newTag: type: string description: Required. The new tag name. - RestoreMarkdownNodesRequest: - required: - - nodes - type: object - properties: - nodes: - type: array - items: - $ref: '#/components/schemas/Node' - description: The nodes to restore to markdown content. - RestoreMarkdownNodesResponse: - type: object - properties: - markdown: - type: string - description: The restored markdown content. SetMemoAttachmentsRequest: required: - name @@ -3337,11 +2899,6 @@ components: filter: type: string description: The filter expression for the shortcut. - SpoilerNode: - type: object - properties: - content: - type: string Status: type: object properties: @@ -3376,95 +2933,6 @@ components: description: |- S3 configuration for cloud storage backend. Reference: https://developers.cloudflare.com/r2/examples/aws/aws-sdk-go/ - StrikethroughNode: - type: object - properties: - content: - type: string - StringifyMarkdownNodesRequest: - required: - - nodes - type: object - properties: - nodes: - type: array - items: - $ref: '#/components/schemas/Node' - description: The nodes to stringify to plain text. - StringifyMarkdownNodesResponse: - type: object - properties: - plainText: - type: string - description: The plain text content. - SubscriptNode: - type: object - properties: - content: - type: string - SuperscriptNode: - type: object - properties: - content: - type: string - TableNode: - type: object - properties: - header: - type: array - items: - $ref: '#/components/schemas/Node' - delimiter: - type: array - items: - type: string - rows: - type: array - items: - $ref: '#/components/schemas/TableNode_Row' - TableNode_Row: - type: object - properties: - cells: - type: array - items: - $ref: '#/components/schemas/Node' - TagNode: - type: object - properties: - content: - type: string - TaskListItemNode: - type: object - properties: - symbol: - type: string - indent: - type: integer - format: int32 - complete: - type: boolean - children: - type: array - items: - $ref: '#/components/schemas/Node' - TextNode: - type: object - properties: - content: - type: string - UnorderedListItemNode: - type: object - properties: - symbol: - type: string - indent: - type: integer - format: int32 - children: - type: array - items: - $ref: '#/components/schemas/Node' UpsertMemoReactionRequest: required: - name @@ -3884,7 +3352,6 @@ tags: - name: AuthService - name: IdentityProviderService - name: InboxService - - name: MarkdownService - name: MemoService - name: ShortcutService - name: UserService diff --git a/server/router/api/v1/acl_config.go b/server/router/api/v1/acl_config.go index c9f64ac5e..c0274f764 100644 --- a/server/router/api/v1/acl_config.go +++ b/server/router/api/v1/acl_config.go @@ -14,7 +14,6 @@ var authenticationAllowlistMethods = map[string]bool{ "/memos.api.v1.UserService/SearchUsers": true, "/memos.api.v1.MemoService/GetMemo": true, "/memos.api.v1.MemoService/ListMemos": true, - "/memos.api.v1.MarkdownService/GetLinkMetadata": true, "/memos.api.v1.AttachmentService/GetAttachmentBinary": true, } diff --git a/server/router/api/v1/markdown_service.go b/server/router/api/v1/markdown_service.go deleted file mode 100644 index 99f6cb5d0..000000000 --- a/server/router/api/v1/markdown_service.go +++ /dev/null @@ -1,306 +0,0 @@ -package v1 - -import ( - "context" - - "github.com/pkg/errors" - "github.com/usememos/gomark" - "github.com/usememos/gomark/ast" - "github.com/usememos/gomark/renderer" - - "github.com/usememos/memos/plugin/httpgetter" - v1pb "github.com/usememos/memos/proto/gen/api/v1" -) - -func (*APIV1Service) ParseMarkdown(_ context.Context, request *v1pb.ParseMarkdownRequest) (*v1pb.ParseMarkdownResponse, error) { - doc, err := gomark.Parse(request.Markdown) - if err != nil { - return nil, errors.Wrap(err, "failed to parse memo content") - } - - nodes := convertFromASTDocument(doc) - return &v1pb.ParseMarkdownResponse{ - Nodes: nodes, - }, nil -} - -func (*APIV1Service) RestoreMarkdownNodes(_ context.Context, request *v1pb.RestoreMarkdownNodesRequest) (*v1pb.RestoreMarkdownNodesResponse, error) { - markdown := gomark.Restore(convertToASTDocument(request.Nodes)) - return &v1pb.RestoreMarkdownNodesResponse{ - Markdown: markdown, - }, nil -} - -func (*APIV1Service) StringifyMarkdownNodes(_ context.Context, request *v1pb.StringifyMarkdownNodesRequest) (*v1pb.StringifyMarkdownNodesResponse, error) { - stringRenderer := renderer.NewStringRenderer() - plainText := stringRenderer.RenderDocument(convertToASTDocument(request.Nodes)) - return &v1pb.StringifyMarkdownNodesResponse{ - PlainText: plainText, - }, nil -} - -func (*APIV1Service) GetLinkMetadata(_ context.Context, request *v1pb.GetLinkMetadataRequest) (*v1pb.LinkMetadata, error) { - htmlMeta, err := httpgetter.GetHTMLMeta(request.Link) - if err != nil { - return nil, err - } - - return &v1pb.LinkMetadata{ - Title: htmlMeta.Title, - Description: htmlMeta.Description, - Image: htmlMeta.Image, - }, nil -} - -func convertFromASTNode(rawNode ast.Node) *v1pb.Node { - node := &v1pb.Node{ - Type: v1pb.NodeType(v1pb.NodeType_value[string(rawNode.Type())]), - } - - switch n := rawNode.(type) { - case *ast.LineBreak: - node.Node = &v1pb.Node_LineBreakNode{} - case *ast.Paragraph: - children := convertFromASTNodes(n.Children) - node.Node = &v1pb.Node_ParagraphNode{ParagraphNode: &v1pb.ParagraphNode{Children: children}} - case *ast.CodeBlock: - node.Node = &v1pb.Node_CodeBlockNode{CodeBlockNode: &v1pb.CodeBlockNode{Language: n.Language, Content: n.Content}} - case *ast.Heading: - children := convertFromASTNodes(n.Children) - node.Node = &v1pb.Node_HeadingNode{HeadingNode: &v1pb.HeadingNode{Level: int32(n.Level), Children: children}} - case *ast.HorizontalRule: - node.Node = &v1pb.Node_HorizontalRuleNode{HorizontalRuleNode: &v1pb.HorizontalRuleNode{Symbol: n.Symbol}} - case *ast.Blockquote: - children := convertFromASTNodes(n.Children) - node.Node = &v1pb.Node_BlockquoteNode{BlockquoteNode: &v1pb.BlockquoteNode{Children: children}} - case *ast.List: - children := convertFromASTNodes(n.Children) - node.Node = &v1pb.Node_ListNode{ListNode: &v1pb.ListNode{Kind: convertListKindFromASTNode(n.Kind), Indent: int32(n.Indent), Children: children}} - case *ast.OrderedListItem: - children := convertFromASTNodes(n.Children) - node.Node = &v1pb.Node_OrderedListItemNode{OrderedListItemNode: &v1pb.OrderedListItemNode{Number: n.Number, Indent: int32(n.Indent), Children: children}} - case *ast.UnorderedListItem: - children := convertFromASTNodes(n.Children) - node.Node = &v1pb.Node_UnorderedListItemNode{UnorderedListItemNode: &v1pb.UnorderedListItemNode{Symbol: n.Symbol, Indent: int32(n.Indent), Children: children}} - case *ast.TaskListItem: - children := convertFromASTNodes(n.Children) - node.Node = &v1pb.Node_TaskListItemNode{TaskListItemNode: &v1pb.TaskListItemNode{Symbol: n.Symbol, Indent: int32(n.Indent), Complete: n.Complete, Children: children}} - case *ast.MathBlock: - node.Node = &v1pb.Node_MathBlockNode{MathBlockNode: &v1pb.MathBlockNode{Content: n.Content}} - case *ast.Table: - node.Node = &v1pb.Node_TableNode{TableNode: convertTableFromASTNode(n)} - case *ast.EmbeddedContent: - node.Node = &v1pb.Node_EmbeddedContentNode{EmbeddedContentNode: &v1pb.EmbeddedContentNode{ResourceName: n.ResourceName, Params: n.Params}} - case *ast.Text: - node.Node = &v1pb.Node_TextNode{TextNode: &v1pb.TextNode{Content: n.Content}} - case *ast.Bold: - node.Node = &v1pb.Node_BoldNode{BoldNode: &v1pb.BoldNode{Symbol: n.Symbol, Children: convertFromASTNodes(n.Children)}} - case *ast.Italic: - node.Node = &v1pb.Node_ItalicNode{ItalicNode: &v1pb.ItalicNode{Symbol: n.Symbol, Children: convertFromASTNodes(n.Children)}} - case *ast.BoldItalic: - childDoc := &ast.Document{Children: n.Children} - plain := renderer.NewStringRenderer().RenderDocument(childDoc) - node.Node = &v1pb.Node_BoldItalicNode{BoldItalicNode: &v1pb.BoldItalicNode{Symbol: n.Symbol, Content: plain}} - case *ast.Code: - node.Node = &v1pb.Node_CodeNode{CodeNode: &v1pb.CodeNode{Content: n.Content}} - case *ast.Image: - node.Node = &v1pb.Node_ImageNode{ImageNode: &v1pb.ImageNode{AltText: n.AltText, Url: n.URL}} - case *ast.Link: - node.Node = &v1pb.Node_LinkNode{LinkNode: &v1pb.LinkNode{Content: convertFromASTNodes(n.Content), Url: n.URL}} - case *ast.AutoLink: - node.Node = &v1pb.Node_AutoLinkNode{AutoLinkNode: &v1pb.AutoLinkNode{Url: n.URL, IsRawText: n.IsRawText}} - case *ast.Tag: - node.Node = &v1pb.Node_TagNode{TagNode: &v1pb.TagNode{Content: n.Content}} - case *ast.Strikethrough: - node.Node = &v1pb.Node_StrikethroughNode{StrikethroughNode: &v1pb.StrikethroughNode{Content: n.Content}} - case *ast.EscapingCharacter: - node.Node = &v1pb.Node_EscapingCharacterNode{EscapingCharacterNode: &v1pb.EscapingCharacterNode{Symbol: n.Symbol}} - case *ast.Math: - node.Node = &v1pb.Node_MathNode{MathNode: &v1pb.MathNode{Content: n.Content}} - case *ast.Highlight: - node.Node = &v1pb.Node_HighlightNode{HighlightNode: &v1pb.HighlightNode{Content: n.Content}} - case *ast.Subscript: - node.Node = &v1pb.Node_SubscriptNode{SubscriptNode: &v1pb.SubscriptNode{Content: n.Content}} - case *ast.Superscript: - node.Node = &v1pb.Node_SuperscriptNode{SuperscriptNode: &v1pb.SuperscriptNode{Content: n.Content}} - case *ast.ReferencedContent: - node.Node = &v1pb.Node_ReferencedContentNode{ReferencedContentNode: &v1pb.ReferencedContentNode{ResourceName: n.ResourceName, Params: n.Params}} - case *ast.Spoiler: - node.Node = &v1pb.Node_SpoilerNode{SpoilerNode: &v1pb.SpoilerNode{Content: n.Content}} - case *ast.HTMLElement: - node.Node = &v1pb.Node_HtmlElementNode{HtmlElementNode: &v1pb.HTMLElementNode{ - TagName: n.TagName, - Attributes: n.Attributes, - Children: convertFromASTNodes(n.Children), - IsSelfClosing: n.IsSelfClosing, - }} - default: - node.Node = &v1pb.Node_TextNode{TextNode: &v1pb.TextNode{}} - } - return node -} - -func convertFromASTNodes(rawNodes []ast.Node) []*v1pb.Node { - nodes := []*v1pb.Node{} - for _, rawNode := range rawNodes { - node := convertFromASTNode(rawNode) - nodes = append(nodes, node) - } - return nodes -} - -func convertFromASTDocument(doc *ast.Document) []*v1pb.Node { - if doc == nil { - return nil - } - return convertFromASTNodes(doc.Children) -} - -func convertTableFromASTNode(node *ast.Table) *v1pb.TableNode { - table := &v1pb.TableNode{ - Header: convertFromASTNodes(node.Header), - Delimiter: node.Delimiter, - } - for _, row := range node.Rows { - table.Rows = append(table.Rows, &v1pb.TableNode_Row{Cells: convertFromASTNodes(row)}) - } - return table -} - -func convertListKindFromASTNode(node ast.ListKind) v1pb.ListNode_Kind { - switch node { - case ast.OrderedList: - return v1pb.ListNode_ORDERED - case ast.UnorderedList: - return v1pb.ListNode_UNORDERED - case ast.DescriptionList: - return v1pb.ListNode_DESCRIPTION - default: - return v1pb.ListNode_KIND_UNSPECIFIED - } -} - -func convertToASTNode(node *v1pb.Node) ast.Node { - switch n := node.Node.(type) { - case *v1pb.Node_LineBreakNode: - return &ast.LineBreak{} - case *v1pb.Node_ParagraphNode: - children := convertToASTNodes(n.ParagraphNode.Children) - return &ast.Paragraph{Children: children} - case *v1pb.Node_CodeBlockNode: - return &ast.CodeBlock{Language: n.CodeBlockNode.Language, Content: n.CodeBlockNode.Content} - case *v1pb.Node_HeadingNode: - children := convertToASTNodes(n.HeadingNode.Children) - return &ast.Heading{Level: int(n.HeadingNode.Level), Children: children} - case *v1pb.Node_HorizontalRuleNode: - return &ast.HorizontalRule{Symbol: n.HorizontalRuleNode.Symbol} - case *v1pb.Node_BlockquoteNode: - children := convertToASTNodes(n.BlockquoteNode.Children) - return &ast.Blockquote{Children: children} - case *v1pb.Node_ListNode: - children := convertToASTNodes(n.ListNode.Children) - return &ast.List{Kind: convertListKindToASTNode(n.ListNode.Kind), Indent: int(n.ListNode.Indent), Children: children} - case *v1pb.Node_OrderedListItemNode: - children := convertToASTNodes(n.OrderedListItemNode.Children) - return &ast.OrderedListItem{Number: n.OrderedListItemNode.Number, Indent: int(n.OrderedListItemNode.Indent), Children: children} - case *v1pb.Node_UnorderedListItemNode: - children := convertToASTNodes(n.UnorderedListItemNode.Children) - return &ast.UnorderedListItem{Symbol: n.UnorderedListItemNode.Symbol, Indent: int(n.UnorderedListItemNode.Indent), Children: children} - case *v1pb.Node_TaskListItemNode: - children := convertToASTNodes(n.TaskListItemNode.Children) - return &ast.TaskListItem{Symbol: n.TaskListItemNode.Symbol, Indent: int(n.TaskListItemNode.Indent), Complete: n.TaskListItemNode.Complete, Children: children} - case *v1pb.Node_MathBlockNode: - return &ast.MathBlock{Content: n.MathBlockNode.Content} - case *v1pb.Node_TableNode: - return convertTableToASTNode(n.TableNode) - case *v1pb.Node_EmbeddedContentNode: - return &ast.EmbeddedContent{ResourceName: n.EmbeddedContentNode.ResourceName, Params: n.EmbeddedContentNode.Params} - case *v1pb.Node_TextNode: - return &ast.Text{Content: n.TextNode.Content} - case *v1pb.Node_BoldNode: - return &ast.Bold{Symbol: n.BoldNode.Symbol, Children: convertToASTNodes(n.BoldNode.Children)} - case *v1pb.Node_ItalicNode: - return &ast.Italic{Symbol: n.ItalicNode.Symbol, Children: convertToASTNodes(n.ItalicNode.Children)} - case *v1pb.Node_BoldItalicNode: - children := []ast.Node{} - if n.BoldItalicNode.Content != "" { - children = append(children, &ast.Text{Content: n.BoldItalicNode.Content}) - } - return &ast.BoldItalic{Symbol: n.BoldItalicNode.Symbol, Children: children} - case *v1pb.Node_CodeNode: - return &ast.Code{Content: n.CodeNode.Content} - case *v1pb.Node_ImageNode: - return &ast.Image{AltText: n.ImageNode.AltText, URL: n.ImageNode.Url} - case *v1pb.Node_LinkNode: - return &ast.Link{Content: convertToASTNodes(n.LinkNode.Content), URL: n.LinkNode.Url} - case *v1pb.Node_AutoLinkNode: - return &ast.AutoLink{URL: n.AutoLinkNode.Url, IsRawText: n.AutoLinkNode.IsRawText} - case *v1pb.Node_TagNode: - return &ast.Tag{Content: n.TagNode.Content} - case *v1pb.Node_StrikethroughNode: - return &ast.Strikethrough{Content: n.StrikethroughNode.Content} - case *v1pb.Node_EscapingCharacterNode: - return &ast.EscapingCharacter{Symbol: n.EscapingCharacterNode.Symbol} - case *v1pb.Node_MathNode: - return &ast.Math{Content: n.MathNode.Content} - case *v1pb.Node_HighlightNode: - return &ast.Highlight{Content: n.HighlightNode.Content} - case *v1pb.Node_SubscriptNode: - return &ast.Subscript{Content: n.SubscriptNode.Content} - case *v1pb.Node_SuperscriptNode: - return &ast.Superscript{Content: n.SuperscriptNode.Content} - case *v1pb.Node_ReferencedContentNode: - return &ast.ReferencedContent{ResourceName: n.ReferencedContentNode.ResourceName, Params: n.ReferencedContentNode.Params} - case *v1pb.Node_SpoilerNode: - return &ast.Spoiler{Content: n.SpoilerNode.Content} - case *v1pb.Node_HtmlElementNode: - var children []ast.Node - if len(n.HtmlElementNode.Children) > 0 { - children = convertToASTNodes(n.HtmlElementNode.Children) - } - return &ast.HTMLElement{ - TagName: n.HtmlElementNode.TagName, - Attributes: n.HtmlElementNode.Attributes, - Children: children, - IsSelfClosing: n.HtmlElementNode.IsSelfClosing, - } - default: - return &ast.Text{} - } -} - -func convertToASTNodes(nodes []*v1pb.Node) []ast.Node { - rawNodes := []ast.Node{} - for _, node := range nodes { - rawNode := convertToASTNode(node) - rawNodes = append(rawNodes, rawNode) - } - return rawNodes -} - -func convertToASTDocument(nodes []*v1pb.Node) *ast.Document { - return &ast.Document{Children: convertToASTNodes(nodes)} -} - -func convertTableToASTNode(node *v1pb.TableNode) *ast.Table { - table := &ast.Table{ - Header: convertToASTNodes(node.Header), - Delimiter: node.Delimiter, - } - for _, row := range node.Rows { - table.Rows = append(table.Rows, convertToASTNodes(row.Cells)) - } - return table -} - -func convertListKindToASTNode(kind v1pb.ListNode_Kind) ast.ListKind { - switch kind { - case v1pb.ListNode_ORDERED: - return ast.OrderedList - case v1pb.ListNode_UNORDERED: - return ast.UnorderedList - default: - // Default to description list. - return ast.DescriptionList - } -} diff --git a/server/router/api/v1/memo_relation_service.go b/server/router/api/v1/memo_relation_service.go index 0e902c500..247f85ac8 100644 --- a/server/router/api/v1/memo_relation_service.go +++ b/server/router/api/v1/memo_relation_service.go @@ -122,7 +122,7 @@ func (s *APIV1Service) convertMemoRelationFromStore(ctx context.Context, memoRel if err != nil { return nil, err } - memoSnippet, err := getMemoContentSnippet(memo.Content) + memoSnippet, err := s.getMemoContentSnippet(memo.Content) if err != nil { return nil, errors.Wrap(err, "failed to get memo content snippet") } @@ -130,7 +130,7 @@ func (s *APIV1Service) convertMemoRelationFromStore(ctx context.Context, memoRel if err != nil { return nil, err } - relatedMemoSnippet, err := getMemoContentSnippet(relatedMemo.Content) + relatedMemoSnippet, err := s.getMemoContentSnippet(relatedMemo.Content) if err != nil { return nil, errors.Wrap(err, "failed to get related memo content snippet") } diff --git a/server/router/api/v1/memo_service.go b/server/router/api/v1/memo_service.go index 2b423f729..6270529c6 100644 --- a/server/router/api/v1/memo_service.go +++ b/server/router/api/v1/memo_service.go @@ -10,9 +10,6 @@ import ( "github.com/lithammer/shortuuid/v4" "github.com/pkg/errors" - "github.com/usememos/gomark" - "github.com/usememos/gomark/ast" - "github.com/usememos/gomark/renderer" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" @@ -53,7 +50,7 @@ func (s *APIV1Service) CreateMemo(ctx context.Context, request *v1pb.CreateMemoR if len(create.Content) > contentLengthLimit { return nil, status.Errorf(codes.InvalidArgument, "content too long (max %d characters)", contentLengthLimit) } - if err := memopayload.RebuildMemoPayload(create); err != nil { + if err := memopayload.RebuildMemoPayload(create, s.MarkdownService); err != nil { return nil, status.Errorf(codes.Internal, "failed to rebuild memo payload: %v", err) } if request.Memo.Location != nil { @@ -338,7 +335,7 @@ func (s *APIV1Service) UpdateMemo(ctx context.Context, request *v1pb.UpdateMemoR return nil, status.Errorf(codes.InvalidArgument, "content too long (max %d characters)", contentLengthLimit) } memo.Content = request.Memo.Content - if err := memopayload.RebuildMemoPayload(memo); err != nil { + if err := memopayload.RebuildMemoPayload(memo, s.MarkdownService); err != nil { return nil, status.Errorf(codes.Internal, "failed to rebuild memo payload: %v", err) } update.Content = &memo.Content @@ -711,17 +708,14 @@ func (s *APIV1Service) RenameMemoTag(ctx context.Context, request *v1pb.RenameMe } for _, memo := range memos { - doc, err := gomark.Parse(memo.Content) + // Rename tag using goldmark + newContent, err := s.MarkdownService.RenameTag([]byte(memo.Content), request.OldTag, request.NewTag) if err != nil { - return nil, status.Errorf(codes.Internal, "failed to parse memo: %v", err) + return nil, status.Errorf(codes.Internal, "failed to rename tag: %v", err) } - memopayload.TraverseASTDocument(doc, func(node ast.Node) { - if tag, ok := node.(*ast.Tag); ok && tag.Content == request.OldTag { - tag.Content = request.NewTag - } - }) - memo.Content = gomark.Restore(doc) - if err := memopayload.RebuildMemoPayload(memo); err != nil { + memo.Content = newContent + + if err := memopayload.RebuildMemoPayload(memo, s.MarkdownService); err != nil { return nil, status.Errorf(codes.Internal, "failed to rebuild memo payload: %v", err) } if err := s.Store.UpdateMemo(ctx, &store.UpdateMemo{ @@ -842,17 +836,13 @@ func convertMemoToWebhookPayload(memo *v1pb.Memo) (*webhook.WebhookRequestPayloa }, nil } -func getMemoContentSnippet(content string) (string, error) { - doc, err := gomark.Parse(content) +func (s *APIV1Service) getMemoContentSnippet(content string) (string, error) { + // Use goldmark service for snippet generation + snippet, err := s.MarkdownService.GenerateSnippet([]byte(content), 64) if err != nil { - return "", errors.Wrap(err, "failed to parse content") + return "", errors.Wrap(err, "failed to generate snippet") } - - plainText := renderer.NewStringRenderer().RenderDocument(doc) - if len(plainText) > 64 { - return substring(plainText, 64) + "...", nil - } - return plainText, nil + return snippet, nil } func substring(s string, length int) string { diff --git a/server/router/api/v1/memo_service_converter.go b/server/router/api/v1/memo_service_converter.go index 07077e229..de14e3954 100644 --- a/server/router/api/v1/memo_service_converter.go +++ b/server/router/api/v1/memo_service_converter.go @@ -8,8 +8,6 @@ import ( "github.com/pkg/errors" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/usememos/gomark" - v1pb "github.com/usememos/memos/proto/gen/api/v1" storepb "github.com/usememos/memos/proto/gen/store" "github.com/usememos/memos/store" @@ -68,15 +66,7 @@ func (s *APIV1Service) convertMemoFromStore(ctx context.Context, memo *store.Mem memoMessage.Attachments = append(memoMessage.Attachments, attachmentResponse) } - doc, err := gomark.Parse(memo.Content) - if err != nil { - return nil, errors.Wrap(err, "failed to parse content") - } - if doc != nil { - memoMessage.Nodes = convertFromASTNodes(doc.Children) - } - - snippet, err := getMemoContentSnippet(memo.Content) + snippet, err := s.getMemoContentSnippet(memo.Content) if err != nil { return nil, errors.Wrap(err, "failed to get memo content snippet") } diff --git a/server/router/api/v1/test/test_helper.go b/server/router/api/v1/test/test_helper.go index c34aafc90..fa4a18b18 100644 --- a/server/router/api/v1/test/test_helper.go +++ b/server/router/api/v1/test/test_helper.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/usememos/memos/internal/profile" + "github.com/usememos/memos/plugin/markdown" apiv1 "github.com/usememos/memos/server/router/api/v1" "github.com/usememos/memos/store" teststore "github.com/usememos/memos/store/test" @@ -36,10 +37,15 @@ func NewTestService(t *testing.T) *TestService { // Create APIV1Service with nil grpcServer since we're testing direct calls secret := "test-secret" + markdownService := markdown.NewService( + markdown.WithTagExtension(), + markdown.WithWikilinkExtension(), + ) service := &apiv1.APIV1Service{ - Secret: secret, - Profile: testProfile, - Store: testStore, + Secret: secret, + Profile: testProfile, + Store: testStore, + MarkdownService: markdownService, } return &TestService{ diff --git a/server/router/api/v1/v1.go b/server/router/api/v1/v1.go index 613131adf..24820d7bf 100644 --- a/server/router/api/v1/v1.go +++ b/server/router/api/v1/v1.go @@ -15,6 +15,7 @@ import ( "google.golang.org/grpc/reflection" "github.com/usememos/memos/internal/profile" + "github.com/usememos/memos/plugin/markdown" v1pb "github.com/usememos/memos/proto/gen/api/v1" "github.com/usememos/memos/store" ) @@ -30,23 +31,28 @@ type APIV1Service struct { v1pb.UnimplementedShortcutServiceServer v1pb.UnimplementedInboxServiceServer v1pb.UnimplementedActivityServiceServer - v1pb.UnimplementedMarkdownServiceServer v1pb.UnimplementedIdentityProviderServiceServer - Secret string - Profile *profile.Profile - Store *store.Store + Secret string + Profile *profile.Profile + Store *store.Store + MarkdownService markdown.Service grpcServer *grpc.Server } func NewAPIV1Service(secret string, profile *profile.Profile, store *store.Store, grpcServer *grpc.Server) *APIV1Service { grpc.EnableTracing = true + markdownService := markdown.NewService( + markdown.WithTagExtension(), + markdown.WithWikilinkExtension(), + ) apiv1Service := &APIV1Service{ - Secret: secret, - Profile: profile, - Store: store, - grpcServer: grpcServer, + Secret: secret, + Profile: profile, + Store: store, + MarkdownService: markdownService, + grpcServer: grpcServer, } grpc_health_v1.RegisterHealthServer(grpcServer, apiv1Service) v1pb.RegisterWorkspaceServiceServer(grpcServer, apiv1Service) @@ -57,7 +63,6 @@ func NewAPIV1Service(secret string, profile *profile.Profile, store *store.Store v1pb.RegisterShortcutServiceServer(grpcServer, apiv1Service) v1pb.RegisterInboxServiceServer(grpcServer, apiv1Service) v1pb.RegisterActivityServiceServer(grpcServer, apiv1Service) - v1pb.RegisterMarkdownServiceServer(grpcServer, apiv1Service) v1pb.RegisterIdentityProviderServiceServer(grpcServer, apiv1Service) reflection.Register(grpcServer) return apiv1Service @@ -109,9 +114,6 @@ func (s *APIV1Service) RegisterGateway(ctx context.Context, echoServer *echo.Ech if err := v1pb.RegisterActivityServiceHandler(ctx, gwMux, conn); err != nil { return err } - if err := v1pb.RegisterMarkdownServiceHandler(ctx, gwMux, conn); err != nil { - return err - } if err := v1pb.RegisterIdentityProviderServiceHandler(ctx, gwMux, conn); err != nil { return err } diff --git a/server/router/rss/rss.go b/server/router/rss/rss.go index a6d30a5a8..bc3ce294b 100644 --- a/server/router/rss/rss.go +++ b/server/router/rss/rss.go @@ -9,10 +9,9 @@ import ( "github.com/gorilla/feeds" "github.com/labstack/echo/v4" - "github.com/usememos/gomark" - "github.com/usememos/gomark/renderer" "github.com/usememos/memos/internal/profile" + "github.com/usememos/memos/plugin/markdown" storepb "github.com/usememos/memos/proto/gen/store" "github.com/usememos/memos/store" ) @@ -22,8 +21,9 @@ const ( ) type RSSService struct { - Profile *profile.Profile - Store *store.Store + Profile *profile.Profile + Store *store.Store + MarkdownService markdown.Service } type RSSHeading struct { @@ -31,10 +31,11 @@ type RSSHeading struct { Description string } -func NewRSSService(profile *profile.Profile, store *store.Store) *RSSService { +func NewRSSService(profile *profile.Profile, store *store.Store, markdownService markdown.Service) *RSSService { return &RSSService{ - Profile: profile, - Store: store, + Profile: profile, + Store: store, + MarkdownService: markdownService, } } @@ -113,7 +114,7 @@ func (s *RSSService) generateRSSFromMemoList(ctx context.Context, memoList []*st feed.Items = make([]*feeds.Item, itemCountLimit) for i := 0; i < itemCountLimit; i++ { memo := memoList[i] - description, err := getRSSItemDescription(memo.Content) + description, err := s.getRSSItemDescription(memo.Content) if err != nil { return "", err } @@ -151,13 +152,12 @@ func (s *RSSService) generateRSSFromMemoList(ctx context.Context, memoList []*st return rss, nil } -func getRSSItemDescription(content string) (string, error) { - doc, err := gomark.Parse(content) +func (s *RSSService) getRSSItemDescription(content string) (string, error) { + html, err := s.MarkdownService.RenderHTML([]byte(content)) if err != nil { return "", err } - result := renderer.NewHTMLRenderer().RenderDocument(doc) - return result, nil + return html, nil } func getRSSHeading(ctx context.Context, stores *store.Store) (RSSHeading, error) { diff --git a/server/runner/memopayload/runner.go b/server/runner/memopayload/runner.go index 59d935cc2..a1b0cb372 100644 --- a/server/runner/memopayload/runner.go +++ b/server/runner/memopayload/runner.go @@ -3,23 +3,23 @@ package memopayload import ( "context" "log/slog" - "slices" "github.com/pkg/errors" - "github.com/usememos/gomark" - "github.com/usememos/gomark/ast" + "github.com/usememos/memos/plugin/markdown" storepb "github.com/usememos/memos/proto/gen/store" "github.com/usememos/memos/store" ) type Runner struct { - Store *store.Store + Store *store.Store + MarkdownService markdown.Service } -func NewRunner(store *store.Store) *Runner { +func NewRunner(store *store.Store, markdownService markdown.Service) *Runner { return &Runner{ - Store: store, + Store: store, + MarkdownService: markdownService, } } @@ -49,7 +49,7 @@ func (r *Runner) RunOnce(ctx context.Context) { // Process batch batchSuccessCount := 0 for _, memo := range memos { - if err := RebuildMemoPayload(memo); err != nil { + if err := RebuildMemoPayload(memo, r.MarkdownService); err != nil { slog.Error("failed to rebuild memo payload", "err", err, "memoID", memo.ID) continue } @@ -71,70 +71,21 @@ func (r *Runner) RunOnce(ctx context.Context) { } } -func RebuildMemoPayload(memo *store.Memo) error { - doc, err := gomark.Parse(memo.Content) - if err != nil { - return errors.Wrap(err, "failed to parse content") - } - +func RebuildMemoPayload(memo *store.Memo, markdownService markdown.Service) error { if memo.Payload == nil { memo.Payload = &storepb.MemoPayload{} } - tags := []string{} - property := &storepb.MemoPayload_Property{} - TraverseASTDocument(doc, func(node ast.Node) { - switch n := node.(type) { - case *ast.Tag: - tag := n.Content - if !slices.Contains(tags, tag) { - tags = append(tags, tag) - } - case *ast.Link, *ast.AutoLink: - property.HasLink = true - case *ast.TaskListItem: - property.HasTaskList = true - if !n.Complete { - property.HasIncompleteTasks = true - } - case *ast.CodeBlock: - property.HasCode = true - case *ast.EmbeddedContent: - // TODO: validate references. - property.References = append(property.References, n.ResourceName) - } - }) - memo.Payload.Tags = tags - memo.Payload.Property = property + + // Use goldmark service to extract all metadata in a single pass (more efficient) + data, err := markdownService.ExtractAll([]byte(memo.Content)) + if err != nil { + return errors.Wrap(err, "failed to extract markdown metadata") + } + + // Set references in property + data.Property.References = data.References + + memo.Payload.Tags = data.Tags + memo.Payload.Property = data.Property return nil } - -func TraverseASTDocument(doc *ast.Document, fn func(ast.Node)) { - if doc == nil { - return - } - traverseASTNodes(doc.Children, fn) -} - -func traverseASTNodes(nodes []ast.Node, fn func(ast.Node)) { - for _, node := range nodes { - fn(node) - switch n := node.(type) { - case *ast.Paragraph: - traverseASTNodes(n.Children, fn) - case *ast.Heading: - traverseASTNodes(n.Children, fn) - case *ast.Blockquote: - traverseASTNodes(n.Children, fn) - case *ast.List: - traverseASTNodes(n.Children, fn) - case *ast.OrderedListItem: - traverseASTNodes(n.Children, fn) - case *ast.UnorderedListItem: - traverseASTNodes(n.Children, fn) - case *ast.TaskListItem: - traverseASTNodes(n.Children, fn) - case *ast.Bold: - traverseASTNodes(n.Children, fn) - } - } -} diff --git a/server/server.go b/server/server.go index d4e58bd82..03e8b1d67 100644 --- a/server/server.go +++ b/server/server.go @@ -81,9 +81,6 @@ func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store rootGroup := echoServer.Group("") - // Create and register RSS routes. - rss.NewRSSService(s.Profile, s.Store).RegisterRoutes(rootGroup) - // Log full stacktraces if we're in dev logStacktraces := profile.IsDev() @@ -98,6 +95,9 @@ func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store s.grpcServer = grpcServer apiV1Service := apiv1.NewAPIV1Service(s.Secret, profile, store, grpcServer) + + // Create and register RSS routes (needs markdown service from apiV1Service). + rss.NewRSSService(s.Profile, s.Store, apiV1Service.MarkdownService).RegisterRoutes(rootGroup) // Register gRPC gateway as api v1. if err := apiV1Service.RegisterGateway(ctx, echoServer); err != nil { return nil, errors.Wrap(err, "failed to register gRPC gateway") diff --git a/web/package.json b/web/package.json index 4f0f18e66..5f5b99bbf 100644 --- a/web/package.json +++ b/web/package.json @@ -50,12 +50,17 @@ "react-hot-toast": "^2.6.0", "react-i18next": "^15.7.3", "react-leaflet": "^4.2.1", + "react-markdown": "^10.1.0", "react-router-dom": "^7.9.1", "react-simple-pull-to-refresh": "^1.3.3", "react-use": "^17.6.0", + "rehype-raw": "^7.0.0", + "remark-gfm": "^4.0.1", + "remark-wiki-link": "^2.0.1", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.13", "textarea-caret": "^3.1.0", + "unist-util-visit": "^5.0.0", "uuid": "^11.1.0" }, "devDependencies": { @@ -66,14 +71,15 @@ "@types/katex": "^0.16.7", "@types/leaflet": "^1.9.20", "@types/lodash-es": "^4.17.12", + "@types/mdast": "^4.0.4", "@types/node": "^24.5.1", "@types/qs": "^6.14.0", "@types/react": "^18.3.24", "@types/react-dom": "^18.3.7", "@types/textarea-caret": "^3.0.4", + "@types/unist": "^3.0.3", "@types/uuid": "^10.0.0", "@vitejs/plugin-react": "^4.7.0", - "code-inspector-plugin": "^1.2.10", "eslint": "^9.35.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 714119a0d..eb15fd8bf 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -134,6 +134,9 @@ importers: react-leaflet: specifier: ^4.2.1 version: 4.2.1(leaflet@1.9.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-markdown: + specifier: ^10.1.0 + version: 10.1.0(@types/react@18.3.24)(react@18.3.1) react-router-dom: specifier: ^7.9.1 version: 7.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -143,6 +146,15 @@ importers: react-use: specifier: ^17.6.0 version: 17.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rehype-raw: + specifier: ^7.0.0 + version: 7.0.0 + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1 + remark-wiki-link: + specifier: ^2.0.1 + version: 2.0.1 tailwind-merge: specifier: ^3.3.1 version: 3.3.1 @@ -152,6 +164,9 @@ importers: textarea-caret: specifier: ^3.1.0 version: 3.1.0 + unist-util-visit: + specifier: ^5.0.0 + version: 5.0.0 uuid: specifier: ^11.1.0 version: 11.1.0 @@ -177,6 +192,9 @@ importers: '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 + '@types/mdast': + specifier: ^4.0.4 + version: 4.0.4 '@types/node': specifier: ^24.5.1 version: 24.5.1 @@ -192,15 +210,15 @@ importers: '@types/textarea-caret': specifier: ^3.0.4 version: 3.0.4 + '@types/unist': + specifier: ^3.0.3 + version: 3.0.3 '@types/uuid': specifier: ^10.0.0 version: 10.0.0 '@vitejs/plugin-react': specifier: ^4.7.0 version: 4.7.0(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.44.0)) - code-inspector-plugin: - specifier: ^1.2.10 - version: 1.2.10 eslint: specifier: ^9.35.0 version: 9.35.0(jiti@2.5.1) @@ -354,24 +372,6 @@ packages: '@chevrotain/utils@11.0.3': resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==} - '@code-inspector/core@1.2.10': - resolution: {integrity: sha512-xTkR4oBrTlRA/S2cXTuZLttCX6+wQgUpBpEK4Ad/e9KBIUIDRne5yoxuvrdy3xkTMkURS2V4SnCTzjFcu4OELQ==} - - '@code-inspector/esbuild@1.2.10': - resolution: {integrity: sha512-+Y7tJTGrqpOgj4ENiq2pE9lE88pFGIumAFJr3K4jZxCT/JD/8bsQvOnNBEBS8BzwWZP6jK/XlaR/YFmw9p3r1A==} - - '@code-inspector/mako@1.2.10': - resolution: {integrity: sha512-IqQt6bdAF1emG47NJntxE+v4m+GUVOmyXjveP/bCUJ0L7yab48H9qsAPyEUtwBSbXGDopvCX0PgQeaubWpS1LQ==} - - '@code-inspector/turbopack@1.2.10': - resolution: {integrity: sha512-6oMeQjaDorIcAiy1IEPzrtozqfgzE2xq6AMc1/gVU44XqYnFZgUTyz5chkpPE1SQ+ZQ+EtgYGJyL6oYAQ0oyZQ==} - - '@code-inspector/vite@1.2.10': - resolution: {integrity: sha512-HsmEa0kIfJUhJf4zjipDFgySKAD/O/f+K2L49xUnAelO6bkhNGmg1QLur9Mzn+5vrKcCGLwa0LGwKVnuBE4Vng==} - - '@code-inspector/webpack@1.2.10': - resolution: {integrity: sha512-7TaYwAiz+ZlckVyKsU24HXghTuYV04mtwtJCIenkLfUSyrEIjUC/rhoYnQ/nUVwWuk0LvWJHUaLlYc65oQsggQ==} - '@dnd-kit/accessibility@3.1.1': resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==} peerDependencies: @@ -1468,12 +1468,21 @@ packages: '@types/d3@7.4.3': resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/geojson@7946.0.16': resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/js-cookie@2.2.7': resolution: {integrity: sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==} @@ -1492,6 +1501,12 @@ packages: '@types/lodash@4.17.20': resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/node@24.5.1': resolution: {integrity: sha512-/SQdmUP2xa+1rdx7VwB9yPq8PaKej8TD5cQ+XfKDPWWC+VDJU4rvVVagXqKUzhKjtFoNA8rXDJAkCxQPAe00+Q==} @@ -1518,6 +1533,12 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/uuid@10.0.0': resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} @@ -1580,21 +1601,15 @@ packages: resolution: {integrity: sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@vitejs/plugin-react@4.7.0': resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - '@vue/compiler-core@3.5.21': - resolution: {integrity: sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==} - - '@vue/compiler-dom@3.5.21': - resolution: {integrity: sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==} - - '@vue/shared@3.5.21': - resolution: {integrity: sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==} - '@xobotyi/scrollbar-width@1.9.5': resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==} @@ -1661,9 +1676,6 @@ packages: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} - async@3.2.6: - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -1672,6 +1684,9 @@ packages: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1723,14 +1738,34 @@ packages: resolution: {integrity: sha512-ryQkDX26yJ3CXzb3hxUVNlg1NKE4REc5crLBq661Nxzr8TNd236SaEf2ffYLXyI5tSABSeguHLqcVq4vf9L3Zg==} engines: {node: '>=12'} - chalk@4.1.1: - resolution: {integrity: sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==} - engines: {node: '>=10'} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + chevrotain-allstar@0.3.1: resolution: {integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==} peerDependencies: @@ -1756,9 +1791,6 @@ packages: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc - code-inspector-plugin@1.2.10: - resolution: {integrity: sha512-XF8U0egv6g19lU4QZcrPu40HmooyHjcIqaKb6fvIPSv8WUeg+qSlyrl7Bm05OBzMmJt/Y/mOdFx8MMuBqbC8Sg==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1766,6 +1798,9 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -2013,6 +2048,9 @@ packages: supports-color: optional: true + decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -2027,6 +2065,10 @@ packages: delaunator@5.0.1: resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-libc@2.1.0: resolution: {integrity: sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==} engines: {node: '>=8'} @@ -2034,6 +2076,9 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -2041,10 +2086,6 @@ packages: dompurify@3.2.7: resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==} - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} - engines: {node: '>=12'} - dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -2056,8 +2097,8 @@ packages: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} error-ex@1.3.4: @@ -2111,6 +2152,10 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + eslint-config-prettier@10.1.8: resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} hasBin: true @@ -2175,8 +2220,8 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} - estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} @@ -2185,6 +2230,9 @@ packages: exsolve@1.0.7: resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2357,6 +2405,27 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-from-parse5@8.0.3: + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-to-parse5@8.0.0: + resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@9.0.1: + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + highlight.js@11.11.1: resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} engines: {node: '>=12.0.0'} @@ -2367,6 +2436,12 @@ packages: html-parse-stringify@3.0.1: resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + hyphenate-style-name@1.1.0: resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} @@ -2402,6 +2477,9 @@ packages: resolution: {integrity: sha512-SP23P27OUKzXWEC/TOyWlwLviofQkCSCKONnc62eItjp69yCZZPqDQtr3Pw5gJDnPeUMqExmKydNZaJO0FU9pw==} engines: {node: '>=12'} + inline-style-parser@0.2.4: + resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} + inline-style-prefixer@7.0.1: resolution: {integrity: sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==} @@ -2416,6 +2494,18 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} + is-alphabetical@1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -2451,6 +2541,12 @@ packages: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} + is-decimal@1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2467,6 +2563,12 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-hexadecimal@1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -2483,6 +2585,10 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -2605,9 +2711,6 @@ packages: resolution: {integrity: sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==} engines: {node: '>=16.0.0'} - launch-ide@1.2.0: - resolution: {integrity: sha512-7nXSPQOt3b2JT52Ge8jp4miFcY+nrUEZxNLWBzrEfjmByDTb9b5ytqMSwGhsNwY6Cntwop+6n7rWIFN0+S8PTw==} - layout-base@1.0.2: resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} @@ -2708,6 +2811,12 @@ packages: long@5.3.2: resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + longest-streak@2.0.4: + resolution: {integrity: sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -2723,6 +2832,9 @@ packages: magic-string@0.30.19: resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + marked@15.0.12: resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} engines: {node: '>= 18'} @@ -2732,6 +2844,60 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.2: + resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + mdast-util-to-markdown@0.6.5: + resolution: {integrity: sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@2.0.0: + resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + mdast-util-wiki-link@0.1.2: + resolution: {integrity: sha512-DTcDyOxKDo3pB3fc0zQlD8myfQjYkW4hazUKI9PUyhtoj9JBeHC2eIdlVXmaT22bZkFAVU2d47B6y2jVKGoUQg==} + mdn-data@2.0.14: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} @@ -2742,6 +2908,93 @@ packages: mermaid@11.11.0: resolution: {integrity: sha512-9lb/VNkZqWTRjVgCV+l1N+t4kyi94y+l5xrmBmbbxZYkfRl5hEDaTPMOcaWKCl1McG8nBEaMlWwkcAEEgjhBgg==} + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-extension-wiki-link@0.0.4: + resolution: {integrity: sha512-dJc8AfnoU8BHkN+7fWZvIS20SMsMS1ZlxQUn6We67MqeKbOiEDZV5eEvCpwqGBijbJbxX3Kxz879L4K9HIiOvw==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -2867,10 +3120,19 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-entities@2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + path-data-parser@0.1.0: resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} @@ -2915,10 +3177,6 @@ packages: points-on-path@0.2.1: resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} - portfinder@1.0.38: - resolution: {integrity: sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==} - engines: {node: '>= 10.12'} - possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -2946,6 +3204,12 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + property-information@6.5.0: + resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -3006,6 +3270,12 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + react-refresh@0.17.0: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} @@ -3087,6 +3357,28 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + remark-wiki-link@2.0.1: + resolution: {integrity: sha512-F8Eut1E7GWfFm4ZDTI6/4ejeZEHZgnVk6E933Yqd/ssYsc4AyI32aGakxwsGcEzbbE7dkWi1EfLlGAdGgOZOsA==} + + repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + resize-observer-polyfill@1.5.1: resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} @@ -3220,6 +3512,9 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + stack-generator@2.0.10: resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} @@ -3255,10 +3550,19 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + style-to-js@1.1.18: + resolution: {integrity: sha512-JFPn62D4kJaPTnhFUI244MThx+FEGbi+9dw1b9yBBQ+1CZpV7QAT8kUtJ7b7EUNdHajjF/0x8fT+16oLJoojLg==} + + style-to-object@1.0.11: + resolution: {integrity: sha512-5A560JmXr7wDyGLK12Nq/EYS38VkGlglVzkis1JEdbGWSnbQIEhZzTJhzURXN5/8WwwFCs/f/VVcmkTppbXLow==} + stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} @@ -3320,6 +3624,12 @@ packages: toggle-selection@1.0.6: resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + ts-api-utils@2.1.0: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} @@ -3384,6 +3694,24 @@ packages: undici-types@7.12.0: resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true @@ -3422,6 +3750,15 @@ packages: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite@7.1.5: resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3486,6 +3823,9 @@ packages: vscode-uri@3.0.8: resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -3538,6 +3878,12 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zwitch@1.0.5: + resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: '@antfu/install-pkg@1.1.0': @@ -3682,48 +4028,6 @@ snapshots: '@chevrotain/utils@11.0.3': {} - '@code-inspector/core@1.2.10': - dependencies: - '@vue/compiler-dom': 3.5.21 - chalk: 4.1.1 - dotenv: 16.6.1 - launch-ide: 1.2.0 - portfinder: 1.0.38 - transitivePeerDependencies: - - supports-color - - '@code-inspector/esbuild@1.2.10': - dependencies: - '@code-inspector/core': 1.2.10 - transitivePeerDependencies: - - supports-color - - '@code-inspector/mako@1.2.10': - dependencies: - '@code-inspector/core': 1.2.10 - transitivePeerDependencies: - - supports-color - - '@code-inspector/turbopack@1.2.10': - dependencies: - '@code-inspector/core': 1.2.10 - '@code-inspector/webpack': 1.2.10 - transitivePeerDependencies: - - supports-color - - '@code-inspector/vite@1.2.10': - dependencies: - '@code-inspector/core': 1.2.10 - chalk: 4.1.1 - transitivePeerDependencies: - - supports-color - - '@code-inspector/webpack@1.2.10': - dependencies: - '@code-inspector/core': 1.2.10 - transitivePeerDependencies: - - supports-color - '@dnd-kit/accessibility@3.1.1(react@18.3.1)': dependencies: react: 18.3.1 @@ -4762,10 +5066,22 @@ snapshots: '@types/d3-transition': 3.0.9 '@types/d3-zoom': 3.0.8 + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.8 + '@types/estree@1.0.8': {} '@types/geojson@7946.0.16': {} + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/js-cookie@2.2.7': {} '@types/json-schema@7.0.15': {} @@ -4782,6 +5098,12 @@ snapshots: '@types/lodash@4.17.20': {} + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + '@types/node@24.5.1': dependencies: undici-types: 7.12.0 @@ -4806,6 +5128,10 @@ snapshots: '@types/trusted-types@2.0.7': optional: true + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + '@types/uuid@10.0.0': {} '@typescript-eslint/eslint-plugin@8.45.0(@typescript-eslint/parser@8.45.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.3))(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.3)': @@ -4901,6 +5227,8 @@ snapshots: '@typescript-eslint/types': 8.45.0 eslint-visitor-keys: 4.2.1 + '@ungap/structured-clone@1.3.0': {} + '@vitejs/plugin-react@4.7.0(vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.44.0))': dependencies: '@babel/core': 7.28.4 @@ -4913,21 +5241,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@vue/compiler-core@3.5.21': - dependencies: - '@babel/parser': 7.28.4 - '@vue/shared': 3.5.21 - entities: 4.5.0 - estree-walker: 2.0.2 - source-map-js: 1.2.1 - - '@vue/compiler-dom@3.5.21': - dependencies: - '@vue/compiler-core': 3.5.21 - '@vue/shared': 3.5.21 - - '@vue/shared@3.5.21': {} - '@xobotyi/scrollbar-width@1.9.5': {} abort-controller-x@0.4.3: {} @@ -5016,8 +5329,6 @@ snapshots: async-function@1.0.0: {} - async@3.2.6: {} - available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 @@ -5028,6 +5339,8 @@ snapshots: cosmiconfig: 7.1.0 resolve: 1.22.10 + bail@2.0.2: {} + balanced-match@1.0.2: {} baseline-browser-mapping@2.8.4: {} @@ -5082,16 +5395,27 @@ snapshots: dependencies: tinycolor2: 1.6.0 - chalk@4.1.1: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 + ccount@2.0.1: {} chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 + character-entities-html4@2.1.0: {} + + character-entities-legacy@1.1.4: {} + + character-entities-legacy@3.0.0: {} + + character-entities@1.2.4: {} + + character-entities@2.0.2: {} + + character-reference-invalid@1.1.4: {} + + character-reference-invalid@2.0.1: {} + chevrotain-allstar@0.3.1(chevrotain@11.0.3): dependencies: chevrotain: 11.0.3 @@ -5126,24 +5450,14 @@ snapshots: - '@types/react' - '@types/react-dom' - code-inspector-plugin@1.2.10: - dependencies: - '@code-inspector/core': 1.2.10 - '@code-inspector/esbuild': 1.2.10 - '@code-inspector/mako': 1.2.10 - '@code-inspector/turbopack': 1.2.10 - '@code-inspector/vite': 1.2.10 - '@code-inspector/webpack': 1.2.10 - chalk: 4.1.1 - transitivePeerDependencies: - - supports-color - color-convert@2.0.1: dependencies: color-name: 1.1.4 color-name@1.1.4: {} + comma-separated-tokens@2.0.3: {} + commander@2.20.3: {} commander@7.2.0: {} @@ -5419,6 +5733,10 @@ snapshots: dependencies: ms: 2.1.3 + decode-named-character-reference@1.2.0: + dependencies: + character-entities: 2.0.2 + deep-is@0.1.4: {} define-data-property@1.1.4: @@ -5437,10 +5755,16 @@ snapshots: dependencies: robust-predicates: 3.0.2 + dequal@2.0.3: {} + detect-libc@2.1.0: {} detect-node-es@1.1.0: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -5449,8 +5773,6 @@ snapshots: optionalDependencies: '@types/trusted-types': 2.0.7 - dotenv@16.6.1: {} - dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -5464,7 +5786,7 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.2.3 - entities@4.5.0: {} + entities@6.0.1: {} error-ex@1.3.4: dependencies: @@ -5608,6 +5930,8 @@ snapshots: escape-string-regexp@4.0.0: {} + escape-string-regexp@5.0.0: {} + eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.5.1)): dependencies: eslint: 9.35.0(jiti@2.5.1) @@ -5710,12 +6034,14 @@ snapshots: estraverse@5.3.0: {} - estree-walker@2.0.2: {} + estree-util-is-identifier-name@3.0.0: {} esutils@2.0.3: {} exsolve@1.0.7: {} + extend@3.0.2: {} + fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -5891,6 +6217,79 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.1.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.3.0 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.18 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-to-parse5@8.0.0: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + highlight.js@11.11.1: {} hoist-non-react-statics@3.3.2: @@ -5901,6 +6300,10 @@ snapshots: dependencies: void-elements: 3.1.0 + html-url-attributes@3.0.1: {} + + html-void-elements@3.0.0: {} + hyphenate-style-name@1.1.0: {} i18next@25.5.2(typescript@5.9.3): @@ -5926,6 +6329,8 @@ snapshots: index-array-by@1.4.2: {} + inline-style-parser@0.2.4: {} + inline-style-prefixer@7.0.1: dependencies: css-in-js-utils: 3.1.0 @@ -5940,6 +6345,20 @@ snapshots: internmap@2.0.3: {} + is-alphabetical@1.0.4: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@1.0.4: + dependencies: + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -5982,6 +6401,10 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-decimal@1.0.4: {} + + is-decimal@2.0.1: {} + is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -5999,6 +6422,10 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-hexadecimal@1.0.4: {} + + is-hexadecimal@2.0.1: {} + is-map@2.0.3: {} is-negative-zero@2.0.3: {} @@ -6010,6 +6437,8 @@ snapshots: is-number@7.0.0: {} + is-plain-obj@4.1.0: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -6125,11 +6554,6 @@ snapshots: vscode-languageserver-textdocument: 1.0.12 vscode-uri: 3.0.8 - launch-ide@1.2.0: - dependencies: - chalk: 4.1.1 - dotenv: 16.6.1 - layout-base@1.0.2: {} layout-base@2.0.1: {} @@ -6206,6 +6630,10 @@ snapshots: long@5.3.2: {} + longest-streak@2.0.4: {} + + longest-streak@3.1.0: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -6222,10 +6650,181 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + markdown-table@3.0.4: {} + marked@15.0.12: {} math-intrinsics@1.1.0: {} + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdast-util-to-markdown@0.6.5: + dependencies: + '@types/unist': 2.0.11 + longest-streak: 2.0.4 + mdast-util-to-string: 2.0.0 + parse-entities: 2.0.0 + repeat-string: 1.6.1 + zwitch: 1.0.5 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + mdast-util-to-string@2.0.0: {} + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdast-util-wiki-link@0.1.2: + dependencies: + '@babel/runtime': 7.28.4 + mdast-util-to-markdown: 0.6.5 + mdn-data@2.0.14: {} merge2@1.4.1: {} @@ -6255,6 +6854,201 @@ snapshots: transitivePeerDependencies: - supports-color + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-wiki-link@0.0.4: + dependencies: + '@babel/runtime': 7.28.4 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.2.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.3 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -6394,6 +7188,25 @@ snapshots: dependencies: callsites: 3.1.0 + parse-entities@2.0.0: + dependencies: + character-entities: 1.2.4 + character-entities-legacy: 1.1.4 + character-reference-invalid: 1.1.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + is-hexadecimal: 1.0.4 + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.2.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + parse-json@5.2.0: dependencies: '@babel/code-frame': 7.27.1 @@ -6401,6 +7214,10 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse5@7.3.0: + dependencies: + entities: 6.0.1 + path-data-parser@0.1.0: {} path-exists@4.0.0: {} @@ -6438,13 +7255,6 @@ snapshots: path-data-parser: 0.1.0 points-on-curve: 0.2.0 - portfinder@1.0.38: - dependencies: - async: 3.2.6 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - possible-typed-array-names@1.1.0: {} postcss@8.5.6: @@ -6469,6 +7279,10 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + property-information@6.5.0: {} + + property-information@7.1.0: {} + punycode@2.3.1: {} quansync@0.2.11: {} @@ -6519,6 +7333,24 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + react-markdown@10.1.0(@types/react@18.3.24)(react@18.3.1): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 18.3.24 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.0 + react: 18.3.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + react-refresh@0.17.0: {} react-remove-scroll-bar@2.3.8(@types/react@18.3.24)(react@18.3.1): @@ -6615,6 +7447,54 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.0 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + remark-wiki-link@2.0.1: + dependencies: + '@babel/runtime': 7.28.4 + mdast-util-wiki-link: 0.1.2 + micromark-extension-wiki-link: 0.0.4 + + repeat-string@1.6.1: {} + resize-observer-polyfill@1.5.1: {} resolve-from@4.0.0: {} @@ -6783,6 +7663,8 @@ snapshots: source-map@0.6.1: {} + space-separated-tokens@2.0.2: {} + stack-generator@2.0.10: dependencies: stackframe: 1.3.4 @@ -6849,8 +7731,21 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-json-comments@3.1.1: {} + style-to-js@1.1.18: + dependencies: + style-to-object: 1.0.11 + + style-to-object@1.0.11: + dependencies: + inline-style-parser: 0.2.4 + stylis@4.2.0: {} stylis@4.3.6: {} @@ -6906,6 +7801,10 @@ snapshots: toggle-selection@1.0.6: {} + trim-lines@3.0.1: {} + + trough@2.2.0: {} + ts-api-utils@2.1.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -6981,6 +7880,39 @@ snapshots: undici-types@7.12.0: {} + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + update-browserslist-db@1.1.3(browserslist@4.26.2): dependencies: browserslist: 4.26.2 @@ -7012,6 +7944,21 @@ snapshots: uuid@11.1.0: {} + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + vite@7.1.5(@types/node@24.5.1)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.44.0): dependencies: esbuild: 0.25.9 @@ -7046,6 +7993,8 @@ snapshots: vscode-uri@3.0.8: {} + web-namespaces@2.0.1: {} + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -7102,3 +8051,7 @@ snapshots: yaml@1.10.2: {} yocto-queue@0.1.0: {} + + zwitch@1.0.5: {} + + zwitch@2.0.4: {} diff --git a/web/src/components/MemoActionMenu.tsx b/web/src/components/MemoActionMenu.tsx index 624721b84..d467a13f4 100644 --- a/web/src/components/MemoActionMenu.tsx +++ b/web/src/components/MemoActionMenu.tsx @@ -17,14 +17,13 @@ import { useState } from "react"; import toast from "react-hot-toast"; import { useLocation } from "react-router-dom"; import ConfirmDialog from "@/components/ConfirmDialog"; -import { markdownServiceClient } from "@/grpcweb"; import useNavigateTo from "@/hooks/useNavigateTo"; import { memoStore, userStore } from "@/store"; import { workspaceStore } from "@/store"; import { State } from "@/types/proto/api/v1/common"; -import { NodeType } from "@/types/proto/api/v1/markdown_service"; import { Memo } from "@/types/proto/api/v1/memo_service"; import { useTranslate } from "@/utils/i18n"; +import { hasCompletedTasks, removeCompletedTasks } from "@/utils/markdown-manipulation"; import { Button } from "./ui/button"; import { DropdownMenu, @@ -44,16 +43,7 @@ interface Props { } const checkHasCompletedTaskList = (memo: Memo) => { - for (const node of memo.nodes) { - if (node.type === NodeType.LIST && node.listNode?.children && node.listNode?.children?.length > 0) { - for (let j = 0; j < node.listNode.children.length; j++) { - if (node.listNode.children[j].type === NodeType.TASK_LIST_ITEM && node.listNode.children[j].taskListItemNode?.complete) { - return true; - } - } - } - } - return false; + return hasCompletedTasks(memo.content); }; const MemoActionMenu = observer((props: Props) => { @@ -160,27 +150,11 @@ const MemoActionMenu = observer((props: Props) => { }; const confirmRemoveCompletedTaskListItems = async () => { - const newNodes = JSON.parse(JSON.stringify(memo.nodes)); - for (const node of newNodes) { - if (node.type === NodeType.LIST && node.listNode?.children?.length > 0) { - const children = node.listNode.children; - for (let i = 0; i < children.length; i++) { - if (children[i].type === NodeType.TASK_LIST_ITEM && children[i].taskListItemNode?.complete) { - // Remove completed taskList item and next line breaks - children.splice(i, 1); - if (children[i]?.type === NodeType.LINE_BREAK) { - children.splice(i, 1); - } - i--; - } - } - } - } - const { markdown } = await markdownServiceClient.restoreMarkdownNodes({ nodes: newNodes }); + const newContent = removeCompletedTasks(memo.content); await memoStore.updateMemo( { name: memo.name, - content: markdown, + content: newContent, }, ["content"], ); diff --git a/web/src/components/MemoContent/Blockquote.tsx b/web/src/components/MemoContent/Blockquote.tsx deleted file mode 100644 index b3e03aff4..000000000 --- a/web/src/components/MemoContent/Blockquote.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Node } from "@/types/proto/api/v1/markdown_service"; -import Renderer from "./Renderer"; -import { BaseProps } from "./types"; - -interface Props extends BaseProps { - children: Node[]; -} - -const Blockquote: React.FC = ({ children }: Props) => { - return ( -
- {children.map((child, index) => ( - - ))} -
- ); -}; - -export default Blockquote; diff --git a/web/src/components/MemoContent/Bold.tsx b/web/src/components/MemoContent/Bold.tsx deleted file mode 100644 index 20259a136..000000000 --- a/web/src/components/MemoContent/Bold.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Node } from "@/types/proto/api/v1/markdown_service"; -import Renderer from "./Renderer"; - -interface Props { - symbol: string; - children: Node[]; -} - -const Bold: React.FC = ({ children }: Props) => { - return ( - - {children.map((child, index) => ( - - ))} - - ); -}; - -export default Bold; diff --git a/web/src/components/MemoContent/BoldItalic.tsx b/web/src/components/MemoContent/BoldItalic.tsx deleted file mode 100644 index d59bc8bb0..000000000 --- a/web/src/components/MemoContent/BoldItalic.tsx +++ /dev/null @@ -1,14 +0,0 @@ -interface Props { - symbol: string; - content: string; -} - -const BoldItalic: React.FC = ({ content }: Props) => { - return ( - - {content} - - ); -}; - -export default BoldItalic; diff --git a/web/src/components/MemoContent/Code.tsx b/web/src/components/MemoContent/Code.tsx deleted file mode 100644 index 15a9ae11a..000000000 --- a/web/src/components/MemoContent/Code.tsx +++ /dev/null @@ -1,9 +0,0 @@ -interface Props { - content: string; -} - -const Code: React.FC = ({ content }: Props) => { - return {content}; -}; - -export default Code; diff --git a/web/src/components/MemoContent/CodeBlock.tsx b/web/src/components/MemoContent/CodeBlock.tsx deleted file mode 100644 index f36ec27c8..000000000 --- a/web/src/components/MemoContent/CodeBlock.tsx +++ /dev/null @@ -1,163 +0,0 @@ -import copy from "copy-to-clipboard"; -import DOMPurify from "dompurify"; -import hljs from "highlight.js"; -import { CopyIcon } from "lucide-react"; -import { observer } from "mobx-react-lite"; -import { useEffect, useMemo } from "react"; -import toast from "react-hot-toast"; -import { cn } from "@/lib/utils"; -import { workspaceStore } from "@/store"; -import MermaidBlock from "./MermaidBlock"; -import { BaseProps } from "./types"; - -// Special languages that are rendered differently. -enum SpecialLanguage { - HTML = "__html", - MERMAID = "mermaid", -} - -interface Props extends BaseProps { - language: string; - content: string; -} - -const CodeBlock: React.FC = ({ language, content }: Props) => { - const formatedLanguage = useMemo(() => (language || "").toLowerCase() || "text", [language]); - - // Users can set Markdown code blocks as `__html` to render HTML directly. - // Content is sanitized to prevent XSS attacks while preserving safe HTML. - if (formatedLanguage === SpecialLanguage.HTML) { - const sanitizedHTML = DOMPurify.sanitize(content, { - // Allow common safe HTML tags and attributes - ALLOWED_TAGS: [ - "div", - "span", - "p", - "br", - "strong", - "b", - "em", - "i", - "u", - "s", - "strike", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "blockquote", - "code", - "pre", - "ul", - "ol", - "li", - "dl", - "dt", - "dd", - "table", - "thead", - "tbody", - "tr", - "th", - "td", - "a", - "img", - "figure", - "figcaption", - "hr", - "small", - "sup", - "sub", - ], - ALLOWED_ATTR: "href title alt src width height class id style target rel colspan rowspan".split(" "), - // Forbid dangerous attributes and tags - FORBID_ATTR: "onerror onload onclick onmouseover onfocus onblur onchange".split(" "), - FORBID_TAGS: "script iframe object embed form input button".split(" "), - }); - - return ( -
- ); - } else if (formatedLanguage === SpecialLanguage.MERMAID) { - return ; - } - - const appTheme = workspaceStore.state.theme; - const isDarkTheme = appTheme.includes("dark"); - - useEffect(() => { - const dynamicImportStyle = async () => { - // Remove any existing highlight.js style - const existingStyle = document.querySelector("style[data-hljs-theme]"); - if (existingStyle) { - existingStyle.remove(); - } - - try { - const cssModule = isDarkTheme - ? await import("highlight.js/styles/github-dark-dimmed.css?inline") - : await import("highlight.js/styles/github.css?inline"); - - // Create and inject the style - const style = document.createElement("style"); - style.textContent = cssModule.default; - style.setAttribute("data-hljs-theme", isDarkTheme ? "dark" : "light"); - document.head.appendChild(style); - } catch (error) { - console.warn("Failed to load highlight.js theme:", error); - } - }; - - dynamicImportStyle(); - }, [appTheme, isDarkTheme]); - - const highlightedCode = useMemo(() => { - try { - const lang = hljs.getLanguage(formatedLanguage); - if (lang) { - return hljs.highlight(content, { - language: formatedLanguage, - }).value; - } - } catch { - // Skip error and use default highlighted code. - } - - // Escape any HTML entities when rendering original content. - return Object.assign(document.createElement("span"), { - textContent: content, - }).innerHTML; - }, [formatedLanguage, content]); - - const copyContent = () => { - copy(content); - toast.success("Copied to clipboard!"); - }; - - return ( -
-
- {formatedLanguage} - -
- -
-
-          
-        
-
-
- ); -}; - -export default observer(CodeBlock); diff --git a/web/src/components/MemoContent/ConditionalComponent.tsx b/web/src/components/MemoContent/ConditionalComponent.tsx new file mode 100644 index 000000000..dd5bc5a3d --- /dev/null +++ b/web/src/components/MemoContent/ConditionalComponent.tsx @@ -0,0 +1,63 @@ +import React from "react"; + +/** + * Creates a conditional component wrapper that checks AST node properties + * before deciding which component to render. + * + * This is more efficient than having every component check its own props, + * and allows us to use specific HTML element types as defaults. + * + * @param CustomComponent - Component to render when condition is met + * @param DefaultComponent - Component/element to render otherwise + * @param condition - Function to check if node matches custom component criteria + */ +export const createConditionalComponent =

>( + CustomComponent: React.ComponentType

, + DefaultComponent: React.ComponentType

| keyof JSX.IntrinsicElements, + condition: (node: any) => boolean, +) => { + return (props: P & { node?: any }) => { + const { node, ...restProps } = props; + + // Check AST node to determine which component to use + if (node && condition(node)) { + return ; + } + + // Render default component/element + if (typeof DefaultComponent === "string") { + return React.createElement(DefaultComponent, restProps); + } + return ; + }; +}; + +/** + * Condition checkers for AST node types + * + * These check the original MDAST node type preserved during transformation: + * - First checks node.data.mdastType (preserved by remarkPreserveType plugin) + * - Falls back to checking HAST properties/className for compatibility + */ +export const isWikiLinkNode = (node: any): boolean => { + // Check preserved mdast type first + if (node?.data?.mdastType === "wikiLink") { + return true; + } + // Fallback: check hast properties + return node?.properties?.className?.includes?.("wikilink") || false; +}; + +export const isTagNode = (node: any): boolean => { + // Check preserved mdast type first + if (node?.data?.mdastType === "tagNode") { + return true; + } + // Fallback: check hast properties + return node?.properties?.className?.includes?.("tag") || false; +}; + +export const isTaskListItemNode = (node: any): boolean => { + // Task list checkboxes are standard GFM - check element type + return node?.properties?.type === "checkbox" || false; +}; diff --git a/web/src/components/MemoContent/DefaultLink.tsx b/web/src/components/MemoContent/DefaultLink.tsx new file mode 100644 index 000000000..21783b303 --- /dev/null +++ b/web/src/components/MemoContent/DefaultLink.tsx @@ -0,0 +1,30 @@ +import React from "react"; + +/** + * Default link component for regular markdown links + * + * Handles external links with proper target and rel attributes. + */ + +interface DefaultLinkProps extends React.AnchorHTMLAttributes { + node?: any; // AST node from react-markdown + href?: string; + children?: React.ReactNode; +} + +export const DefaultLink: React.FC = ({ href, children, ...props }) => { + const isExternal = href?.startsWith("http://") || href?.startsWith("https://"); + + return ( + e.stopPropagation()} + > + {children} + + ); +}; diff --git a/web/src/components/MemoContent/EmbeddedContent/EmbeddedAttachment.tsx b/web/src/components/MemoContent/EmbeddedContent/EmbeddedAttachment.tsx deleted file mode 100644 index 782487eac..000000000 --- a/web/src/components/MemoContent/EmbeddedContent/EmbeddedAttachment.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { observer } from "mobx-react-lite"; -import { useEffect } from "react"; -import MemoAttachmentListView from "@/components/MemoAttachmentListView"; -import useLoading from "@/hooks/useLoading"; -import { cn } from "@/lib/utils"; -import { attachmentStore } from "@/store"; -import Error from "./Error"; - -interface Props { - resourceId: string; - params: string; -} - -const getAdditionalClassNameWithParams = (params: URLSearchParams) => { - const additionalClassNames = []; - if (params.has("align")) { - const align = params.get("align"); - if (align === "center") { - additionalClassNames.push("mx-auto"); - } - } - if (params.has("size")) { - const size = params.get("size"); - if (size === "lg") { - additionalClassNames.push("w-full"); - } else if (size === "md") { - additionalClassNames.push("w-2/3"); - } else if (size === "sm") { - additionalClassNames.push("w-1/3"); - } - } - if (params.has("width")) { - const width = params.get("width"); - additionalClassNames.push(`w-[${width}]`); - } - return additionalClassNames.join(" "); -}; - -const EmbeddedAttachment = observer(({ resourceId: uid, params: paramsStr }: Props) => { - const loadingState = useLoading(); - const attachment = attachmentStore.getAttachmentByName(uid); - const params = new URLSearchParams(paramsStr); - - useEffect(() => { - attachmentStore.fetchAttachmentByName(`attachments/${uid}`).finally(() => loadingState.setFinish()); - }, [uid]); - - if (loadingState.isLoading) { - return null; - } - if (!attachment) { - return ; - } - - return ( -

- -
- ); -}); - -export default EmbeddedAttachment; diff --git a/web/src/components/MemoContent/EmbeddedContent/EmbeddedMemo.tsx b/web/src/components/MemoContent/EmbeddedContent/EmbeddedMemo.tsx deleted file mode 100644 index 651d204bd..000000000 --- a/web/src/components/MemoContent/EmbeddedContent/EmbeddedMemo.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import copy from "copy-to-clipboard"; -import { ArrowUpRightIcon } from "lucide-react"; -import { observer } from "mobx-react-lite"; -import { useContext, useEffect } from "react"; -import toast from "react-hot-toast"; -import { Link } from "react-router-dom"; -import MemoAttachmentListView from "@/components/MemoAttachmentListView"; -import useLoading from "@/hooks/useLoading"; -import { cn } from "@/lib/utils"; -import { memoStore } from "@/store"; -import { extractMemoIdFromName } from "@/store/common"; -import MemoContent from ".."; -import { RendererContext } from "../types"; -import Error from "./Error"; - -interface Props { - resourceId: string; - params: string; -} - -const EmbeddedMemo = observer(({ resourceId: uid, params: paramsStr }: Props) => { - const context = useContext(RendererContext); - const loadingState = useLoading(); - const memoName = `memos/${uid}`; - const memo = memoStore.getMemoByName(memoName); - - useEffect(() => { - memoStore.getOrFetchMemoByName(memoName).finally(() => loadingState.setFinish()); - }, [memoName]); - - if (loadingState.isLoading) { - return null; - } - if (!memo) { - return ; - } - - const params = new URLSearchParams(paramsStr); - const useSnippet = params.has("snippet"); - const inlineMode = params.has("inline"); - if (!useSnippet && (memo.name === context.memoName || context.embeddedMemos.has(memoName))) { - return ; - } - - // Add the memo to the set of embedded memos. This is used to prevent infinite loops when a memo embeds itself. - context.embeddedMemos.add(memoName); - const contentNode = useSnippet ? ( -
{memo.snippet}
- ) : ( - <> - - - - ); - if (inlineMode) { - return
{contentNode}
; - } - - const copyMemoUid = (uid: string) => { - copy(uid); - toast.success("Copied memo UID to clipboard"); - }; - - return ( -
-
-
- -
-
- copyMemoUid(extractMemoIdFromName(memo.name))} - > - {extractMemoIdFromName(memo.name).slice(0, 6)} - - - - -
-
- {contentNode} -
- ); -}); - -export default EmbeddedMemo; diff --git a/web/src/components/MemoContent/EmbeddedContent/Error.tsx b/web/src/components/MemoContent/EmbeddedContent/Error.tsx deleted file mode 100644 index 7b46926d5..000000000 --- a/web/src/components/MemoContent/EmbeddedContent/Error.tsx +++ /dev/null @@ -1,9 +0,0 @@ -interface Props { - message: string; -} - -const Error = ({ message }: Props) => { - return

{message}

; -}; - -export default Error; diff --git a/web/src/components/MemoContent/EmbeddedContent/index.tsx b/web/src/components/MemoContent/EmbeddedContent/index.tsx deleted file mode 100644 index 8eaadc120..000000000 --- a/web/src/components/MemoContent/EmbeddedContent/index.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import EmbeddedAttachment from "./EmbeddedAttachment"; -import EmbeddedMemo from "./EmbeddedMemo"; -import Error from "./Error"; - -interface Props { - resourceName: string; - params: string; -} - -const extractResourceTypeAndId = (resourceName: string) => { - const [resourceType, resourceId] = resourceName.split("/"); - return { resourceType, resourceId }; -}; - -const EmbeddedContent = ({ resourceName, params }: Props) => { - const { resourceType, resourceId } = extractResourceTypeAndId(resourceName); - if (resourceType === "memos") { - return ; - } else if (resourceType === "attachments") { - return ; - } - return ; -}; - -export default EmbeddedContent; diff --git a/web/src/components/MemoContent/EscapingCharacter.tsx b/web/src/components/MemoContent/EscapingCharacter.tsx deleted file mode 100644 index ffd567250..000000000 --- a/web/src/components/MemoContent/EscapingCharacter.tsx +++ /dev/null @@ -1,9 +0,0 @@ -interface Props { - symbol: string; -} - -const EscapingCharacter: React.FC = ({ symbol }: Props) => { - return {symbol}; -}; - -export default EscapingCharacter; diff --git a/web/src/components/MemoContent/HTMLElement.tsx b/web/src/components/MemoContent/HTMLElement.tsx deleted file mode 100644 index 1131cb9e6..000000000 --- a/web/src/components/MemoContent/HTMLElement.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { createElement } from "react"; -import { Node } from "@/types/proto/api/v1/markdown_service"; -import Renderer from "./Renderer"; - -interface Props { - tagName: string; - attributes: { [key: string]: string }; - children: Node[]; - isSelfClosing: boolean; -} - -const HTMLElement: React.FC = ({ tagName, attributes, children, isSelfClosing }: Props) => { - if (isSelfClosing) { - return createElement(tagName, attributes); - } - - return createElement( - tagName, - attributes, - children.map((child, index) => ), - ); -}; - -export default HTMLElement; diff --git a/web/src/components/MemoContent/Heading.tsx b/web/src/components/MemoContent/Heading.tsx deleted file mode 100644 index 1417c0aa5..000000000 --- a/web/src/components/MemoContent/Heading.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Node } from "@/types/proto/api/v1/markdown_service"; -import Renderer from "./Renderer"; -import { BaseProps } from "./types"; - -interface Props extends BaseProps { - level: number; - children: Node[]; -} - -const Heading: React.FC = ({ level, children }: Props) => { - const Head = `h${level}` as keyof JSX.IntrinsicElements; - const className = (() => { - switch (level) { - case 1: - return "text-5xl leading-normal font-bold"; - case 2: - return "text-3xl leading-normal font-medium"; - case 3: - return "text-xl leading-normal font-medium"; - case 4: - return "text-lg font-bold"; - } - })(); - - return ( - - {children.map((child, index) => ( - - ))} - - ); -}; - -export default Heading; diff --git a/web/src/components/MemoContent/Highlight.tsx b/web/src/components/MemoContent/Highlight.tsx deleted file mode 100644 index c3ffc1e02..000000000 --- a/web/src/components/MemoContent/Highlight.tsx +++ /dev/null @@ -1,9 +0,0 @@ -interface Props { - content: string; -} - -const Highlight: React.FC = ({ content }: Props) => { - return {content}; -}; - -export default Highlight; diff --git a/web/src/components/MemoContent/HorizontalRule.tsx b/web/src/components/MemoContent/HorizontalRule.tsx deleted file mode 100644 index 23834eb0b..000000000 --- a/web/src/components/MemoContent/HorizontalRule.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Separator } from "@/components/ui/separator"; -import { BaseProps } from "./types"; - -interface Props extends BaseProps { - symbol: string; -} - -const HorizontalRule: React.FC = () => { - return ; -}; - -export default HorizontalRule; diff --git a/web/src/components/MemoContent/Image.tsx b/web/src/components/MemoContent/Image.tsx deleted file mode 100644 index 755b9f315..000000000 --- a/web/src/components/MemoContent/Image.tsx +++ /dev/null @@ -1,10 +0,0 @@ -interface Props { - altText: string; - url: string; -} - -const Image: React.FC = ({ altText, url }: Props) => { - return {altText}; -}; - -export default Image; diff --git a/web/src/components/MemoContent/Italic.tsx b/web/src/components/MemoContent/Italic.tsx deleted file mode 100644 index a10ce0b7e..000000000 --- a/web/src/components/MemoContent/Italic.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Node } from "@/types/proto/api/v1/markdown_service"; -import Renderer from "./Renderer"; - -interface Props { - symbol: string; - children: Node[]; -} - -const Italic: React.FC = ({ children }: Props) => { - return ( - - {children.map((child, index) => ( - - ))} - - ); -}; - -export default Italic; diff --git a/web/src/components/MemoContent/LineBreak.tsx b/web/src/components/MemoContent/LineBreak.tsx deleted file mode 100644 index b60b1bf67..000000000 --- a/web/src/components/MemoContent/LineBreak.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const LineBreak = () => { - return
; -}; - -export default LineBreak; diff --git a/web/src/components/MemoContent/Link.tsx b/web/src/components/MemoContent/Link.tsx deleted file mode 100644 index 994d66dee..000000000 --- a/web/src/components/MemoContent/Link.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { useState } from "react"; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; -import { markdownServiceClient } from "@/grpcweb"; -import { workspaceStore } from "@/store"; -import { LinkMetadata, Node } from "@/types/proto/api/v1/markdown_service"; -import Renderer from "./Renderer"; - -interface Props { - url: string; - content?: Node[]; -} - -const getFaviconWithGoogleS2 = (url: string) => { - try { - const urlObject = new URL(url); - return `https://www.google.com/s2/favicons?sz=128&domain=${urlObject.hostname}`; - } catch { - return undefined; - } -}; - -const Link: React.FC = ({ content, url }: Props) => { - const workspaceMemoRelatedSetting = workspaceStore.state.memoRelatedSetting; - const [initialized, setInitialized] = useState(false); - const [showTooltip, setShowTooltip] = useState(false); - const [linkMetadata, setLinkMetadata] = useState(); - - const handleMouseEnter = async () => { - if (!workspaceMemoRelatedSetting.enableLinkPreview) { - return; - } - - setShowTooltip(true); - if (!initialized) { - try { - const linkMetadata = await markdownServiceClient.getLinkMetadata({ link: url }); - setLinkMetadata(linkMetadata); - } catch (error) { - console.error("Error fetching URL metadata:", error); - } - setInitialized(true); - } - }; - - return ( - - - - setShowTooltip(false)} - onClick={(e) => e.stopPropagation()} - > - {content ? content.map((child, index) => ) : url} - - - {linkMetadata && ( - -
-
- {linkMetadata?.title} -

{linkMetadata?.title}

-
- {linkMetadata.description && ( -

{linkMetadata.description}

- )} - {linkMetadata.image && ( - {linkMetadata.title} - )} -
-
- )} -
-
- ); -}; - -export default Link; diff --git a/web/src/components/MemoContent/List.tsx b/web/src/components/MemoContent/List.tsx deleted file mode 100644 index 0c8b5daa2..000000000 --- a/web/src/components/MemoContent/List.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { head } from "lodash-es"; -import React from "react"; -import { cn } from "@/lib/utils"; -import { ListNode_Kind, Node, NodeType } from "@/types/proto/api/v1/markdown_service"; -import Renderer from "./Renderer"; - -interface Props { - index: string; - kind: ListNode_Kind; - indent: number; - children: Node[]; -} - -const List: React.FC = ({ kind, indent, children }: Props) => { - let prevNode: Node | null = null; - let skipNextLineBreakFlag = false; - - const getListContainer = () => { - switch (kind) { - case ListNode_Kind.ORDERED: - return "ol"; - case ListNode_Kind.UNORDERED: - return "ul"; - case ListNode_Kind.DESCRIPTION: - return "dl"; - default: - return "div"; - } - }; - - const getAttributes = () => { - const attrs: any = { - style: { paddingLeft: `${indent > 0 ? indent * 10 : 20}px` }, - }; - const firstChild = head(children); - if (firstChild?.type === NodeType.ORDERED_LIST_ITEM) { - attrs.start = firstChild.orderedListItemNode?.number; - } else if (firstChild?.type === NodeType.TASK_LIST_ITEM) { - attrs.style = { paddingLeft: `${indent * 8}px` }; - } - return attrs; - }; - - return React.createElement( - getListContainer(), - { - className: cn(kind === ListNode_Kind.ORDERED ? "list-decimal" : kind === ListNode_Kind.UNORDERED ? "list-disc" : "list-none"), - ...getAttributes(), - }, - children.map((child, index) => { - if (prevNode?.type !== NodeType.LINE_BREAK && child.type === NodeType.LINE_BREAK && skipNextLineBreakFlag) { - skipNextLineBreakFlag = false; - return null; - } - - prevNode = child; - skipNextLineBreakFlag = true; - return ; - }), - ); -}; - -export default List; diff --git a/web/src/components/MemoContent/Math.tsx b/web/src/components/MemoContent/Math.tsx deleted file mode 100644 index ace2bbbf7..000000000 --- a/web/src/components/MemoContent/Math.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import TeX from "@matejmazur/react-katex"; -import { cn } from "@/lib/utils"; -import "katex/dist/katex.min.css"; - -interface Props { - content: string; - block?: boolean; -} - -const Math: React.FC = ({ content, block }: Props) => { - return ; -}; - -export default Math; diff --git a/web/src/components/MemoContent/MemoContentContext.tsx b/web/src/components/MemoContent/MemoContentContext.tsx new file mode 100644 index 000000000..ce4a43a94 --- /dev/null +++ b/web/src/components/MemoContent/MemoContentContext.tsx @@ -0,0 +1,27 @@ +import { createContext } from "react"; + +/** + * Context for MemoContent rendering + * + * Provides memo metadata and configuration to child components + * Used by custom react-markdown components (TaskListItem, WikiLink, Tag, etc.) + */ + +export interface MemoContentContextType { + /** The memo resource name (e.g., "memos/123") */ + memoName?: string; + + /** Whether content is readonly (non-editable) */ + readonly: boolean; + + /** Whether to disable tag/link filtering */ + disableFilter?: boolean; + + /** Parent page path (for navigation) */ + parentPage?: string; +} + +export const MemoContentContext = createContext({ + readonly: true, + disableFilter: false, +}); diff --git a/web/src/components/MemoContent/MermaidBlock.tsx b/web/src/components/MemoContent/MermaidBlock.tsx deleted file mode 100644 index 3dd303f4a..000000000 --- a/web/src/components/MemoContent/MermaidBlock.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { useEffect, useRef } from "react"; - -interface Props { - content: string; -} - -const MermaidBlock: React.FC = ({ content }: Props) => { - const mermaidDockBlock = useRef(null); - - useEffect(() => { - const initializeMermaid = async () => { - const mermaid = (await import("mermaid")).default; - mermaid.initialize({ startOnLoad: false, theme: "default" }); - if (mermaidDockBlock.current) { - mermaid.run({ - nodes: [mermaidDockBlock.current], - }); - } - }; - - initializeMermaid(); - }, [content]); - - return ( -
-      {content}
-    
- ); -}; - -export default MermaidBlock; diff --git a/web/src/components/MemoContent/OrderedListItem.tsx b/web/src/components/MemoContent/OrderedListItem.tsx deleted file mode 100644 index f6666a35e..000000000 --- a/web/src/components/MemoContent/OrderedListItem.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Node } from "@/types/proto/api/v1/markdown_service"; -import Renderer from "./Renderer"; -import { BaseProps } from "./types"; - -interface Props extends BaseProps { - number: string; - indent: number; - children: Node[]; -} - -const OrderedListItem: React.FC = ({ children }: Props) => { - return ( -
  • - {children.map((child, index) => ( - - ))} -
  • - ); -}; - -export default OrderedListItem; diff --git a/web/src/components/MemoContent/Paragraph.tsx b/web/src/components/MemoContent/Paragraph.tsx deleted file mode 100644 index 93ce5af8f..000000000 --- a/web/src/components/MemoContent/Paragraph.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Node } from "@/types/proto/api/v1/markdown_service"; -import Renderer from "./Renderer"; -import { BaseProps } from "./types"; - -interface Props extends BaseProps { - children: Node[]; -} - -const Paragraph: React.FC = ({ children }: Props) => { - return ( -

    - {children.map((child, index) => ( - - ))} -

    - ); -}; - -export default Paragraph; diff --git a/web/src/components/MemoContent/ReferencedContent/Error.tsx b/web/src/components/MemoContent/ReferencedContent/Error.tsx deleted file mode 100644 index 7b46926d5..000000000 --- a/web/src/components/MemoContent/ReferencedContent/Error.tsx +++ /dev/null @@ -1,9 +0,0 @@ -interface Props { - message: string; -} - -const Error = ({ message }: Props) => { - return

    {message}

    ; -}; - -export default Error; diff --git a/web/src/components/MemoContent/ReferencedContent/ReferencedMemo.tsx b/web/src/components/MemoContent/ReferencedContent/ReferencedMemo.tsx deleted file mode 100644 index e4d66b559..000000000 --- a/web/src/components/MemoContent/ReferencedContent/ReferencedMemo.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { observer } from "mobx-react-lite"; -import { useContext, useEffect } from "react"; -import useLoading from "@/hooks/useLoading"; -import useNavigateTo from "@/hooks/useNavigateTo"; -import { memoStore } from "@/store"; -import { memoNamePrefix } from "@/store/common"; -import { RendererContext } from "../types"; -import Error from "./Error"; - -interface Props { - resourceId: string; - params: string; -} - -const ReferencedMemo = observer(({ resourceId: uid, params: paramsStr }: Props) => { - const navigateTo = useNavigateTo(); - const loadingState = useLoading(); - const memoName = `${memoNamePrefix}${uid}`; - const memo = memoStore.getMemoByName(memoName); - const params = new URLSearchParams(paramsStr); - const context = useContext(RendererContext); - - useEffect(() => { - memoStore.getOrFetchMemoByName(memoName).finally(() => loadingState.setFinish()); - }, [memoName]); - - if (loadingState.isLoading) { - return null; - } - if (!memo) { - return ; - } - - const paramsText = params.has("text") ? params.get("text") : undefined; - const displayContent = paramsText || (memo.snippet.length > 12 ? `${memo.snippet.slice(0, 12)}...` : memo.snippet); - - const handleGotoMemoDetailPage = () => { - navigateTo(`/${memo.name}`, { - state: { - from: context.parentPage, - }, - }); - }; - - return ( - - {displayContent} - - ); -}); - -export default ReferencedMemo; diff --git a/web/src/components/MemoContent/ReferencedContent/index.tsx b/web/src/components/MemoContent/ReferencedContent/index.tsx deleted file mode 100644 index 374aec2d1..000000000 --- a/web/src/components/MemoContent/ReferencedContent/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import Error from "./Error"; -import ReferencedMemo from "./ReferencedMemo"; - -interface Props { - resourceName: string; - params: string; -} - -const extractResourceTypeAndId = (resourceName: string) => { - const [resourceType, resourceId] = resourceName.split("/"); - return { resourceType, resourceId }; -}; - -const ReferencedContent = ({ resourceName, params }: Props) => { - const { resourceType, resourceId } = extractResourceTypeAndId(resourceName); - if (resourceType === "memos") { - return ; - } - return ; -}; - -export default ReferencedContent; diff --git a/web/src/components/MemoContent/Renderer.tsx b/web/src/components/MemoContent/Renderer.tsx deleted file mode 100644 index e6ec848f8..000000000 --- a/web/src/components/MemoContent/Renderer.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import { - AutoLinkNode, - BlockquoteNode, - BoldItalicNode, - BoldNode, - CodeBlockNode, - CodeNode, - EmbeddedContentNode, - EscapingCharacterNode, - HeadingNode, - HighlightNode, - HorizontalRuleNode, - HTMLElementNode, - ImageNode, - ItalicNode, - LinkNode, - ListNode, - MathBlockNode, - MathNode, - Node, - NodeType, - OrderedListItemNode, - ParagraphNode, - ReferencedContentNode, - SpoilerNode, - StrikethroughNode, - SubscriptNode, - SuperscriptNode, - TableNode, - TagNode, - TaskListItemNode, - TextNode, - UnorderedListItemNode, -} from "@/types/proto/api/v1/markdown_service"; -import Blockquote from "./Blockquote"; -import Bold from "./Bold"; -import BoldItalic from "./BoldItalic"; -import Code from "./Code"; -import CodeBlock from "./CodeBlock"; -import EmbeddedContent from "./EmbeddedContent"; -import EscapingCharacter from "./EscapingCharacter"; -import HTMLElement from "./HTMLElement"; -import Heading from "./Heading"; -import Highlight from "./Highlight"; -import HorizontalRule from "./HorizontalRule"; -import Image from "./Image"; -import Italic from "./Italic"; -import LineBreak from "./LineBreak"; -import Link from "./Link"; -import List from "./List"; -import Math from "./Math"; -import OrderedListItem from "./OrderedListItem"; -import Paragraph from "./Paragraph"; -import ReferencedContent from "./ReferencedContent"; -import Spoiler from "./Spoiler"; -import Strikethrough from "./Strikethrough"; -import Subscript from "./Subscript"; -import Superscript from "./Superscript"; -import Table from "./Table"; -import Tag from "./Tag"; -import TaskListItem from "./TaskListItem"; -import Text from "./Text"; -import UnorderedListItem from "./UnorderedListItem"; - -interface Props { - index: string; - node: Node; -} - -const Renderer: React.FC = ({ index, node }: Props) => { - switch (node.type) { - case NodeType.LINE_BREAK: - return ; - case NodeType.PARAGRAPH: - return ; - case NodeType.CODE_BLOCK: - return ; - case NodeType.HEADING: - return ; - case NodeType.HORIZONTAL_RULE: - return ; - case NodeType.BLOCKQUOTE: - return
    ; - case NodeType.LIST: - return ; - case NodeType.ORDERED_LIST_ITEM: - return ; - case NodeType.UNORDERED_LIST_ITEM: - return ; - case NodeType.TASK_LIST_ITEM: - return ; - case NodeType.MATH_BLOCK: - return ; - case NodeType.TABLE: - return ; - case NodeType.EMBEDDED_CONTENT: - return ; - case NodeType.TEXT: - return ; - case NodeType.BOLD: - return ; - case NodeType.ITALIC: - return ; - case NodeType.BOLD_ITALIC: - return ; - case NodeType.CODE: - return ; - case NodeType.IMAGE: - return ; - case NodeType.LINK: - return ; - case NodeType.AUTO_LINK: - return ; - case NodeType.TAG: - return ; - case NodeType.STRIKETHROUGH: - return ; - case NodeType.MATH: - return ; - case NodeType.HIGHLIGHT: - return ; - case NodeType.ESCAPING_CHARACTER: - return ; - case NodeType.SUBSCRIPT: - return ; - case NodeType.SUPERSCRIPT: - return ; - case NodeType.REFERENCED_CONTENT: - return ; - case NodeType.SPOILER: - return ; - case NodeType.HTML_ELEMENT: - return ; - default: - return null; - } -}; - -export default Renderer; diff --git a/web/src/components/MemoContent/Spoiler.tsx b/web/src/components/MemoContent/Spoiler.tsx deleted file mode 100644 index 62a362745..000000000 --- a/web/src/components/MemoContent/Spoiler.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { useState } from "react"; -import { cn } from "@/lib/utils"; - -interface Props { - content: string; -} - -const Spoiler: React.FC = ({ content }: Props) => { - const [isRevealed, setIsRevealed] = useState(false); - - return ( - setIsRevealed(!isRevealed)} - > - {content} - - ); -}; - -export default Spoiler; diff --git a/web/src/components/MemoContent/Strikethrough.tsx b/web/src/components/MemoContent/Strikethrough.tsx deleted file mode 100644 index 0bfcde6d8..000000000 --- a/web/src/components/MemoContent/Strikethrough.tsx +++ /dev/null @@ -1,9 +0,0 @@ -interface Props { - content: string; -} - -const Strikethrough: React.FC = ({ content }: Props) => { - return {content}; -}; - -export default Strikethrough; diff --git a/web/src/components/MemoContent/Subscript.tsx b/web/src/components/MemoContent/Subscript.tsx deleted file mode 100644 index 1878f59b0..000000000 --- a/web/src/components/MemoContent/Subscript.tsx +++ /dev/null @@ -1,9 +0,0 @@ -interface Props { - content: string; -} - -const Subscript: React.FC = ({ content }: Props) => { - return {content}; -}; - -export default Subscript; diff --git a/web/src/components/MemoContent/Superscript.tsx b/web/src/components/MemoContent/Superscript.tsx deleted file mode 100644 index 381692511..000000000 --- a/web/src/components/MemoContent/Superscript.tsx +++ /dev/null @@ -1,9 +0,0 @@ -interface Props { - content: string; -} - -const Superscript: React.FC = ({ content }: Props) => { - return {content}; -}; - -export default Superscript; diff --git a/web/src/components/MemoContent/Table.tsx b/web/src/components/MemoContent/Table.tsx deleted file mode 100644 index 11464089f..000000000 --- a/web/src/components/MemoContent/Table.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Node, TableNode_Row } from "@/types/proto/api/v1/markdown_service"; -import Renderer from "./Renderer"; - -interface Props { - index: string; - header: Node[]; - rows: TableNode_Row[]; -} - -const Table = ({ header, rows }: Props) => { - return ( -
    - - - {header.map((h, i) => ( - - ))} - - - - {rows.map((row, i) => ( - - {row.cells.map((r, j) => ( - - ))} - - ))} - -
    - -
    - -
    - ); -}; - -export default Table; diff --git a/web/src/components/MemoContent/Tag.tsx b/web/src/components/MemoContent/Tag.tsx index 63534a083..02092306d 100644 --- a/web/src/components/MemoContent/Tag.tsx +++ b/web/src/components/MemoContent/Tag.tsx @@ -1,4 +1,3 @@ -import { observer } from "mobx-react-lite"; import { useContext } from "react"; import { useLocation } from "react-router-dom"; import useNavigateTo from "@/hooks/useNavigateTo"; @@ -6,18 +5,34 @@ import { cn } from "@/lib/utils"; import { Routes } from "@/router"; import { memoFilterStore } from "@/store"; import { stringifyFilters, MemoFilter } from "@/store/memoFilter"; -import { RendererContext } from "./types"; +import { MemoContentContext } from "./MemoContentContext"; -interface Props { - content: string; +/** + * Custom span component for #tag elements + * + * Handles tag clicks for filtering memos. + * The remark-tag plugin creates span elements with class="tag". + * + * Note: This component should only be used for tags. + * Regular spans are handled by the default span element. + */ + +interface TagProps extends React.HTMLAttributes { + node?: any; // AST node from react-markdown + "data-tag"?: string; + children?: React.ReactNode; } -const Tag = observer(({ content }: Props) => { - const context = useContext(RendererContext); +export const Tag: React.FC = ({ "data-tag": dataTag, children, className, ...props }) => { + const context = useContext(MemoContentContext); const location = useLocation(); const navigateTo = useNavigateTo(); - const handleTagClick = () => { + const tag = dataTag || ""; + + const handleTagClick = (e: React.MouseEvent) => { + e.stopPropagation(); + if (context.disableFilter) { return; } @@ -27,34 +42,30 @@ const Tag = observer(({ content }: Props) => { const pathname = context.parentPage || Routes.ROOT; const searchParams = new URLSearchParams(); - searchParams.set("filter", stringifyFilters([{ factor: "tagSearch", value: content }])); + searchParams.set("filter", stringifyFilters([{ factor: "tagSearch", value: tag }])); navigateTo(`${pathname}?${searchParams.toString()}`); return; } - const isActive = memoFilterStore.getFiltersByFactor("tagSearch").some((filter: MemoFilter) => filter.value === content); + const isActive = memoFilterStore.getFiltersByFactor("tagSearch").some((filter: MemoFilter) => filter.value === tag); if (isActive) { - memoFilterStore.removeFilter((f: MemoFilter) => f.factor === "tagSearch" && f.value === content); + memoFilterStore.removeFilter((f: MemoFilter) => f.factor === "tagSearch" && f.value === tag); } else { memoFilterStore.addFilter({ factor: "tagSearch", - value: content, + value: tag, }); } }; return ( - # - {content} + {children} ); -}); - -export default Tag; +}; diff --git a/web/src/components/MemoContent/TaskListItem.tsx b/web/src/components/MemoContent/TaskListItem.tsx index dba2c4ab2..e9365a2a5 100644 --- a/web/src/components/MemoContent/TaskListItem.tsx +++ b/web/src/components/MemoContent/TaskListItem.tsx @@ -1,58 +1,76 @@ -import { observer } from "mobx-react-lite"; import { useContext } from "react"; -import { Checkbox } from "@/components/ui/checkbox"; -import { markdownServiceClient } from "@/grpcweb"; -import { cn } from "@/lib/utils"; import { memoStore } from "@/store"; -import { Node, TaskListItemNode } from "@/types/proto/api/v1/markdown_service"; -import Renderer from "./Renderer"; -import { RendererContext } from "./types"; +import { toggleTaskAtIndex } from "@/utils/markdown-manipulation"; +import { MemoContentContext } from "./MemoContentContext"; -interface Props { - node: Node; - index: string; - symbol: string; - indent: number; - complete: boolean; - children: Node[]; +/** + * Custom checkbox component for react-markdown task lists + * + * Handles interactive task checkbox clicks and updates memo content. + * This component is used via react-markdown's components prop. + * + * Note: This component should only be used for task list checkboxes. + * Regular inputs are handled by the default input element. + */ + +interface TaskListItemProps extends React.InputHTMLAttributes { + node?: any; // AST node from react-markdown + checked?: boolean; } -const TaskListItem = observer(({ node, complete, children }: Props) => { - const context = useContext(RendererContext); +export const TaskListItem: React.FC = ({ checked, ...props }) => { + const context = useContext(MemoContentContext); - const handleCheckboxChange = async (on: boolean) => { + const handleChange = async (e: React.ChangeEvent) => { + e.stopPropagation(); + + // Don't update if readonly or no memo context if (context.readonly || !context.memoName) { return; } - (node.taskListItemNode as TaskListItemNode)!.complete = on; - const { markdown } = await markdownServiceClient.restoreMarkdownNodes({ nodes: context.nodes }); + const newChecked = e.target.checked; + + // Find the task index by walking up the DOM + const listItem = e.target.closest("li.task-list-item"); + if (!listItem) { + return; + } + + // Get task index from data attribute, or calculate by counting + const taskIndexStr = listItem.getAttribute("data-task-index"); + let taskIndex = 0; + + if (taskIndexStr !== null) { + taskIndex = parseInt(taskIndexStr); + } else { + // Fallback: Calculate index by counting previous task list items + const allTaskItems = listItem.closest("ul, ol")?.querySelectorAll("li.task-list-item") || []; + for (let i = 0; i < allTaskItems.length; i++) { + if (allTaskItems[i] === listItem) { + taskIndex = i; + break; + } + } + } + + // Update memo content using the string manipulation utility + const memo = memoStore.getMemoByName(context.memoName); + if (!memo) { + return; + } + + const newContent = toggleTaskAtIndex(memo.content, taskIndex, newChecked); await memoStore.updateMemo( { - name: context.memoName, - content: markdown, + name: memo.name, + content: newContent, }, ["content"], ); }; - return ( -
  • - - handleCheckboxChange(checked === true)} - /> - -

    - {children.map((child, index) => ( - - ))} -

    -
  • - ); -}); - -export default TaskListItem; + // Override the disabled prop from remark-gfm (which defaults to true) + // We want interactive checkboxes, only disabled when readonly + return ; +}; diff --git a/web/src/components/MemoContent/Text.tsx b/web/src/components/MemoContent/Text.tsx deleted file mode 100644 index 3cacdffeb..000000000 --- a/web/src/components/MemoContent/Text.tsx +++ /dev/null @@ -1,9 +0,0 @@ -interface Props { - content: string; -} - -const Text: React.FC = ({ content }: Props) => { - return {content}; -}; - -export default Text; diff --git a/web/src/components/MemoContent/UnorderedListItem.tsx b/web/src/components/MemoContent/UnorderedListItem.tsx deleted file mode 100644 index fed44645f..000000000 --- a/web/src/components/MemoContent/UnorderedListItem.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { Node } from "@/types/proto/api/v1/markdown_service"; -import Renderer from "./Renderer"; - -interface Props { - symbol: string; - indent: number; - children: Node[]; -} - -const UnorderedListItem: React.FC = ({ children }: Props) => { - return ( -
  • - {children.map((child, index) => ( - - ))} -
  • - ); -}; - -export default UnorderedListItem; diff --git a/web/src/components/MemoContent/WikiLink.tsx b/web/src/components/MemoContent/WikiLink.tsx new file mode 100644 index 000000000..f979b604b --- /dev/null +++ b/web/src/components/MemoContent/WikiLink.tsx @@ -0,0 +1,44 @@ +import React from "react"; + +/** + * Custom link component for react-markdown wikilinks + * + * Handles [[wikilink]] rendering with custom styling and click behavior. + * The remark-wiki-link plugin converts [[target]] to anchor elements. + * + * Note: This component should only be used for wikilinks. + * Regular links are handled by the default anchor element. + */ + +interface WikiLinkProps extends React.AnchorHTMLAttributes { + node?: any; // AST node from react-markdown + href?: string; + children?: React.ReactNode; +} + +export const WikiLink: React.FC = ({ href, children, ...props }) => { + // Extract target from href + // remark-wiki-link creates hrefs like "#/wiki/target" + const target = href?.replace("#/wiki/", "") || ""; + + const handleClick = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + + // TODO: Implement wikilink navigation + // This could navigate to memo detail, show preview, etc. + console.log("Wikilink clicked:", target); + }; + + return ( + + {children} + + ); +}; diff --git a/web/src/components/MemoContent/index.tsx b/web/src/components/MemoContent/index.tsx index 116c18d59..c65e74010 100644 --- a/web/src/components/MemoContent/index.tsx +++ b/web/src/components/MemoContent/index.tsx @@ -1,26 +1,32 @@ import { observer } from "mobx-react-lite"; import { memo, useEffect, useRef, useState } from "react"; +import ReactMarkdown from "react-markdown"; +import rehypeRaw from "rehype-raw"; +import remarkGfm from "remark-gfm"; +import remarkWikiLink from "remark-wiki-link"; import useCurrentUser from "@/hooks/useCurrentUser"; import { cn } from "@/lib/utils"; import { memoStore } from "@/store"; -import { Node, NodeType } from "@/types/proto/api/v1/markdown_service"; import { useTranslate } from "@/utils/i18n"; +import { remarkPreserveType } from "@/utils/remark-plugins/remark-preserve-type"; +import { remarkTag } from "@/utils/remark-plugins/remark-tag"; import { isSuperUser } from "@/utils/user"; -import Renderer from "./Renderer"; -import { RendererContext } from "./types"; +import { createConditionalComponent, isTagNode, isTaskListItemNode, isWikiLinkNode } from "./ConditionalComponent"; +import { DefaultLink } from "./DefaultLink"; +import { MemoContentContext } from "./MemoContentContext"; +import { Tag } from "./Tag"; +import { TaskListItem } from "./TaskListItem"; +import { WikiLink } from "./WikiLink"; // MAX_DISPLAY_HEIGHT is the maximum height of the memo content to display in compact mode. const MAX_DISPLAY_HEIGHT = 256; interface Props { - nodes: Node[]; + content: string; memoName?: string; compact?: boolean; readonly?: boolean; disableFilter?: boolean; - // embeddedMemos is a set of memo resource names that are embedded in the current memo. - // This is used to prevent infinite loops when a memo embeds itself. - embeddedMemos?: Set; className?: string; contentClassName?: string; onClick?: (e: React.MouseEvent) => void; @@ -31,7 +37,7 @@ interface Props { type ContentCompactView = "ALL" | "SNIPPET"; const MemoContent = observer((props: Props) => { - const { className, contentClassName, nodes, memoName, embeddedMemos, onClick, onDoubleClick } = props; + const { className, contentClassName, content, memoName, onClick, onDoubleClick } = props; const t = useTranslate(); const currentUser = useCurrentUser(); const memoContentContainerRef = useRef(null); @@ -39,6 +45,14 @@ const MemoContent = observer((props: Props) => { const memo = memoName ? memoStore.getMemoByName(memoName) : null; const allowEdit = !props.readonly && memo && (currentUser?.name === memo.creator || isSuperUser(currentUser)); + // Context for custom components + const contextValue = { + memoName, + readonly: !allowEdit, + disableFilter: props.disableFilter, + parentPage: props.parentPage, + }; + // Initial compact mode. useEffect(() => { if (!props.compact) { @@ -54,6 +68,7 @@ const MemoContent = observer((props: Props) => { }, []); const onMemoContentClick = async (e: React.MouseEvent) => { + // Image clicks and other handlers if (onClick) { onClick(e); } @@ -65,48 +80,40 @@ const MemoContent = observer((props: Props) => { } }; - let prevNode: Node | null = null; - let skipNextLineBreakFlag = false; const compactStates = { ALL: { text: t("memo.show-more"), nextState: "SNIPPET" }, SNIPPET: { text: t("memo.show-less"), nextState: "ALL" }, }; return ( - +
    - {nodes.map((node, index) => { - if (prevNode?.type !== NodeType.LINE_BREAK && node.type === NodeType.LINE_BREAK && skipNextLineBreakFlag) { - skipNextLineBreakFlag = false; - return null; - } - prevNode = node; - skipNextLineBreakFlag = true; - return ; - })} - {showCompactMode == "ALL" && ( -
    - )} + + {content} +
    + {showCompactMode == "ALL" && ( +
    + )} {showCompactMode != undefined && (
    {
    )}
    -
    + ); }); diff --git a/web/src/components/MemoContent/types/context.ts b/web/src/components/MemoContent/types/context.ts deleted file mode 100644 index 13e59b467..000000000 --- a/web/src/components/MemoContent/types/context.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { createContext } from "react"; -import { Node } from "@/types/proto/api/v1/markdown_service"; - -interface Context { - nodes: Node[]; - // embeddedMemos is a set of memo resource names that are embedded in the current memo. - // This is used to prevent infinite loops when a memo embeds itself. - embeddedMemos: Set; - memoName?: string; - readonly?: boolean; - disableFilter?: boolean; - parentPage?: string; -} - -export const RendererContext = createContext({ - nodes: [], - embeddedMemos: new Set(), -}); diff --git a/web/src/components/MemoContent/types/index.ts b/web/src/components/MemoContent/types/index.ts deleted file mode 100644 index 0103e4f65..000000000 --- a/web/src/components/MemoContent/types/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from "./context"; - -export interface BaseProps { - index: string; - className?: string; -} diff --git a/web/src/components/MemoEditor/ActionButton/AddMemoRelationPopover.tsx b/web/src/components/MemoEditor/ActionButton/AddMemoRelationPopover.tsx index 3f53eba73..a7131246e 100644 --- a/web/src/components/MemoEditor/ActionButton/AddMemoRelationPopover.tsx +++ b/web/src/components/MemoEditor/ActionButton/AddMemoRelationPopover.tsx @@ -118,7 +118,7 @@ const AddMemoRelationPopover = () => { placeholder={t("reference.search-placeholder")} value={searchText} onChange={(e) => setSearchText(e.target.value)} - className="mb-2" + className="mb-2 !text-sm" />
    {filteredMemos.length === 0 ? ( diff --git a/web/src/components/MemoEditor/Editor/index.tsx b/web/src/components/MemoEditor/Editor/index.tsx index b87b97290..fbeb98bb7 100644 --- a/web/src/components/MemoEditor/Editor/index.tsx +++ b/web/src/components/MemoEditor/Editor/index.tsx @@ -1,8 +1,6 @@ -import { last } from "lodash-es"; import { forwardRef, ReactNode, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react"; -import { markdownServiceClient } from "@/grpcweb"; import { cn } from "@/lib/utils"; -import { Node, NodeType, OrderedListItemNode, TaskListItemNode, UnorderedListItemNode } from "@/types/proto/api/v1/markdown_service"; +import { detectLastListItem, generateListContinuation } from "@/utils/markdown-list-detection"; import { Command } from "../types/command"; import CommandSuggestions from "./CommandSuggestions"; import TagSuggestions from "./TagSuggestions"; @@ -154,20 +152,6 @@ const Editor = forwardRef(function Editor(props: Props, ref: React.ForwardedRef< updateEditorHeight(); }, []); - const getLastNode = (nodes: Node[]): Node | undefined => { - const lastNode = last(nodes); - if (!lastNode) { - return undefined; - } - if (lastNode.type === NodeType.LIST) { - const children = lastNode.listNode?.children; - if (children) { - return getLastNode(children); - } - } - return lastNode; - }; - const handleEditorKeyDown = async (event: React.KeyboardEvent) => { if (event.key === "Enter" && !isInIME) { if (event.shiftKey || event.ctrlKey || event.metaKey || event.altKey) { @@ -176,41 +160,11 @@ const Editor = forwardRef(function Editor(props: Props, ref: React.ForwardedRef< const cursorPosition = editorActions.getCursorPosition(); const prevContent = editorActions.getContent().substring(0, cursorPosition); - const { nodes } = await markdownServiceClient.parseMarkdown({ markdown: prevContent }); - const lastNode = getLastNode(nodes); - if (!lastNode) { - return; - } - // Get the indentation of the previous line - const lines = prevContent.split("\n"); - const lastLine = lines[lines.length - 1]; - const indentationMatch = lastLine.match(/^\s*/); - let insertText = indentationMatch ? indentationMatch[0] : ""; // Keep the indentation of the previous line - if (lastNode.type === NodeType.TASK_LIST_ITEM) { - const { symbol } = lastNode.taskListItemNode as TaskListItemNode; - insertText += `${symbol} [ ] `; - } else if (lastNode.type === NodeType.UNORDERED_LIST_ITEM) { - const { symbol } = lastNode.unorderedListItemNode as UnorderedListItemNode; - insertText += `${symbol} `; - } else if (lastNode.type === NodeType.ORDERED_LIST_ITEM) { - const { number } = lastNode.orderedListItemNode as OrderedListItemNode; - insertText += `${Number(number) + 1}. `; - } else if (lastNode.type === NodeType.TABLE) { - const columns = lastNode.tableNode?.header.length; - if (!columns) { - return; - } - - insertText += "| "; - for (let i = 1; i < columns; i++) { - insertText += " | "; - } - insertText += " |"; - } - - if (insertText) { - // Insert the text at the current cursor position. + // Detect list item using regex-based detection + const listInfo = detectLastListItem(prevContent); + if (listInfo.type) { + const insertText = generateListContinuation(listInfo); editorActions.insertText(insertText); } } diff --git a/web/src/components/MemoView.tsx b/web/src/components/MemoView.tsx index 2eb00f684..79e5fc6fe 100644 --- a/web/src/components/MemoView.tsx +++ b/web/src/components/MemoView.tsx @@ -239,7 +239,7 @@ const MemoView: React.FC = observer((props: Props) => { *, + .prose ul.contains-task-list li.task-list-item > * { + margin: 0 !important; + } + + /* Override parent space-y utility for task lists */ + .markdown-content ul.contains-task-list + *, + .prose ul.contains-task-list + * { + margin-top: 0.25rem !important; + } + + * + .markdown-content ul.contains-task-list, + * + .prose ul.contains-task-list { + margin-top: 0.25rem !important; + } + + /* ======================================== + * Markdown Content Styles + * Custom minimal styles for markdown rendering + * ======================================== */ + + .markdown-content { + color: var(--foreground); + white-space: pre-line; /* Preserve newlines but collapse multiple spaces */ + } + + /* Block elements should not inherit pre-line */ + .markdown-content h1, + .markdown-content h2, + .markdown-content h3, + .markdown-content h4, + .markdown-content h5, + .markdown-content h6, + .markdown-content p, + .markdown-content ul, + .markdown-content ol, + .markdown-content pre, + .markdown-content blockquote, + .markdown-content table { + white-space: normal; + } + + /* Code blocks need pre-wrap */ + .markdown-content pre { + white-space: pre-wrap; + } + + /* Headings */ + .markdown-content h1 { + font-size: 1.875rem; + font-weight: 700; + line-height: 2.25rem; + margin-top: 1rem; + margin-bottom: 0.5rem; + } + + .markdown-content h2 { + font-size: 1.5rem; + font-weight: 600; + line-height: 2rem; + margin-top: 0.75rem; + margin-bottom: 0.5rem; + } + + .markdown-content h3 { + font-size: 1.25rem; + font-weight: 600; + line-height: 1.75rem; + margin-top: 0.75rem; + margin-bottom: 0.5rem; + } + + .markdown-content h4, + .markdown-content h5, + .markdown-content h6 { + font-size: 1.125rem; + font-weight: 600; + line-height: 1.75rem; + margin-top: 0.5rem; + margin-bottom: 0.5rem; + } + + /* First heading has no top margin */ + .markdown-content > :first-child { + margin-top: 0; + } + + /* Paragraphs */ + .markdown-content p { + margin: 0.5rem 0; + } + + /* Links */ + .markdown-content a { + color: var(--primary); + text-decoration: underline; + transition: opacity 150ms; + } + + .markdown-content a:hover { + opacity: 0.8; + } + + /* Lists - MINIMAL spacing */ + .markdown-content ul, + .markdown-content ol { + margin: 0.5rem 0; + padding-left: 1.5em; + list-style-position: outside; + } + + .markdown-content ul { + list-style-type: disc; + } + + .markdown-content ol { + list-style-type: decimal; + } + + .markdown-content li { + margin: 0.125rem 0; + line-height: 1.5; + } + + .markdown-content li:first-child { + margin-top: 0; + } + + .markdown-content li:last-child { + margin-bottom: 0; + } + + /* Nested lists */ + .markdown-content li > ul, + .markdown-content li > ol { + margin: 0.25rem 0; + } + + /* Code */ + .markdown-content code { + font-family: var(--font-mono); + font-size: 0.875em; + background: var(--muted); + padding: 0.125rem 0.25rem; + border-radius: 0.25rem; + } + + .markdown-content pre { + font-family: var(--font-mono); + font-size: 0.875rem; + background: var(--muted); + padding: 0.75rem 1rem; + border-radius: 0.5rem; + overflow-x: auto; + margin: 0.75rem 0; + } + + .markdown-content pre code { + background: none; + padding: 0; + font-size: inherit; + } + + /* Blockquotes */ + .markdown-content blockquote { + border-left: 3px solid var(--border); + padding-left: 1rem; + margin: 0.75rem 0; + color: var(--muted-foreground); + font-style: italic; + } + + /* Horizontal rule */ + .markdown-content hr { + border: none; + border-top: 1px solid var(--border); + margin: 1rem 0; + } + + /* Tables */ + .markdown-content table { + width: 100%; + border-collapse: collapse; + margin: 0.75rem 0; + } + + .markdown-content th, + .markdown-content td { + border: 1px solid var(--border); + padding: 0.5rem; + text-align: left; + } + + .markdown-content th { + background: var(--muted); + font-weight: 600; + } + + /* Images */ + .markdown-content img { + max-width: 100%; + height: auto; + border-radius: 0.5rem; + margin: 0.75rem 0; + } + + /* Strong/Bold */ + .markdown-content strong { + font-weight: 600; + } + + /* Emphasis/Italic */ + .markdown-content em { + font-style: italic; + } + + /* Inline elements shouldn't add vertical spacing */ + .markdown-content code, + .markdown-content strong, + .markdown-content em, + .markdown-content a { + vertical-align: baseline; + } } diff --git a/web/src/store/memoFilter.ts b/web/src/store/memoFilter.ts index 567779388..126751646 100644 --- a/web/src/store/memoFilter.ts +++ b/web/src/store/memoFilter.ts @@ -7,7 +7,7 @@ * Filters are URL-driven and shareable - copying the URL preserves the filter state. */ import { uniqBy } from "lodash-es"; -import { makeObservable, observable, action } from "mobx"; +import { makeObservable, observable, action, computed } from "mobx"; import { StandardState } from "./base-store"; /** @@ -94,6 +94,7 @@ class MemoFilterState extends StandardState { makeObservable(this, { filters: observable, shortcut: observable, + hasActiveFilters: computed, addFilter: action, removeFilter: action, removeFiltersByFactor: action, diff --git a/web/src/types/proto/api/v1/markdown_service.ts b/web/src/types/proto/api/v1/markdown_service.ts deleted file mode 100644 index 3bf55c43e..000000000 --- a/web/src/types/proto/api/v1/markdown_service.ts +++ /dev/null @@ -1,3464 +0,0 @@ -// Code generated by protoc-gen-ts_proto. DO NOT EDIT. -// versions: -// protoc-gen-ts_proto v2.6.1 -// protoc unknown -// source: api/v1/markdown_service.proto - -/* eslint-disable */ -import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; - -export const protobufPackage = "memos.api.v1"; - -export enum NodeType { - NODE_UNSPECIFIED = "NODE_UNSPECIFIED", - /** LINE_BREAK - Block nodes. */ - LINE_BREAK = "LINE_BREAK", - PARAGRAPH = "PARAGRAPH", - CODE_BLOCK = "CODE_BLOCK", - HEADING = "HEADING", - HORIZONTAL_RULE = "HORIZONTAL_RULE", - BLOCKQUOTE = "BLOCKQUOTE", - LIST = "LIST", - ORDERED_LIST_ITEM = "ORDERED_LIST_ITEM", - UNORDERED_LIST_ITEM = "UNORDERED_LIST_ITEM", - TASK_LIST_ITEM = "TASK_LIST_ITEM", - MATH_BLOCK = "MATH_BLOCK", - TABLE = "TABLE", - EMBEDDED_CONTENT = "EMBEDDED_CONTENT", - /** TEXT - Inline nodes. */ - TEXT = "TEXT", - BOLD = "BOLD", - ITALIC = "ITALIC", - BOLD_ITALIC = "BOLD_ITALIC", - CODE = "CODE", - IMAGE = "IMAGE", - LINK = "LINK", - AUTO_LINK = "AUTO_LINK", - TAG = "TAG", - STRIKETHROUGH = "STRIKETHROUGH", - ESCAPING_CHARACTER = "ESCAPING_CHARACTER", - MATH = "MATH", - HIGHLIGHT = "HIGHLIGHT", - SUBSCRIPT = "SUBSCRIPT", - SUPERSCRIPT = "SUPERSCRIPT", - REFERENCED_CONTENT = "REFERENCED_CONTENT", - SPOILER = "SPOILER", - HTML_ELEMENT = "HTML_ELEMENT", - UNRECOGNIZED = "UNRECOGNIZED", -} - -export function nodeTypeFromJSON(object: any): NodeType { - switch (object) { - case 0: - case "NODE_UNSPECIFIED": - return NodeType.NODE_UNSPECIFIED; - case 1: - case "LINE_BREAK": - return NodeType.LINE_BREAK; - case 2: - case "PARAGRAPH": - return NodeType.PARAGRAPH; - case 3: - case "CODE_BLOCK": - return NodeType.CODE_BLOCK; - case 4: - case "HEADING": - return NodeType.HEADING; - case 5: - case "HORIZONTAL_RULE": - return NodeType.HORIZONTAL_RULE; - case 6: - case "BLOCKQUOTE": - return NodeType.BLOCKQUOTE; - case 7: - case "LIST": - return NodeType.LIST; - case 8: - case "ORDERED_LIST_ITEM": - return NodeType.ORDERED_LIST_ITEM; - case 9: - case "UNORDERED_LIST_ITEM": - return NodeType.UNORDERED_LIST_ITEM; - case 10: - case "TASK_LIST_ITEM": - return NodeType.TASK_LIST_ITEM; - case 11: - case "MATH_BLOCK": - return NodeType.MATH_BLOCK; - case 12: - case "TABLE": - return NodeType.TABLE; - case 13: - case "EMBEDDED_CONTENT": - return NodeType.EMBEDDED_CONTENT; - case 51: - case "TEXT": - return NodeType.TEXT; - case 52: - case "BOLD": - return NodeType.BOLD; - case 53: - case "ITALIC": - return NodeType.ITALIC; - case 54: - case "BOLD_ITALIC": - return NodeType.BOLD_ITALIC; - case 55: - case "CODE": - return NodeType.CODE; - case 56: - case "IMAGE": - return NodeType.IMAGE; - case 57: - case "LINK": - return NodeType.LINK; - case 58: - case "AUTO_LINK": - return NodeType.AUTO_LINK; - case 59: - case "TAG": - return NodeType.TAG; - case 60: - case "STRIKETHROUGH": - return NodeType.STRIKETHROUGH; - case 61: - case "ESCAPING_CHARACTER": - return NodeType.ESCAPING_CHARACTER; - case 62: - case "MATH": - return NodeType.MATH; - case 63: - case "HIGHLIGHT": - return NodeType.HIGHLIGHT; - case 64: - case "SUBSCRIPT": - return NodeType.SUBSCRIPT; - case 65: - case "SUPERSCRIPT": - return NodeType.SUPERSCRIPT; - case 66: - case "REFERENCED_CONTENT": - return NodeType.REFERENCED_CONTENT; - case 67: - case "SPOILER": - return NodeType.SPOILER; - case 68: - case "HTML_ELEMENT": - return NodeType.HTML_ELEMENT; - case -1: - case "UNRECOGNIZED": - default: - return NodeType.UNRECOGNIZED; - } -} - -export function nodeTypeToNumber(object: NodeType): number { - switch (object) { - case NodeType.NODE_UNSPECIFIED: - return 0; - case NodeType.LINE_BREAK: - return 1; - case NodeType.PARAGRAPH: - return 2; - case NodeType.CODE_BLOCK: - return 3; - case NodeType.HEADING: - return 4; - case NodeType.HORIZONTAL_RULE: - return 5; - case NodeType.BLOCKQUOTE: - return 6; - case NodeType.LIST: - return 7; - case NodeType.ORDERED_LIST_ITEM: - return 8; - case NodeType.UNORDERED_LIST_ITEM: - return 9; - case NodeType.TASK_LIST_ITEM: - return 10; - case NodeType.MATH_BLOCK: - return 11; - case NodeType.TABLE: - return 12; - case NodeType.EMBEDDED_CONTENT: - return 13; - case NodeType.TEXT: - return 51; - case NodeType.BOLD: - return 52; - case NodeType.ITALIC: - return 53; - case NodeType.BOLD_ITALIC: - return 54; - case NodeType.CODE: - return 55; - case NodeType.IMAGE: - return 56; - case NodeType.LINK: - return 57; - case NodeType.AUTO_LINK: - return 58; - case NodeType.TAG: - return 59; - case NodeType.STRIKETHROUGH: - return 60; - case NodeType.ESCAPING_CHARACTER: - return 61; - case NodeType.MATH: - return 62; - case NodeType.HIGHLIGHT: - return 63; - case NodeType.SUBSCRIPT: - return 64; - case NodeType.SUPERSCRIPT: - return 65; - case NodeType.REFERENCED_CONTENT: - return 66; - case NodeType.SPOILER: - return 67; - case NodeType.HTML_ELEMENT: - return 68; - case NodeType.UNRECOGNIZED: - default: - return -1; - } -} - -export interface ParseMarkdownRequest { - /** The markdown content to parse. */ - markdown: string; -} - -export interface ParseMarkdownResponse { - /** The parsed markdown nodes. */ - nodes: Node[]; -} - -export interface RestoreMarkdownNodesRequest { - /** The nodes to restore to markdown content. */ - nodes: Node[]; -} - -export interface RestoreMarkdownNodesResponse { - /** The restored markdown content. */ - markdown: string; -} - -export interface StringifyMarkdownNodesRequest { - /** The nodes to stringify to plain text. */ - nodes: Node[]; -} - -export interface StringifyMarkdownNodesResponse { - /** The plain text content. */ - plainText: string; -} - -export interface GetLinkMetadataRequest { - /** The link URL to get metadata for. */ - link: string; -} - -export interface LinkMetadata { - /** The title of the linked page. */ - title: string; - /** The description of the linked page. */ - description: string; - /** The URL of the preview image for the linked page. */ - image: string; -} - -export interface Node { - type: NodeType; - /** Block nodes. */ - lineBreakNode?: LineBreakNode | undefined; - paragraphNode?: ParagraphNode | undefined; - codeBlockNode?: CodeBlockNode | undefined; - headingNode?: HeadingNode | undefined; - horizontalRuleNode?: HorizontalRuleNode | undefined; - blockquoteNode?: BlockquoteNode | undefined; - listNode?: ListNode | undefined; - orderedListItemNode?: OrderedListItemNode | undefined; - unorderedListItemNode?: UnorderedListItemNode | undefined; - taskListItemNode?: TaskListItemNode | undefined; - mathBlockNode?: MathBlockNode | undefined; - tableNode?: TableNode | undefined; - embeddedContentNode?: - | EmbeddedContentNode - | undefined; - /** Inline nodes. */ - textNode?: TextNode | undefined; - boldNode?: BoldNode | undefined; - italicNode?: ItalicNode | undefined; - boldItalicNode?: BoldItalicNode | undefined; - codeNode?: CodeNode | undefined; - imageNode?: ImageNode | undefined; - linkNode?: LinkNode | undefined; - autoLinkNode?: AutoLinkNode | undefined; - tagNode?: TagNode | undefined; - strikethroughNode?: StrikethroughNode | undefined; - escapingCharacterNode?: EscapingCharacterNode | undefined; - mathNode?: MathNode | undefined; - highlightNode?: HighlightNode | undefined; - subscriptNode?: SubscriptNode | undefined; - superscriptNode?: SuperscriptNode | undefined; - referencedContentNode?: ReferencedContentNode | undefined; - spoilerNode?: SpoilerNode | undefined; - htmlElementNode?: HTMLElementNode | undefined; -} - -export interface LineBreakNode { -} - -export interface ParagraphNode { - children: Node[]; -} - -export interface CodeBlockNode { - language: string; - content: string; -} - -export interface HeadingNode { - level: number; - children: Node[]; -} - -export interface HorizontalRuleNode { - symbol: string; -} - -export interface BlockquoteNode { - children: Node[]; -} - -export interface ListNode { - kind: ListNode_Kind; - indent: number; - children: Node[]; -} - -export enum ListNode_Kind { - KIND_UNSPECIFIED = "KIND_UNSPECIFIED", - ORDERED = "ORDERED", - UNORDERED = "UNORDERED", - DESCRIPTION = "DESCRIPTION", - UNRECOGNIZED = "UNRECOGNIZED", -} - -export function listNode_KindFromJSON(object: any): ListNode_Kind { - switch (object) { - case 0: - case "KIND_UNSPECIFIED": - return ListNode_Kind.KIND_UNSPECIFIED; - case 1: - case "ORDERED": - return ListNode_Kind.ORDERED; - case 2: - case "UNORDERED": - return ListNode_Kind.UNORDERED; - case 3: - case "DESCRIPTION": - return ListNode_Kind.DESCRIPTION; - case -1: - case "UNRECOGNIZED": - default: - return ListNode_Kind.UNRECOGNIZED; - } -} - -export function listNode_KindToNumber(object: ListNode_Kind): number { - switch (object) { - case ListNode_Kind.KIND_UNSPECIFIED: - return 0; - case ListNode_Kind.ORDERED: - return 1; - case ListNode_Kind.UNORDERED: - return 2; - case ListNode_Kind.DESCRIPTION: - return 3; - case ListNode_Kind.UNRECOGNIZED: - default: - return -1; - } -} - -export interface OrderedListItemNode { - number: string; - indent: number; - children: Node[]; -} - -export interface UnorderedListItemNode { - symbol: string; - indent: number; - children: Node[]; -} - -export interface TaskListItemNode { - symbol: string; - indent: number; - complete: boolean; - children: Node[]; -} - -export interface MathBlockNode { - content: string; -} - -export interface TableNode { - header: Node[]; - delimiter: string[]; - rows: TableNode_Row[]; -} - -export interface TableNode_Row { - cells: Node[]; -} - -export interface EmbeddedContentNode { - /** The resource name of the embedded content. */ - resourceName: string; - /** Additional parameters for the embedded content. */ - params: string; -} - -export interface TextNode { - content: string; -} - -export interface BoldNode { - symbol: string; - children: Node[]; -} - -export interface ItalicNode { - symbol: string; - children: Node[]; -} - -export interface BoldItalicNode { - symbol: string; - content: string; -} - -export interface CodeNode { - content: string; -} - -export interface ImageNode { - altText: string; - url: string; -} - -export interface LinkNode { - content: Node[]; - url: string; -} - -export interface AutoLinkNode { - url: string; - isRawText: boolean; -} - -export interface TagNode { - content: string; -} - -export interface StrikethroughNode { - content: string; -} - -export interface EscapingCharacterNode { - symbol: string; -} - -export interface MathNode { - content: string; -} - -export interface HighlightNode { - content: string; -} - -export interface SubscriptNode { - content: string; -} - -export interface SuperscriptNode { - content: string; -} - -export interface ReferencedContentNode { - /** The resource name of the referenced content. */ - resourceName: string; - /** Additional parameters for the referenced content. */ - params: string; -} - -export interface SpoilerNode { - content: string; -} - -export interface HTMLElementNode { - tagName: string; - attributes: { [key: string]: string }; - children: Node[]; - isSelfClosing: boolean; -} - -export interface HTMLElementNode_AttributesEntry { - key: string; - value: string; -} - -function createBaseParseMarkdownRequest(): ParseMarkdownRequest { - return { markdown: "" }; -} - -export const ParseMarkdownRequest: MessageFns = { - encode(message: ParseMarkdownRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.markdown !== "") { - writer.uint32(10).string(message.markdown); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): ParseMarkdownRequest { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseParseMarkdownRequest(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.markdown = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): ParseMarkdownRequest { - return ParseMarkdownRequest.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): ParseMarkdownRequest { - const message = createBaseParseMarkdownRequest(); - message.markdown = object.markdown ?? ""; - return message; - }, -}; - -function createBaseParseMarkdownResponse(): ParseMarkdownResponse { - return { nodes: [] }; -} - -export const ParseMarkdownResponse: MessageFns = { - encode(message: ParseMarkdownResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - for (const v of message.nodes) { - Node.encode(v!, writer.uint32(10).fork()).join(); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): ParseMarkdownResponse { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseParseMarkdownResponse(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.nodes.push(Node.decode(reader, reader.uint32())); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): ParseMarkdownResponse { - return ParseMarkdownResponse.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): ParseMarkdownResponse { - const message = createBaseParseMarkdownResponse(); - message.nodes = object.nodes?.map((e) => Node.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseRestoreMarkdownNodesRequest(): RestoreMarkdownNodesRequest { - return { nodes: [] }; -} - -export const RestoreMarkdownNodesRequest: MessageFns = { - encode(message: RestoreMarkdownNodesRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - for (const v of message.nodes) { - Node.encode(v!, writer.uint32(10).fork()).join(); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): RestoreMarkdownNodesRequest { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseRestoreMarkdownNodesRequest(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.nodes.push(Node.decode(reader, reader.uint32())); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): RestoreMarkdownNodesRequest { - return RestoreMarkdownNodesRequest.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): RestoreMarkdownNodesRequest { - const message = createBaseRestoreMarkdownNodesRequest(); - message.nodes = object.nodes?.map((e) => Node.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseRestoreMarkdownNodesResponse(): RestoreMarkdownNodesResponse { - return { markdown: "" }; -} - -export const RestoreMarkdownNodesResponse: MessageFns = { - encode(message: RestoreMarkdownNodesResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.markdown !== "") { - writer.uint32(10).string(message.markdown); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): RestoreMarkdownNodesResponse { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseRestoreMarkdownNodesResponse(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.markdown = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): RestoreMarkdownNodesResponse { - return RestoreMarkdownNodesResponse.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): RestoreMarkdownNodesResponse { - const message = createBaseRestoreMarkdownNodesResponse(); - message.markdown = object.markdown ?? ""; - return message; - }, -}; - -function createBaseStringifyMarkdownNodesRequest(): StringifyMarkdownNodesRequest { - return { nodes: [] }; -} - -export const StringifyMarkdownNodesRequest: MessageFns = { - encode(message: StringifyMarkdownNodesRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - for (const v of message.nodes) { - Node.encode(v!, writer.uint32(10).fork()).join(); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): StringifyMarkdownNodesRequest { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseStringifyMarkdownNodesRequest(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.nodes.push(Node.decode(reader, reader.uint32())); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): StringifyMarkdownNodesRequest { - return StringifyMarkdownNodesRequest.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): StringifyMarkdownNodesRequest { - const message = createBaseStringifyMarkdownNodesRequest(); - message.nodes = object.nodes?.map((e) => Node.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseStringifyMarkdownNodesResponse(): StringifyMarkdownNodesResponse { - return { plainText: "" }; -} - -export const StringifyMarkdownNodesResponse: MessageFns = { - encode(message: StringifyMarkdownNodesResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.plainText !== "") { - writer.uint32(10).string(message.plainText); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): StringifyMarkdownNodesResponse { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseStringifyMarkdownNodesResponse(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.plainText = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): StringifyMarkdownNodesResponse { - return StringifyMarkdownNodesResponse.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): StringifyMarkdownNodesResponse { - const message = createBaseStringifyMarkdownNodesResponse(); - message.plainText = object.plainText ?? ""; - return message; - }, -}; - -function createBaseGetLinkMetadataRequest(): GetLinkMetadataRequest { - return { link: "" }; -} - -export const GetLinkMetadataRequest: MessageFns = { - encode(message: GetLinkMetadataRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.link !== "") { - writer.uint32(10).string(message.link); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): GetLinkMetadataRequest { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseGetLinkMetadataRequest(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.link = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): GetLinkMetadataRequest { - return GetLinkMetadataRequest.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): GetLinkMetadataRequest { - const message = createBaseGetLinkMetadataRequest(); - message.link = object.link ?? ""; - return message; - }, -}; - -function createBaseLinkMetadata(): LinkMetadata { - return { title: "", description: "", image: "" }; -} - -export const LinkMetadata: MessageFns = { - encode(message: LinkMetadata, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.title !== "") { - writer.uint32(10).string(message.title); - } - if (message.description !== "") { - writer.uint32(18).string(message.description); - } - if (message.image !== "") { - writer.uint32(26).string(message.image); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): LinkMetadata { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseLinkMetadata(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.title = reader.string(); - continue; - } - case 2: { - if (tag !== 18) { - break; - } - - message.description = reader.string(); - continue; - } - case 3: { - if (tag !== 26) { - break; - } - - message.image = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): LinkMetadata { - return LinkMetadata.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): LinkMetadata { - const message = createBaseLinkMetadata(); - message.title = object.title ?? ""; - message.description = object.description ?? ""; - message.image = object.image ?? ""; - return message; - }, -}; - -function createBaseNode(): Node { - return { - type: NodeType.NODE_UNSPECIFIED, - lineBreakNode: undefined, - paragraphNode: undefined, - codeBlockNode: undefined, - headingNode: undefined, - horizontalRuleNode: undefined, - blockquoteNode: undefined, - listNode: undefined, - orderedListItemNode: undefined, - unorderedListItemNode: undefined, - taskListItemNode: undefined, - mathBlockNode: undefined, - tableNode: undefined, - embeddedContentNode: undefined, - textNode: undefined, - boldNode: undefined, - italicNode: undefined, - boldItalicNode: undefined, - codeNode: undefined, - imageNode: undefined, - linkNode: undefined, - autoLinkNode: undefined, - tagNode: undefined, - strikethroughNode: undefined, - escapingCharacterNode: undefined, - mathNode: undefined, - highlightNode: undefined, - subscriptNode: undefined, - superscriptNode: undefined, - referencedContentNode: undefined, - spoilerNode: undefined, - htmlElementNode: undefined, - }; -} - -export const Node: MessageFns = { - encode(message: Node, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.type !== NodeType.NODE_UNSPECIFIED) { - writer.uint32(8).int32(nodeTypeToNumber(message.type)); - } - if (message.lineBreakNode !== undefined) { - LineBreakNode.encode(message.lineBreakNode, writer.uint32(90).fork()).join(); - } - if (message.paragraphNode !== undefined) { - ParagraphNode.encode(message.paragraphNode, writer.uint32(98).fork()).join(); - } - if (message.codeBlockNode !== undefined) { - CodeBlockNode.encode(message.codeBlockNode, writer.uint32(106).fork()).join(); - } - if (message.headingNode !== undefined) { - HeadingNode.encode(message.headingNode, writer.uint32(114).fork()).join(); - } - if (message.horizontalRuleNode !== undefined) { - HorizontalRuleNode.encode(message.horizontalRuleNode, writer.uint32(122).fork()).join(); - } - if (message.blockquoteNode !== undefined) { - BlockquoteNode.encode(message.blockquoteNode, writer.uint32(130).fork()).join(); - } - if (message.listNode !== undefined) { - ListNode.encode(message.listNode, writer.uint32(138).fork()).join(); - } - if (message.orderedListItemNode !== undefined) { - OrderedListItemNode.encode(message.orderedListItemNode, writer.uint32(146).fork()).join(); - } - if (message.unorderedListItemNode !== undefined) { - UnorderedListItemNode.encode(message.unorderedListItemNode, writer.uint32(154).fork()).join(); - } - if (message.taskListItemNode !== undefined) { - TaskListItemNode.encode(message.taskListItemNode, writer.uint32(162).fork()).join(); - } - if (message.mathBlockNode !== undefined) { - MathBlockNode.encode(message.mathBlockNode, writer.uint32(170).fork()).join(); - } - if (message.tableNode !== undefined) { - TableNode.encode(message.tableNode, writer.uint32(178).fork()).join(); - } - if (message.embeddedContentNode !== undefined) { - EmbeddedContentNode.encode(message.embeddedContentNode, writer.uint32(186).fork()).join(); - } - if (message.textNode !== undefined) { - TextNode.encode(message.textNode, writer.uint32(410).fork()).join(); - } - if (message.boldNode !== undefined) { - BoldNode.encode(message.boldNode, writer.uint32(418).fork()).join(); - } - if (message.italicNode !== undefined) { - ItalicNode.encode(message.italicNode, writer.uint32(426).fork()).join(); - } - if (message.boldItalicNode !== undefined) { - BoldItalicNode.encode(message.boldItalicNode, writer.uint32(434).fork()).join(); - } - if (message.codeNode !== undefined) { - CodeNode.encode(message.codeNode, writer.uint32(442).fork()).join(); - } - if (message.imageNode !== undefined) { - ImageNode.encode(message.imageNode, writer.uint32(450).fork()).join(); - } - if (message.linkNode !== undefined) { - LinkNode.encode(message.linkNode, writer.uint32(458).fork()).join(); - } - if (message.autoLinkNode !== undefined) { - AutoLinkNode.encode(message.autoLinkNode, writer.uint32(466).fork()).join(); - } - if (message.tagNode !== undefined) { - TagNode.encode(message.tagNode, writer.uint32(474).fork()).join(); - } - if (message.strikethroughNode !== undefined) { - StrikethroughNode.encode(message.strikethroughNode, writer.uint32(482).fork()).join(); - } - if (message.escapingCharacterNode !== undefined) { - EscapingCharacterNode.encode(message.escapingCharacterNode, writer.uint32(490).fork()).join(); - } - if (message.mathNode !== undefined) { - MathNode.encode(message.mathNode, writer.uint32(498).fork()).join(); - } - if (message.highlightNode !== undefined) { - HighlightNode.encode(message.highlightNode, writer.uint32(506).fork()).join(); - } - if (message.subscriptNode !== undefined) { - SubscriptNode.encode(message.subscriptNode, writer.uint32(514).fork()).join(); - } - if (message.superscriptNode !== undefined) { - SuperscriptNode.encode(message.superscriptNode, writer.uint32(522).fork()).join(); - } - if (message.referencedContentNode !== undefined) { - ReferencedContentNode.encode(message.referencedContentNode, writer.uint32(530).fork()).join(); - } - if (message.spoilerNode !== undefined) { - SpoilerNode.encode(message.spoilerNode, writer.uint32(538).fork()).join(); - } - if (message.htmlElementNode !== undefined) { - HTMLElementNode.encode(message.htmlElementNode, writer.uint32(546).fork()).join(); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): Node { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 8) { - break; - } - - message.type = nodeTypeFromJSON(reader.int32()); - continue; - } - case 11: { - if (tag !== 90) { - break; - } - - message.lineBreakNode = LineBreakNode.decode(reader, reader.uint32()); - continue; - } - case 12: { - if (tag !== 98) { - break; - } - - message.paragraphNode = ParagraphNode.decode(reader, reader.uint32()); - continue; - } - case 13: { - if (tag !== 106) { - break; - } - - message.codeBlockNode = CodeBlockNode.decode(reader, reader.uint32()); - continue; - } - case 14: { - if (tag !== 114) { - break; - } - - message.headingNode = HeadingNode.decode(reader, reader.uint32()); - continue; - } - case 15: { - if (tag !== 122) { - break; - } - - message.horizontalRuleNode = HorizontalRuleNode.decode(reader, reader.uint32()); - continue; - } - case 16: { - if (tag !== 130) { - break; - } - - message.blockquoteNode = BlockquoteNode.decode(reader, reader.uint32()); - continue; - } - case 17: { - if (tag !== 138) { - break; - } - - message.listNode = ListNode.decode(reader, reader.uint32()); - continue; - } - case 18: { - if (tag !== 146) { - break; - } - - message.orderedListItemNode = OrderedListItemNode.decode(reader, reader.uint32()); - continue; - } - case 19: { - if (tag !== 154) { - break; - } - - message.unorderedListItemNode = UnorderedListItemNode.decode(reader, reader.uint32()); - continue; - } - case 20: { - if (tag !== 162) { - break; - } - - message.taskListItemNode = TaskListItemNode.decode(reader, reader.uint32()); - continue; - } - case 21: { - if (tag !== 170) { - break; - } - - message.mathBlockNode = MathBlockNode.decode(reader, reader.uint32()); - continue; - } - case 22: { - if (tag !== 178) { - break; - } - - message.tableNode = TableNode.decode(reader, reader.uint32()); - continue; - } - case 23: { - if (tag !== 186) { - break; - } - - message.embeddedContentNode = EmbeddedContentNode.decode(reader, reader.uint32()); - continue; - } - case 51: { - if (tag !== 410) { - break; - } - - message.textNode = TextNode.decode(reader, reader.uint32()); - continue; - } - case 52: { - if (tag !== 418) { - break; - } - - message.boldNode = BoldNode.decode(reader, reader.uint32()); - continue; - } - case 53: { - if (tag !== 426) { - break; - } - - message.italicNode = ItalicNode.decode(reader, reader.uint32()); - continue; - } - case 54: { - if (tag !== 434) { - break; - } - - message.boldItalicNode = BoldItalicNode.decode(reader, reader.uint32()); - continue; - } - case 55: { - if (tag !== 442) { - break; - } - - message.codeNode = CodeNode.decode(reader, reader.uint32()); - continue; - } - case 56: { - if (tag !== 450) { - break; - } - - message.imageNode = ImageNode.decode(reader, reader.uint32()); - continue; - } - case 57: { - if (tag !== 458) { - break; - } - - message.linkNode = LinkNode.decode(reader, reader.uint32()); - continue; - } - case 58: { - if (tag !== 466) { - break; - } - - message.autoLinkNode = AutoLinkNode.decode(reader, reader.uint32()); - continue; - } - case 59: { - if (tag !== 474) { - break; - } - - message.tagNode = TagNode.decode(reader, reader.uint32()); - continue; - } - case 60: { - if (tag !== 482) { - break; - } - - message.strikethroughNode = StrikethroughNode.decode(reader, reader.uint32()); - continue; - } - case 61: { - if (tag !== 490) { - break; - } - - message.escapingCharacterNode = EscapingCharacterNode.decode(reader, reader.uint32()); - continue; - } - case 62: { - if (tag !== 498) { - break; - } - - message.mathNode = MathNode.decode(reader, reader.uint32()); - continue; - } - case 63: { - if (tag !== 506) { - break; - } - - message.highlightNode = HighlightNode.decode(reader, reader.uint32()); - continue; - } - case 64: { - if (tag !== 514) { - break; - } - - message.subscriptNode = SubscriptNode.decode(reader, reader.uint32()); - continue; - } - case 65: { - if (tag !== 522) { - break; - } - - message.superscriptNode = SuperscriptNode.decode(reader, reader.uint32()); - continue; - } - case 66: { - if (tag !== 530) { - break; - } - - message.referencedContentNode = ReferencedContentNode.decode(reader, reader.uint32()); - continue; - } - case 67: { - if (tag !== 538) { - break; - } - - message.spoilerNode = SpoilerNode.decode(reader, reader.uint32()); - continue; - } - case 68: { - if (tag !== 546) { - break; - } - - message.htmlElementNode = HTMLElementNode.decode(reader, reader.uint32()); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): Node { - return Node.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): Node { - const message = createBaseNode(); - message.type = object.type ?? NodeType.NODE_UNSPECIFIED; - message.lineBreakNode = (object.lineBreakNode !== undefined && object.lineBreakNode !== null) - ? LineBreakNode.fromPartial(object.lineBreakNode) - : undefined; - message.paragraphNode = (object.paragraphNode !== undefined && object.paragraphNode !== null) - ? ParagraphNode.fromPartial(object.paragraphNode) - : undefined; - message.codeBlockNode = (object.codeBlockNode !== undefined && object.codeBlockNode !== null) - ? CodeBlockNode.fromPartial(object.codeBlockNode) - : undefined; - message.headingNode = (object.headingNode !== undefined && object.headingNode !== null) - ? HeadingNode.fromPartial(object.headingNode) - : undefined; - message.horizontalRuleNode = (object.horizontalRuleNode !== undefined && object.horizontalRuleNode !== null) - ? HorizontalRuleNode.fromPartial(object.horizontalRuleNode) - : undefined; - message.blockquoteNode = (object.blockquoteNode !== undefined && object.blockquoteNode !== null) - ? BlockquoteNode.fromPartial(object.blockquoteNode) - : undefined; - message.listNode = (object.listNode !== undefined && object.listNode !== null) - ? ListNode.fromPartial(object.listNode) - : undefined; - message.orderedListItemNode = (object.orderedListItemNode !== undefined && object.orderedListItemNode !== null) - ? OrderedListItemNode.fromPartial(object.orderedListItemNode) - : undefined; - message.unorderedListItemNode = - (object.unorderedListItemNode !== undefined && object.unorderedListItemNode !== null) - ? UnorderedListItemNode.fromPartial(object.unorderedListItemNode) - : undefined; - message.taskListItemNode = (object.taskListItemNode !== undefined && object.taskListItemNode !== null) - ? TaskListItemNode.fromPartial(object.taskListItemNode) - : undefined; - message.mathBlockNode = (object.mathBlockNode !== undefined && object.mathBlockNode !== null) - ? MathBlockNode.fromPartial(object.mathBlockNode) - : undefined; - message.tableNode = (object.tableNode !== undefined && object.tableNode !== null) - ? TableNode.fromPartial(object.tableNode) - : undefined; - message.embeddedContentNode = (object.embeddedContentNode !== undefined && object.embeddedContentNode !== null) - ? EmbeddedContentNode.fromPartial(object.embeddedContentNode) - : undefined; - message.textNode = (object.textNode !== undefined && object.textNode !== null) - ? TextNode.fromPartial(object.textNode) - : undefined; - message.boldNode = (object.boldNode !== undefined && object.boldNode !== null) - ? BoldNode.fromPartial(object.boldNode) - : undefined; - message.italicNode = (object.italicNode !== undefined && object.italicNode !== null) - ? ItalicNode.fromPartial(object.italicNode) - : undefined; - message.boldItalicNode = (object.boldItalicNode !== undefined && object.boldItalicNode !== null) - ? BoldItalicNode.fromPartial(object.boldItalicNode) - : undefined; - message.codeNode = (object.codeNode !== undefined && object.codeNode !== null) - ? CodeNode.fromPartial(object.codeNode) - : undefined; - message.imageNode = (object.imageNode !== undefined && object.imageNode !== null) - ? ImageNode.fromPartial(object.imageNode) - : undefined; - message.linkNode = (object.linkNode !== undefined && object.linkNode !== null) - ? LinkNode.fromPartial(object.linkNode) - : undefined; - message.autoLinkNode = (object.autoLinkNode !== undefined && object.autoLinkNode !== null) - ? AutoLinkNode.fromPartial(object.autoLinkNode) - : undefined; - message.tagNode = (object.tagNode !== undefined && object.tagNode !== null) - ? TagNode.fromPartial(object.tagNode) - : undefined; - message.strikethroughNode = (object.strikethroughNode !== undefined && object.strikethroughNode !== null) - ? StrikethroughNode.fromPartial(object.strikethroughNode) - : undefined; - message.escapingCharacterNode = - (object.escapingCharacterNode !== undefined && object.escapingCharacterNode !== null) - ? EscapingCharacterNode.fromPartial(object.escapingCharacterNode) - : undefined; - message.mathNode = (object.mathNode !== undefined && object.mathNode !== null) - ? MathNode.fromPartial(object.mathNode) - : undefined; - message.highlightNode = (object.highlightNode !== undefined && object.highlightNode !== null) - ? HighlightNode.fromPartial(object.highlightNode) - : undefined; - message.subscriptNode = (object.subscriptNode !== undefined && object.subscriptNode !== null) - ? SubscriptNode.fromPartial(object.subscriptNode) - : undefined; - message.superscriptNode = (object.superscriptNode !== undefined && object.superscriptNode !== null) - ? SuperscriptNode.fromPartial(object.superscriptNode) - : undefined; - message.referencedContentNode = - (object.referencedContentNode !== undefined && object.referencedContentNode !== null) - ? ReferencedContentNode.fromPartial(object.referencedContentNode) - : undefined; - message.spoilerNode = (object.spoilerNode !== undefined && object.spoilerNode !== null) - ? SpoilerNode.fromPartial(object.spoilerNode) - : undefined; - message.htmlElementNode = (object.htmlElementNode !== undefined && object.htmlElementNode !== null) - ? HTMLElementNode.fromPartial(object.htmlElementNode) - : undefined; - return message; - }, -}; - -function createBaseLineBreakNode(): LineBreakNode { - return {}; -} - -export const LineBreakNode: MessageFns = { - encode(_: LineBreakNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): LineBreakNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseLineBreakNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): LineBreakNode { - return LineBreakNode.fromPartial(base ?? {}); - }, - fromPartial(_: DeepPartial): LineBreakNode { - const message = createBaseLineBreakNode(); - return message; - }, -}; - -function createBaseParagraphNode(): ParagraphNode { - return { children: [] }; -} - -export const ParagraphNode: MessageFns = { - encode(message: ParagraphNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - for (const v of message.children) { - Node.encode(v!, writer.uint32(10).fork()).join(); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): ParagraphNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseParagraphNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.children.push(Node.decode(reader, reader.uint32())); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): ParagraphNode { - return ParagraphNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): ParagraphNode { - const message = createBaseParagraphNode(); - message.children = object.children?.map((e) => Node.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseCodeBlockNode(): CodeBlockNode { - return { language: "", content: "" }; -} - -export const CodeBlockNode: MessageFns = { - encode(message: CodeBlockNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.language !== "") { - writer.uint32(10).string(message.language); - } - if (message.content !== "") { - writer.uint32(18).string(message.content); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): CodeBlockNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseCodeBlockNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.language = reader.string(); - continue; - } - case 2: { - if (tag !== 18) { - break; - } - - message.content = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): CodeBlockNode { - return CodeBlockNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): CodeBlockNode { - const message = createBaseCodeBlockNode(); - message.language = object.language ?? ""; - message.content = object.content ?? ""; - return message; - }, -}; - -function createBaseHeadingNode(): HeadingNode { - return { level: 0, children: [] }; -} - -export const HeadingNode: MessageFns = { - encode(message: HeadingNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.level !== 0) { - writer.uint32(8).int32(message.level); - } - for (const v of message.children) { - Node.encode(v!, writer.uint32(18).fork()).join(); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): HeadingNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseHeadingNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 8) { - break; - } - - message.level = reader.int32(); - continue; - } - case 2: { - if (tag !== 18) { - break; - } - - message.children.push(Node.decode(reader, reader.uint32())); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): HeadingNode { - return HeadingNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): HeadingNode { - const message = createBaseHeadingNode(); - message.level = object.level ?? 0; - message.children = object.children?.map((e) => Node.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseHorizontalRuleNode(): HorizontalRuleNode { - return { symbol: "" }; -} - -export const HorizontalRuleNode: MessageFns = { - encode(message: HorizontalRuleNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.symbol !== "") { - writer.uint32(10).string(message.symbol); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): HorizontalRuleNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseHorizontalRuleNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.symbol = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): HorizontalRuleNode { - return HorizontalRuleNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): HorizontalRuleNode { - const message = createBaseHorizontalRuleNode(); - message.symbol = object.symbol ?? ""; - return message; - }, -}; - -function createBaseBlockquoteNode(): BlockquoteNode { - return { children: [] }; -} - -export const BlockquoteNode: MessageFns = { - encode(message: BlockquoteNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - for (const v of message.children) { - Node.encode(v!, writer.uint32(10).fork()).join(); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): BlockquoteNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseBlockquoteNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.children.push(Node.decode(reader, reader.uint32())); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): BlockquoteNode { - return BlockquoteNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): BlockquoteNode { - const message = createBaseBlockquoteNode(); - message.children = object.children?.map((e) => Node.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseListNode(): ListNode { - return { kind: ListNode_Kind.KIND_UNSPECIFIED, indent: 0, children: [] }; -} - -export const ListNode: MessageFns = { - encode(message: ListNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.kind !== ListNode_Kind.KIND_UNSPECIFIED) { - writer.uint32(8).int32(listNode_KindToNumber(message.kind)); - } - if (message.indent !== 0) { - writer.uint32(16).int32(message.indent); - } - for (const v of message.children) { - Node.encode(v!, writer.uint32(26).fork()).join(); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): ListNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseListNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 8) { - break; - } - - message.kind = listNode_KindFromJSON(reader.int32()); - continue; - } - case 2: { - if (tag !== 16) { - break; - } - - message.indent = reader.int32(); - continue; - } - case 3: { - if (tag !== 26) { - break; - } - - message.children.push(Node.decode(reader, reader.uint32())); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): ListNode { - return ListNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): ListNode { - const message = createBaseListNode(); - message.kind = object.kind ?? ListNode_Kind.KIND_UNSPECIFIED; - message.indent = object.indent ?? 0; - message.children = object.children?.map((e) => Node.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseOrderedListItemNode(): OrderedListItemNode { - return { number: "", indent: 0, children: [] }; -} - -export const OrderedListItemNode: MessageFns = { - encode(message: OrderedListItemNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.number !== "") { - writer.uint32(10).string(message.number); - } - if (message.indent !== 0) { - writer.uint32(16).int32(message.indent); - } - for (const v of message.children) { - Node.encode(v!, writer.uint32(26).fork()).join(); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): OrderedListItemNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseOrderedListItemNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.number = reader.string(); - continue; - } - case 2: { - if (tag !== 16) { - break; - } - - message.indent = reader.int32(); - continue; - } - case 3: { - if (tag !== 26) { - break; - } - - message.children.push(Node.decode(reader, reader.uint32())); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): OrderedListItemNode { - return OrderedListItemNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): OrderedListItemNode { - const message = createBaseOrderedListItemNode(); - message.number = object.number ?? ""; - message.indent = object.indent ?? 0; - message.children = object.children?.map((e) => Node.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseUnorderedListItemNode(): UnorderedListItemNode { - return { symbol: "", indent: 0, children: [] }; -} - -export const UnorderedListItemNode: MessageFns = { - encode(message: UnorderedListItemNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.symbol !== "") { - writer.uint32(10).string(message.symbol); - } - if (message.indent !== 0) { - writer.uint32(16).int32(message.indent); - } - for (const v of message.children) { - Node.encode(v!, writer.uint32(26).fork()).join(); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): UnorderedListItemNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseUnorderedListItemNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.symbol = reader.string(); - continue; - } - case 2: { - if (tag !== 16) { - break; - } - - message.indent = reader.int32(); - continue; - } - case 3: { - if (tag !== 26) { - break; - } - - message.children.push(Node.decode(reader, reader.uint32())); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): UnorderedListItemNode { - return UnorderedListItemNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): UnorderedListItemNode { - const message = createBaseUnorderedListItemNode(); - message.symbol = object.symbol ?? ""; - message.indent = object.indent ?? 0; - message.children = object.children?.map((e) => Node.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseTaskListItemNode(): TaskListItemNode { - return { symbol: "", indent: 0, complete: false, children: [] }; -} - -export const TaskListItemNode: MessageFns = { - encode(message: TaskListItemNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.symbol !== "") { - writer.uint32(10).string(message.symbol); - } - if (message.indent !== 0) { - writer.uint32(16).int32(message.indent); - } - if (message.complete !== false) { - writer.uint32(24).bool(message.complete); - } - for (const v of message.children) { - Node.encode(v!, writer.uint32(34).fork()).join(); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): TaskListItemNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseTaskListItemNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.symbol = reader.string(); - continue; - } - case 2: { - if (tag !== 16) { - break; - } - - message.indent = reader.int32(); - continue; - } - case 3: { - if (tag !== 24) { - break; - } - - message.complete = reader.bool(); - continue; - } - case 4: { - if (tag !== 34) { - break; - } - - message.children.push(Node.decode(reader, reader.uint32())); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): TaskListItemNode { - return TaskListItemNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): TaskListItemNode { - const message = createBaseTaskListItemNode(); - message.symbol = object.symbol ?? ""; - message.indent = object.indent ?? 0; - message.complete = object.complete ?? false; - message.children = object.children?.map((e) => Node.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseMathBlockNode(): MathBlockNode { - return { content: "" }; -} - -export const MathBlockNode: MessageFns = { - encode(message: MathBlockNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.content !== "") { - writer.uint32(10).string(message.content); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): MathBlockNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseMathBlockNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.content = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): MathBlockNode { - return MathBlockNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): MathBlockNode { - const message = createBaseMathBlockNode(); - message.content = object.content ?? ""; - return message; - }, -}; - -function createBaseTableNode(): TableNode { - return { header: [], delimiter: [], rows: [] }; -} - -export const TableNode: MessageFns = { - encode(message: TableNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - for (const v of message.header) { - Node.encode(v!, writer.uint32(10).fork()).join(); - } - for (const v of message.delimiter) { - writer.uint32(18).string(v!); - } - for (const v of message.rows) { - TableNode_Row.encode(v!, writer.uint32(26).fork()).join(); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): TableNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseTableNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.header.push(Node.decode(reader, reader.uint32())); - continue; - } - case 2: { - if (tag !== 18) { - break; - } - - message.delimiter.push(reader.string()); - continue; - } - case 3: { - if (tag !== 26) { - break; - } - - message.rows.push(TableNode_Row.decode(reader, reader.uint32())); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): TableNode { - return TableNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): TableNode { - const message = createBaseTableNode(); - message.header = object.header?.map((e) => Node.fromPartial(e)) || []; - message.delimiter = object.delimiter?.map((e) => e) || []; - message.rows = object.rows?.map((e) => TableNode_Row.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseTableNode_Row(): TableNode_Row { - return { cells: [] }; -} - -export const TableNode_Row: MessageFns = { - encode(message: TableNode_Row, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - for (const v of message.cells) { - Node.encode(v!, writer.uint32(10).fork()).join(); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): TableNode_Row { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseTableNode_Row(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.cells.push(Node.decode(reader, reader.uint32())); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): TableNode_Row { - return TableNode_Row.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): TableNode_Row { - const message = createBaseTableNode_Row(); - message.cells = object.cells?.map((e) => Node.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseEmbeddedContentNode(): EmbeddedContentNode { - return { resourceName: "", params: "" }; -} - -export const EmbeddedContentNode: MessageFns = { - encode(message: EmbeddedContentNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.resourceName !== "") { - writer.uint32(10).string(message.resourceName); - } - if (message.params !== "") { - writer.uint32(18).string(message.params); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): EmbeddedContentNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseEmbeddedContentNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.resourceName = reader.string(); - continue; - } - case 2: { - if (tag !== 18) { - break; - } - - message.params = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): EmbeddedContentNode { - return EmbeddedContentNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): EmbeddedContentNode { - const message = createBaseEmbeddedContentNode(); - message.resourceName = object.resourceName ?? ""; - message.params = object.params ?? ""; - return message; - }, -}; - -function createBaseTextNode(): TextNode { - return { content: "" }; -} - -export const TextNode: MessageFns = { - encode(message: TextNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.content !== "") { - writer.uint32(10).string(message.content); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): TextNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseTextNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.content = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): TextNode { - return TextNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): TextNode { - const message = createBaseTextNode(); - message.content = object.content ?? ""; - return message; - }, -}; - -function createBaseBoldNode(): BoldNode { - return { symbol: "", children: [] }; -} - -export const BoldNode: MessageFns = { - encode(message: BoldNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.symbol !== "") { - writer.uint32(10).string(message.symbol); - } - for (const v of message.children) { - Node.encode(v!, writer.uint32(18).fork()).join(); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): BoldNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseBoldNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.symbol = reader.string(); - continue; - } - case 2: { - if (tag !== 18) { - break; - } - - message.children.push(Node.decode(reader, reader.uint32())); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): BoldNode { - return BoldNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): BoldNode { - const message = createBaseBoldNode(); - message.symbol = object.symbol ?? ""; - message.children = object.children?.map((e) => Node.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseItalicNode(): ItalicNode { - return { symbol: "", children: [] }; -} - -export const ItalicNode: MessageFns = { - encode(message: ItalicNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.symbol !== "") { - writer.uint32(10).string(message.symbol); - } - for (const v of message.children) { - Node.encode(v!, writer.uint32(18).fork()).join(); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): ItalicNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseItalicNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.symbol = reader.string(); - continue; - } - case 2: { - if (tag !== 18) { - break; - } - - message.children.push(Node.decode(reader, reader.uint32())); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): ItalicNode { - return ItalicNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): ItalicNode { - const message = createBaseItalicNode(); - message.symbol = object.symbol ?? ""; - message.children = object.children?.map((e) => Node.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseBoldItalicNode(): BoldItalicNode { - return { symbol: "", content: "" }; -} - -export const BoldItalicNode: MessageFns = { - encode(message: BoldItalicNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.symbol !== "") { - writer.uint32(10).string(message.symbol); - } - if (message.content !== "") { - writer.uint32(18).string(message.content); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): BoldItalicNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseBoldItalicNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.symbol = reader.string(); - continue; - } - case 2: { - if (tag !== 18) { - break; - } - - message.content = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): BoldItalicNode { - return BoldItalicNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): BoldItalicNode { - const message = createBaseBoldItalicNode(); - message.symbol = object.symbol ?? ""; - message.content = object.content ?? ""; - return message; - }, -}; - -function createBaseCodeNode(): CodeNode { - return { content: "" }; -} - -export const CodeNode: MessageFns = { - encode(message: CodeNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.content !== "") { - writer.uint32(10).string(message.content); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): CodeNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseCodeNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.content = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): CodeNode { - return CodeNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): CodeNode { - const message = createBaseCodeNode(); - message.content = object.content ?? ""; - return message; - }, -}; - -function createBaseImageNode(): ImageNode { - return { altText: "", url: "" }; -} - -export const ImageNode: MessageFns = { - encode(message: ImageNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.altText !== "") { - writer.uint32(10).string(message.altText); - } - if (message.url !== "") { - writer.uint32(18).string(message.url); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): ImageNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseImageNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.altText = reader.string(); - continue; - } - case 2: { - if (tag !== 18) { - break; - } - - message.url = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): ImageNode { - return ImageNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): ImageNode { - const message = createBaseImageNode(); - message.altText = object.altText ?? ""; - message.url = object.url ?? ""; - return message; - }, -}; - -function createBaseLinkNode(): LinkNode { - return { content: [], url: "" }; -} - -export const LinkNode: MessageFns = { - encode(message: LinkNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - for (const v of message.content) { - Node.encode(v!, writer.uint32(10).fork()).join(); - } - if (message.url !== "") { - writer.uint32(18).string(message.url); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): LinkNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseLinkNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.content.push(Node.decode(reader, reader.uint32())); - continue; - } - case 2: { - if (tag !== 18) { - break; - } - - message.url = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): LinkNode { - return LinkNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): LinkNode { - const message = createBaseLinkNode(); - message.content = object.content?.map((e) => Node.fromPartial(e)) || []; - message.url = object.url ?? ""; - return message; - }, -}; - -function createBaseAutoLinkNode(): AutoLinkNode { - return { url: "", isRawText: false }; -} - -export const AutoLinkNode: MessageFns = { - encode(message: AutoLinkNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.url !== "") { - writer.uint32(10).string(message.url); - } - if (message.isRawText !== false) { - writer.uint32(16).bool(message.isRawText); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): AutoLinkNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseAutoLinkNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.url = reader.string(); - continue; - } - case 2: { - if (tag !== 16) { - break; - } - - message.isRawText = reader.bool(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): AutoLinkNode { - return AutoLinkNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): AutoLinkNode { - const message = createBaseAutoLinkNode(); - message.url = object.url ?? ""; - message.isRawText = object.isRawText ?? false; - return message; - }, -}; - -function createBaseTagNode(): TagNode { - return { content: "" }; -} - -export const TagNode: MessageFns = { - encode(message: TagNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.content !== "") { - writer.uint32(10).string(message.content); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): TagNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseTagNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.content = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): TagNode { - return TagNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): TagNode { - const message = createBaseTagNode(); - message.content = object.content ?? ""; - return message; - }, -}; - -function createBaseStrikethroughNode(): StrikethroughNode { - return { content: "" }; -} - -export const StrikethroughNode: MessageFns = { - encode(message: StrikethroughNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.content !== "") { - writer.uint32(10).string(message.content); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): StrikethroughNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseStrikethroughNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.content = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): StrikethroughNode { - return StrikethroughNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): StrikethroughNode { - const message = createBaseStrikethroughNode(); - message.content = object.content ?? ""; - return message; - }, -}; - -function createBaseEscapingCharacterNode(): EscapingCharacterNode { - return { symbol: "" }; -} - -export const EscapingCharacterNode: MessageFns = { - encode(message: EscapingCharacterNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.symbol !== "") { - writer.uint32(10).string(message.symbol); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): EscapingCharacterNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseEscapingCharacterNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.symbol = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): EscapingCharacterNode { - return EscapingCharacterNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): EscapingCharacterNode { - const message = createBaseEscapingCharacterNode(); - message.symbol = object.symbol ?? ""; - return message; - }, -}; - -function createBaseMathNode(): MathNode { - return { content: "" }; -} - -export const MathNode: MessageFns = { - encode(message: MathNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.content !== "") { - writer.uint32(10).string(message.content); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): MathNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseMathNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.content = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): MathNode { - return MathNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): MathNode { - const message = createBaseMathNode(); - message.content = object.content ?? ""; - return message; - }, -}; - -function createBaseHighlightNode(): HighlightNode { - return { content: "" }; -} - -export const HighlightNode: MessageFns = { - encode(message: HighlightNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.content !== "") { - writer.uint32(10).string(message.content); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): HighlightNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseHighlightNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.content = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): HighlightNode { - return HighlightNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): HighlightNode { - const message = createBaseHighlightNode(); - message.content = object.content ?? ""; - return message; - }, -}; - -function createBaseSubscriptNode(): SubscriptNode { - return { content: "" }; -} - -export const SubscriptNode: MessageFns = { - encode(message: SubscriptNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.content !== "") { - writer.uint32(10).string(message.content); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): SubscriptNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseSubscriptNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.content = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): SubscriptNode { - return SubscriptNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): SubscriptNode { - const message = createBaseSubscriptNode(); - message.content = object.content ?? ""; - return message; - }, -}; - -function createBaseSuperscriptNode(): SuperscriptNode { - return { content: "" }; -} - -export const SuperscriptNode: MessageFns = { - encode(message: SuperscriptNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.content !== "") { - writer.uint32(10).string(message.content); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): SuperscriptNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseSuperscriptNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.content = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): SuperscriptNode { - return SuperscriptNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): SuperscriptNode { - const message = createBaseSuperscriptNode(); - message.content = object.content ?? ""; - return message; - }, -}; - -function createBaseReferencedContentNode(): ReferencedContentNode { - return { resourceName: "", params: "" }; -} - -export const ReferencedContentNode: MessageFns = { - encode(message: ReferencedContentNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.resourceName !== "") { - writer.uint32(10).string(message.resourceName); - } - if (message.params !== "") { - writer.uint32(18).string(message.params); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): ReferencedContentNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseReferencedContentNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.resourceName = reader.string(); - continue; - } - case 2: { - if (tag !== 18) { - break; - } - - message.params = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): ReferencedContentNode { - return ReferencedContentNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): ReferencedContentNode { - const message = createBaseReferencedContentNode(); - message.resourceName = object.resourceName ?? ""; - message.params = object.params ?? ""; - return message; - }, -}; - -function createBaseSpoilerNode(): SpoilerNode { - return { content: "" }; -} - -export const SpoilerNode: MessageFns = { - encode(message: SpoilerNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.content !== "") { - writer.uint32(10).string(message.content); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): SpoilerNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseSpoilerNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.content = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): SpoilerNode { - return SpoilerNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): SpoilerNode { - const message = createBaseSpoilerNode(); - message.content = object.content ?? ""; - return message; - }, -}; - -function createBaseHTMLElementNode(): HTMLElementNode { - return { tagName: "", attributes: {}, children: [], isSelfClosing: false }; -} - -export const HTMLElementNode: MessageFns = { - encode(message: HTMLElementNode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.tagName !== "") { - writer.uint32(10).string(message.tagName); - } - Object.entries(message.attributes).forEach(([key, value]) => { - HTMLElementNode_AttributesEntry.encode({ key: key as any, value }, writer.uint32(18).fork()).join(); - }); - for (const v of message.children) { - Node.encode(v!, writer.uint32(26).fork()).join(); - } - if (message.isSelfClosing !== false) { - writer.uint32(32).bool(message.isSelfClosing); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): HTMLElementNode { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseHTMLElementNode(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.tagName = reader.string(); - continue; - } - case 2: { - if (tag !== 18) { - break; - } - - const entry2 = HTMLElementNode_AttributesEntry.decode(reader, reader.uint32()); - if (entry2.value !== undefined) { - message.attributes[entry2.key] = entry2.value; - } - continue; - } - case 3: { - if (tag !== 26) { - break; - } - - message.children.push(Node.decode(reader, reader.uint32())); - continue; - } - case 4: { - if (tag !== 32) { - break; - } - - message.isSelfClosing = reader.bool(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): HTMLElementNode { - return HTMLElementNode.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): HTMLElementNode { - const message = createBaseHTMLElementNode(); - message.tagName = object.tagName ?? ""; - message.attributes = Object.entries(object.attributes ?? {}).reduce<{ [key: string]: string }>( - (acc, [key, value]) => { - if (value !== undefined) { - acc[key] = globalThis.String(value); - } - return acc; - }, - {}, - ); - message.children = object.children?.map((e) => Node.fromPartial(e)) || []; - message.isSelfClosing = object.isSelfClosing ?? false; - return message; - }, -}; - -function createBaseHTMLElementNode_AttributesEntry(): HTMLElementNode_AttributesEntry { - return { key: "", value: "" }; -} - -export const HTMLElementNode_AttributesEntry: MessageFns = { - encode(message: HTMLElementNode_AttributesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { - if (message.key !== "") { - writer.uint32(10).string(message.key); - } - if (message.value !== "") { - writer.uint32(18).string(message.value); - } - return writer; - }, - - decode(input: BinaryReader | Uint8Array, length?: number): HTMLElementNode_AttributesEntry { - const reader = input instanceof BinaryReader ? input : new BinaryReader(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseHTMLElementNode_AttributesEntry(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: { - if (tag !== 10) { - break; - } - - message.key = reader.string(); - continue; - } - case 2: { - if (tag !== 18) { - break; - } - - message.value = reader.string(); - continue; - } - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skip(tag & 7); - } - return message; - }, - - create(base?: DeepPartial): HTMLElementNode_AttributesEntry { - return HTMLElementNode_AttributesEntry.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): HTMLElementNode_AttributesEntry { - const message = createBaseHTMLElementNode_AttributesEntry(); - message.key = object.key ?? ""; - message.value = object.value ?? ""; - return message; - }, -}; - -export type MarkdownServiceDefinition = typeof MarkdownServiceDefinition; -export const MarkdownServiceDefinition = { - name: "MarkdownService", - fullName: "memos.api.v1.MarkdownService", - methods: { - /** - * ParseMarkdown parses the given markdown content and returns a list of nodes. - * This is a utility method that transforms markdown text into structured nodes. - */ - parseMarkdown: { - name: "ParseMarkdown", - requestType: ParseMarkdownRequest, - requestStream: false, - responseType: ParseMarkdownResponse, - responseStream: false, - options: { - _unknownFields: { - 578365826: [ - new Uint8Array([ - 27, - 58, - 1, - 42, - 34, - 22, - 47, - 97, - 112, - 105, - 47, - 118, - 49, - 47, - 109, - 97, - 114, - 107, - 100, - 111, - 119, - 110, - 58, - 112, - 97, - 114, - 115, - 101, - ]), - ], - }, - }, - }, - /** - * RestoreMarkdownNodes restores the given nodes to markdown content. - * This is the inverse operation of ParseMarkdown. - */ - restoreMarkdownNodes: { - name: "RestoreMarkdownNodes", - requestType: RestoreMarkdownNodesRequest, - requestStream: false, - responseType: RestoreMarkdownNodesResponse, - responseStream: false, - options: { - _unknownFields: { - 578365826: [ - new Uint8Array([ - 29, - 58, - 1, - 42, - 34, - 24, - 47, - 97, - 112, - 105, - 47, - 118, - 49, - 47, - 109, - 97, - 114, - 107, - 100, - 111, - 119, - 110, - 58, - 114, - 101, - 115, - 116, - 111, - 114, - 101, - ]), - ], - }, - }, - }, - /** - * StringifyMarkdownNodes stringify the given nodes to plain text content. - * This removes all markdown formatting and returns plain text. - */ - stringifyMarkdownNodes: { - name: "StringifyMarkdownNodes", - requestType: StringifyMarkdownNodesRequest, - requestStream: false, - responseType: StringifyMarkdownNodesResponse, - responseStream: false, - options: { - _unknownFields: { - 578365826: [ - new Uint8Array([ - 31, - 58, - 1, - 42, - 34, - 26, - 47, - 97, - 112, - 105, - 47, - 118, - 49, - 47, - 109, - 97, - 114, - 107, - 100, - 111, - 119, - 110, - 58, - 115, - 116, - 114, - 105, - 110, - 103, - 105, - 102, - 121, - ]), - ], - }, - }, - }, - /** - * GetLinkMetadata returns metadata for a given link. - * This is useful for generating link previews. - */ - getLinkMetadata: { - name: "GetLinkMetadata", - requestType: GetLinkMetadataRequest, - requestStream: false, - responseType: LinkMetadata, - responseStream: false, - options: { - _unknownFields: { - 578365826: [ - new Uint8Array([ - 36, - 18, - 34, - 47, - 97, - 112, - 105, - 47, - 118, - 49, - 47, - 109, - 97, - 114, - 107, - 100, - 111, - 119, - 110, - 47, - 108, - 105, - 110, - 107, - 115, - 58, - 103, - 101, - 116, - 77, - 101, - 116, - 97, - 100, - 97, - 116, - 97, - ]), - ], - }, - }, - }, - }, -} as const; - -type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; - -export type DeepPartial = T extends Builtin ? T - : T extends globalThis.Array ? globalThis.Array> - : T extends ReadonlyArray ? ReadonlyArray> - : T extends {} ? { [K in keyof T]?: DeepPartial } - : Partial; - -export interface MessageFns { - encode(message: T, writer?: BinaryWriter): BinaryWriter; - decode(input: BinaryReader | Uint8Array, length?: number): T; - create(base?: DeepPartial): T; - fromPartial(object: DeepPartial): T; -} diff --git a/web/src/types/proto/api/v1/memo_service.ts b/web/src/types/proto/api/v1/memo_service.ts index 9eabbcb1c..3b401bf76 100644 --- a/web/src/types/proto/api/v1/memo_service.ts +++ b/web/src/types/proto/api/v1/memo_service.ts @@ -11,7 +11,6 @@ import { FieldMask } from "../../google/protobuf/field_mask"; import { Timestamp } from "../../google/protobuf/timestamp"; import { Attachment } from "./attachment_service"; import { State, stateFromJSON, stateToNumber } from "./common"; -import { Node } from "./markdown_service"; export const protobufPackage = "memos.api.v1"; @@ -110,8 +109,6 @@ export interface Memo { | undefined; /** Required. The content of the memo in Markdown format. */ content: string; - /** Output only. The parsed nodes from the content. */ - nodes: Node[]; /** The visibility of the memo. */ visibility: Visibility; /** Output only. The tags extracted from the content. */ @@ -587,7 +584,6 @@ function createBaseMemo(): Memo { updateTime: undefined, displayTime: undefined, content: "", - nodes: [], visibility: Visibility.VISIBILITY_UNSPECIFIED, tags: [], pinned: false, @@ -624,9 +620,6 @@ export const Memo: MessageFns = { if (message.content !== "") { writer.uint32(58).string(message.content); } - for (const v of message.nodes) { - Node.encode(v!, writer.uint32(66).fork()).join(); - } if (message.visibility !== Visibility.VISIBILITY_UNSPECIFIED) { writer.uint32(72).int32(visibilityToNumber(message.visibility)); } @@ -723,14 +716,6 @@ export const Memo: MessageFns = { message.content = reader.string(); continue; } - case 8: { - if (tag !== 66) { - break; - } - - message.nodes.push(Node.decode(reader, reader.uint32())); - continue; - } case 9: { if (tag !== 72) { break; @@ -832,7 +817,6 @@ export const Memo: MessageFns = { message.updateTime = object.updateTime ?? undefined; message.displayTime = object.displayTime ?? undefined; message.content = object.content ?? ""; - message.nodes = object.nodes?.map((e) => Node.fromPartial(e)) || []; message.visibility = object.visibility ?? Visibility.VISIBILITY_UNSPECIFIED; message.tags = object.tags?.map((e) => e) || []; message.pinned = object.pinned ?? false; diff --git a/web/src/utils/markdown-list-detection.ts b/web/src/utils/markdown-list-detection.ts new file mode 100644 index 000000000..f143adaa6 --- /dev/null +++ b/web/src/utils/markdown-list-detection.ts @@ -0,0 +1,83 @@ +/** + * Utilities for detecting list patterns in markdown text + * + * Used by the editor for auto-continuation of lists when user presses Enter + */ + +export interface ListItemInfo { + type: "task" | "unordered" | "ordered" | null; + symbol?: string; // For task/unordered lists: "- ", "* ", "+ " + number?: number; // For ordered lists: 1, 2, 3, etc. + indent?: string; // Leading whitespace +} + +/** + * Detect the list item type of the last line before cursor + * + * @param contentBeforeCursor - Markdown content from start to cursor position + * @returns List item information, or null if not a list item + */ +export function detectLastListItem(contentBeforeCursor: string): ListItemInfo { + const lines = contentBeforeCursor.split("\n"); + const lastLine = lines[lines.length - 1]; + + // Extract indentation + const indentMatch = lastLine.match(/^(\s*)/); + const indent = indentMatch ? indentMatch[1] : ""; + + // Task list: - [ ] or - [x] or - [X] + const taskMatch = lastLine.match(/^(\s*)([-*+])\s+\[([ xX])\]\s+/); + if (taskMatch) { + return { + type: "task", + symbol: taskMatch[2], // -, *, or + + indent, + }; + } + + // Unordered list: - foo or * foo or + foo + const unorderedMatch = lastLine.match(/^(\s*)([-*+])\s+/); + if (unorderedMatch) { + return { + type: "unordered", + symbol: unorderedMatch[2], + indent, + }; + } + + // Ordered list: 1. foo or 2) foo + const orderedMatch = lastLine.match(/^(\s*)(\d+)[.)]\s+/); + if (orderedMatch) { + return { + type: "ordered", + number: parseInt(orderedMatch[2]), + indent, + }; + } + + return { + type: null, + indent, + }; +} + +/** + * Generate the text to insert when pressing Enter on a list item + * + * @param listInfo - Information about the current list item + * @returns Text to insert at cursor + */ +export function generateListContinuation(listInfo: ListItemInfo): string { + const indent = listInfo.indent || ""; + + switch (listInfo.type) { + case "task": + return `${indent}${listInfo.symbol} [ ] `; + case "unordered": + return `${indent}${listInfo.symbol} `; + case "ordered": + return `${indent}${(listInfo.number || 0) + 1}. `; + default: + return indent; + } +} diff --git a/web/src/utils/markdown-manipulation.ts b/web/src/utils/markdown-manipulation.ts new file mode 100644 index 000000000..72ed684e4 --- /dev/null +++ b/web/src/utils/markdown-manipulation.ts @@ -0,0 +1,211 @@ +/** + * Utilities for manipulating markdown strings (GitHub-style approach) + * + * These functions modify the raw markdown text directly without parsing to AST. + * This is the same approach GitHub uses for task list updates. + */ + +/** + * Toggle a task checkbox at a specific line number + * + * @param markdown - The full markdown content + * @param lineNumber - Zero-based line number + * @param checked - New checked state + * @returns Updated markdown string + */ +export function toggleTaskAtLine(markdown: string, lineNumber: number, checked: boolean): string { + const lines = markdown.split("\n"); + + if (lineNumber < 0 || lineNumber >= lines.length) { + return markdown; + } + + const line = lines[lineNumber]; + + // Match task list patterns: - [ ], - [x], - [X], etc. + const taskPattern = /^(\s*[-*+]\s+)\[([ xX])\](\s+.*)$/; + const match = line.match(taskPattern); + + if (!match) { + // Not a task list item + return markdown; + } + + const [, prefix, , suffix] = match; + const newCheckmark = checked ? "x" : " "; + lines[lineNumber] = `${prefix}[${newCheckmark}]${suffix}`; + + return lines.join("\n"); +} + +/** + * Toggle a task checkbox by its index (nth task in the document) + * + * @param markdown - The full markdown content + * @param taskIndex - Zero-based index of the task (0 = first task, 1 = second task, etc.) + * @param checked - New checked state + * @returns Updated markdown string + */ +export function toggleTaskAtIndex(markdown: string, taskIndex: number, checked: boolean): string { + const lines = markdown.split("\n"); + const taskPattern = /^(\s*[-*+]\s+)\[([ xX])\](\s+.*)$/; + + let currentTaskIndex = 0; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const match = line.match(taskPattern); + + if (match) { + if (currentTaskIndex === taskIndex) { + const [, prefix, , suffix] = match; + const newCheckmark = checked ? "x" : " "; + lines[i] = `${prefix}[${newCheckmark}]${suffix}`; + break; + } + currentTaskIndex++; + } + } + + return lines.join("\n"); +} + +/** + * Remove all completed tasks from markdown + * + * @param markdown - The full markdown content + * @returns Markdown with completed tasks removed + */ +export function removeCompletedTasks(markdown: string): string { + const lines = markdown.split("\n"); + const completedTaskPattern = /^(\s*[-*+]\s+)\[([xX])\](\s+.*)$/; + const result: string[] = []; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + // Skip completed tasks + if (completedTaskPattern.test(line)) { + // Also skip the following line if it's empty (preserve spacing) + if (i + 1 < lines.length && lines[i + 1].trim() === "") { + i++; // Skip next line + } + continue; + } + + result.push(line); + } + + return result.join("\n"); +} + +/** + * Count tasks in markdown + * + * @param markdown - The full markdown content + * @returns Object with task counts + */ +export function countTasks(markdown: string): { + total: number; + completed: number; + incomplete: number; +} { + const lines = markdown.split("\n"); + const taskPattern = /^(\s*[-*+]\s+)\[([ xX])\](\s+.*)$/; + + let total = 0; + let completed = 0; + + for (const line of lines) { + const match = line.match(taskPattern); + if (match) { + total++; + const checkmark = match[2]; + if (checkmark.toLowerCase() === "x") { + completed++; + } + } + } + + return { + total, + completed, + incomplete: total - completed, + }; +} + +/** + * Check if markdown has any completed tasks + * + * @param markdown - The full markdown content + * @returns True if there are completed tasks + */ +export function hasCompletedTasks(markdown: string): boolean { + const completedTaskPattern = /^(\s*[-*+]\s+)\[([xX])\](\s+.*)$/m; + return completedTaskPattern.test(markdown); +} + +/** + * Get the line number of the nth task + * + * @param markdown - The full markdown content + * @param taskIndex - Zero-based task index + * @returns Line number, or -1 if not found + */ +export function getTaskLineNumber(markdown: string, taskIndex: number): number { + const lines = markdown.split("\n"); + const taskPattern = /^(\s*[-*+]\s+)\[([ xX])\](\s+.*)$/; + + let currentTaskIndex = 0; + + for (let i = 0; i < lines.length; i++) { + if (taskPattern.test(lines[i])) { + if (currentTaskIndex === taskIndex) { + return i; + } + currentTaskIndex++; + } + } + + return -1; +} + +/** + * Extract all task items with their metadata + * + * @param markdown - The full markdown content + * @returns Array of task metadata + */ +export interface TaskItem { + lineNumber: number; + taskIndex: number; + checked: boolean; + content: string; + indentation: number; +} + +export function extractTasks(markdown: string): TaskItem[] { + const lines = markdown.split("\n"); + const taskPattern = /^(\s*)([-*+]\s+)\[([ xX])\](\s+.*)$/; + const tasks: TaskItem[] = []; + + let taskIndex = 0; + + for (let lineNumber = 0; lineNumber < lines.length; lineNumber++) { + const line = lines[lineNumber]; + const match = line.match(taskPattern); + + if (match) { + const [, indentStr, , checkmark, content] = match; + tasks.push({ + lineNumber, + taskIndex: taskIndex++, + checked: checkmark.toLowerCase() === "x", + content: content.trim(), + indentation: indentStr.length, + }); + } + } + + return tasks; +} diff --git a/web/src/utils/remark-plugins/remark-preserve-type.ts b/web/src/utils/remark-plugins/remark-preserve-type.ts new file mode 100644 index 000000000..83e7d8207 --- /dev/null +++ b/web/src/utils/remark-plugins/remark-preserve-type.ts @@ -0,0 +1,32 @@ +import type { Root } from "mdast"; +import { visit } from "unist-util-visit"; + +/** + * Remark plugin to preserve original mdast node types in the data field + * + * This allows us to check the original node type even after + * transformation to hast (HTML AST). + * + * The original type is stored in data.mdastType and will be available + * in the hast node as data.mdastType. + */ +export const remarkPreserveType = () => { + return (tree: Root) => { + visit(tree, (node: any) => { + // Skip text nodes and standard element types + if (node.type === "text" || node.type === "root") { + return; + } + + // Preserve the original mdast type in data + if (!node.data) { + node.data = {}; + } + + // Store original type for custom node types + if (node.type !== "paragraph" && node.type !== "heading" && node.type !== "list" && node.type !== "listItem") { + node.data.mdastType = node.type; + } + }); + }; +}; diff --git a/web/src/utils/remark-plugins/remark-tag.ts b/web/src/utils/remark-plugins/remark-tag.ts new file mode 100644 index 000000000..a051dc163 --- /dev/null +++ b/web/src/utils/remark-plugins/remark-tag.ts @@ -0,0 +1,124 @@ +import type { Root, Text } from "mdast"; +import { visit } from "unist-util-visit"; + +/** + * Custom remark plugin for #tag syntax + * + * Parses #tag patterns in text nodes and converts them to HTML nodes. + * This matches the goldmark backend TagNode implementation. + * + * Examples: + * #work → #work + * #2024_plans → #2024_plans + * #work-notes → #work-notes + * + * Rules: + * - Tag must start with # followed by alphanumeric, underscore, or hyphen + * - Tag ends at whitespace, punctuation (except - and _), or end of line + * - Tags at start of line after ## are headings, not tags + */ + +/** + * Check if character is valid for tag content + */ +function isTagChar(char: string): boolean { + return /[a-zA-Z0-9_-]/.test(char); +} + +/** + * Parse tags from text and return segments + */ +function parseTagsFromText(text: string): Array<{ type: "text" | "tag"; value: string }> { + const segments: Array<{ type: "text" | "tag"; value: string }> = []; + let i = 0; + + while (i < text.length) { + // Check for tag pattern + if (text[i] === "#" && i + 1 < text.length && isTagChar(text[i + 1])) { + // Check if this might be a heading (## at start or after whitespace) + const prevChar = i > 0 ? text[i - 1] : ""; + const nextChar = i + 1 < text.length ? text[i + 1] : ""; + + if (prevChar === "#" || nextChar === "#" || nextChar === " ") { + // This is a heading, not a tag + segments.push({ type: "text", value: text[i] }); + i++; + continue; + } + + // Extract tag content + let j = i + 1; + while (j < text.length && isTagChar(text[j])) { + j++; + } + + const tagContent = text.slice(i + 1, j); + + // Validate tag length + if (tagContent.length > 0 && tagContent.length <= 100) { + segments.push({ type: "tag", value: tagContent }); + i = j; + continue; + } + } + + // Regular text + let j = i + 1; + while (j < text.length && text[j] !== "#") { + j++; + } + segments.push({ type: "text", value: text.slice(i, j) }); + i = j; + } + + return segments; +} + +/** + * Remark plugin to parse #tag syntax + */ +export const remarkTag = () => { + return (tree: Root) => { + visit(tree, "text", (node: Text, index, parent) => { + if (!parent || index === null) return; + + const text = node.value; + const segments = parseTagsFromText(text); + + // If no tags found, leave node as-is + if (segments.every((seg) => seg.type === "text")) { + return; + } + + // Replace text node with multiple nodes (text + tag nodes) + const newNodes = segments.map((segment) => { + if (segment.type === "tag") { + // Create a custom mdast node that remark-rehype will convert to + // This allows ReactMarkdown's component mapping (span: Tag) to work + return { + type: "tagNode" as any, + value: segment.value, + data: { + hName: "span", + hProperties: { + className: "tag", + "data-tag": segment.value, + }, + hChildren: [{ type: "text", value: `#${segment.value}` }], + }, + }; + } else { + // Keep as text node + return { + type: "text" as const, + value: segment.value, + }; + } + }); + + // Replace the current node with the new nodes + // @ts-expect-error - mdast types are complex, this is safe + parent.children.splice(index, 1, ...newNodes); + }); + }; +}; diff --git a/web/vite.config.mts b/web/vite.config.mts index d39adc157..b515b3cef 100644 --- a/web/vite.config.mts +++ b/web/vite.config.mts @@ -1,5 +1,4 @@ import react from "@vitejs/plugin-react"; -import { codeInspectorPlugin } from "code-inspector-plugin"; import { resolve } from "path"; import { defineConfig } from "vite"; import tailwindcss from "@tailwindcss/vite"; @@ -12,13 +11,7 @@ if (process.env.DEV_PROXY_SERVER && process.env.DEV_PROXY_SERVER.length > 0) { // https://vitejs.dev/config/ export default defineConfig({ - plugins: [ - react(), - tailwindcss(), - codeInspectorPlugin({ - bundler: "vite", - }), - ], + plugins: [react(), tailwindcss()], server: { host: "0.0.0.0", port: 3001,