Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ jobs:
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs/dist
# 用绝对路径:job 设了 working-directory: docs,upload-pages-artifact 内部
# 的 tar 会继承该目录,相对路径 docs/dist 会被当成 docs/docs/dist 而找不到。
path: ${{ github.workspace }}/docs/dist

deploy:
needs: build
Expand Down
6 changes: 4 additions & 2 deletions docs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ dist
.vite
*.local

# 根 .gitignore 忽略了所有 pnpm-lock.yaml,这里为文档站重新纳入
# 供 CI 的 --frozen-lockfile 与依赖缓存使用
# 根 .gitignore 用 *.json / pnpm-lock.yaml 忽略了这些文件
# 这里为文档站重新纳入版本控制(构建/类型检查/依赖锁定需要)
!pnpm-lock.yaml
!tsconfig.json
!package.json
30 changes: 30 additions & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "codeforge-website",
"type": "module",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite-ssg build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.5.13",
"vue-i18n": "^11.4.6",
"vue-router": "^4.5.0"
},
"devDependencies": {
"@unhead/vue": "^1.11.14",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/tsconfig": "^0.7.0",
"autoprefixer": "^10.4.20",
"markdown-it-anchor": "^9.2.0",
"postcss": "^8.4.49",
"tailwindcss": "^3.4.17",
"typescript": "^5.7.2",
"unplugin-vue-markdown": "^28.3.1",
"vite": "^6.0.7",
"vite-ssg": "^0.24.1",
"vue-tsc": "^2.2.0"
}
}
9 changes: 9 additions & 0 deletions docs/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {"@/*": ["./src/*"]},
"types": ["vite/client"]
},
"include": ["src/**/*.ts", "src/**/*.vue", "vite.config.ts", "env.d.ts"]
}
63 changes: 48 additions & 15 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,14 @@
:file-name="currentFileName"
@close="showDiff = false"/>

<!-- 与剪贴板比较 -->
<DiffView v-if="clipboardDiff"
:original="clipboardDiff.original"
:modified="code"
:file-name="currentFileName"
:title="t('diff.clipboardTitle')"
@close="clipboardDiff = null"/>

<!-- 应用 AI 代码前的差异预览 -->
<DiffView v-if="applyPreview"
:original="code"
Expand Down Expand Up @@ -540,6 +548,7 @@ import {useGitStatus} from './composables/useGitStatus'
import {useSessionTabs} from './composables/useSessionTabs'
import {useEditorContextMenu} from './composables/useEditorContextMenu'
import {useRunConfig} from './composables/useRunConfig'
import {useGlobalShortcuts} from './composables/useGlobalShortcuts'
import EditorTabs from './components/EditorTabs.vue'
import IndentControl from './components/IndentControl.vue'
import Sidebar from './components/Sidebar.vue'
Expand Down Expand Up @@ -1108,6 +1117,25 @@ const editorView = shallowRef<any>(null)
// ===== 文本变换命令(排序行/大小写/去重/去行尾空白)=====
const {transformSelectionOrLine, sortLines, removeDuplicateLines, trimTrailingWhitespace} = useTextCommands(editorView)

// 复制为 Markdown 代码块(选区或全文,带语言围栏)
const copyAsMarkdown = async () => {
const view = editorView.value
if (!view) {
return
}
const sel = view.state.selection.main
const text = sel.empty ? view.state.doc.toString() : view.state.doc.sliceString(sel.from, sel.to)
const lang = (currentLanguage.value || '').toLowerCase().replace(/\d+$/, '')
const fence = '```' + lang + '\n' + text.replace(/\n$/, '') + '\n```'
try {
await navigator.clipboard.writeText(fence)
toast.success(t('app.copiedMarkdown'))
}
catch (error) {
toast.error(t('app.copyFailed') + error)
}
}

