diff --git a/modules/ublog/src/main/UblogPost.scala b/modules/ublog/src/main/UblogPost.scala index d3a381d5f1f2a..0f67a3505f145 100644 --- a/modules/ublog/src/main/UblogPost.scala +++ b/modules/ublog/src/main/UblogPost.scala @@ -59,6 +59,7 @@ object UblogPost: val intro: String val image: Option[UblogImage] val created: Recorded + val updated: Option[Recorded] val lived: Option[Recorded] def slug = UblogPost.slug(title) def isLichess = created.by.is(UserId.lichess) @@ -70,6 +71,7 @@ object UblogPost: intro: String, image: Option[UblogImage], created: Recorded, + updated: Option[Recorded], lived: Option[Recorded], topics: List[UblogTopic] ) extends BasePost diff --git a/modules/ublog/src/main/ui/UblogUi.scala b/modules/ublog/src/main/ui/UblogUi.scala index 95695dfbc3542..55bdadd1d64bc 100644 --- a/modules/ublog/src/main/ui/UblogUi.scala +++ b/modules/ublog/src/main/ui/UblogUi.scala @@ -345,8 +345,9 @@ final class UblogUi(helpers: Helpers, atomUi: AtomUi)(picfitUrl: lila.core.misc. private def renderPost(post: UblogPost.PreviewPost, authorName: String) = frag( - tag("id")(post.id), + tag("id")(s"$netBaseUrl${urlOfPost(post)}"), tag("published")(post.lived.map(_.at).map(atomUi.atomDate)), + tag("updated")(post.updated.orElse(post.lived).map(_.at).map(atomUi.atomDate)), link( rel := "alternate", tpe := "text/html", @@ -361,11 +362,13 @@ final class UblogUi(helpers: Helpers, atomUi: AtomUi)(picfitUrl: lila.core.misc. ) }, tag("content")(tpe := "html")( - thumbnail(post, _.Size.Large), - "
", // yes, scalatags encodes it. - post.intro + frag( + thumbnail(post, _.Size.Large), + br, + post.intro + ).render // html as escaped string in xml ), - tag("tag")("media:thumbnail")(attr("url") := thumbnailUrl(post, _.Size.Large)), + tag("media:thumbnail")(attr("url") := thumbnailUrl(post, _.Size.Large)), tag("author")(tag("name")(authorName)) ) diff --git a/ui/chess/src/glyphs.ts b/ui/chess/src/glyphs.ts index 4c2e9b846ca20..ea2dc03da40da 100644 --- a/ui/chess/src/glyphs.ts +++ b/ui/chess/src/glyphs.ts @@ -28,63 +28,166 @@ export function annotationShapes(node: Tree.Node): DrawShape[] { } else return []; } -// We can render glyphs as text, but people are used to these SVGs as the "Big 5" glyphs -// and right now they look better -const prependDropShadow = (svgBase: string) => - ` -${svgBase}`; -// NOTE: -// Base svg was authored with Inkscape. -// On Inkscape, by using "Object to Path", text is converted to path, which enables consistent layout on browser. -// Inkscape's output includes unnecessary attributes which can be cleaned up with https://lean-svg.netlify.app. -// Wrap it by `transform="translate(...) scale(...)"` so that it sits at the right top corner. -// Small tweak (e.g. changing color, scaling size, etc...) can be done by directly modifying svg below. +const composeGlyph = (fill: string, path: string) => + `${path}`; + const glyphToSvg: Dictionary = { // Inaccuracy - '?!': prependDropShadow(` - - -`), + '?!': composeGlyph( + '#56b4e9', + '', + ), // Mistake - '?': prependDropShadow(` - - -`), + '?': composeGlyph( + '#e69f00', + '', + ), // Blunder - '??': prependDropShadow(` - - -`), + '??': composeGlyph( + '#df5353', + '', + ), // Interesting move - '!?': prependDropShadow(` - - -`), + '!?': composeGlyph( + '#ea45d8', + '', + ), // Good move - '!': prependDropShadow(` - - -`), + '!': composeGlyph( + '#22ac38', + '', + ), // Brilliant move - '!!': prependDropShadow(` - - -`), + '!!': composeGlyph( + '#168226', + '', + ), // Correct move in a puzzle - '✓': prependDropShadow(` - - -`), + '✓': composeGlyph( + '#22ac38', + '', + ), // Incorrect move in a puzzle - '✗': prependDropShadow(` - - -`), + '✗': composeGlyph( + '#df5353', + '', + ), + + // Only move + '□': composeGlyph('#a04048', ''), + + // Zugzwang + '⨀': composeGlyph( + '#9171f2', + '', + ), + + // Equal position + '=': composeGlyph( + '#f5918f', + '', + ), + + // Unclear position + '∞': composeGlyph( + '#f5918f', + '', + ), + + // White is slightly better + '⩲': composeGlyph( + '#999', + '', + ), + + // Black is slightly better + '⩱': composeGlyph( + '#555', + '', + ), + + // White is better + '±': composeGlyph( + '#aaa', + '', + ), + + // Black is better + '∓': composeGlyph( + '#444', + '', + ), + + // White is winning + '+−': composeGlyph( + '#bbb', + '', + ), + + // Black is winning + '-+': composeGlyph( + '#333', + '', + ), + + // Novelty + N: composeGlyph( + '#90c290', + '', + ), + + // Development + '↑↑': composeGlyph( + '#c87e9d', + '', + ), + + // Initiative + '↑': composeGlyph( + '#2660a4', + '', + ), + + // Attack + '→': composeGlyph( + '#fb0e3d', + '', + ), + + // Counterplay + '⇆': composeGlyph( + '#ff784f', + '', + ), + + // Time trouble + '⊕': composeGlyph( + '#c2095a', + '', + ), + + // With compensation + '=∞': composeGlyph( + '#180aae', + '', + ), + + // With the idea + '∆': composeGlyph( + '#c8c831', + '', + ), }; + +// NOTE: Inkscape tips +// On Inkscape, by using "Object to Path", text is converted to path, which enables consistent layout on browser. +// Inkscape's output includes unnecessary attributes which can be cleaned up with https://lean-svg.netlify.app. +// Wrap it by `transform="translate(...) scale(...)"` so that it sits at the right top corner. +// Small tweak (e.g. changing color, scaling size, etc...) can be done by directly modifying svg below.