From 29c8e4d4a88c7ffd136bcb36f31c188d5239b63b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 08:50:52 +0000 Subject: [PATCH 1/9] Initial plan From 749dd3c2cde65fdd641cad3178f602462e335e7a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 08:58:14 +0000 Subject: [PATCH 2/9] Add contributors and last edit date to DB player profile Agent-Logs-Url: https://github.com/benckx/elephantchess/sessions/0a890f00-58d8-43ce-8a8a-87a83674942e Co-authored-by: benckx <8626080+benckx@users.noreply.github.com> --- .../dto/database/DatabasePlayerProfileEdit.kt | 3 +- .../servicelayer/services/DatabaseService.kt | 6 +- .../webapp/rendering/DatabasePageRenderer.kt | 37 ++++++++++- .../public/css/database/database-player.css | 10 +++ .../templates/database/database_player.html | 1 + .../rendering/DatabasePageRendererTest.kt | 65 +++++++++++++++++++ 6 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 webapp/src/test/kotlin/io/elephantchess/webapp/rendering/DatabasePageRendererTest.kt diff --git a/webapp-service-layer/src/main/kotlin/io/elephantchess/servicelayer/dto/database/DatabasePlayerProfileEdit.kt b/webapp-service-layer/src/main/kotlin/io/elephantchess/servicelayer/dto/database/DatabasePlayerProfileEdit.kt index 0962c3e32..6b417086d 100644 --- a/webapp-service-layer/src/main/kotlin/io/elephantchess/servicelayer/dto/database/DatabasePlayerProfileEdit.kt +++ b/webapp-service-layer/src/main/kotlin/io/elephantchess/servicelayer/dto/database/DatabasePlayerProfileEdit.kt @@ -8,5 +8,6 @@ data class DatabasePlayerProfileEdit( val profileText: String?, val sources: List, val editComment: String?, - val enabled: Boolean + val enabled: Boolean, + val versionTime: Long? ) diff --git a/webapp-service-layer/src/main/kotlin/io/elephantchess/servicelayer/services/DatabaseService.kt b/webapp-service-layer/src/main/kotlin/io/elephantchess/servicelayer/services/DatabaseService.kt index 623381710..fc599c665 100644 --- a/webapp-service-layer/src/main/kotlin/io/elephantchess/servicelayer/services/DatabaseService.kt +++ b/webapp-service-layer/src/main/kotlin/io/elephantchess/servicelayer/services/DatabaseService.kt @@ -404,7 +404,8 @@ class DatabaseService( profileText = profileVersion.profile, sources = fetchPlayerProfileSources(playerId, profileVersion.version), editComment = profileVersion.comment, - enabled = profileVersion.enabled + enabled = profileVersion.enabled, + versionTime = profileVersion.versionTime.toEpochMilliseconds() ) } else { val player = referencePlayerDaoService.findPlayer(playerId) @@ -418,7 +419,8 @@ class DatabaseService( profileText = null, sources = emptyList(), editComment = null, - enabled = true + enabled = true, + versionTime = null ) } } diff --git a/webapp/src/main/kotlin/io/elephantchess/webapp/rendering/DatabasePageRenderer.kt b/webapp/src/main/kotlin/io/elephantchess/webapp/rendering/DatabasePageRenderer.kt index 8e1ea10e0..811c6372a 100644 --- a/webapp/src/main/kotlin/io/elephantchess/webapp/rendering/DatabasePageRenderer.kt +++ b/webapp/src/main/kotlin/io/elephantchess/webapp/rendering/DatabasePageRenderer.kt @@ -7,6 +7,11 @@ import io.elephantchess.servicelayer.dto.database.* import io.elephantchess.utils.cropToFirstNWords import io.elephantchess.utils.formatWithChineseName import io.github.reactivecircus.cache4k.Cache +import io.ktor.util.escapeHTML +import java.time.Instant +import java.time.ZoneOffset.UTC +import java.time.format.DateTimeFormatter +import java.util.Locale import kotlin.time.Duration.Companion.hours class DatabasePageRenderer(private val htmlRenderer: HtmlRenderer) { @@ -30,6 +35,13 @@ class DatabasePageRenderer(private val htmlRenderer: HtmlRenderer) { fetchEditorsUsername: suspend () -> List, ): String { val description = edit.profileText + var cachedEditors: List? = null + suspend fun fetchContributors(): List { + return cachedEditors ?: fetchEditorsUsername() + .distinct() + .sorted() + .also { cachedEditors = it } + } val playerNameEncodedResolver = SimpleValueTagResolver("player_name_encoded", databasePlayer.urlName) val playerIdResolver = SimpleValueTagResolver("player_id", databasePlayer.id) @@ -55,7 +67,7 @@ class DatabasePageRenderer(private val htmlRenderer: HtmlRenderer) { } val authorMeta = CallbackTagResolver("author_meta") { - val editors = fetchEditorsUsername() + val editors = fetchContributors() if (editors.isNotEmpty()) { meta("author", editors.sorted().joinToString(", ")) } else { @@ -63,6 +75,28 @@ class DatabasePageRenderer(private val htmlRenderer: HtmlRenderer) { } } + val profileMetaResolver = CallbackTagResolver("profile_meta_info") { + val contributors = fetchContributors().joinToString(", ") { it.escapeHTML() } + val lastEditDate = edit.versionTime + ?.let { Instant.ofEpochMilli(it).atZone(UTC).toLocalDate() } + ?.format(DateTimeFormatter.ofPattern("d MMM yyyy", Locale.ENGLISH)) + if (contributors.isEmpty() && lastEditDate == null) { + "" + } else { + buildString { + if (contributors.isNotEmpty()) { + append("""Contributors: $contributors""") + } + if (lastEditDate != null) { + if (isNotEmpty()) { + append(""" · """) + } + append("""Last edit: $lastEditDate""") + } + } + } + } + val noIndexMeta = CallbackTagResolver("meta_no_index_conditional") { if (!edit.enabled || requestedVersion != null) { WebFragmentResolver("meta_no_index").resolveContent().firstOrNull() ?: "" @@ -82,6 +116,7 @@ class DatabasePageRenderer(private val htmlRenderer: HtmlRenderer) { sourcesResolver, styleResolver, authorMeta, + profileMetaResolver, noIndexMeta ), canonicalPath = "/database/player/${databasePlayer.urlName}" diff --git a/webapp/src/main/resources/public/css/database/database-player.css b/webapp/src/main/resources/public/css/database/database-player.css index 7256feed6..109e0e7c7 100644 --- a/webapp/src/main/resources/public/css/database/database-player.css +++ b/webapp/src/main/resources/public/css/database/database-player.css @@ -22,6 +22,11 @@ margin-bottom: 12px; } +#profile-meta-info { + margin-bottom: 15px; + font-size: 13px; +} + #player-profile-description .profile-sources { margin-top: 20px; padding: 15px; @@ -101,6 +106,11 @@ } @media (max-width: 1000px) { + #profile-meta-info { + font-size: 24px; + margin-bottom: 22px; + } + #player-profile-description { font-size: 34px; margin-bottom: 20px; diff --git a/webapp/src/main/resources/templates/database/database_player.html b/webapp/src/main/resources/templates/database/database_player.html index ab40796c7..b35bab788 100644 --- a/webapp/src/main/resources/templates/database/database_player.html +++ b/webapp/src/main/resources/templates/database/database_player.html @@ -20,6 +20,7 @@