// AI 自然语言生成 / 选区改写
const showGenerate = ref(false)
const generateSelection = ref('')
Expand Down Expand Up @@ -1542,6 +1570,21 @@ const openDiff = () => {
}
showDiff.value = true
}
// 与剪贴板内容比较(剪贴板为原始,当前编辑内容为修改)
const clipboardDiff = ref<{ original: string } | null>(null)
const compareWithClipboard = async () => {
try {
const text = await navigator.clipboard.readText()
if (!text) {
toast.info(t('app.clipboardEmpty'))
return
}
clipboardDiff.value = {original: text}
}
catch (error) {
toast.error(t('app.clipboardReadFailed') + error)
}
}
const togglePreview = () => {
showPreview.value = !showPreview.value
}
Expand Down Expand Up @@ -2096,7 +2139,7 @@ const isOverlayOpen = () =>
|| showHistory.value || showViewer.value || showRunPrompt.value
|| showQuickOpen.value || showGenerate.value || showSearch.value
|| showCommandPalette.value || showDiff.value || showGoToLine.value || showOutline.value || showSnippets.value
|| applyPreview.value != null
|| applyPreview.value != null || clipboardDiff.value != null

// 全局快捷键(绑定可在设置中自定义)
const {matchAction: matchShortcut, reload: reloadShortcuts, getBinding, formatCombo} = useShortcuts()
Expand Down Expand Up @@ -2171,6 +2214,7 @@ const paletteCommands = computed<PaletteCommand[]>(() => [
{id: 'formatWithAi', label: t('command.formatWithAi'), icon: Sparkles, run: () => formatWithAi()},
{id: 'history', label: t('command.history'), icon: History, run: () => { showHistory.value = true }},
{id: 'diff', label: t('command.diff'), icon: GitCompare, run: () => openDiff()},
{id: 'compareClipboard', label: t('command.compareClipboard'), icon: GitCompare, run: () => compareWithClipboard()},
{id: 'preview', label: t('command.preview'), icon: Eye, run: () => togglePreview()},
{id: 'git', label: t('command.git'), icon: GitBranch, run: () => openGit()},
{id: 'tasks', label: t('command.tasks'), icon: ListChecks, run: () => openTasks()},
Expand All @@ -2187,6 +2231,7 @@ const paletteCommands = computed<PaletteCommand[]>(() => [
{id: 'toLowerCase', label: t('command.toLowerCase'), group: t('command.groupText'), icon: CaseLower, run: () => transformSelectionOrLine(s => s.toLowerCase())},
{id: 'removeDuplicateLines', label: t('command.removeDuplicateLines'), group: t('command.groupText'), icon: ListChecks, run: () => removeDuplicateLines()},
{id: 'trimTrailingWhitespace', label: t('command.trimTrailingWhitespace'), group: t('command.groupText'), icon: Eraser, run: () => trimTrailingWhitespace()},
{id: 'copyAsMarkdown', label: t('command.copyAsMarkdown'), group: t('command.groupText'), icon: Code2, run: () => copyAsMarkdown()},
{id: 'toggleAutoReveal', label: t('command.toggleAutoReveal'), icon: FolderOpen, run: () => toggleAutoReveal()},
{id: 'toggleSidebar', label: t('command.toggleSidebar'), icon: PanelLeft, hint: hintOf('toggleSidebar'), run: () => toggleSidebar()},
{id: 'toggleZen', label: t('command.toggleZen'), icon: Minimize2, run: () => toggleZen()},
Expand All @@ -2200,18 +2245,8 @@ const paletteCommands = computed<PaletteCommand[]>(() => [
{id: 'settings', label: t('command.settings'), icon: SettingsIcon, run: () => { showSettings.value = true }}
])

const onGlobalKeydown = (e: KeyboardEvent) => {
if (isOverlayOpen()) {
return
}
const action = matchShortcut(e)
if (action && shortcutDispatch[action]) {
// 捕获阶段拦截:阻止事件到达编辑器(避免 Cmd+Enter 等被插入换行)
e.preventDefault()
e.stopPropagation()
shortcutDispatch[action]()
}
}
// 全局快捷键(捕获拦截 + 派发)抽离到 useGlobalShortcuts
useGlobalShortcuts(matchShortcut, shortcutDispatch, isOverlayOpen)

const {init: initTheme, setTheme: setAppTheme} = useTheme()

Expand All @@ -2236,7 +2271,6 @@ onMounted(async () => {
// 恢复上次打开的文件标签
await restoreSession()

window.addEventListener('keydown', onGlobalKeydown, true)
window.addEventListener('lsp:open-location', onLspOpenLocation)
window.addEventListener('lsp:code-actions', onLspCodeActions)

Expand All @@ -2246,7 +2280,6 @@ onMounted(async () => {

onUnmounted(() => {
cleanupEventListeners()
window.removeEventListener('keydown', onGlobalKeydown, true)
window.removeEventListener('lsp:open-location', onLspOpenLocation)
window.removeEventListener('lsp:code-actions', onLspCodeActions)
})
Expand Down
24 changes: 24 additions & 0 deletions src/composables/useGlobalShortcuts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// 全局快捷键:捕获阶段拦截 keydown,匹配到动作则阻止默认并派发。
import {onMounted, onUnmounted} from 'vue'

export function useGlobalShortcuts(
matchShortcut: (e: KeyboardEvent) => string | null,
dispatch: Record<string, () => void>,
isOverlayOpen: () => boolean
) {
const onGlobalKeydown = (e: KeyboardEvent) => {
if (isOverlayOpen()) {
return
}
const action = matchShortcut(e)
if (action && dispatch[action]) {
// 捕获阶段拦截:阻止事件到达编辑器(避免 Cmd+Enter 等被插入换行)
e.preventDefault()
e.stopPropagation()
dispatch[action]()
}
}

onMounted(() => window.addEventListener('keydown', onGlobalKeydown, true))
onUnmounted(() => window.removeEventListener('keydown', onGlobalKeydown, true))
}
6 changes: 6 additions & 0 deletions src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,8 @@
"toLowerCase": "Transform to lowercase",
"removeDuplicateLines": "Remove duplicate lines",
"trimTrailingWhitespace": "Trim trailing whitespace",
"copyAsMarkdown": "Copy as Markdown code block",
"compareClipboard": "Compare with clipboard",
"groupText": "Text",
"toggleAutoReveal": "Toggle: auto-reveal active file",
"toggleSidebar": "Toggle sidebar",
Expand Down Expand Up @@ -1057,6 +1059,7 @@
},
"diff": {
"title": "Diff",
"clipboardTitle": "Compare with clipboard",
"close": "Close",
"subtitle": "Saved (red) → Current (green)",
"noDiff": "No differences, content is identical",
Expand Down Expand Up @@ -1320,6 +1323,9 @@
"untitled": "Untitled",
"multiEngine": "This type has multiple run engines; selected \"{name}\", switch manually in the dropdown",
"pathCopied": "Path copied",
"copiedMarkdown": "Copied as Markdown code block",
"clipboardEmpty": "Clipboard is empty",
"clipboardReadFailed": "Failed to read clipboard: ",
"copyFailed": "Copy failed: ",
"notTextFile": "Not a text file, cannot open",
"openFailed": "Open failed: ",
Expand Down
6 changes: 6 additions & 0 deletions src/i18n/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,8 @@
"toLowerCase": "转为小写",
"removeDuplicateLines": "删除重复行",
"trimTrailingWhitespace": "去除行尾空白",
"copyAsMarkdown": "复制为 Markdown 代码块",
"compareClipboard": "与剪贴板比较",
"groupText": "文本",
"toggleAutoReveal": "切换:自动定位当前文件",
"toggleSidebar": "切换侧栏",
Expand Down Expand Up @@ -1057,6 +1059,7 @@
},
"diff": {
"title": "差异对比",
"clipboardTitle": "与剪贴板比较",
"close": "关闭",
"subtitle": "已保存(红) → 当前(绿)",
"noDiff": "没有差异,内容一致",
Expand Down Expand Up @@ -1320,6 +1323,9 @@
"untitled": "未命名",
"multiEngine": "该类型可用多个运行引擎,已选「{name}」,可在下拉手动切换",
"pathCopied": "已复制路径",
"copiedMarkdown": "已复制为 Markdown 代码块",
"clipboardEmpty": "剪贴板为空",
"clipboardReadFailed": "读取剪贴板失败: ",
"copyFailed": "复制失败: ",
"notTextFile": "不是文本文件,无法打开",
"openFailed": "打开失败: ",
Expand Down
Loading