diff --git a/docs/changelog.html b/docs/changelog.html new file mode 100644 index 0000000..53fc0cf --- /dev/null +++ b/docs/changelog.html @@ -0,0 +1,75 @@ + + + + + + VoidRaft - Changelog + + + + + + + + +
+ +
+
+

VoidRaft 更新日志

+
+ + +
+
+ +
+ + + + +
+
+

正在加载版本信息...

+
+ + +
+ +
+ + + +
+ + + +
+
+ + + + + \ No newline at end of file diff --git a/docs/css/changelog.css b/docs/css/changelog.css new file mode 100644 index 0000000..5620b03 --- /dev/null +++ b/docs/css/changelog.css @@ -0,0 +1,312 @@ +/* 更新日志页面样式 */ +.nav-links { + margin-bottom: 30px; + display: flex; + gap: 15px; +} + +.loading-container { + text-align: center; + padding: 40px 0; + background-color: transparent; +} + +.loading-spinner { + border: 4px solid rgba(0, 0, 0, 0.1); + border-left-color: var(--primary-color); + border-radius: 50%; + width: 40px; + height: 40px; + animation: spin 1s linear infinite; + margin: 0 auto 20px; +} + +.theme-dark .loading-spinner { + border-color: rgba(255, 255, 255, 0.1); + border-left-color: var(--primary-color); +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.error-container { + text-align: center; + color: var(--error-color); + padding: 20px; + border: 2px dashed var(--error-color); + margin: 20px 0; + border-radius: 4px; + background-color: rgba(var(--card-bg-rgb), 0.7); +} + +.error-container i { + font-size: 24px; + margin-bottom: 10px; +} + +/* 更新日志容器 */ +.changelog-container { + display: none; + position: relative; + z-index: 1; +} + +.release { + margin-bottom: 40px; + border-left: 4px solid var(--primary-color); + padding-left: 20px; + background-color: rgba(var(--card-bg-rgb), 0.5); + padding: 15px 20px; + border-radius: 4px; +} + +.release-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; +} + +.release-version { + font-size: 24px; + font-weight: bold; + color: var(--primary-color); +} + +.release-date { + color: var(--text-color); + opacity: 0.7; + font-size: 14px; +} + +.release-badge { + display: inline-block; + padding: 3px 8px; + border-radius: 12px; + font-size: 12px; + margin-left: 10px; + background-color: var(--primary-color); + color: #000; +} + +.release-badge.pre-release { + background-color: var(--warning-color); +} + +.release-description { + margin-bottom: 20px; + line-height: 1.6; +} + +.release-assets { + background-color: rgba(var(--light-bg-rgb), 0.7); + padding: 15px; + border-radius: 4px; + margin-top: 15px; +} + +.release-assets-title { + font-size: 16px; + margin-bottom: 10px; +} + +.asset-list { + list-style-type: none; + padding: 0; + margin: 0; +} + +.asset-item { + display: flex; + align-items: center; + padding: 8px 0; + border-bottom: 1px solid rgba(128, 128, 128, 0.2); +} + +.asset-item:last-child { + border-bottom: none; +} + +.asset-icon { + margin-right: 10px; + color: var(--accent-color); +} + +.asset-name { + flex-grow: 1; +} + +.asset-size { + font-size: 12px; + color: var(--text-color); + opacity: 0.7; +} + +/* 资源下载按钮 */ +.download-btn { + margin-left: 10px; + padding: 3px 10px; + background-color: var(--primary-color); + color: white; + border: none; + border-radius: 4px; + text-decoration: none; + font-size: 12px; + transition: all 0.2s ease; + display: inline-block; + text-align: center; +} + +.download-btn:hover { + background-color: var(--secondary-color); +} + +.markdown-content { + line-height: 1.8; + overflow-wrap: break-word; + background-color: transparent; +} + +.markdown-content h1, +.markdown-content h2, +.markdown-content h3 { + margin-top: 20px; + margin-bottom: 10px; +} + +.markdown-content ul, +.markdown-content ol { + padding-left: 20px; + margin-bottom: 15px; +} + +.markdown-content code { + font-family: 'IBM Plex Mono', monospace; + background-color: rgba(128, 128, 128, 0.1); + padding: 2px 4px; + border-radius: 3px; + font-size: 90%; +} + +.markdown-content pre { + background-color: rgba(128, 128, 128, 0.1); + padding: 15px; + border-radius: 4px; + overflow-x: auto; + margin: 15px 0; +} + +.markdown-content pre code { + background-color: transparent; + padding: 0; +} + +.markdown-content a { + color: var(--primary-color); + text-decoration: none; +} + +.markdown-content a:hover { + text-decoration: underline; +} + +.data-source { + padding: 10px 15px; + margin-bottom: 20px; + background-color: rgba(var(--light-bg-rgb), 0.7); + border-radius: 4px; + font-size: 14px; + text-align: right; + opacity: 0.7; +} + +.data-source a { + color: var(--primary-color); + text-decoration: none; + font-weight: bold; +} + +.data-source a:hover { + text-decoration: underline; +} + +/* 移动设备响应式优化 */ +@media (max-width: 768px) { + .release-header { + flex-direction: column; + align-items: flex-start; + gap: 8px; + } + + .release-assets { + padding: 12px 8px; + } + + .asset-item { + flex-wrap: wrap; + padding: 12px 0; + position: relative; + } + + .asset-name { + width: 100%; + margin-bottom: 8px; + word-break: break-all; + } + + .asset-size { + margin-left: 25px; + } + + .download-btn { + margin-left: 10px; + padding: 5px 12px; + } +} + +@media (max-width: 480px) { + .release { + padding-left: 12px; + } + + .asset-item { + flex-direction: column; + align-items: flex-start; + } + + .asset-icon { + margin-bottom: 5px; + } + + .asset-size { + margin-left: 0; + margin-top: 5px; + } + + .download-btn { + margin-left: 0; + margin-top: 10px; + width: 100%; + text-align: center; + padding: 8px; + } + + .markdown-content pre { + padding: 10px; + margin: 10px 0; + } +} + +/* 确保日志页面页脚样式一致 */ +.footer { + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; +} + +.footer-text { + margin: 0 0 15px 0; +} \ No newline at end of file diff --git a/docs/css/styles.css b/docs/css/styles.css new file mode 100644 index 0000000..18d2914 --- /dev/null +++ b/docs/css/styles.css @@ -0,0 +1,717 @@ +@import url('https://fonts.googleapis.com/css2?family=Space+Mono&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&display=swap'); + +/* 浅色主题 */ +:root { + --bg-color: #fefefe; + --text-color: #000000; + --primary-color: #F08080; + --primary-color-rgb: 240, 128, 128; + --secondary-color: #ff006e; + --accent-color: #073B4C; + --card-bg: #ffffff; + --card-bg-rgb: 255, 255, 255; + --border-color: #000000; + --light-bg: #f0f0f0; + --light-bg-rgb: 240, 240, 240; + --shadow-color: rgba(240, 128, 128, 0.5); + --success-color: #27c93f; + --warning-color: #FFD166; + --error-color: #ff006e; + --info-color: #118ab2; + --code-bg: #ffffff; + --code-bg-rgb: 255, 255, 255; + --preview-header-bg: #f0f0f0; + --preview-header-bg-rgb: 240, 240, 240; + --grid-color-1: rgba(0, 0, 0, 0.08); + --grid-color-2: rgba(0, 0, 0, 0.05); + --header-title-color: #000000; +} + +/* 暗色主题变量 */ +.theme-dark { + --bg-color: #121212; + --text-color: #ffffff; + --primary-color: #F08080; + --primary-color-rgb: 240, 128, 128; + --secondary-color: #ff006e; + --accent-color: #118ab2; + --card-bg: #1e1e1e; + --card-bg-rgb: 30, 30, 30; + --border-color: #ffffff; + --light-bg: #2a2a2a; + --light-bg-rgb: 42, 42, 42; + --shadow-color: rgba(240, 128, 128, 0.5); + --success-color: #27c93f; + --warning-color: #FFD166; + --error-color: #ff006e; + --info-color: #118ab2; + --code-bg: #1e1e1e; + --code-bg-rgb: 30, 30, 30; + --preview-header-bg: #252526; + --preview-header-bg-rgb: 37, 37, 38; + --grid-color-1: rgba(255, 255, 255, 0.08); + --grid-color-2: rgba(255, 255, 255, 0.05); + --header-title-color: #000000; +} + +/* 主题切换和语言切换的过渡效果 */ +.theme-transition, +.theme-transition *, +.lang-transition, +.lang-transition * { + transition: all 0.3s ease !important; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +@keyframes gridMove { + 0% { + background-position: 0px 0px, 0px 0px, 0px 0px, 0px 0px; + } + 100% { + background-position: 80px 80px, 80px 80px, 20px 20px, 20px 20px; + } +} + +body { + background-color: var(--bg-color); + background-image: + linear-gradient(var(--grid-color-1) 1px, transparent 1px), + linear-gradient(90deg, var(--grid-color-1) 1px, transparent 1px), + linear-gradient(var(--grid-color-2) 0.5px, transparent 0.5px), + linear-gradient(90deg, var(--grid-color-2) 0.5px, transparent 0.5px); + background-size: 80px 80px, 80px 80px, 20px 20px, 20px 20px; + background-position: center; + animation: gridMove 40s linear infinite; + font-family: 'Space Mono', monospace; + color: var(--text-color); + line-height: 1.6; + padding: 20px; + transition: background-color 0.3s ease, color 0.3s ease; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +/* 卡片容器 */ +.card { + background-color: var(--card-bg); + background-image: + linear-gradient(var(--grid-color-1) 1px, transparent 1px), + linear-gradient(90deg, var(--grid-color-1) 1px, transparent 1px), + linear-gradient(var(--grid-color-2) 0.5px, transparent 0.5px), + linear-gradient(90deg, var(--grid-color-2) 0.5px, transparent 0.5px); + background-size: 80px 80px, 80px 80px, 20px 20px, 20px 20px; + background-position: center; + border: 4px solid var(--border-color); + box-shadow: 12px 12px 0 var(--shadow-color); + margin-bottom: 40px; + overflow: hidden; + transition: transform 0.3s ease, box-shadow 0.3s ease; + position: relative; + z-index: 10; +} + +.card:hover { + transform: translateY(-5px); + box-shadow: 16px 16px 0 var(--shadow-color); +} + +/* 卡片头部 */ +.card-header { + background-color: rgba(var(--primary-color-rgb), 0.9); + border-bottom: 4px solid var(--border-color); + padding: 20px; + display: flex; + justify-content: space-between; + align-items: center; + position: relative; + z-index: 1; +} + +.card-title { + font-size: 24px; + font-weight: bold; + margin: 0; + color: var(--header-title-color); +} + +.card-controls { + display: flex; + gap: 10px; +} + +.btn { + display: inline-block; + padding: 10px 20px; + background: var(--secondary-color); + color: #fff; + text-decoration: none; + font-weight: bold; + border: 3px solid var(--border-color); + box-shadow: 4px 4px 0 var(--shadow-color); + transition: all 0.2s ease; + cursor: pointer; + font-family: 'Space Mono', monospace; + font-size: 14px; +} + +.btn:hover { + background: var(--card-bg); + color: var(--primary-color); + border: 3px solid var(--primary-color); + box-shadow: none; +} + +.btn-secondary { + background: var(--light-bg); + color: var(--text-color); +} + +.btn-secondary:hover { + background: var(--card-bg); + color: var(--primary-color); + border: 3px solid var(--primary-color); +} + +/* 卡片内容 */ +.card-content { + padding: 30px; + position: relative; + z-index: 1; + background-color: rgba(var(--card-bg-rgb), 0.5); +} + +/* Logo区域 */ +.logo-container { + text-align: center; + margin-bottom: 40px; +} + +.logo-frame { + width: 150px; + height: 150px; + background: var(--card-bg); + border: 4px solid var(--border-color); + margin: 0 auto; + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 20px; +} + +.logo-image { + width: 130px; + height: 130px; + object-fit: contain; + border: 2px solid var(--border-color); +} + +.logo-text { + font-size: 32px; + font-weight: bold; + margin: 0; +} + +.tagline { + font-size: 16px; + margin: 10px 0 0; + color: var(--accent-color); +} + +/* 介绍区域 */ +.intro-box { + border: 2px dashed var(--border-color); + padding: 20px; + background-color: rgba(var(--light-bg-rgb), 0.7); + margin-bottom: 30px; + text-align: center; +} + +.intro-text { + font-size: 16px; + margin-bottom: 0; +} + +/* 按钮组 */ +.button-group { + display: flex; + justify-content: center; + gap: 20px; + margin: 30px 0; +} + +/* 特性网格 */ +.features-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 30px; + margin: 40px 0; +} + +/* 特性卡片 */ +.feature-card { + background-color: rgba(var(--card-bg-rgb), 0.8); + border: 3px solid var(--border-color); + box-shadow: 5px 5px 0 var(--shadow-color); + padding: 20px; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.feature-card:hover { + transform: translateY(-3px); + box-shadow: 7px 7px 0 var(--shadow-color); +} + +.feature-icon { + font-size: 24px; + margin-bottom: 15px; + color: var(--secondary-color); +} + +.feature-title { + font-size: 18px; + font-weight: bold; + margin-bottom: 10px; +} + +.feature-desc { + font-size: 14px; +} + +/* 预览区域 */ +.preview-container { + display: flex; + flex-wrap: wrap; + gap: 20px; + margin: 30px 0; +} + +@media (max-width: 768px) { + .preview-container { + grid-template-columns: 1fr; + } +} + +/* 预览窗口 */ +.preview-window { + border: 3px solid var(--border-color); + border-radius: 8px; + overflow: hidden; + margin: 10px; + flex: 1; + min-width: 300px; + background-color: rgba(var(--card-bg-rgb), 0.7); + display: flex; + flex-direction: column; + box-shadow: 5px 5px 0 var(--shadow-color); +} + +/* 预览头部 */ +.preview-header { + background-color: rgba(var(--preview-header-bg-rgb), 0.9); + padding: 10px; + display: flex; + align-items: center; + border-bottom: 2px solid var(--border-color); +} + +.preview-controls { + display: flex; + gap: 6px; + margin-right: 15px; +} + +.preview-btn { + width: 12px; + height: 12px; + border-radius: 50%; + border: 0.5px solid rgba(0, 0, 0, 0.1); +} + +.preview-btn:nth-child(1) { + background-color: #ff5f56; +} + +.preview-btn:nth-child(2) { + background-color: #ffbd2e; +} + +.preview-btn:nth-child(3) { + background-color: #27c93f; +} + +.preview-title { + font-size: 13px; + opacity: 0.8; + color: var(--text-color); + font-weight: normal; +} + +/* 预览内容 */ +.preview-content { + padding: 15px; + flex-grow: 1; + overflow: auto; + background-color: rgba(var(--code-bg-rgb), 0.5); +} + +/* 代码块容器 */ +.code-block-wrapper { + background-color: rgba(var(--code-bg-rgb), 0.8); + border: 2px solid var(--border-color); + border-radius: 4px; + overflow: hidden; + margin-bottom: 8px; +} + +/* 块头部 */ +.block-header { + background-color: rgba(var(--light-bg-rgb), 0.8); + padding: 8px 12px; + border-bottom: 2px solid var(--border-color); + display: flex; + justify-content: space-between; + align-items: center; +} + +.block-language { + color: rgba(128, 128, 128, 0.8); + font-family: 'IBM Plex Mono', monospace; + display: flex; + align-items: center; +} + +.block-language::before { + content: ''; + display: inline-block; + width: 12px; + height: 12px; + margin-right: 5px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23888'%3E%3Cpath d='M9.7,16.7L5.3,12.3C4.9,11.9 4.9,11.1 5.3,10.7C5.7,10.3 6.3,10.3 6.7,10.7L10.5,14.5L17.3,7.7C17.7,7.3 18.3,7.3 18.7,7.7C19.1,8.1 19.1,8.7 18.7,9.1L11.3,16.7C10.9,17.1 10.1,17.1 9.7,16.7Z'/%3E%3C/svg%3E"); + background-size: contain; + background-repeat: no-repeat; +} + +.code-block { + font-family: 'IBM Plex Mono', monospace; + font-size: 13px; + line-height: 1.6; + margin: 0; + white-space: pre; + tab-size: 4; + -moz-tab-size: 4; + padding: 10px; +} + +.theme-dark .code-block-wrapper { + border-color: rgba(255, 255, 255, 0.15); +} + +.theme-dark .block-header { + background-color: rgba(255, 255, 255, 0.05); + border-color: rgba(255, 255, 255, 0.15); +} + +.theme-dark .block-language { + color: rgba(255, 255, 255, 0.6); +} + +.theme-dark .block-language::before { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23aaa'%3E%3Cpath d='M9.7,16.7L5.3,12.3C4.9,11.9 4.9,11.1 5.3,10.7C5.7,10.3 6.3,10.3 6.7,10.7L10.5,14.5L17.3,7.7C17.7,7.3 18.3,7.3 18.7,7.7C19.1,8.1 19.1,8.7 18.7,9.1L11.3,16.7C10.9,17.1 10.1,17.1 9.7,16.7Z'/%3E%3C/svg%3E"); +} + +.theme-dark .code-block { + color: #d4d4d4; +} + +/* 代码高亮 */ +.theme-dark .keyword { color: #c586c0; } +.theme-dark .function { color: #dcdcaa; } +.theme-dark .variable { color: #9cdcfe; } +.theme-dark .string { color: #ce9178; } +.theme-dark .comment { color: #6a9955; } +.theme-dark .class { color: #4ec9b0; } +.theme-dark .parameter { color: #9cdcfe; } +.theme-dark .built-in { color: #4ec9b0; } + +/* 浅色主题代码高亮 */ +.keyword { color: #af00db; } +.function { color: #795e26; } +.variable { color: #001080; } +.string { color: #a31515; } +.comment { color: #008000; } +.class { color: #267f99; } +.parameter { color: #001080; } +.built-in { color: #267f99; } + +.preview-image { + width: 100%; + height: 100%; + object-fit: cover; + display: block; + border: none; + transition: opacity 0.3s ease; +} + +.theme-dark .light-theme-img { + display: none !important; +} + +.theme-dark .dark-theme-img { + display: block; +} + +body:not(.theme-dark) .dark-theme-img { + display: none !important; +} + +body:not(.theme-dark) .light-theme-img { + display: block !important; +} + +/* 技术栈列表 */ +.tech-list { + list-style: none; + padding: 0; + margin: 0; +} + +/* 技术栈列表 */ +.tech-item { + padding: 15px; + margin-bottom: 15px; + border: 2px solid var(--border-color); + background-color: rgba(var(--light-bg-rgb), 0.7); + display: flex; + align-items: center; +} + +.tech-icon { + margin-right: 15px; + color: var(--secondary-color); + font-size: 20px; + width: 30px; + text-align: center; +} + +.tech-name { + font-weight: bold; + margin-right: 10px; +} + +.tech-desc { + font-size: 14px; + color: var(--accent-color); +} + +/* 页脚 */ +.footer { + border-top: 2px solid var(--border-color); + padding: 20px 0; + margin-top: 40px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + background-color: transparent; + position: relative; + z-index: 1; +} + +.footer-text { + margin: 0 0 15px 0; + font-size: 14px; + opacity: 0.7; +} + +.footer-links { + display: flex; + gap: 15px; + justify-content: center; +} + +.footer-link { + color: var(--secondary-color); + text-decoration: none; + font-size: 14px; + transition: color 0.3s; +} + +.footer-link:hover { + color: var(--primary-color); + text-decoration: underline; +} + +/* 响应式设计 */ +@media (max-width: 768px) { + .button-group { + flex-direction: column; + align-items: center; + } + + .btn { + width: 100%; + text-align: center; + } + + .features-grid { + grid-template-columns: 1fr; + } +} + +@media (max-width: 480px) { + .card-header { + flex-direction: column; + gap: 15px; + } + + .card-controls { + width: 100%; + } + + .logo-frame { + width: 120px; + height: 120px; + } + + .logo-image { + width: 100px; + height: 100px; + } +} + +/* 针对移动设备的响应式优化 */ +@media (max-width: 768px) { + body { + padding: 10px; + } + + .container { + padding: 10px; + } + + .card { + margin-bottom: 30px; + } + + .card-header { + flex-direction: column; + gap: 15px; + text-align: center; + } + + .card-controls { + width: 100%; + justify-content: center; + } + + .button-group { + flex-wrap: wrap; + gap: 15px; + } + + /* 预览区域优化 */ + .preview-content { + max-width: 100%; + overflow-x: auto; + } + + .code-block { + white-space: pre-wrap; + word-break: break-word; + font-size: 13px; + line-height: 1.4; + } + + .block-header { + padding: 6px 10px; + } + + /* 日志界面导航链接优化 */ + .nav-links { + flex-direction: column; + gap: 10px; + align-items: stretch; + } + + .nav-links .btn { + width: 100%; + text-align: center; + } +} + +@media (max-width: 480px) { + /* 特性卡片优化 */ + .features-grid { + grid-template-columns: 1fr; + gap: 20px; + } + + /* 预览窗口优化 */ + .preview-container { + flex-direction: column; + } + + .preview-window { + margin-bottom: 20px; + width: 100%; + } + + /* 技术栈列表小屏幕优化 */ + .tech-item { + flex-wrap: wrap; + } + + .tech-desc { + width: 100%; + padding-left: 40px; /* 图标宽度+右边距 */ + margin-top: 5px; + } + + /* 日志界面资源列表项优化 */ + .asset-item { + flex-wrap: wrap; + padding: 15px 0; + } + + .asset-name { + width: 100%; + word-break: break-all; + margin-bottom: 10px; + } + + .asset-size { + order: 2; + margin-top: 10px; + } + + .download-btn { + order: 3; + margin-left: 0; + margin-top: 10px; + width: 100%; + text-align: center; + padding: 8px; + } + + /* 页脚链接优化 */ + .footer { + flex-direction: column; + text-align: center; + } + + .footer-links { + margin-top: 15px; + justify-content: center; + } +} \ No newline at end of file diff --git a/docs/img/favicon.ico b/docs/img/favicon.ico new file mode 100644 index 0000000..613d223 Binary files /dev/null and b/docs/img/favicon.ico differ diff --git a/docs/img/logo.png b/docs/img/logo.png new file mode 100644 index 0000000..613d223 Binary files /dev/null and b/docs/img/logo.png differ diff --git a/docs/img/screenshot-dark.png b/docs/img/screenshot-dark.png new file mode 100644 index 0000000..dee08b5 Binary files /dev/null and b/docs/img/screenshot-dark.png differ diff --git a/docs/img/screenshot-light.png b/docs/img/screenshot-light.png new file mode 100644 index 0000000..54612a5 Binary files /dev/null and b/docs/img/screenshot-light.png differ diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..4bc9c7a --- /dev/null +++ b/docs/index.html @@ -0,0 +1,205 @@ + + + + + + VoidRaft - An elegant text snippet recording tool designed for developers. + + + + + + + +
+ +
+
+

VoidRaft

+
+ + +
+
+ +
+ +
+
+ VoidRaft Logo +
+

VoidRaft

+

优雅的文本片段记录工具

+
+ +
+

专为开发者打造,随时随地记录、整理和管理各种文本片段。

+
+ + + + +

核心特性

+ +
+
+
+ +
+

开发者友好

+

多语言代码块支持,为30+种编程语言提供语法高亮

+
+ +
+
+ +
+

代码格式化

+

内置Prettier支持,一键美化代码

+
+ +
+
+ +
+

自定义主题

+

深色/浅色主题,支持完全自定义

+
+ +
+
+ +
+

多窗口支持

+

同时编辑多个文档

+
+ +
+
+ +
+

块状编辑

+

将内容分割为独立的代码块,每个块可设置不同语言

+
+ +
+
+ +
+

丰富扩展

+

彩虹括号、VSCode风格搜索、颜色选择器、翻译工具等多种扩展

+
+
+ + +

预览

+ +
+
+
+
+ + + +
+
voidraft
+
+
+
+
+
javascript
+
+
+function createDocument() {
+  const doc = new Document();
+  
+  doc.addCodeBlock('javascript', `
+  function greeting(name) {
+    return `Hello, ${name}!`;
+  }
+  
+  console.log(greeting('World'));
+  `);
+  
+  return doc;
+}
+
+ +
+
+
text
+
+
+// VoidRaft - An elegant text snippet recording tool
+// Multi-language support | Code formatting | Custom themes
+// A modern text editor designed for developers
+
+
+
+
+ VoidRaft 界面预览 + +
+
+ + +

技术栈

+ +
    +
  • +
    + Wails3 + 跨平台桌面应用框架 +
  • +
  • +
    + Go 1.21+ + 快速高效的后端语言 +
  • +
  • +
    + Vue 3 + TypeScript + 现代化前端框架 +
  • +
  • +
    + CodeMirror 6 + 支持扩展的现代化代码编辑器 +
  • +
  • +
    + SQLite + 轻量级文档存储数据库 +
  • +
+
+ + + +
+
+ + + + \ No newline at end of file diff --git a/docs/js/changelog.js b/docs/js/changelog.js new file mode 100644 index 0000000..46081b7 --- /dev/null +++ b/docs/js/changelog.js @@ -0,0 +1,494 @@ +/** + * VoidRaft - Changelog Script + * Fetches release information from GitHub API with Gitea fallback + */ +document.addEventListener('DOMContentLoaded', () => { + // Repository information + const REPOS = { + github: { + owner: 'landaiqing', + name: 'voidraft', + apiUrl: 'https://api.github.com/repos/landaiqing/voidraft/releases', + releasesUrl: 'https://github.com/landaiqing/voidraft/releases' + }, + gitea: { + owner: 'landaiqing', + name: 'voidraft', + domain: 'git.landaiqing.cn', + apiUrl: 'https://git.landaiqing.cn/api/v1/repos/landaiqing/voidraft/releases', + releasesUrl: 'https://git.landaiqing.cn/landaiqing/voidraft/releases' + } + }; + + // Error messages with i18n support + const MESSAGES = { + loading: { + en: 'Loading releases...', + zh: '正在加载版本信息...' + }, + noReleases: { + en: 'No release information found', + zh: '没有找到版本发布信息' + }, + fetchError: { + en: 'Failed to load release information. Please try again later.', + zh: '无法获取版本信息,请稍后再试' + }, + githubApiError: { + en: 'GitHub API returned an error status: ', + zh: 'GitHub API返回错误状态: ' + }, + giteaApiError: { + en: 'Gitea API returned an error status: ', + zh: 'Gitea API返回错误状态: ' + }, + dataSource: { + en: 'Data source: ', + zh: '数据来源: ' + }, + downloads: { + en: 'Downloads', + zh: '下载资源' + }, + download: { + en: 'Download', + zh: '下载' + }, + preRelease: { + en: 'Pre-release', + zh: '预发布' + } + }; + + // Element references + const elements = { + loading: document.getElementById('loading'), + changelog: document.getElementById('changelog'), + error: document.getElementById('error-message') + }; + + // Initialize + init(); + + /** + * Initialize the changelog + */ + function init() { + // Try GitHub API first + fetchReleases('github') + .catch(() => fetchReleases('gitea')) + .catch(error => { + elements.loading.style.display = 'none'; + showError(MESSAGES.fetchError[getCurrentLang()]); + }); + } + + /** + * Get current language + */ + function getCurrentLang() { + return window.currentLang || 'en'; + } + + /** + * Fetch releases from specified source + * @param {string} source - 'github' or 'gitea' + */ + async function fetchReleases(source) { + const apiUrl = REPOS[source].apiUrl; + const errorMessageKey = source === 'github' ? 'githubApiError' : 'giteaApiError'; + + // Setup timeout for GitHub + const options = { + headers: { 'Accept': 'application/json' } + }; + + if (source === 'github') { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout + options.signal = controller.signal; + options.headers['Accept'] = 'application/vnd.github.v3+json'; + + try { + const response = await fetch(apiUrl, options); + clearTimeout(timeoutId); + + if (!response.ok) { + throw new Error(`${MESSAGES[errorMessageKey][getCurrentLang()]}${response.status}`); + } + + const releases = await response.json(); + + if (!releases || releases.length === 0) { + throw new Error(MESSAGES.noReleases[getCurrentLang()]); + } + + // Display releases + elements.loading.style.display = 'none'; + displayReleases(releases, source); + elements.changelog.style.display = 'block'; + + return releases; + } catch (error) { + clearTimeout(timeoutId); + throw error; + } + } else { + const response = await fetch(apiUrl, options); + + if (!response.ok) { + throw new Error(`${MESSAGES[errorMessageKey][getCurrentLang()]}${response.status}`); + } + + const releases = await response.json(); + + // Hide loading indicator + elements.loading.style.display = 'none'; + + if (!releases || releases.length === 0) { + throw new Error(MESSAGES.noReleases[getCurrentLang()]); + } + + // Display releases + displayReleases(releases, source); + elements.changelog.style.display = 'block'; + + return releases; + } + } + + /** + * Show error message + */ + function showError(message) { + const errorMessageElement = elements.error.querySelector('p'); + errorMessageElement.textContent = message; + elements.error.style.display = 'block'; + } + + /** + * Display releases + * @param {Array} releases - Array of release objects + * @param {string} source - 'github' or 'gitea' + */ + function displayReleases(releases, source) { + // Clear existing content + elements.changelog.innerHTML = ''; + + // Add data source indicator + const sourceElement = createSourceElement(source); + elements.changelog.appendChild(sourceElement); + + // Create release elements + releases.forEach(release => { + const releaseElement = createReleaseElement(release, source); + elements.changelog.appendChild(releaseElement); + }); + } + + /** + * Create source element + */ + function createSourceElement(source) { + const sourceElement = document.createElement('div'); + sourceElement.className = 'data-source'; + + // Create source label with i18n support + const sourceLabel = document.createElement('span'); + sourceLabel.setAttribute('data-en', MESSAGES.dataSource.en); + sourceLabel.setAttribute('data-zh', MESSAGES.dataSource.zh); + sourceLabel.textContent = MESSAGES.dataSource[getCurrentLang()]; + + // Create link + const sourceLink = document.createElement('a'); + sourceLink.href = REPOS[source].releasesUrl; + sourceLink.textContent = source === 'github' ? 'GitHub' : 'Gitea'; + sourceLink.target = '_blank'; + + // Assemble elements + sourceElement.appendChild(sourceLabel); + sourceElement.appendChild(sourceLink); + + return sourceElement; + } + + /** + * Create release element + * @param {Object} release - Release data + * @param {string} source - 'github' or 'gitea' + */ + function createReleaseElement(release, source) { + const releaseElement = document.createElement('div'); + releaseElement.className = 'release'; + + // Format release date + const releaseDate = new Date(release.published_at || release.created_at); + const formattedDate = formatDate(releaseDate); + + // Create header + const headerElement = createReleaseHeader(release, formattedDate); + releaseElement.appendChild(headerElement); + + // Add release description + if (release.body) { + const descriptionElement = document.createElement('div'); + descriptionElement.className = 'release-description markdown-content'; + descriptionElement.innerHTML = parseMarkdown(release.body); + releaseElement.appendChild(descriptionElement); + } + + // Add download assets + const assets = getAssetsFromRelease(release, source); + if (assets && assets.length > 0) { + const assetsElement = createAssetsElement(assets); + releaseElement.appendChild(assetsElement); + } + + return releaseElement; + } + + /** + * Create release header + */ + function createReleaseHeader(release, formattedDate) { + const headerElement = document.createElement('div'); + headerElement.className = 'release-header'; + + // Version element + const versionElement = document.createElement('div'); + versionElement.className = 'release-version'; + + // Version text + const versionText = document.createElement('span'); + versionText.textContent = release.name || release.tag_name; + versionElement.appendChild(versionText); + + // Pre-release badge + if (release.prerelease) { + const preReleaseTag = document.createElement('span'); + preReleaseTag.className = 'release-badge pre-release'; + preReleaseTag.setAttribute('data-en', MESSAGES.preRelease.en); + preReleaseTag.setAttribute('data-zh', MESSAGES.preRelease.zh); + preReleaseTag.textContent = MESSAGES.preRelease[getCurrentLang()]; + versionElement.appendChild(preReleaseTag); + } + + // Date element + const dateElement = document.createElement('div'); + dateElement.className = 'release-date'; + dateElement.textContent = formattedDate; + + headerElement.appendChild(versionElement); + headerElement.appendChild(dateElement); + + return headerElement; + } + + /** + * Get assets from release based on source + */ + function getAssetsFromRelease(release, source) { + let assets = []; + + if (source === 'github') { + assets = release.assets || []; + } else { // Gitea + assets = release.assets || []; + // Check for Gitea-specific asset structure + if (!assets.length && release.attachments) { + assets = release.attachments.map(attachment => ({ + name: attachment.name, + size: attachment.size, + browser_download_url: attachment.browser_download_url + })); + } + } + + return assets; + } + + /** + * Create assets element + */ + function createAssetsElement(assets) { + const assetsElement = document.createElement('div'); + assetsElement.className = 'release-assets'; + + // Assets title + const assetsTitle = document.createElement('div'); + assetsTitle.className = 'release-assets-title'; + assetsTitle.setAttribute('data-en', MESSAGES.downloads.en); + assetsTitle.setAttribute('data-zh', MESSAGES.downloads.zh); + assetsTitle.textContent = MESSAGES.downloads[getCurrentLang()]; + + // Asset list + const assetList = document.createElement('ul'); + assetList.className = 'asset-list'; + + // Add each asset + assets.forEach(asset => { + const assetItem = createAssetItem(asset); + assetList.appendChild(assetItem); + }); + + assetsElement.appendChild(assetsTitle); + assetsElement.appendChild(assetList); + + return assetsElement; + } + + /** + * Create asset item + */ + function createAssetItem(asset) { + const assetItem = document.createElement('li'); + assetItem.className = 'asset-item'; + + // File icon + const iconElement = document.createElement('i'); + iconElement.className = `asset-icon fas fa-${getFileIcon(asset.name)}`; + + // File name + const nameElement = document.createElement('span'); + nameElement.className = 'asset-name'; + nameElement.textContent = asset.name; + + // File size + const sizeElement = document.createElement('span'); + sizeElement.className = 'asset-size'; + sizeElement.textContent = formatFileSize(asset.size); + + // Download link + const downloadLink = document.createElement('a'); + downloadLink.className = 'download-btn'; + downloadLink.href = asset.browser_download_url; + downloadLink.target = '_blank'; + downloadLink.setAttribute('data-en', MESSAGES.download.en); + downloadLink.setAttribute('data-zh', MESSAGES.download.zh); + downloadLink.textContent = MESSAGES.download[getCurrentLang()]; + + // Assemble asset item + assetItem.appendChild(iconElement); + assetItem.appendChild(nameElement); + assetItem.appendChild(sizeElement); + assetItem.appendChild(downloadLink); + + return assetItem; + } + + /** + * Get file icon based on extension + */ + function getFileIcon(filename) { + const ext = filename.split('.').pop().toLowerCase(); + + switch (ext) { + case 'zip': + case 'gz': + case 'tar': + case '7z': + return 'file-archive'; + case 'exe': + return 'file-code'; + case 'dmg': + return 'apple'; + case 'deb': + case 'rpm': + return 'linux'; + case 'json': + case 'xml': + return 'file-alt'; + default: + return 'file'; + } + } + + /** + * Format file size + */ + function formatFileSize(bytes) { + if (bytes === 0) return '0 Bytes'; + + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + + return parseFloat((bytes / Math.pow(1024, i)).toFixed(2)) + ' ' + sizes[i]; + } + + /** + * Format date + */ + function formatDate(date) { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + + return `${year}-${month}-${day}`; + } + + /** + * Simple Markdown parser + * Note: This is a very basic implementation that handles only common Markdown syntax + */ + function parseMarkdown(markdown) { + if (!markdown) return ''; + + // Links - [text](url) + markdown = markdown.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); + + // Headings - # Heading + markdown = markdown.replace(/^### (.*$)/gm, '

$1

'); + markdown = markdown.replace(/^## (.*$)/gm, '

$1

'); + markdown = markdown.replace(/^# (.*$)/gm, '

$1

'); + + // Bold - **text** + markdown = markdown.replace(/\*\*(.*?)\*\*/g, '$1'); + + // Italic - *text* + markdown = markdown.replace(/\*(.*?)\*/g, '$1'); + + // Code blocks - ```code``` + markdown = markdown.replace(/```([\s\S]*?)```/g, '
$1
'); + + // Inline code - `code` + markdown = markdown.replace(/`([^`]+)`/g, '$1'); + + // Lists - * item + markdown = markdown.replace(/^\* (.*$)/gm, ''); + + // Lists - 1. item + markdown = markdown.replace(/^\d+\. (.*$)/gm, '
  1. $1
'); + + // Merge adjacent list items + markdown = markdown.replace(/<\/ul>\s*