From c296fa106e588534955c54c59d51b17d8743ff91 Mon Sep 17 00:00:00 2001 From: undefined Date: Thu, 15 Jan 2026 00:24:59 +0800 Subject: [PATCH] ui: collapse problem_solution long content by default --- framework/eslint-config/package.json | 6 +- framework/framework/package.json | 4 +- framework/utils/package.json | 2 +- package.json | 6 +- packages/common/subtask.ts | 2 +- packages/components/package.json | 6 +- packages/geoip/package.json | 2 +- packages/hydrojudge/package.json | 12 ++-- packages/hydrooj/package.json | 14 ++--- packages/scoreboard-xcpcio/package.json | 4 +- .../discussion/collapsible.page.styl | 40 ++++++++++++ .../discussion/collapsible.page.tsx | 62 +++++++++++++++++++ .../components/discussion/comments.page.styl | 2 + .../components/discussion/comments.page.tsx | 9 +-- packages/ui-default/locales/zh.yaml | 2 + packages/ui-default/package.json | 26 ++++---- .../components/comments_solution.html | 4 +- packages/ui-next/package.json | 4 +- 18 files changed, 155 insertions(+), 52 deletions(-) create mode 100644 packages/ui-default/components/discussion/collapsible.page.styl create mode 100644 packages/ui-default/components/discussion/collapsible.page.tsx diff --git a/framework/eslint-config/package.json b/framework/eslint-config/package.json index 6198e25f7..912e85e6f 100644 --- a/framework/eslint-config/package.json +++ b/framework/eslint-config/package.json @@ -6,9 +6,9 @@ "repository": "https://github.com/hydro-dev/Hydro", "dependencies": { "@antfu/eslint-config": "^6.7.3", - "@eslint-react/eslint-plugin": "^2.4.0", - "@typescript-eslint/eslint-plugin": "^8.50.1", - "@typescript-eslint/parser": "^8.50.1", + "@eslint-react/eslint-plugin": "^2.6.2", + "@typescript-eslint/eslint-plugin": "^8.53.0", + "@typescript-eslint/parser": "^8.53.0", "eslint-plugin-de-morgan": "^2.0.0", "eslint-plugin-github": "^6.0.0", "eslint-plugin-react-hooks": "^7.0.1", diff --git a/framework/framework/package.json b/framework/framework/package.json index f60566e91..9a58ce7d4 100644 --- a/framework/framework/package.json +++ b/framework/framework/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@hydrooj/utils": "workspace:^", - "@koa/router": "^15.1.1", + "@koa/router": "^15.2.0", "@mongodb-js/saslprep": "^1.4.4", "emoji-regex": "^10.6.0", "fs-extra": "^11.3.3", @@ -20,7 +20,7 @@ "sanitize-filename": "^1.6.3", "schemastery": "^3.17.2", "shorty.js": "^1.0.1", - "ws": "^8.18.3" + "ws": "^8.19.0" }, "devDependencies": { "@types/fs-extra": "^11.0.4", diff --git a/framework/utils/package.json b/framework/utils/package.json index e78ca0c38..fbce643b4 100644 --- a/framework/utils/package.json +++ b/framework/utils/package.json @@ -18,7 +18,7 @@ "js-yaml": "^4.1.1", "reggol": "^2.1.0", "search-query-parser": "^1.6.0", - "systeminformation": "^5.28.2" + "systeminformation": "^5.30.3" }, "devDependencies": { "@types/fs-extra": "^11.0.4" diff --git a/package.json b/package.json index aaf3dd076..58642cbeb 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "@types/autocannon": "^7.12.7", "@types/chai": "^5.2.3", "@types/cross-spawn": "^6.0.6", - "@types/node": "24.10.1", + "@types/node": "24.10.7", "@types/semver": "^7.7.1", "autocannon": "^8.0.0", "cac": "^6.7.14", @@ -53,11 +53,11 @@ "mongodb-memory-server": "11.0.1", "nyc": "^17.1.0", "ora": "^9.0.0", - "oxlint": "^1.35.0", + "oxlint": "^1.39.0", "package-json": "^10.0.1", "semver": "^7.7.3", "simple-git": "^3.30.0", - "supertest": "^7.1.4", + "supertest": "^7.2.2", "supports-hyperlinks": "^4.4.0", "typescript": "5.8.3", "wtfnode": "^0.10.1" diff --git a/packages/common/subtask.ts b/packages/common/subtask.ts index f571d7a80..6c8f19238 100644 --- a/packages/common/subtask.ts +++ b/packages/common/subtask.ts @@ -94,7 +94,7 @@ interface ParsedSubtask { export function readSubtasksFromFiles(files: string[], config) { const subtask: Record = {}; - for (const s of config.subtasks || []) if (s.id) subtask[s.id] = s; + for (const s of config.subtasks || []) if (s.id && Number.isSafeInteger(s.id)) subtask[s.id] = s; for (const file of files) { let match = false; for (const rule of SubtaskMatcher) { diff --git a/packages/components/package.json b/packages/components/package.json index e141aba83..9c0732913 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -9,7 +9,7 @@ ], "dependencies": { "allotment": "^1.20.5", - "diff": "^8.0.2", + "diff": "^8.0.3", "js-yaml": "^4.1.1", "lodash": "^4.17.21", "react-dnd": "^16.0.1", @@ -20,8 +20,8 @@ "devDependencies": { "@types/diff": "^8.0.0", "@types/js-yaml": "^4.0.9", - "@types/lodash": "^4.17.21", - "sass": "^1.97.1" + "@types/lodash": "^4.17.23", + "sass": "^1.97.2" }, "peerDependencies": { "react": "*", diff --git a/packages/geoip/package.json b/packages/geoip/package.json index 1dd26056c..0bc2902f1 100644 --- a/packages/geoip/package.json +++ b/packages/geoip/package.json @@ -6,7 +6,7 @@ "author": "undefined ", "license": "AGPL-3.0-or-later", "dependencies": { - "maxmind": "^5.0.1" + "maxmind": "^5.0.3" }, "preferUnplugged": true } diff --git a/packages/hydrojudge/package.json b/packages/hydrojudge/package.json index 5d9daf752..bac77377c 100644 --- a/packages/hydrojudge/package.json +++ b/packages/hydrojudge/package.json @@ -9,16 +9,16 @@ "@hydrooj/common": "workspace:^", "@hydrooj/utils": "workspace:^", "@opentelemetry/api": "^1.9.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.208.0", - "@opentelemetry/sdk-node": "^0.208.0", - "@zip.js/zip.js": "^2.8.11", + "@opentelemetry/exporter-trace-otlp-http": "^0.209.0", + "@opentelemetry/sdk-node": "^0.209.0", + "@zip.js/zip.js": "^2.8.15", "cac": "^6.7.14", - "p-queue": "^9.0.1", + "p-queue": "^9.1.0", "schemastery": "^3.17.2", "semver": "^7.7.3", "shell-quote": "^1.8.3", - "superagent": "^10.2.3", - "ws": "^8.18.3" + "superagent": "^10.3.0", + "ws": "^8.19.0" }, "preferUnplugged": true, "license": "AGPL-3.0-or-later", diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index 28ee3e587..581befb4c 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -16,7 +16,7 @@ "@aws-sdk/lib-storage": "3.726.1", "@aws-sdk/s3-presigned-post": "3.726.1", "@aws-sdk/s3-request-presigner": "3.726.1", - "@babel/code-frame": "^7.27.1", + "@babel/code-frame": "^7.28.6", "@cordisjs/plugin-timer": "1.1.1", "@hydrooj/common": "workspace:^", "@hydrooj/framework": "workspace:^", @@ -24,7 +24,7 @@ "@mongodb-js/saslprep": "^1.4.4", "@simplewebauthn/server": "13.2.2", "@undefined-moe/require-resolve-hook": "^1.1.0", - "@zip.js/zip.js": "^2.8.11", + "@zip.js/zip.js": "^2.8.15", "adm-zip": "0.5.16", "cac": "^6.7.14", "cordis": "4.0.0-beta.5", @@ -44,26 +44,26 @@ "nanoid": "^5.1.6", "nodemailer": "^7.0.12", "notp": "^2.0.3", - "p-queue": "^9.0.1", + "p-queue": "^9.1.0", "sanitize-filename": "^1.6.3", "schemastery": "^3.17.2", "semver": "^7.7.3", "shorty.js": "^1.0.1", - "superagent": "^10.2.3", + "superagent": "^10.3.0", "tar": "6.2.1", "thirty-two": "^1.0.2", "ua-parser-js": "1.0.41" }, "devDependencies": { "@types/adm-zip": "^0.5.7", - "@types/babel__code-frame": "^7.0.6", + "@types/babel__code-frame": "^7.27.0", "@types/fs-extra": "^11.0.4", "@types/js-yaml": "^4.0.9", "@types/koa-static-cache": "^5.1.4", - "@types/lodash": "^4.17.21", + "@types/lodash": "^4.17.23", "@types/mime-types": "^3.0.1", "@types/mongodb-uri": "^0.9.4", - "@types/nodemailer": "^7.0.4", + "@types/nodemailer": "^7.0.5", "@types/notp": "^2.0.5", "@types/semver": "^7.7.1", "@types/superagent": "^8.1.9", diff --git a/packages/scoreboard-xcpcio/package.json b/packages/scoreboard-xcpcio/package.json index 19cdae6fd..0dcf1c9e1 100644 --- a/packages/scoreboard-xcpcio/package.json +++ b/packages/scoreboard-xcpcio/package.json @@ -8,9 +8,9 @@ "postinstall": "node -r @hydrooj/register install.ts" }, "dependencies": { - "@xcpcio/board-app": "^0.76.8" + "@xcpcio/board-app": "^0.76.9" }, "devDependencies": { - "@xcpcio/types": "^0.76.8" + "@xcpcio/types": "^0.76.9" } } diff --git a/packages/ui-default/components/discussion/collapsible.page.styl b/packages/ui-default/components/discussion/collapsible.page.styl new file mode 100644 index 000000000..5778d93de --- /dev/null +++ b/packages/ui-default/components/discussion/collapsible.page.styl @@ -0,0 +1,40 @@ +.collapsible-content + position: relative + + &::after + content: "" + position: absolute + bottom: rem(16px) + left: 0 + right: 0 + height: rem(55px) + background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1)) + pointer-events: none + transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1) + opacity: 1 + z-index: 1 + + &__inner + overflow: hidden + max-height: rem(300px) + transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1) + + .collapsible-toggle + text-align: center + opacity: 0 + + &.visible + opacity: 1 + + &__link + color: $primary-color + text-decoration: none + display: inline-block + position: relative + overflow: hidden + +.collapsible-content.expanded + &::after + opacity: 0 !important + .collapsible-content__inner + max-height: none diff --git a/packages/ui-default/components/discussion/collapsible.page.tsx b/packages/ui-default/components/discussion/collapsible.page.tsx new file mode 100644 index 000000000..7ed86e8ce --- /dev/null +++ b/packages/ui-default/components/discussion/collapsible.page.tsx @@ -0,0 +1,62 @@ +import 'jquery.easing'; + +import $ from 'jquery'; +import { AutoloadPage } from 'vj/misc/Page'; +import { i18n } from 'vj/utils'; + +function initCollapsibleContent() { + $('.collapsible-content').each(function () { + const $content = $(this); + let $inner = $content.find('.collapsible-content__inner'); + if ($inner.length) return; + const $children = $content.children().detach(); + $inner = $('
'); + $inner.append($children); + $content.append($inner); + $content.find('.collapsible-toggle').remove(); + const scrollHeight = $inner[0].scrollHeight; + const clientHeight = $inner[0].clientHeight; + if (scrollHeight > clientHeight + 10) { + const $toggle = $(``); + $content.append($toggle); + const $toggleLink = $toggle.find('.collapsible-toggle__link'); + requestAnimationFrame(() => { + $toggle.addClass('visible'); + }); + $toggleLink.on('click', (e) => { + e.preventDefault(); + if ($content.hasClass('expanded')) { + const currentHeight = $inner[0].scrollHeight; + $inner.css('max-height', `${currentHeight}px`); + requestAnimationFrame(() => { + $content.removeClass('expanded'); + $inner.css('max-height', ''); + $toggleLink.text(i18n('Show more')); + }); + } else { + // Expand: get full height by temporarily removing max-height, then animate to it + const currentMaxHeight = $inner.css('max-height'); + $inner.css('max-height', 'none'); + const fullHeight = $inner[0].scrollHeight; + $inner.css('max-height', currentMaxHeight); + requestAnimationFrame(() => { + $inner.css('max-height', `${fullHeight}px`); + $content.addClass('expanded'); + $toggleLink.text(i18n('Show less')); + }); + } + }); + } else $content.addClass('expanded'); + }); +} + +const commentsPage = new AutoloadPage('commentsPage', () => { + initCollapsibleContent(); + $(document).on('vjContentNew', () => { + initCollapsibleContent(); + }); +}); + +export default commentsPage; diff --git a/packages/ui-default/components/discussion/comments.page.styl b/packages/ui-default/components/discussion/comments.page.styl index 318865a5d..8a29bb0fc 100644 --- a/packages/ui-default/components/discussion/comments.page.styl +++ b/packages/ui-default/components/discussion/comments.page.styl @@ -133,6 +133,8 @@ $downvote-color = red box-shadow: none !important cursor: default +.dczcomments__replies .collapsible-content__inner + max-height: rem(200px) +mobile() .dczcomments__item diff --git a/packages/ui-default/components/discussion/comments.page.tsx b/packages/ui-default/components/discussion/comments.page.tsx index 35d7e85d8..63dac612e 100644 --- a/packages/ui-default/components/discussion/comments.page.tsx +++ b/packages/ui-default/components/discussion/comments.page.tsx @@ -1,11 +1,11 @@ import 'jquery.easing'; import $ from 'jquery'; -import { ConfirmDialog } from 'vj/components/dialog'; +import { confirm } from 'vj/components/dialog'; import CommentBox from 'vj/components/discussion/CommentBox'; import { AutoloadPage } from 'vj/misc/Page'; import { - delay, i18n, request, slideDown, slideUp, tpl, + delay, i18n, request, slideDown, slideUp, } from 'vj/utils'; const $replyTemplate = $('.commentbox-container').eq(0).clone(); @@ -162,10 +162,7 @@ async function onCommentClickDelete(type, ev) { const message = (type === 'comment') ? 'Confirm deleting this comment? Its replies will be deleted as well.' : 'Confirm deleting this reply?'; - const action = await new ConfirmDialog({ - $body: tpl.typoMsg(i18n(message)), - }).open(); - if (action !== 'yes') return; + if (!await confirm(i18n(message))) return; const $evTarget = $(ev.currentTarget); const form = JSON.parse($evTarget.attr('data-form')); diff --git a/packages/ui-default/locales/zh.yaml b/packages/ui-default/locales/zh.yaml index 154c82ca4..4039dcdb1 100644 --- a/packages/ui-default/locales/zh.yaml +++ b/packages/ui-default/locales/zh.yaml @@ -878,7 +878,9 @@ Settings: 设置 Share: 分享 She: 她 Show all tags: 显示所有标签 +Show less: 显示更少 Show MOD badge: 展示 MOD 徽章 +Show more: 显示更多 Show tags: 显示标签 Show: 显示 Sign In: 登录 diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 87f7ba385..e13ebcdb5 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -20,10 +20,10 @@ "@fontsource/source-code-pro": "^5.2.7", "@fontsource/ubuntu-mono": "^5.2.8", "@hydrooj/utils": "workspace:^", - "@mantine/core": "^8.3.10", - "@mantine/hooks": "^8.3.10", - "@mantine/notifications": "^8.3.10", - "@sentry/browser": "^10.32.1", + "@mantine/core": "^8.3.12", + "@mantine/hooks": "^8.3.12", + "@mantine/notifications": "^8.3.12", + "@sentry/browser": "^10.33.0", "@sentry/webpack-plugin": "^4.6.1", "@simplewebauthn/browser": "13.2.2", "@svgr/webpack": "^8.1.0", @@ -31,12 +31,12 @@ "@types/jquery": "^3.5.33", "@types/jquery.transit": "^0.9.33", "@types/json-schema": "^7.0.15", - "@types/katex": "^0.16.7", + "@types/katex": "^0.16.8", "@types/markdown-it": "^14.1.2", "@types/nunjucks": "^3.2.6", "@types/pickadate": "^3.5.35", "@types/qrcode": "^1.5.6", - "@types/react": "^19.2.7", + "@types/react": "^19.2.8", "@types/react-dom": "^19.2.3", "@types/redux-logger": "^3.0.13", "@types/uuid": "^11.0.0", @@ -55,12 +55,12 @@ "core-js": "^3.47.0", "core-js-compat": "^3.47.0", "css-loader": "^7.1.2", - "diff": "^8.0.2", + "diff": "^8.0.3", "diff-dom": "^5.2.1", "echarts": "^6.0.0", "emojis-keywords": "2.0.0", "emojis-list": "2.1.0", - "esbuild-loader": "^4.4.1", + "esbuild-loader": "^4.4.2", "flatpickr": "^4.6.13", "idb": "^8.0.3", "jquery": "^3.7.1", @@ -69,7 +69,7 @@ "jquery.transit": "^0.9.12", "mantine-contextmenu": "^8.3.11", "matchmedia-polyfill": "^0.3.2", - "md-editor-rt": "^6.2.1", + "md-editor-rt": "^6.3.1", "mini-css-extract-plugin": "^2.9.4", "moment": "^2.30.1", "monaco-editor": "0.52.2", @@ -80,7 +80,7 @@ "nanoid": "^5.1.6", "normalize.css": "^8.0.1", "nprogress": "^0.2.0", - "p-queue": "^9.0.1", + "p-queue": "^9.1.0", "pickadate": "^3.6.4", "postcss-loader": "7.3.4", "postcss-preset-mantine": "^1.18.0", @@ -101,7 +101,7 @@ "redux-promise-middleware": "^6.2.0", "redux-thunk": "^3.1.0", "rupture": "^0.7.1", - "sass": "^1.97.1", + "sass": "^1.97.2", "sass-loader": "^16.0.6", "schemastery": "^3.17.2", "shorty.js": "^1.0.1", @@ -118,8 +118,8 @@ "ts-loader": "^9.5.4", "web-streams-polyfill": "^4.2.0", "webpack": "^5.104.1", - "webpack-bundle-analyzer": "^5.1.0", - "webpack-dev-server": "^5.2.2", + "webpack-bundle-analyzer": "^5.1.1", + "webpack-dev-server": "^5.2.3", "webpack-manifest-plugin": "^6.0.1", "webpackbar": "^7.0.0" }, diff --git a/packages/ui-default/templates/components/comments_solution.html b/packages/ui-default/templates/components/comments_solution.html index 593ce15c1..7528ab050 100644 --- a/packages/ui-default/templates/components/comments_solution.html +++ b/packages/ui-default/templates/components/comments_solution.html @@ -124,7 +124,7 @@ {% endif %} -
+
{{ doc['content']|markdown|safe }}
@@ -154,7 +154,7 @@ {% endif %}
-
+
{{ rdoc['content']|markdown|safe }}
diff --git a/packages/ui-next/package.json b/packages/ui-next/package.json index 5914a58ee..235c0fd09 100644 --- a/packages/ui-next/package.json +++ b/packages/ui-next/package.json @@ -13,9 +13,9 @@ "react-dom": "^19.2.3" }, "devDependencies": { - "@types/react": "^19.2.7", + "@types/react": "^19.2.8", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react-swc": "^4.2.2", - "vite": "^8.0.0-beta.5" + "vite": "^8.0.0-beta.7" } }