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.