{{page_title}}


+
{{profile_meta_info}}
{{player_profile_description}}
diff --git a/webapp/src/test/kotlin/io/elephantchess/webapp/rendering/DatabasePageRendererTest.kt b/webapp/src/test/kotlin/io/elephantchess/webapp/rendering/DatabasePageRendererTest.kt new file mode 100644 index 000000000..9b344a1f1 --- /dev/null +++ b/webapp/src/test/kotlin/io/elephantchess/webapp/rendering/DatabasePageRendererTest.kt @@ -0,0 +1,65 @@ +package io.elephantchess.webapp.rendering + +import io.elephantchess.htmlrenderer.HtmlRenderer +import io.elephantchess.htmlrenderer.TemplateRenderer +import io.elephantchess.servicelayer.dto.database.DatabasePlayer +import io.elephantchess.servicelayer.dto.database.DatabasePlayerProfileEdit +import kotlinx.coroutines.test.runTest +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class DatabasePageRendererTest { + + private fun pageRenderer(): DatabasePageRenderer { + val templateRenderer = object : TemplateRenderer(baseTagResolvers = emptyList(), disabledTemplates = emptyList()) {} + val htmlRenderer = HtmlRenderer(isMinificationEnabled = false, cdnFolder = null, webTemplateRenderer = templateRenderer) + return DatabasePageRenderer(htmlRenderer) + } + + @Test + fun `renderPlayerPage includes contributors and last edit date`() = runTest { + val output = pageRenderer().renderPlayerPage( + databasePlayer = DatabasePlayer("player-id", "John Doe", null, null), + requestedVersion = null, + edit = DatabasePlayerProfileEdit( + playerId = "player-id", + canonicalName = "John Doe", + chineseName = null, + gender = null, + profileText = "some text", + sources = emptyList(), + editComment = null, + enabled = true, + versionTime = 1704067200000 + ), + fetchEditorsUsername = { listOf("Bob", "Alice", "Bob", " + + {{body_init}}

Changelog


+ {{changelog_toc}} +

May 2026 + + Copy link + +

+
+

2026-05-25

+
    +
  • + Add visible warning when getting close to the perpetual checking rule limit + (related to issue #455) +
  • +
  • + User profile game stats (W / D / L) + (issue #628) +
  • +
  • + CSS fix: scope profile online-indicator spacing override to restore username gap + (issue #701) +
  • +
  • + On user profile, show only 2 latest PvP games on mobile + (issue #697) +
  • +
  • + Render dynamic PvP/PvB page titles from game participants + (issue #704) +
  • +
  • + Include short checkmated/stalemated games in total and Manchu counters in + Global page + (issue #699) +
  • +
  • + Database search: show player-relative WIN/LOSS outcomes for + player-specific queries + (issue #444) +
  • +
  • + Limit users to 3 new PvP games per time category + (issue #339) +
  • +
  • + Add move timestamps to PvP game DTO; reflect clock + chat state when navigating moves + (issue #556) +
  • +
  • + Dynamic matching of pending games when inviters come back online + (issue #420) +
  • +
  • + Technical changes (admin console, shutdown handler) +
  • +
+

2026-05-24

+ +

2026-05-23

+
    +
  • + Add Manchu chess (Yitong) variant for PvP and PvB (only Fairy). + (issue #687) +
  • +
  • + Renamed Simple Board into Test Board. +
  • +
  • + Distribution of Board GUI will be bumped to 0.1.0. +
  • +
  • + Restyle PvB “Play Bot” modal to match PvP new-game UX and add depth scroller + (issue #690) +
  • +
+

2026-05-22

+
    +
  • + Stabilize analysis summary chart rendering to remove analysis-page startup stalls. Fix seems to work. + (issue #677) +
  • +
+

2026-05-19

+
    +
  • + Hide empty second game-thumbnails row on browse pages when first page contains 3 or fewer games + (issue #633) +
  • +
  • + Technical change: Ktor/kotlinx.html TagResolver support and migrate server-side renderer HTML snippets away + from raw strings + (issue #679) +
  • +
  • + Technical change (re-ordering routes) +
  • +
+

2026-05-18

+
    +
  • + Replace lobby open-games table with responsive card list layout + (issue #601) +
  • +
  • + Fix check highlight loss on move format change; gate coordinates style on show-coordinates; add + Chinese-on-one-side coordinates options + (issue #662) +
  • +
  • + Render advanced settings as a centered modal on mobile + (issue #671) +
  • +
+

2026-05-17

+
    +
  • + Add a "Coordinates" advanced setting to choose between Arabic numerals on both sides, Chinese numerals + on both sides, Chinese numerals on red side only (default), or UCI / Algebraic letters (a–i) + (issue #362) +
  • +
  • + Add colorblind-friendly black piece toggle and advanced settings overlay in the settings manager + (issue #606) +
  • +
  • + Add a "copy link" anchor icon next to each month heading on this changelog page + (issue #657) +
  • +
  • + Add a table of contents (clustered by quarter) to this changelog page + (issue #657) +
  • +
  • + Show move annotation symbols in move history on PvP, PvB and DB pages + (issue #366) +
  • +
  • + Fix user settings page layout on mobile + (issue #635) +
  • +
  • + Add email confirmation on sign up + (issue #28) +
  • +
  • + Server-side rendering of Database games +
  • +
  • + Distribution of Board GUI is bumped to 0.0.4 for the + colorblind-friendly mode and the coordinates style. +
  • +
  • + Fix profile settings unset/null handling for country flag and description + (issue #30) +
  • +
  • + Fix Logout requiring two clicks by deleting identification cookies with the same path used to set them + (issue #494) +
  • +
+

2026-05-16

+
    +
  • + Add confirmation prompt before cancelling PvP and PvB games + (issue #341) +
  • +
  • + Add a sticky mini time counter in the bottom-right corner on mobile for PvP games + (issue #440) +
  • +
  • + Option to transfer guest activity (games, bot games, puzzles, database searches) and ratings to a newly + registered account on sign-up + (PR #610) +
  • +
  • + Show a mini board tooltip when hovering engine PV moves on the Analysis Board + (PR #614) +
  • +
  • + Eval line chart: replace two-color series with single blue line and add background bands and fix engine name + in analysis summary report + (issue #643) +
  • +
  • + Technical change (Gradle build to Kotlin DSL) + (issue #639) +
  • +
  • + Technical change (canonical URL on user profile) + (issue #641) +
  • +
  • + Technical changes (dependencies upgrades, refactor call "listLatestPvbGames") +
  • +
+

2026-05-15

+
    +
  • + Add "user is typing" indicator to PvP chat + (issue #412) +
  • +
  • + Add eval history line chart to analysis summary report + (issue #169) +
  • +
+

2026-05-14

+
    +
  • + Import ICCS format moves in the Analysis Board + (issue #182) +
  • +
  • + Draw an elbowed arrow for knight moves in the engine analysis + (issue #281). + Distribution of Board GUI is bumped to 0.0.3. +
  • +
  • + Add "My Databases Searches" view: list and re-run previous Database + searches (issue #488) +
  • +
  • + Adapt the user drop down menu so it uses links (i.e. HTML anchors) instead of click events, which makes it + possible to e.g. open the view in a new tab + (issue #535) +
  • +
  • + Add view of user sessions (IP, country, etc.) + (issue #622) +
  • +
  • + Allow Vietnamese characters in username + (issue #492) +
  • +
  • + Display user profiles with blank description + (issue #576) +
  • +
  • + Add move tree history on the test board page + (issue #152) +
  • +
  • + Fixed issue where it would download the PGN twice +
  • +
  • + Upgrade Fairy Stockfish version to 14.0.1 (reverted, doesn't seem to work on the server) +
  • +
+

2026-05-13

+
    +
  • + Exclude auto-resigned games from the "Latest Bot Games" section on the lobby (they remain visible when + browsing PvB games) + (issue #527) +
  • +
  • + Generate a table of contents at the top of the FAQ page + (issue #578) +
  • +
  • + Show latest PvP games on user profile page + (issue #197) +
  • +
  • + Decrease number of default game thumbs in all the "browse" pages +
  • +
  • + Dynamic matching: when users come back online, their pending games are now automatically paired + with other compatible pending games whose inviters are also online (whereas previously matching only + happened at game creation time) + (issue #420) +
  • +

2026-05-12

  • @@ -114,6 +407,12 @@

    2026-05-01

    end (e.g. by disabling browser extensions or using a different network).
+

April 2026 + + Copy link + +

+

2026-04-30

  • @@ -203,6 +502,10 @@

    2026-04-12

    automatically when it happens.
+

March 2026 Copy link +

+

2026-03-23

  • @@ -227,12 +530,20 @@

    2026-03-04

    Technical changes (libraries upgrades)
+

February 2026 Copy link

+

2026-02-23

  • E-mail format validation fix
+

January 2026 Copy link

+

2026-01-30

  • @@ -539,6 +850,10 @@

    2026-01-05

    Technical changes (dynamic sitemap)
+

December 2025 Copy link

+

2025-12-29

  • @@ -630,6 +945,10 @@

    2025-12-02

    Technical changes (admin console)
+

November 2025 Copy link

+

2025-11-19

+

October 2025 Copy link

+

2025-10-28

  • @@ -808,6 +1131,10 @@

    2025-10-04

    DB engine version upgrade
+

September 2025 Copy link

+

2025-09-11

+

August 2025 Copy link +

+

2025-08-25

  • @@ -915,6 +1246,10 @@

    2025-08-02

    Technical changes (admin console)
+

July 2025 Copy link

+

2025-07-31

+

June 2025 Copy link

+

2025-06-29

  • @@ -1140,6 +1479,10 @@

    2025-06-22

    Technical changes (libraries upgrades)
+

May 2025 Copy link

+

2025-05-19

  • @@ -1202,6 +1545,10 @@

    2025-05-03

    (issue #57)
+

April 2025 Copy link +

+

2025-04-28

  • @@ -1242,6 +1589,10 @@

    2025-04-19

    Fix issue where "Cancel Game" button wasn't shown
+

March 2025 Copy link +

+

2025-03-24

+

February 2025 Copy link

+

2025-02-24

  • @@ -1407,6 +1762,10 @@

    2025-02-08

    Technical changes (logging library, libraries upgrades, naming)
+

January 2025 Copy link

+

2025-01-29

+

December 2024 Copy link

+

2024-12-31

  • @@ -2032,6 +2395,10 @@

    2024-12-01

    Technical changes (admin console)
+

November 2024 Copy link

+

2024-11-30

  • @@ -2139,12 +2506,20 @@

    2024-11-02

    Minor CSS changes
+

October 2024 Copy link

+

2024-10-12

  • Technical changes (libraries upgrades)
+

September 2024 Copy link

+

2024-09-04

  • @@ -2160,6 +2535,10 @@

    2024-09-02

    Fix CSS issue where the 2 Analyze buttons would be visible
+

August 2024 Copy link +

+

2024-08-31

  • @@ -2201,6 +2580,10 @@

    2024-08-03

    href="/analysis">Analysis Board. Still in beta.
+

June 2024 Copy link

+

2024-06-14

  • @@ -2222,6 +2605,10 @@

    2024-06-05

    Technical changes (admin console)
+

May 2024 Copy link

+

2024-05-25

  • @@ -2302,6 +2689,10 @@

    2024-05-01

    Cookie consent banner and related information in the FAQ page
+

April 2024 Copy link +

+

2024-04-29

  • @@ -2477,6 +2868,10 @@

    2024-04-01

    6 (previously there was no difference).
+

March 2024 Copy link +

+

2024-03-31

  • @@ -2538,6 +2933,10 @@

    2024-03-16

    Admin console page to monitor latest analyzed games
+

February 2024 Copy link

+

2024-02-25

  • @@ -2560,6 +2959,10 @@

    2024-02-10

    Technical bugfix (user session IP in the wrong format)
+

January 2024 Copy link

+

2024-01-02

  • @@ -2574,6 +2977,10 @@

    2024-01-02

    Minor style changes (remove bold style on table row hovering, 2 digits year format)
+

December 2023 Copy link

+

2023-12-29

  • @@ -2620,6 +3027,10 @@

    2023-12-03

    Technical changes (admin console)
+

November 2023 Copy link

+

2023-11-27

  • @@ -2686,6 +3097,10 @@

    2023-11-01

    Fix issue where password recovery wouldn't show any feedback when providing valid email
+

October 2023 Copy link

+

2023-10-29

  • @@ -2723,6 +3138,10 @@

    2023-10-01

    Technical changes regarding the batch jobs (no significant change for end users)
+

September 2023 Copy link

+

2023-09-24

  • @@ -2780,6 +3199,10 @@

    2023-09-02

    Fix bug when updating analysis in the Analysis Board
+

August 2023 Copy link +

+

2023-08-28

  • @@ -2941,6 +3364,10 @@

    2023-08-01

    Minor UI improvements
+

July 2023 Copy link

+

2023-07-31

  • @@ -3036,6 +3463,10 @@

    2023-07-21

    Increase the max length to 2000.
+

June 2023 Copy link

+

2023-06-17

  • @@ -3080,6 +3511,10 @@

    2023-06-03

    interesting to persist engine result data that don't contain a cp value.
+

May 2023 Copy link

+

2023-05-29

  • @@ -3197,6 +3632,10 @@

    2023-05-07

    version.
+

April 2023 Copy link +

+

2023-04-29 / 2023-04-30

  • @@ -3277,6 +3716,10 @@

    2023-04-01

    Key shortcut to go to next puzzle (Enter)
+

March 2023 Copy link +

+

2023-03-31

  • @@ -3319,7 +3762,7 @@

    2023-03-26

    Add option to rotate the board
  • - Add link to the simple board tool + Add link to the test board tool
  • Bug fix on the new puzzle system (mated by puzzle was not rendered) @@ -3470,6 +3913,10 @@

    2023-03-06

    (available only to logged users)
+

February 2023 Copy link

+

2023-02-11

  • @@ -3479,6 +3926,10 @@

    2023-02-11

    Configuration of the server
+

January 2023 Copy link

+

2023-01-23

  • diff --git a/webapp/src/main/resources/templates/about/developers/board-gui-example.html b/webapp/src/main/resources/templates/about/developers/board-gui-example.html index 6785e8207..063085dac 100644 --- a/webapp/src/main/resources/templates/about/developers/board-gui-example.html +++ b/webapp/src/main/resources/templates/about/developers/board-gui-example.html @@ -42,6 +42,7 @@

    board-gui.js – minimal example

    + @@ -104,7 +105,22 @@

    Using xiangqi.js without the GUI

  • calculateFen(moves, startFen) – compute the FEN after a list of moves
  • validateStartFen(fen) – throws if the position is checkmate / stalemate / invalid
  • exportMovesToPgnLine(moves), translateMovesToWxf(...), etc.
  • +
  • DEFAULT_START_FEN – standard Xiangqi starting position
  • +
  • MANCHU_START_FEN – Manchu chess (Yitong) starting position
+

Manchu chess (Yitong) variant

+

+ xiangqi.js also supports the Manchu (or Yitong) variant. In this variant, Red + plays with a single super-chariot (FEN char M) at a1 instead of the usual chariots, horses and + cannons. The super-chariot combines the movement powers of chariot, horse and cannon. +

+

+ Use the constant MANCHU_START_FEN to load the Manchu starting position: +

+
boardGui.loadFen(MANCHU_START_FEN);
+

+ All legal-move and checkmate logic automatically handles the super-chariot, so no extra configuration is needed. +

+ diff --git a/webapp/src/main/resources/templates/browse_pvb_games.html b/webapp/src/main/resources/templates/browse_pvb_games.html index 073246c9b..176f4ee10 100644 --- a/webapp/src/main/resources/templates/browse_pvb_games.html +++ b/webapp/src/main/resources/templates/browse_pvb_games.html @@ -1,13 +1,12 @@ {{header_init}} - Chinese Chess (Xiangqi) Games Database – Player vs Bot + Browse PvB games - @@ -20,7 +19,7 @@

Latest Player-vs-Bot Games


- {{game_thumb}}[[iterations:12; {gameType: 'pvb', parentClass: 'pvb-game-thumb'}]] + {{game_thumb}}[[iterations:6; {gameType: 'pvb', parentClass: 'pvb-game-thumb'}]]