diff --git a/.gitignore b/.gitignore index a3c9ad4..e1ad394 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ adminmailer/adminmailer local/backups /tmp *.exe +.DS_Store diff --git a/public/parsing.wasm b/public/parsing.wasm index 202bf53..4090682 100755 Binary files a/public/parsing.wasm and b/public/parsing.wasm differ diff --git a/public/style.css b/public/style.css index 913e780..a486da1 100644 --- a/public/style.css +++ b/public/style.css @@ -1176,7 +1176,7 @@ img, video { .br2, .post-content code, .post-content pre > code, .post-content pre.hmn-code { border-radius: 0.25rem; } -.br3, .edu-article .edu-resource { +.br3, figure { border-radius: 0.5rem; } .br4 { @@ -4602,7 +4602,7 @@ code, .code { .pa2, .tab, header .root-item > a, header .submenu > a { padding: 0.5rem; } -.pa3, .edu-article .edu-resource, header #login-popup { +.pa3, header #login-popup { padding: 1rem; } .pa4 { @@ -6353,7 +6353,7 @@ input[type=submit]:not(.button-small), .notice { .f3 { font-size: 1.5rem; } -.f4, .edu-article .resource-title { +.f4 { font-size: 1.25rem; } .f5 { @@ -7426,7 +7426,7 @@ article code { border-color: #ccc; border-color: var(--theme-color-dimmest); } -.bg--dim, .post-content code, .post-content pre > code, .post-content pre.hmn-code, .edu-article .edu-resource { +.bg--dim, .post-content code, .post-content pre > code, .post-content pre.hmn-code, figure { background-color: #f0f0f0; background-color: var(--dim-background); } @@ -8026,6 +8026,14 @@ pre { padding: 0.7em; overflow-x: auto; } +figure { + display: flex; + flex-direction: column; + margin: 1rem 0; + padding: 1rem 1rem 0; } + figure figcaption { + padding: 0.5rem 0; } + .toolbar { background-color: #fff; background-color: var(--editor-toolbar-background); @@ -8301,9 +8309,6 @@ nav.timecodes { text-align: center; margin: 10px 0; } -.edu-article .resource-title { - font-weight: bold; } - .edu-article .note { color: red; } diff --git a/src/parsing/embed.go b/src/parsing/embed.go index 66d633d..760085d 100644 --- a/src/parsing/embed.go +++ b/src/parsing/embed.go @@ -5,7 +5,6 @@ import ( "github.com/yuin/goldmark" "github.com/yuin/goldmark/ast" - gast "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/renderer" "github.com/yuin/goldmark/renderer/html" @@ -23,6 +22,26 @@ var ( // TODO: Tweets ) +// Returns the HTML used to embed the given url, or false if the media cannot be embedded. +// +// The provided string need only start with the URL; if there is trailing content after the URL, it +// will be ignored. +func htmlForURLEmbed(url string, preview bool) (string, []byte, bool) { + if match := extract(REYoutubeLong, []byte(url), "vid"); match != nil { + return makeYoutubeEmbed(string(match), preview), match, true + } else if match := extract(REYoutubeShort, []byte(url), "vid"); match != nil { + return makeYoutubeEmbed(string(match), preview), match, true + } else if match := extract(REVimeo, []byte(url), "vid"); match != nil { + return previewOrLegitEmbed("Vimeo", ` +
+ +
+ `, preview), match, true + } + + return "", nil, false +} + // ---------------------- // Parser and delimiters // ---------------------- @@ -40,30 +59,13 @@ func (s embedParser) Trigger() []byte { func (s embedParser) Open(parent ast.Node, reader text.Reader, pc parser.Context) (ast.Node, parser.State) { restOfLine, _ := reader.PeekLine() - html := "" - var match []byte - if ytLongMatch := extract(REYoutubeLong, restOfLine, "vid"); ytLongMatch != nil { - match = ytLongMatch - html = makeYoutubeEmbed(string(ytLongMatch), s.Preview) - } else if ytShortMatch := extract(REYoutubeShort, restOfLine, "vid"); ytShortMatch != nil { - match = ytShortMatch - html = makeYoutubeEmbed(string(ytShortMatch), s.Preview) - } else if vimeoMatch := extract(REVimeo, restOfLine, "vid"); vimeoMatch != nil { - match = vimeoMatch - html = s.previewOrLegitEmbed("Vimeo", ` -
-
- -
-
`) - } - - if html == "" { + if html, match, ok := htmlForURLEmbed(string(restOfLine), s.Preview); ok { + html = `
` + html + `
` + reader.Advance(len(match)) + return NewEmbed(html), parser.NoChildren + } else { return nil, parser.NoChildren } - - reader.Advance(len(match)) - return NewEmbed(html), parser.NoChildren } func (s embedParser) Continue(node ast.Node, reader text.Reader, pc parser.Context) parser.State { @@ -80,37 +82,29 @@ func (s embedParser) CanAcceptIndentedLine() bool { return false } -func (s embedParser) previewOrLegitEmbed(name string, legitHtml string) string { - if s.Preview { +func previewOrLegitEmbed(name, legitHTML string, preview bool) string { + if preview { return ` -
-
-
- ` + name + ` embed -
-
-
-` +
+
+ ` + name + ` embed +
+
+ ` + } else { + return legitHTML } - - return legitHtml } func makeYoutubeEmbed(vid string, preview bool) string { if preview { - return ` -
- -
-` + return `` } else { return ` -
-
- -
-
-` +
+ +
+ ` } } @@ -119,21 +113,21 @@ func makeYoutubeEmbed(vid string, preview bool) string { // ---------------------- type EmbedNode struct { - gast.BaseBlock + ast.BaseBlock HTML string } func (n *EmbedNode) Dump(source []byte, level int) { - gast.DumpHelper(n, source, level, nil, nil) + ast.DumpHelper(n, source, level, nil, nil) } -var KindEmbed = gast.NewNodeKind("Embed") +var KindEmbed = ast.NewNodeKind("Embed") -func (n *EmbedNode) Kind() gast.NodeKind { +func (n *EmbedNode) Kind() ast.NodeKind { return KindEmbed } -func NewEmbed(HTML string) gast.Node { +func NewEmbed(HTML string) ast.Node { return &EmbedNode{ HTML: HTML, } @@ -161,11 +155,11 @@ func (r *EmbedHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegistere reg.Register(KindEmbed, r.renderEmbed) } -func (r *EmbedHTMLRenderer) renderEmbed(w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) { +func (r *EmbedHTMLRenderer) renderEmbed(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { if entering { w.WriteString(n.(*EmbedNode).HTML) } - return gast.WalkSkipChildren, nil + return ast.WalkSkipChildren, nil } // ---------------------- diff --git a/src/parsing/ggcode.go b/src/parsing/ggcode.go index d0c46eb..25b3290 100644 --- a/src/parsing/ggcode.go +++ b/src/parsing/ggcode.go @@ -6,10 +6,8 @@ import ( "regexp" "git.handmade.network/hmn/hmn/src/hmnurl" - "git.handmade.network/hmn/hmn/src/utils" "github.com/yuin/goldmark" "github.com/yuin/goldmark/ast" - gast "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/renderer" "github.com/yuin/goldmark/renderer/html" @@ -43,7 +41,7 @@ var ggcodeTags = map[string]ggcodeTag{ Filter: ggcodeFilterEdu, Renderer: func(c ggcodeRendererContext, n *ggcodeNode, entering bool) error { if entering { - term, _ := n.Args["term"] + term := n.Args["term"] c.W.WriteString(fmt.Sprintf( ``, hmnurl.BuildEducationGlossary(term), @@ -55,18 +53,6 @@ var ggcodeTags = map[string]ggcodeTag{ return nil }, }, - "resource": { - Filter: ggcodeFilterEdu, - Renderer: func(c ggcodeRendererContext, n *ggcodeNode, entering bool) error { - if entering { - c.W.WriteString(`
`) - c.W.WriteString(fmt.Sprintf(` %s`, n.Args["url"], utils.OrDefault(n.Args["name"], "[missing `name`]"))) - } else { - c.W.WriteString("
") - } - return nil - }, - }, "note": { Filter: ggcodeFilterEdu, Renderer: func(c ggcodeRendererContext, n *ggcodeNode, entering bool) error { @@ -83,14 +69,22 @@ var ggcodeTags = map[string]ggcodeTag{ Renderer: func(c ggcodeRendererContext, n *ggcodeNode, entering bool) error { if entering { c.W.WriteString(`
`) - var srcAttr, altAttr string - if src := n.Args["src"]; src != "" { - srcAttr = fmt.Sprintf(` src="%s"`, src) + + src := n.Args["src"] + alt := n.Args["alt"] + if embedHTML, _, canEmbed := htmlForURLEmbed(src, c.Opts.Previews); canEmbed { + c.W.WriteString(embedHTML) + } else { + var srcAttr, altAttr string + if src != "" { + srcAttr = fmt.Sprintf(` src="%s"`, src) + } + if alt != "" { + altAttr = fmt.Sprintf(` alt="%s"`, alt) + } + c.W.WriteString(fmt.Sprintf(``, srcAttr, altAttr)) } - if alt := n.Args["alt"]; alt != "" { - altAttr = fmt.Sprintf(` alt="%s"`, alt) - } - c.W.WriteString(fmt.Sprintf(``, srcAttr, altAttr)) + c.W.WriteString(`
`) } else { c.W.WriteString(`
`) @@ -195,7 +189,7 @@ func (s ggcodeInlineParser) Trigger() []byte { return []byte("!()") } -func (s ggcodeInlineParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node { +func (s ggcodeInlineParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node { restOfLine, segment := block.PeekLine() // Gets the rest of the line (starting at the current parser cursor index), and the segment representing the indices in the source text. if match := extractMap(reGGCodeInline, restOfLine); match != nil { name := string(match["name"]) @@ -240,7 +234,7 @@ func (p ggcodeDelimiterParser) CanOpenCloser(opener, closer *parser.Delimiter) b return opener.Char == '(' && closer.Char == ')' } -func (p ggcodeDelimiterParser) OnMatch(consumes int) gast.Node { +func (p ggcodeDelimiterParser) OnMatch(consumes int) ast.Node { return p.Node } @@ -249,7 +243,7 @@ func (p ggcodeDelimiterParser) OnMatch(consumes int) gast.Node { // ---------------------- type ggcodeNode struct { - gast.BaseBlock + ast.BaseBlock Name string Args map[string]string } @@ -257,12 +251,12 @@ type ggcodeNode struct { var _ ast.Node = &ggcodeNode{} func (n *ggcodeNode) Dump(source []byte, level int) { - gast.DumpHelper(n, source, level, n.Args, nil) + ast.DumpHelper(n, source, level, n.Args, nil) } -var kindGGCode = gast.NewNodeKind("ggcode") +var kindGGCode = ast.NewNodeKind("ggcode") -func (n *ggcodeNode) Kind() gast.NodeKind { +func (n *ggcodeNode) Kind() ast.NodeKind { return kindGGCode } @@ -290,7 +284,7 @@ func (r *ggcodeHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegister reg.Register(kindGGCode, r.render) } -func (r *ggcodeHTMLRenderer) render(w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) { +func (r *ggcodeHTMLRenderer) render(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { node := n.(*ggcodeNode) var renderer ggcodeRenderer = defaultGGCodeRenderer if tag, ok := ggcodeTags[node.Name]; ok { @@ -303,7 +297,7 @@ func (r *ggcodeHTMLRenderer) render(w util.BufWriter, source []byte, n gast.Node Source: source, Opts: r.Opts, }, node, entering) - return gast.WalkContinue, err + return ast.WalkContinue, err } func defaultGGCodeRenderer(c ggcodeRendererContext, n *ggcodeNode, entering bool) error { diff --git a/src/rawdata/scss/_content.scss b/src/rawdata/scss/_content.scss index 8901deb..61cbdd0 100644 --- a/src/rawdata/scss/_content.scss +++ b/src/rawdata/scss/_content.scss @@ -173,3 +173,15 @@ pre { overflow-x: auto; } } + +figure { + @extend .bg--dim, .br3; + display: flex; + flex-direction: column; + margin: $spacing-medium 0; + padding: $spacing-medium $spacing-medium 0; + + figcaption { + padding: $spacing-small 0; + } +} diff --git a/src/rawdata/scss/_education.scss b/src/rawdata/scss/_education.scss index fd6ac17..e1493ba 100644 --- a/src/rawdata/scss/_education.scss +++ b/src/rawdata/scss/_education.scss @@ -1,13 +1,4 @@ .edu-article { - .edu-resource { - @extend .pa3, .bg--dim, .br3; - } - - .resource-title { - @extend .f4; - font-weight: bold; - } - .note { color: red; }