Compare commits
3 Commits
3373abe563
...
18db27a22c
| Author | SHA1 | Date |
|---|---|---|
|
|
18db27a22c | |
|
|
44a2c54da3 | |
|
|
f846aecee5 |
|
|
@ -2,6 +2,15 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
# [1.2.0](https://gitea.cuifuqi.online/utools-plug-in/markdown/compare/v1.1.0...v1.2.0) (2026-01-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **editor:** 添加图片预览功能并支持缩放 ([3373abe](https://gitea.cuifuqi.online/utools-plug-in/markdown/commits/3373abe563096eef18a631fc1c3125bee642a825))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 1.1.0 (2026-01-28)
|
# 1.1.0 (2026-01-28)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "project-helper",
|
"name": "project-helper",
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "project-helper",
|
"name": "project-helper",
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons-vue": "^7.0.1",
|
"@ant-design/icons-vue": "^7.0.1",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"name": "project-helper",
|
"name": "project-helper",
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"description": "markdown 笔记本",
|
"description": "markdown 笔记本",
|
||||||
"author": "Project Helper",
|
"author": "Project Helper",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,11 @@ window.services = {
|
||||||
window.utools.shellShowItemInFolder(itemPath);
|
window.utools.shellShowItemInFolder(itemPath);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 打开外部链接
|
||||||
|
openExternal(url) {
|
||||||
|
window.utools.shellOpenExternal(url);
|
||||||
|
},
|
||||||
|
|
||||||
// --- Image Service ---
|
// --- Image Service ---
|
||||||
|
|
||||||
// 保存图片
|
// 保存图片
|
||||||
|
|
@ -168,7 +173,11 @@ window.services = {
|
||||||
},
|
},
|
||||||
|
|
||||||
async gitStatus(dirPath) {
|
async gitStatus(dirPath) {
|
||||||
return this.execGit("status --short", dirPath);
|
return this.execGit("-c core.quotePath=false status --short", dirPath);
|
||||||
|
},
|
||||||
|
|
||||||
|
async gitBranch(dirPath) {
|
||||||
|
return this.execGit("branch --show-current", dirPath);
|
||||||
},
|
},
|
||||||
|
|
||||||
async gitAdd(dirPath, files = ".") {
|
async gitAdd(dirPath, files = ".") {
|
||||||
|
|
@ -189,6 +198,48 @@ window.services = {
|
||||||
return this.execGit("pull", dirPath);
|
return this.execGit("pull", dirPath);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async gitRemoteUrl(dirPath) {
|
||||||
|
try {
|
||||||
|
const configPath = path.join(dirPath, '.git', 'config');
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
return { success: false, error: 'Config file not found' };
|
||||||
|
}
|
||||||
|
const content = await fs.promises.readFile(configPath, 'utf-8');
|
||||||
|
|
||||||
|
// 简单解析 ini
|
||||||
|
// 寻找 [remote "origin"] 及其下的 url
|
||||||
|
const lines = content.split('\n');
|
||||||
|
let inRemoteOrigin = false;
|
||||||
|
let url = null;
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
if (trimmed === '[remote "origin"]') {
|
||||||
|
inRemoteOrigin = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (inRemoteOrigin) {
|
||||||
|
if (trimmed.startsWith('[')) {
|
||||||
|
// 进入下一个 section,结束查找
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (trimmed.startsWith('url =')) {
|
||||||
|
url = trimmed.substring(5).trim();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url) {
|
||||||
|
return { success: true, stdout: url };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: false, error: 'Remote origin url not found' };
|
||||||
|
} catch (e) {
|
||||||
|
return { success: false, error: e.message };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// --- Search Service ---
|
// --- Search Service ---
|
||||||
|
|
||||||
// 搜索文件 (文件名)
|
// 搜索文件 (文件名)
|
||||||
|
|
|
||||||
|
|
@ -5,26 +5,46 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="git-content">
|
<div v-else class="git-content">
|
||||||
|
<div class="git-header">
|
||||||
|
<div class="git-branch" v-if="state.branch">
|
||||||
|
<BranchesOutlined />
|
||||||
|
<span class="branch-name">{{ state.branch }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="git-toolbar">
|
||||||
|
<a-button @click="openRemoteUrl" :disabled="!state.remoteUrl" title="打开 Git 仓库" size="small">
|
||||||
|
<template #icon><GithubOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="handleRefresh" :loading="state.loading" size="small">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="git-actions">
|
<div class="git-actions">
|
||||||
<a-button-group>
|
<a-button-group class="action-group">
|
||||||
<a-button @click="handlePull" :loading="state.loading">
|
<a-button @click="handlePull" :loading="state.loading" block>
|
||||||
<template #icon><ArrowDownOutlined /></template>
|
<template #icon><ArrowDownOutlined /></template>
|
||||||
拉取
|
拉取
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button @click="handlePush" :loading="state.loading">
|
<a-button @click="handlePush" :loading="state.loading" block>
|
||||||
<template #icon><ArrowUpOutlined /></template>
|
<template #icon><ArrowUpOutlined /></template>
|
||||||
推送
|
推送
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-button-group>
|
</a-button-group>
|
||||||
|
|
||||||
<a-button @click="handleRefresh" :loading="state.loading">
|
|
||||||
<template #icon><ReloadOutlined /></template>
|
|
||||||
</a-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="git-status">
|
<div class="git-status">
|
||||||
<h3>状态</h3>
|
<div class="status-header">
|
||||||
<pre class="status-output">{{ state.statusOutput || '无变更' }}</pre>
|
<h3>状态</h3>
|
||||||
|
<span class="status-count" v-if="state.statusFiles.length">{{ state.statusFiles.length }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="status-list" v-if="state.statusFiles.length > 0">
|
||||||
|
<div v-for="(file, index) in state.statusFiles" :key="index" class="status-item">
|
||||||
|
<span class="status-badge" :class="getStatusClass(file.status)">{{ file.status }}</span>
|
||||||
|
<span class="file-path" :title="file.path">{{ file.path }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="empty-status">无变更</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="git-commit">
|
<div class="git-commit">
|
||||||
|
|
@ -51,15 +71,15 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, watch } from 'vue';
|
import { ref, onMounted, watch } from 'vue';
|
||||||
import { ArrowDownOutlined, ArrowUpOutlined, ReloadOutlined } from '@ant-design/icons-vue';
|
import { ArrowDownOutlined, ArrowUpOutlined, ReloadOutlined, GithubOutlined, BranchesOutlined } from '@ant-design/icons-vue';
|
||||||
import { useGit } from '../composables/useGit';
|
import { useGit } from '../composables/useGit';
|
||||||
import { useFileTree } from '../composables/useFileTree';
|
import { useFileTree } from '../composables/useFileTree';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
const { state, checkGitRepo, getStatus, commit, push, pull } = useGit();
|
const { state, checkGitRepo, getStatus, commit, push, pull, openRemoteUrl } = useGit();
|
||||||
const { state: fileTreeState } = useFileTree();
|
const { state: fileTreeState } = useFileTree();
|
||||||
|
|
||||||
const commitMessage = ref('');
|
const commitMessage = ref(`md helper`);
|
||||||
|
|
||||||
// 监听根目录变化,重新检查 Git 状态
|
// 监听根目录变化,重新检查 Git 状态
|
||||||
watch(() => fileTreeState.rootPath, async (newPath) => {
|
watch(() => fileTreeState.rootPath, async (newPath) => {
|
||||||
|
|
@ -114,6 +134,13 @@ const handlePull = async () => {
|
||||||
message.error('拉取异常: ' + e.message);
|
message.error('拉取异常: ' + e.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getStatusClass = (status) => {
|
||||||
|
if (status.includes('M')) return 'status-modified';
|
||||||
|
if (status.includes('A') || status.includes('?')) return 'status-added';
|
||||||
|
if (status.includes('D')) return 'status-deleted';
|
||||||
|
return '';
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
@ -127,25 +154,111 @@ const handlePull = async () => {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.git-actions {
|
.git-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.git-branch {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--primary-color, #1890ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-name {
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.git-toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.git-actions {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-group {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-group .ant-btn {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.git-status {
|
.git-status {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-output {
|
.status-header {
|
||||||
background-color: var(--app-background);
|
display: flex;
|
||||||
color: var(--text-color);
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-count {
|
||||||
|
background-color: var(--border-color);
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-list {
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
padding: 8px;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
background-color: var(--app-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
display: inline-block;
|
||||||
|
width: 24px;
|
||||||
|
margin-right: 8px;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-modified { color: #1890ff; }
|
||||||
|
.status-added { color: #52c41a; }
|
||||||
|
.status-deleted { color: #f5222d; }
|
||||||
|
|
||||||
|
.file-path {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-status {
|
||||||
|
text-align: center;
|
||||||
|
color: #999;
|
||||||
|
padding: 16px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 4px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ import { reactive } from 'vue';
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
isGitRepo: false,
|
isGitRepo: false,
|
||||||
statusOutput: '',
|
statusOutput: '',
|
||||||
|
statusFiles: [],
|
||||||
|
remoteUrl: '',
|
||||||
|
branch: '',
|
||||||
loading: false
|
loading: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -14,6 +17,36 @@ export function useGit() {
|
||||||
if (state.isGitRepo) {
|
if (state.isGitRepo) {
|
||||||
// 自动获取一次状态
|
// 自动获取一次状态
|
||||||
await getStatus(rootDir);
|
await getStatus(rootDir);
|
||||||
|
await getRemoteUrl(rootDir);
|
||||||
|
await getBranch(rootDir);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBranch = async (rootDir) => {
|
||||||
|
try {
|
||||||
|
const res = await window.services.gitBranch(rootDir);
|
||||||
|
if (res.success) {
|
||||||
|
state.branch = res.stdout.trim();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to get branch", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRemoteUrl = async (rootDir) => {
|
||||||
|
try {
|
||||||
|
const res = await window.services.gitRemoteUrl(rootDir);
|
||||||
|
if (res.success) {
|
||||||
|
state.remoteUrl = res.stdout.trim();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to get remote url", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const openRemoteUrl = () => {
|
||||||
|
if (state.remoteUrl) {
|
||||||
|
window.services.openExternal(state.remoteUrl);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -23,6 +56,17 @@ export function useGit() {
|
||||||
const res = await window.services.gitStatus(rootDir);
|
const res = await window.services.gitStatus(rootDir);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
state.statusOutput = res.stdout;
|
state.statusOutput = res.stdout;
|
||||||
|
// 解析状态文件列表
|
||||||
|
state.statusFiles = res.stdout
|
||||||
|
.split('\n')
|
||||||
|
.filter(line => line.trim())
|
||||||
|
.map(line => {
|
||||||
|
// git status --short 格式: XY PATH
|
||||||
|
// 前两个字符是状态,后面是路径
|
||||||
|
const status = line.substring(0, 2);
|
||||||
|
const path = line.substring(3);
|
||||||
|
return { status, path };
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
|
|
@ -65,6 +109,9 @@ export function useGit() {
|
||||||
state,
|
state,
|
||||||
checkGitRepo,
|
checkGitRepo,
|
||||||
getStatus,
|
getStatus,
|
||||||
|
getRemoteUrl,
|
||||||
|
getBranch,
|
||||||
|
openRemoteUrl,
|
||||||
commit,
|
commit,
|
||||||
push,
|
push,
|
||||||
pull
|
pull
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue