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 `
-
-
![](https://img.youtube.com/vi/` + vid + `/hqdefault.jpg)
-
-`
+ 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;
}