()
const { t } = useI18n()
const appStore = useAppStore()
const updateStore = useUpdateStore()
+const { isMac } = usePlatform()
const onLanguageClick: MenuProps["onClick"] = ({ key }) => {
appStore.setLanguage(key as Language)
diff --git a/src/components/UpdateModal.vue b/src/components/UpdateModal.vue
index 0610326..9db245e 100644
--- a/src/components/UpdateModal.vue
+++ b/src/components/UpdateModal.vue
@@ -31,7 +31,10 @@
{{ t("update.log") }}
- {{ logText }}
+
+
+
{{ t("update.emptyLog") }}
+
@@ -62,6 +65,7 @@
import dayjs from "dayjs"
import { ArrowRightOutlined, BulbOutlined } from "@ant-design/icons-vue"
import { useI18n } from "vue-i18n"
+import MarkdownBlock from "@/components/MarkdownBlock.vue"
import { useUpdateStore } from "@/stores/update"
const { t } = useI18n()
@@ -75,7 +79,8 @@ const releaseDate = computed(() =>
info.value?.date ? dayjs(info.value.date).format("YYYY-MM-DD") : "",
)
-const logText = computed(() => info.value?.body?.trim() || t("update.emptyLog"))
+/** 是否存在可渲染的更新日志内容 */
+const hasLog = computed(() => Boolean(info.value?.body?.trim()))
function onCancel() {
store.cancel()
@@ -125,14 +130,11 @@ function onConfirm() {
}
.update-log {
- white-space: pre-wrap;
word-break: break-word;
padding: 12px 14px;
border-radius: 10px;
border: 1px solid rgba(15, 23, 42, 0.08);
background: rgba(148, 163, 184, 0.12);
- font-size: 13px;
- line-height: 1.7;
opacity: 0.9;
}
diff --git a/src/components/WindowsTitleBar.vue b/src/components/WindowsTitleBar.vue
index b5a87b5..3bf03dd 100644
--- a/src/components/WindowsTitleBar.vue
+++ b/src/components/WindowsTitleBar.vue
@@ -10,7 +10,7 @@
Frpilot
-
+
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index ead2eb7..0bd08c5 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -13,6 +13,7 @@
"settings": "Settings",
"minimize": "Minimize",
"maximize": "Maximize",
+ "maximizeDisabled": "Maximizing is not needed on macOS",
"restore": "Restore",
"close": "Close"
},
diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json
index 985674b..d295542 100644
--- a/src/i18n/locales/zh-CN.json
+++ b/src/i18n/locales/zh-CN.json
@@ -13,6 +13,7 @@
"settings": "设置",
"minimize": "最小化",
"maximize": "最大化",
+ "maximizeDisabled": "macOS 下无需最大化窗口",
"restore": "还原",
"close": "关闭"
},
diff --git a/src/types/components.d.ts b/src/types/components.d.ts
index 09021ec..8a0af41 100644
--- a/src/types/components.d.ts
+++ b/src/types/components.d.ts
@@ -43,6 +43,7 @@ declare module 'vue' {
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
FrpCard: typeof import('./../components/frp/FrpCard.vue')['default']
MacTitleBar: typeof import('./../components/MacTitleBar.vue')['default']
+ MarkdownBlock: typeof import('./../components/MarkdownBlock.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
ServerCard: typeof import('./../components/server/ServerCard.vue')['default']
diff --git a/src/utils/markdown.ts b/src/utils/markdown.ts
new file mode 100644
index 0000000..1dba44d
--- /dev/null
+++ b/src/utils/markdown.ts
@@ -0,0 +1,31 @@
+import MarkdownIt from "markdown-it"
+
+/**
+ * 更新日志(Release Notes)Markdown 渲染器。
+ *
+ * 安全约束:
+ * - `html: false`:禁用原始 HTML,杜绝注入;
+ * - markdown-it 默认的 `validateLink` 会拦截 `javascript:` 等危险链接。
+ */
+const md = new MarkdownIt({
+ html: false,
+ linkify: true,
+ breaks: true,
+})
+
+// 外链在新窗口打开(链接点击在组件中已被接管,统一交由系统浏览器处理)
+const defaultLinkOpen = md.renderer.rules.link_open
+md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
+ const token = tokens[idx]
+ token.attrSet("target", "_blank")
+ token.attrSet("rel", "noopener noreferrer")
+ return defaultLinkOpen
+ ? defaultLinkOpen(tokens, idx, options, env, self)
+ : self.renderToken(tokens, idx, options)
+}
+
+/** 将 Markdown 文本渲染为 HTML 字符串(空内容返回空串) */
+export function renderMarkdown(source?: string | null): string {
+ if (!source || !source.trim()) return ""
+ return md.render(source)
+}