diff --git a/internal/handlers/user.go b/internal/handlers/user.go index cfd0466..c8d8a77 100644 --- a/internal/handlers/user.go +++ b/internal/handlers/user.go @@ -244,3 +244,145 @@ func UpdateUserType(c *gin.Context) { "data": userInfo, }) } + +// UpdateProfileRequest 更新用户信息请求 +type UpdateProfileRequest struct { + Nickname string `json:"nickname" binding:"required"` + UserType string `json:"user_type" binding:"required,oneof=ordinary-person management-person"` +} + +// UpdateProfile 更新用户信息 +func UpdateProfile(c *gin.Context) { + var req UpdateProfileRequest + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "success": false, + "message": "请求参数错误", + "error": err.Error(), + }) + return + } + + // 从上下文获取用户信息(由认证中间件设置) + username, exists := c.Get("username") + if !exists { + c.JSON(http.StatusUnauthorized, gin.H{ + "success": false, + "message": "未授权访问", + }) + return + } + + db := database.GetDB() + var user models.User + if err := db.Where("username = ?", username).First(&user).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{ + "success": false, + "message": "用户不存在", + }) + return + } + + // 更新用户信息 + user.Nickname = req.Nickname + user.UserType = req.UserType + if err := db.Save(&user).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "success": false, + "message": "更新用户信息失败", + "error": err.Error(), + }) + return + } + + // 返回更新后的用户信息 + userInfo := models.UserInfoResponse{ + Username: user.Username, + Avatar: user.Avatar, + Nickname: user.Nickname, + UserType: user.UserType, + } + + c.JSON(http.StatusOK, gin.H{ + "success": true, + "message": "用户信息更新成功", + "data": userInfo, + }) +} + +// ChangePasswordRequest 修改密码请求 +type ChangePasswordRequest struct { + OldPassword string `json:"old_password" binding:"required"` + NewPassword string `json:"new_password" binding:"required,min=6"` +} + +// ChangePassword 修改密码 +func ChangePassword(c *gin.Context) { + var req ChangePasswordRequest + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "success": false, + "message": "请求参数错误,新密码长度至少为6位", + "error": err.Error(), + }) + return + } + + // 从上下文获取用户信息(由认证中间件设置) + username, exists := c.Get("username") + if !exists { + c.JSON(http.StatusUnauthorized, gin.H{ + "success": false, + "message": "未授权访问", + }) + return + } + + db := database.GetDB() + var user models.User + if err := db.Where("username = ?", username).First(&user).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{ + "success": false, + "message": "用户不存在", + }) + return + } + + // 验证旧密码 + if !user.CheckPassword(req.OldPassword) { + c.JSON(http.StatusUnauthorized, gin.H{ + "success": false, + "message": "当前密码错误", + }) + return + } + + // 更新密码 + if err := user.HashPassword(req.NewPassword); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "success": false, + "message": "密码加密失败", + "error": err.Error(), + }) + return + } + + // 清除旧的token,强制重新登录 + user.Token = "" + + if err := db.Save(&user).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "success": false, + "message": "密码更新失败", + "error": err.Error(), + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "success": true, + "message": "密码修改成功,请重新登录", + }) +} diff --git a/main.go b/main.go index 024514d..8e45ce5 100644 --- a/main.go +++ b/main.go @@ -40,7 +40,9 @@ func main() { auth := api.Group("", middleware.Auth()) { // 用户相关API - auth.PUT("/user/type", handlers.UpdateUserType) // 更新用户类型 + auth.PUT("/user/type", handlers.UpdateUserType) // 更新用户类型 + auth.PUT("/user/profile", handlers.UpdateProfile) // 更新用户信息 + auth.PUT("/user/password", handlers.ChangePassword) // 修改密码 // 练习题相关API(需要登录) auth.GET("/practice/questions", handlers.GetPracticeQuestions) // 获取练习题目列表 diff --git a/web/package.json b/web/package.json index 77b132f..4310e0a 100644 --- a/web/package.json +++ b/web/package.json @@ -16,7 +16,8 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-markdown": "^10.1.0", - "react-router-dom": "^6.21.3" + "react-router-dom": "^6.21.3", + "remark-gfm": "^4.0.1" }, "devDependencies": { "@types/node": "^20.11.5", diff --git a/web/src/components/AnswerResult.tsx b/web/src/components/AnswerResult.tsx index 3d78c47..bc624e8 100644 --- a/web/src/components/AnswerResult.tsx +++ b/web/src/components/AnswerResult.tsx @@ -2,6 +2,7 @@ import React, { useState } from 'react' import { Alert, Typography, Card, Space, Progress, Button, Spin } from 'antd' import { CheckOutlined, CloseOutlined, TrophyOutlined, CommentOutlined, BulbOutlined, FileTextOutlined, ReloadOutlined } from '@ant-design/icons' import ReactMarkdown from 'react-markdown' +import remarkGfm from 'remark-gfm' import { fetchWithAuth } from '../utils/request' import type { AnswerResult as AnswerResultType } from '../types/question' @@ -218,21 +219,175 @@ const AnswerResult: React.FC = ({ } > -
+
{explanation ? (

{children}

, - h1: ({ children }) =>

{children}

, - h2: ({ children }) =>

{children}

, - h3: ({ children }) =>

{children}

, - ul: ({ children }) =>
    {children}
, - ol: ({ children }) =>
    {children}
, - li: ({ children }) =>
  • {children}
  • , - code: ({ children }) => {children}, - pre: ({ children }) =>
    {children}
    , - blockquote: ({ children }) =>
    {children}
    , + p: ({ children }) => ( +

    + {children} +

    + ), + h1: ({ children }) => ( +

    + {children} +

    + ), + h2: ({ children }) => ( +

    + {children} +

    + ), + h3: ({ children }) => ( +

    + {children} +

    + ), + ul: ({ children }) => ( +
      + {children} +
    + ), + ol: ({ children }) => ( +
      + {children} +
    + ), + li: ({ children }) => ( +
  • + {children} +
  • + ), + code: ({ children, className }) => { + const isInline = !className + return isInline ? ( + + {children} + + ) : ( + + {children} + + ) + }, + pre: ({ children }) => ( +
    +                      {children}
    +                    
    + ), + blockquote: ({ children }) => ( +
    + {children} +
    + ), + table: ({ children }) => ( +
    + + {children} +
    +
    + ), + th: ({ children }) => ( + + {children} + + ), + td: ({ children }) => ( + + {children} + + ), + strong: ({ children }) => ( + {children} + ), + em: ({ children }) => ( + {children} + ), + hr: () => ( +
    + ), }} > {explanation} diff --git a/web/src/pages/Home.module.less b/web/src/pages/Home.module.less index f0fc068..12a03b0 100644 --- a/web/src/pages/Home.module.less +++ b/web/src/pages/Home.module.less @@ -43,7 +43,7 @@ .userInfo { background: rgba(255, 255, 255, 0.85); - padding: 10px 16px; + padding: 12px 16px; border-radius: 16px; backdrop-filter: blur(30px) saturate(180%); -webkit-backdrop-filter: blur(30px) saturate(180%); @@ -52,21 +52,39 @@ 0 1px 3px rgba(0, 0, 0, 0.02), 0 0 0 1px rgba(0, 0, 0, 0.03); border: 0.5px solid rgba(0, 0, 0, 0.04); + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:hover { + background: rgba(255, 255, 255, 0.95); + box-shadow: + 0 4px 12px rgba(0, 0, 0, 0.06), + 0 2px 6px rgba(0, 0, 0, 0.04), + 0 0 0 1px rgba(0, 0, 0, 0.04); + transform: translateY(-2px); + } .userDetails { display: flex; flex-direction: column; - gap: 2px; + gap: 3px; .userNickname { color: #1d1d1f !important; font-size: 15px; font-weight: 600; + line-height: 1.2; } .userUsername { color: #6e6e73 !important; font-size: 12px; + line-height: 1.2; + } + + .userType { + font-size: 11px; + color: #6e6e73; } } } diff --git a/web/src/pages/Home.tsx b/web/src/pages/Home.tsx index e3503cd..68d34fd 100644 --- a/web/src/pages/Home.tsx +++ b/web/src/pages/Home.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' -import { Card, Statistic, Row, Col, Typography, message, Space, Avatar, Button, Modal, Form, Radio, Alert } from 'antd' +import { Card, Statistic, Row, Col, Typography, message, Space, Avatar, Button, Modal, Form, Radio, Alert, Input, Switch, InputNumber, Divider, Badge, Dropdown } from 'antd' +import type { MenuProps } from 'antd' import { FileTextOutlined, CheckCircleOutlined, @@ -12,6 +13,9 @@ import { LogoutOutlined, SettingOutlined, UnorderedListOutlined as ListOutlined, + LockOutlined, + IdcardOutlined, + DownOutlined, } from '@ant-design/icons' import * as questionApi from '../api/question' import { fetchWithAuth } from '../utils/request' @@ -77,8 +81,13 @@ const Home: React.FC = () => { const navigate = useNavigate() const [userInfo, setUserInfo] = useState(null) const [userTypeModalVisible, setUserTypeModalVisible] = useState(false) // 用户类型补充模态框 + const [editProfileVisible, setEditProfileVisible] = useState(false) // 修改用户信息模态框 + const [changePasswordVisible, setChangePasswordVisible] = useState(false) // 修改密码模态框 + const [practiceSettingsVisible, setPracticeSettingsVisible] = useState(false) // 答题设置模态框 const [loading, setLoading] = useState(false) const [userTypeForm] = Form.useForm() + const [editProfileForm] = Form.useForm() + const [changePasswordForm] = Form.useForm() const [statistics, setStatistics] = useState({ total_questions: 0, answered_questions: 0, @@ -87,6 +96,22 @@ const Home: React.FC = () => { accuracy: 0, }) + // 答题设置状态 + const [autoNext, setAutoNext] = useState(() => { + const saved = localStorage.getItem('autoNextEnabled') + return saved !== null ? saved === 'true' : true + }) + + const [autoNextDelay, setAutoNextDelay] = useState(() => { + const saved = localStorage.getItem('autoNextDelay') + return saved !== null ? parseInt(saved, 10) : 2 + }) + + const [randomMode, setRandomMode] = useState(() => { + const saved = localStorage.getItem('randomModeEnabled') + return saved !== null ? saved === 'true' : false + }) + // 加载统计数据 const loadStatistics = async () => { try { @@ -154,6 +179,152 @@ const Home: React.FC = () => { } } + // 修改用户信息 + const handleEditProfile = () => { + if (userInfo) { + editProfileForm.setFieldsValue({ + nickname: userInfo.nickname, + user_type: userInfo.user_type, + }) + setEditProfileVisible(true) + } + } + + const handleUpdateProfile = async (values: { nickname: string; user_type: string }) => { + setLoading(true) + try { + const response = await fetchWithAuth('/api/user/profile', { + method: 'PUT', + body: JSON.stringify(values), + }) + + const data = await response.json() + + if (data.success) { + // 更新本地存储的用户信息 + const user = JSON.parse(localStorage.getItem('user') || '{}') + user.nickname = data.data.nickname + user.user_type = data.data.user_type + localStorage.setItem('user', JSON.stringify(user)) + setUserInfo(user) + + message.success('个人信息更新成功') + setEditProfileVisible(false) + } else { + message.error(data.message || '更新失败') + } + } catch (err) { + message.error('网络错误,请稍后重试') + console.error('更新用户信息错误:', err) + } finally { + setLoading(false) + } + } + + // 修改密码 + const handleChangePassword = async (values: { old_password: string; new_password: string }) => { + setLoading(true) + try { + const response = await fetchWithAuth('/api/user/password', { + method: 'PUT', + body: JSON.stringify(values), + }) + + const data = await response.json() + + if (data.success) { + message.success('密码修改成功,请重新登录') + changePasswordForm.resetFields() + setChangePasswordVisible(false) + // 清除登录信息并跳转到登录页 + setTimeout(() => { + localStorage.removeItem('token') + localStorage.removeItem('user') + navigate('/login') + }, 1000) + } else { + message.error(data.message || '修改失败') + } + } catch (err) { + message.error('网络错误,请稍后重试') + console.error('修改密码错误:', err) + } finally { + setLoading(false) + } + } + + // 答题设置相关函数 + const toggleAutoNext = () => { + const newValue = !autoNext + setAutoNext(newValue) + localStorage.setItem('autoNextEnabled', String(newValue)) + } + + const toggleRandomMode = () => { + const newValue = !randomMode + setRandomMode(newValue) + localStorage.setItem('randomModeEnabled', String(newValue)) + } + + const handleDelayChange = (value: number | null) => { + if (value !== null && value >= 1 && value <= 10) { + setAutoNextDelay(value) + localStorage.setItem('autoNextDelay', String(value)) + } + } + + // 获取用户类型显示文本 + const getUserTypeText = (type?: string) => { + if (!type) return '未设置' + return type === 'ordinary-person' ? '普通涉密人员' : '保密管理人员' + } + + // 退出登录 + const handleLogout = () => { + Modal.confirm({ + title: '确定要退出登录吗?', + onOk: () => { + localStorage.removeItem('token') + localStorage.removeItem('user') + setUserInfo(null) + message.success('已退出登录') + navigate('/login') + }, + }) + } + + // 用户菜单项 + const userMenuItems: MenuProps['items'] = [ + { + key: 'edit-profile', + icon: , + label: '修改个人信息', + onClick: handleEditProfile, + }, + { + key: 'change-password', + icon: , + label: '修改密码', + onClick: () => setChangePasswordVisible(true), + }, + { + key: 'practice-settings', + icon: , + label: '答题设置', + onClick: () => setPracticeSettingsVisible(true), + }, + { + type: 'divider', + }, + { + key: 'logout', + icon: , + label: '退出登录', + danger: true, + onClick: handleLogout, + }, + ] + // 点击题型卡片 const handleTypeClick = async (type: string) => { try { @@ -181,20 +352,6 @@ const Home: React.FC = () => { } } - // 退出登录 - const handleLogout = () => { - Modal.confirm({ - title: '确定要退出登录吗?', - onOk: () => { - localStorage.removeItem('token') - localStorage.removeItem('user') - setUserInfo(null) - message.success('已退出登录') - navigate('/login') - }, - }) - } - return (
    {/* 头部 */} @@ -210,27 +367,30 @@ const Home: React.FC = () => {
    {/* 用户信息 */} {userInfo && ( -
    - - } - /> -
    - {userInfo.nickname} - @{userInfo.username} -
    - -
    -
    + +
    + + } + /> +
    + {userInfo.nickname} + @{userInfo.username} + + {getUserTypeText(userInfo.user_type)} + + } + /> +
    + +
    +
    +
    )}
    @@ -406,6 +566,203 @@ const Home: React.FC = () => { + + {/* 修改个人信息模态框 */} + setEditProfileVisible(false)} + footer={null} + destroyOnClose + > +
    + + } placeholder="请输入姓名" /> + + + + + 普通涉密人员 + 保密管理人员 + + + + + + + + + +
    +
    + + {/* 修改密码模态框 */} + setChangePasswordVisible(false)} + footer={null} + destroyOnClose + > + +
    + + } placeholder="请输入当前密码" /> + + + + } placeholder="请输入新密码(至少6位)" /> + + + ({ + validator(_, value) { + if (!value || getFieldValue('new_password') === value) { + return Promise.resolve() + } + return Promise.reject(new Error('两次输入的密码不一致')) + }, + }), + ]} + > + } placeholder="请再次输入新密码" /> + + + + + + + + +
    +
    + + {/* 答题设置模态框 */} + setPracticeSettingsVisible(false)} + footer={[ + + ]} + width={480} + > + + + +
    +
    + 自动下一题 + + 答对题目后自动跳转到下一题 + +
    +
    + + + {autoNext ? '已开启' : '已关闭'} + +
    +
    + + {autoNext && ( +
    +
    + 延迟时间 + + 设置答对后等待多久跳转到下一题 + +
    +
    + + 秒后自动跳转 +
    +
    + )} + +
    +
    + 随机题目 + + 开启后点击下一题时随机跳转 + +
    +
    + + + {randomMode ? '已开启' : '已关闭'} + +
    +
    +
    +
    ) } diff --git a/web/yarn.lock b/web/yarn.lock index f519c9b..29d3ef8 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -1338,6 +1338,11 @@ escape-string-regexp@^4.0.0: resolved "https://mirrors.yuchat.top/repository/npmjs/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +escape-string-regexp@^5.0.0: + version "5.0.0" + resolved "https://mirrors.yuchat.top/repository/npmjs/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" + integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== + eslint-plugin-react-hooks@^4.6.0: version "4.6.2" resolved "https://mirrors.yuchat.top/repository/npmjs/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz" @@ -1922,11 +1927,26 @@ make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" +markdown-table@^3.0.0: + version "3.0.4" + resolved "https://mirrors.yuchat.top/repository/npmjs/markdown-table/-/markdown-table-3.0.4.tgz#fe44d6d410ff9d6f2ea1797a3f60aa4d2b631c2a" + integrity sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw== + math-intrinsics@^1.1.0: version "1.1.0" resolved "https://mirrors.yuchat.top/repository/npmjs/math-intrinsics/-/math-intrinsics-1.1.0.tgz" integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== +mdast-util-find-and-replace@^3.0.0: + version "3.0.2" + resolved "https://mirrors.yuchat.top/repository/npmjs/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz#70a3174c894e14df722abf43bc250cbae44b11df" + integrity sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg== + dependencies: + "@types/mdast" "^4.0.0" + escape-string-regexp "^5.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + mdast-util-from-markdown@^2.0.0: version "2.0.2" resolved "https://mirrors.yuchat.top/repository/npmjs/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz#4850390ca7cf17413a9b9a0fbefcd1bc0eb4160a" @@ -1945,6 +1965,71 @@ mdast-util-from-markdown@^2.0.0: micromark-util-types "^2.0.0" unist-util-stringify-position "^4.0.0" +mdast-util-gfm-autolink-literal@^2.0.0: + version "2.0.1" + resolved "https://mirrors.yuchat.top/repository/npmjs/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz#abd557630337bd30a6d5a4bd8252e1c2dc0875d5" + integrity sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ== + dependencies: + "@types/mdast" "^4.0.0" + ccount "^2.0.0" + devlop "^1.0.0" + mdast-util-find-and-replace "^3.0.0" + micromark-util-character "^2.0.0" + +mdast-util-gfm-footnote@^2.0.0: + version "2.1.0" + resolved "https://mirrors.yuchat.top/repository/npmjs/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz#7778e9d9ca3df7238cc2bd3fa2b1bf6a65b19403" + integrity sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ== + dependencies: + "@types/mdast" "^4.0.0" + devlop "^1.1.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + +mdast-util-gfm-strikethrough@^2.0.0: + version "2.0.0" + resolved "https://mirrors.yuchat.top/repository/npmjs/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz#d44ef9e8ed283ac8c1165ab0d0dfd058c2764c16" + integrity sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-gfm-table@^2.0.0: + version "2.0.0" + resolved "https://mirrors.yuchat.top/repository/npmjs/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz#7a435fb6223a72b0862b33afbd712b6dae878d38" + integrity sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg== + dependencies: + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + markdown-table "^3.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-gfm-task-list-item@^2.0.0: + version "2.0.0" + resolved "https://mirrors.yuchat.top/repository/npmjs/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz#e68095d2f8a4303ef24094ab642e1047b991a936" + integrity sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ== + dependencies: + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-gfm@^3.0.0: + version "3.1.0" + resolved "https://mirrors.yuchat.top/repository/npmjs/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz#2cdf63b92c2a331406b0fb0db4c077c1b0331751" + integrity sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ== + dependencies: + mdast-util-from-markdown "^2.0.0" + mdast-util-gfm-autolink-literal "^2.0.0" + mdast-util-gfm-footnote "^2.0.0" + mdast-util-gfm-strikethrough "^2.0.0" + mdast-util-gfm-table "^2.0.0" + mdast-util-gfm-task-list-item "^2.0.0" + mdast-util-to-markdown "^2.0.0" + mdast-util-mdx-expression@^2.0.0: version "2.0.1" resolved "https://mirrors.yuchat.top/repository/npmjs/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz#43f0abac9adc756e2086f63822a38c8d3c3a5096" @@ -2059,6 +2144,85 @@ micromark-core-commonmark@^2.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" +micromark-extension-gfm-autolink-literal@^2.0.0: + version "2.1.0" + resolved "https://mirrors.yuchat.top/repository/npmjs/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz#6286aee9686c4462c1e3552a9d505feddceeb935" + integrity sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-footnote@^2.0.0: + version "2.1.0" + resolved "https://mirrors.yuchat.top/repository/npmjs/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz#4dab56d4e398b9853f6fe4efac4fc9361f3e0750" + integrity sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw== + dependencies: + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-strikethrough@^2.0.0: + version "2.1.0" + resolved "https://mirrors.yuchat.top/repository/npmjs/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz#86106df8b3a692b5f6a92280d3879be6be46d923" + integrity sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw== + dependencies: + devlop "^1.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-table@^2.0.0: + version "2.1.1" + resolved "https://mirrors.yuchat.top/repository/npmjs/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz#fac70bcbf51fe65f5f44033118d39be8a9b5940b" + integrity sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg== + dependencies: + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-tagfilter@^2.0.0: + version "2.0.0" + resolved "https://mirrors.yuchat.top/repository/npmjs/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz#f26d8a7807b5985fba13cf61465b58ca5ff7dc57" + integrity sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg== + dependencies: + micromark-util-types "^2.0.0" + +micromark-extension-gfm-task-list-item@^2.0.0: + version "2.1.0" + resolved "https://mirrors.yuchat.top/repository/npmjs/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz#bcc34d805639829990ec175c3eea12bb5b781f2c" + integrity sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw== + dependencies: + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm@^3.0.0: + version "3.0.0" + resolved "https://mirrors.yuchat.top/repository/npmjs/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz#3e13376ab95dd7a5cfd0e29560dfe999657b3c5b" + integrity sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w== + dependencies: + micromark-extension-gfm-autolink-literal "^2.0.0" + micromark-extension-gfm-footnote "^2.0.0" + micromark-extension-gfm-strikethrough "^2.0.0" + micromark-extension-gfm-table "^2.0.0" + micromark-extension-gfm-tagfilter "^2.0.0" + micromark-extension-gfm-task-list-item "^2.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-types "^2.0.0" + micromark-factory-destination@^2.0.0: version "2.0.1" resolved "https://mirrors.yuchat.top/repository/npmjs/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz#8fef8e0f7081f0474fbdd92deb50c990a0264639" @@ -2838,6 +3002,18 @@ react@^18.2.0: dependencies: loose-envify "^1.1.0" +remark-gfm@^4.0.1: + version "4.0.1" + resolved "https://mirrors.yuchat.top/repository/npmjs/remark-gfm/-/remark-gfm-4.0.1.tgz#33227b2a74397670d357bf05c098eaf8513f0d6b" + integrity sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-gfm "^3.0.0" + micromark-extension-gfm "^3.0.0" + remark-parse "^11.0.0" + remark-stringify "^11.0.0" + unified "^11.0.0" + remark-parse@^11.0.0: version "11.0.0" resolved "https://mirrors.yuchat.top/repository/npmjs/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" @@ -2859,6 +3035,15 @@ remark-rehype@^11.0.0: unified "^11.0.0" vfile "^6.0.0" +remark-stringify@^11.0.0: + version "11.0.0" + resolved "https://mirrors.yuchat.top/repository/npmjs/remark-stringify/-/remark-stringify-11.0.0.tgz#4c5b01dd711c269df1aaae11743eb7e2e7636fd3" + integrity sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-to-markdown "^2.0.0" + unified "^11.0.0" + resize-observer-polyfill@^1.5.1: version "1.5.1" resolved "https://mirrors.yuchat.top/repository/npmjs/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"