source-code-mgmt 1.13.0 → 1.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +27 -13
- package/README.md +49 -14
- package/lib/client.js +155 -217
- package/lib/index.js +539 -725
- package/package.json +3 -3
package/lib/client.js
CHANGED
|
@@ -74,6 +74,39 @@ window.__ModuleLoader__.load({
|
|
|
74
74
|
if (!res.ok) throw new Error("HTTP " + String(res.status));
|
|
75
75
|
return await res.json();
|
|
76
76
|
}
|
|
77
|
+
/** POST 并逐行消费 NDJSON 事件流({type:'step'|'out'|'result'},用于一键安装进度)。返回 result 对象。 */
|
|
78
|
+
async function jpostStream(path, body, onEvent) {
|
|
79
|
+
const res = await fetch(API + path + (path.indexOf("?") >= 0 ? "&" : "?") + "lang=" + currentLang(), {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: { "content-type": "application/json" },
|
|
82
|
+
body: JSON.stringify(body || {}),
|
|
83
|
+
});
|
|
84
|
+
if (!res.ok || !res.body) {
|
|
85
|
+
const text = await res.text().catch(() => "");
|
|
86
|
+
throw new Error("HTTP " + String(res.status) + (text ? ": " + text : ""));
|
|
87
|
+
}
|
|
88
|
+
const reader = res.body.getReader();
|
|
89
|
+
const decoder = new TextDecoder();
|
|
90
|
+
let buf = "";
|
|
91
|
+
let result = null;
|
|
92
|
+
for (;;) {
|
|
93
|
+
const { done, value } = await reader.read();
|
|
94
|
+
if (done) break;
|
|
95
|
+
buf += decoder.decode(value, { stream: true });
|
|
96
|
+
let nl;
|
|
97
|
+
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
98
|
+
const line = buf.slice(0, nl).trim();
|
|
99
|
+
buf = buf.slice(nl + 1);
|
|
100
|
+
if (!line) continue;
|
|
101
|
+
let evt;
|
|
102
|
+
try { evt = JSON.parse(line); } catch { continue }
|
|
103
|
+
if (evt && evt.type === "result") result = evt.result;
|
|
104
|
+
else if (onEvent) { try { onEvent(evt); } catch {} }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (result === null) throw new Error("no result");
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
77
110
|
|
|
78
111
|
// ---------- preload cache ----------
|
|
79
112
|
// 只在打开插件时按需拉取仓库状态;DSH 打开(插件激活)时只预取静态的 env / ssh /
|
|
@@ -179,7 +212,7 @@ window.__ModuleLoader__.load({
|
|
|
179
212
|
const h = React.createElement;
|
|
180
213
|
|
|
181
214
|
// ---------- i18n: follows DSH’s language setting (Settings → General → Language), live ----------
|
|
182
|
-
const EN_DICT = {"展开":"Expand","折叠":"Collapse","展开该部分":"Expand this section","折叠该部分":"Collapse this section","已安装":"Installed","❌ 未安装":"❌ Not installed","复制安装命令":"Copy install command","一键安装(会自动选包管理器)":"Install automatically (picks a package manager)","安装中…":"Installing…","安装":"Install"," 执行成功":" executed successfully","(如未生效需以管理员身份重试)":" (retry as administrator if it did not take effect)","安装失败":"Install failed","① 环境检查":"① Environment","✅ 均存在":"✅ All present","操作系统":"OS","已找到":"Found","检测中":"Checking","失败: ":"Failed: ","未找到的工具可用上方「安装」按钮一键安装(":"Missing tools can be installed with the \"Install\" button above (","Windows 用 winget / 内置功能":"winget / built-in features on Windows","用系统包管理器":"your system package manager",",可能需管理员权限);也可手动复制安装命令执行,安装后点「重新检查」。":"; admin rights may be required). You can also copy the install command and run it manually, then click \"Re-check\".","重新检查":"Re-check",":已存在,无需重复操作":": already exists, no need to repeat","成功":" succeeded","失败":" failed","失败:":"Failed: ","平台":"Platform","选择代码托管平台:GitHub 或 Gitee(默认 GitHub),③代码管理会跟随切换":"Choose the code hosting platform: GitHub or Gitee (GitHub default); step ③ follows this switch","GitHub(默认)":"GitHub (default)","② SSH 密钥与连接":"② SSH Key & Connection","密钥":"Key"," 已生成":" generated","⚠️ 未生成":"⚠️ Not generated","GH 登录":"GH login","已登录":"Logged in","⚠️ 未登录":"⚠️ Not logged in","SSH 配置":"SSH config"," 已配置(443)":" configured (443)","⚠️ 未配置/限制 22 端口需配置":"⚠️ Not configured / port 22 blocked — use 443","生成密钥":"Generate key","配置 SSH(config)":"Configure SSH config","配置 SSH":"Configure SSH","测试连接":"Test connection","SSH 连接成功(":"SSH connection succeeded (","已认证":"authenticated","连接失败,请确认密钥已上传到 ":"Connection failed. Make sure the key is uploaded to "," 或已登录 gh":" or that gh is logged in","测试失败:":"Test failed: ","公钥(复制上传到 ":"Public key (copy and upload to ","Gitee → 设置 → SSH 公钥":"Gitee → Settings → SSH keys",",或运行 gh auth login 自动上传):":", or run gh auth login to upload it automatically):","该目录不是 git 仓库":"This folder is not a git repository","读取令牌状态失败:":"Failed to read token status: ","已保存 Gitee 令牌(账号:":"Gitee token saved (account: ","保存令牌失败":"Failed to save token","保存令牌失败:":"Failed to save token: ","已清除 Gitee 令牌":"Gitee token cleared","清除失败":"Clear failed","清除令牌失败:":"Failed to clear token: ","已推送":"Pushed","推送失败":"Push failed","推送失败:":"Push failed: ","已拉取远程更新(本地已对齐到远程最新状态)":"Pulled remote updates (local is now aligned with the latest remote state)","拉取失败":"Pull failed","拉取失败:":"Pull failed: ","已拉取远程更新并推送更改":"Pulled remote updates and pushed changes","合并推送失败":"Merge-push failed","合并推送失败:":"Merge-push failed: ","强制推送会用本地版本覆盖远程仓库,远程上非本地的更改将被丢弃。确定继续?":"Force-push overwrites the remote repo with your local version; remote-only changes will be lost. Continue?","已强制推送,远程已更新为本地状态":"Force-pushed; the remote is now your local state","强制推送失败":"Force-push failed","强制推送失败:":"Force-push failed: ","强制拉取会把远程更新并入本地。如果本地有不想保留的内容,将按冲突处理或遗失。确定继续?":"Force-pull merges remote updates into local; anything you don't want kept may conflict or be lost. Continue?","已强制拉取远程更新":"Force-pulled remote updates","强制拉取失败":"Force-pull failed","强制拉取失败:":"Force-pull failed: ","请先加载一个有效的文件夹":"Load a valid folder first","确定把仓库 ":"Change repo "," 改为「":" visibility to「","公开":"Public","私有":"Private","」?修改可见性可能影响 Star、关注者等。":"」? Changing visibility may affect stars, watchers, etc.","已把仓库设置为「":"Repo set to「","修改可见性失败":"Failed to change visibility","修改可见性失败:":"Failed to change visibility: ","仓库已创建并推送:":"Repo created and pushed: ","创建失败":"Create failed","创建失败:":"Create failed: ","未选择目录":"No folder selected","选择失败:":"Selection failed: ","请输入或选择目录路径":"Enter or pick a folder path","添加目录失败":"Failed to add folder","添加目录失败:":"Failed to add folder: ","删除失败":"Delete failed","删除失败:":"Delete failed: ","强制对齐会用远程分支覆盖本地(丢弃本地未推送的更改与提交),确定继续?":"Force-align overwrites local with the remote branch (drops unpushed local changes and commits). Continue?","已强制对齐到远程分支":"Force-aligned to the remote branch","强制对齐失败":"Force-align failed","强制对齐失败:":"Force-align failed: ","已是 git 仓库":"Already a git repository","已创建 git 仓库,请自行拉取或推送":"Git repository created — pull or push as you wish","创建 git 失败":"Failed to init git","创建 git 失败:":"Failed to init git: ","暂存失败":"Stage failed","暂存失败:":"Stage failed: ","取消暂存失败":"Unstage failed","取消暂存失败:":"Unstage failed: ","已提交 ":"Committed "," 个文件":" file(s)","提交失败":"Commit failed","提交失败:":"Commit failed: ","已提交「":"Committed「","(自动生成)":" (auto-generated)","」并推送":"」 and pushed","已推送本地已有的提交":"Pushed existing local commits","读取分支失败":"Failed to load branches","读取分支失败:":"Failed to load branches: ","切换到分支「":"Switch to branch「","」?(若当前有未提交改动,git 会拒绝切换)":"」? (git will refuse if you have uncommitted changes)","已切换到分支 ":"Switched to branch ","切换分支失败":"Failed to switch branch","切换分支失败:":"Failed to switch branch: ","读取历史失败":"Failed to load history","读取历史失败:":"Failed to load history: ","revert 提交「":"Revert commit「","」——会生成一个反向提交,期间可能产生冲突,确定继续?":"」 — this creates a reverse commit and may cause conflicts. Continue?","已 revert 提交 ":"Reverted commit ","revert 失败":"Revert failed","revert 失败:":"Revert failed: ","cherry-pick 提交「":"Cherry-pick commit「","」到当前分支——期间可能产生冲突,确定继续?":"」 onto the current branch — this may cause conflicts. Continue?","已 cherry-pick 提交 ":"Cherry-picked commit ","cherry-pick 失败":"Cherry-pick failed","cherry-pick 失败:":"Cherry-pick failed: ","读取失败":"Failed to load","已复制":"Copied","短哈希":"short hash","完整哈希":"full hash","提交信息":"commit message","复制失败:请手动复制":"Copy failed — copy it manually","③ 代码管理":"③ Code Management","Gitee 私人令牌(OpenAPI,需 projects 权限)":"Gitee personal token (OpenAPI, requires projects permission)","✅ 已配置":"✅ Configured","(账号 ":" (account ","清除令牌":"Clear token","粘贴 Gitee 私人令牌(https://gitee.com/personal_access_tokens)":"Paste the Gitee personal token (https://gitee.com/personal_access_tokens)","保存令牌":"Save token","令牌只保存在本机 ~/.dsh/storages(0600),不会写进插件目录;需勾选个人令牌的 projects 权限。公钥需已上传到 Gitee(② 里检查)。":"The token is stored only on this machine at ~/.dsh/storages (0600), never in the plugin directory; the personal token must have projects permission. Your public key must be uploaded to Gitee (checked in ②).","选择工作区":"Workspace","选择 DSH 已登记的工作区文件夹":"Choose a workspace folder registered in DSH","(暂无可选工作区)":"(no workspaces available)","— 请选择 —":"— Select —","选择目录":"Choose folder","自定义目录:":"Custom folder: ","删除该目录记录":"Remove this folder entry","删除该目录记录(不删除实际文件夹)":"Remove this folder entry (does not delete the actual folder)","目录":"Folder","分支":"Branch","(无)":"(none)","切换分支":"Switch branch","切换":"Switch","查看提交历史":"View commit history","历史":"History","远程":"Remote","改动":"Changes","无":"None","查看改动的文件列表":"View the list of changed files","查看":"View","同步":"Sync","本地领先 ":"Ahead by "," 提交":" commit(s)","、落后 ":", behind by ","落后 ":"behind by ","与远程一致":"Up to date with remote","查看本地与远程的提交差异":"View commit differences between local and remote","⚠️ 以下 ":"⚠️ The following "," 项 >100MB(超过 GitHub 限制,推送时将自动忽略不上传):":" item(s) >100MB (over GitHub's limit — auto-ignored on push):","/(整个文件夹)":"/ (entire folder)","创建 Git":"Init Git","仅初始化 git 仓库,不拉取不推送,由你决定下一步":"Initializes a git repo only — no pull/push; you decide the next step","本地有改动且远程有更新,可直接用「强制对齐」将本地重置为远程状态":"You have local changes AND remote updates — use \"Force align\" to reset local to the remote state","强制对齐":"Force align","本地完全重置为远程分支(丢弃本地差异),解决“文件相同仍显示同步差异”的情况":"Fully resets local to the remote branch (drops local differences); fixes \"files identical but sync still shows a difference\"","推送更改":"Push changes","拉取更新":"Pull updates","✓ 已是最新":"✓ Up to date","⟳ 正在刷新状态,联网同步 GitHub/Gitee 最新数据…":"⟳ Refreshing status — syncing the latest GitHub/Gitee data…","⟳ 切换平台,正在重新检测「":"⟳ Switching platform, re-checking「","」仓库状态…":"」 repo status…","推送中…":"Pushing…","推送暂存":"Push staged","把已暂存的内容用填写的(或自动生成的)信息提交后推送到远程;不会自动暂存其他未暂存的改动":"Commits only what is staged (with your message, or auto-generated) and pushes it; does not auto-stage other changes","刷新中…":"Refreshing…","刷新状态":"Refresh status","提交信息(留空则用默认)":"Commit message (default if empty)","填写提交信息后点「提交」;留空则自动生成(chore: update <文件夹名>)":"Type a message then click \"Commit\"; leave empty to auto-generate (chore: update <folder>)","提交":"Commit","仓库名称自动取文件夹名(不可修改)":"Repo name is taken from the folder name (read-only)","(未加载文件夹)":"(no folder loaded)","仓库可见性:私有 / 公开":"Repo visibility: private / public","(修改当前仓库的可见性)":" (changes the current repo's visibility)","新建仓库并推送":"Create repo & push","修改仓库状态":"Change repo status","✓ 仓库已是「":"✓ Repo is already「","」状态,如需修改请调整左侧可见性选择。":"」 — to change it, adjust the visibility dropdown.","将把仓库从「":"Will change the repo from「","」改为「":"」 to「","」,点击「修改仓库状态」执行。":"」 — click \"Change repo status\" to apply.","⚠️ 同名仓库已经创建(无法读取当前可见性,可能未":"⚠️ A repo with this name already exists (couldn't read its visibility — maybe not ","配置 Gitee 令牌":"Gitee token configured","登录 gh":"logged into gh","✓ 同名仓库 ":"✓ No repo named "," 不存在,可在 ":" exists — you can create it on ","「新建仓库并推送」创建(可选私有/公开)。":" with \"Create repo & push\" (private or public).","已忽略未上传(":"Skipped uploads ("," 项 >100MB):":" item(s) >100MB):","已提交":"Committed"," 并推送":" and pushed","(推送省略)":" (push skipped)","查看详情":"View details","改动文件":"Changed files","提交历史":"Commit history","与远程同步差异":"Sync differences vs remote","关闭":"Close","已安装 dsh-better-sidebar,此处只列改动文件列表;具体改动内容请到 dsh-better-sidebar 的「源代码管理面板」查看。":"dsh-better-sidebar is installed — this lists changed files only; see the actual changes in its \"Source Control panel\".","点击收起":"Click to collapse","点击查看改动内容":"Click to view the diff","新文件(untracked)暂无内容 diff":"New (untracked) file — no diff yet","已暂存":"Staged","未暂存":"Unstaged","取消暂存":"Unstage","暂存":"Stage","加载中…":"Loading…","▾ 收起":"▾ Collapse","▸ 查看":"▸ View","正在加载改动内容…":"Loading changes…","(无内容差异)":"(no diff)","当前没有改动。":"No changes.","正在读取分支…":"Loading branches…","(无分支)":"(no branches)","(当前)":"(current)","正在读取历史…":"Loading history…","(暂无提交)":"(no commits)","更多操作(右键也可打开)":"More actions (right-click also works)","查看提交差异":"View commit diff","复制短哈希":"Copy short hash","复制完整哈希":"Copy full hash","复制提交信息":"Copy commit message","还原此提交":"Revert this commit","拾取此提交":"Cherry-pick this commit","正在加载提交 diff…":"Loading commit diff…","本地落后 ":"Behind by "," 个提交(远程有而本地没有):":" commit(s) (on remote, not local):"," 个提交(本地有而远程没有):":" commit(s) (on local, not remote):","✓ 已与远程同步,无差异。":"✓ In sync with remote — no differences.","选择代码目录":"Choose a code folder","可手动输入/粘贴目录绝对路径,或点击「浏览…」弹出本地文件夹选择器。确认后将添加到下方下拉并记住,下次打开无需重新选择。":"Paste an absolute folder path, or click \"Browse…\" for the native picker. Confirmed folders are added to the dropdown and remembered.","C:\\Users\\你的用户名\\项目目录":"C:\\Users\\your-name\\project","浏览…":"Browse…","取消":"Cancel","确定":"OK","新增":"Added","删除":"Deleted","重命名":"Renamed","修改":"Modified","旧版本":"Old","新版本":"New","源代码管理":"Source Control","按顺序完成:①环境检查 → ②SSH 密钥与连接 → ③代码管理。推送会自动忽略 >100MB 的文件(":"Work through in order: ① Environment → ② SSH Key & Connection → ③ Code Management. Files over 100MB are auto-ignored on push ("," 限制)并说明原因。":" limit) with the reason shown.","代码管理":"Code Management","拖动调整面板宽度":"Drag to resize the panel","二进制文件,无法查看文本内容":"Binary file — text preview unavailable","(空文件)":"(empty file)"};
|
|
215
|
+
const EN_DICT = {"展开":"Expand","折叠":"Collapse","展开该部分":"Expand this section","折叠该部分":"Collapse this section","已安装":"Installed","❌ 未安装":"❌ Not installed","复制安装命令":"Copy install command","一键安装(会自动选包管理器)":"Install automatically (picks a package manager)","安装中…":"Installing…","安装中,请稍候…":"Installing, please wait…","安装完成":"Installation complete","安装完成,需要重启 DSH 生效。点击「重启 DSH」立即重启,页面会短暂断开,恢复后请刷新。":"Installation complete — restart DSH to apply. Click \"Restart DSH\" to restart now; the page will briefly disconnect, refresh once it is back.","重启 DSH":"Restart DSH","正在重启 DSH,页面将短暂断开;恢复后请刷新网页。":"Restarting DSH — the page will briefly disconnect; refresh once it is back.","重启失败:":"Restart failed: ","重启似乎未生效,请手动重启 dsh。":"The restart does not seem to have taken effect — please restart dsh manually.",",需要重启 DSH 生效":" — restart DSH to apply","安装":"Install"," 执行成功":" executed successfully","(已装到 ~/.local/bin,免 sudo)":" (installed to ~/.local/bin, no sudo needed)","(如未生效需以管理员身份重试)":" (retry as administrator if it did not take effect)","安装失败":"Install failed","① 环境检查":"① Environment","✅ 均存在":"✅ All present","操作系统":"OS","已找到":"Found","检测中":"Checking","失败: ":"Failed: ","未找到的工具可用上方「安装」按钮一键安装(":"Missing tools can be installed with the \"Install\" button above (","Windows 用 winget / 内置功能":"winget / built-in features on Windows","用系统包管理器":"your system package manager",",可能需管理员权限);也可手动复制安装命令执行,安装后点「重新检查」。":"; admin rights may be required). You can also copy the install command and run it manually, then click \"Re-check\".","重新检查":"Re-check",":已存在,无需重复操作":": already exists, no need to repeat","成功":" succeeded","失败":" failed","失败:":"Failed: ","平台":"Platform","选择代码托管平台:GitHub 或 Gitee(默认 GitHub),③代码管理会跟随切换":"Choose the code hosting platform: GitHub or Gitee (GitHub default); step ③ follows this switch","GitHub(默认)":"GitHub (default)","② SSH 密钥与连接":"② SSH Key & Connection","密钥":"Key"," 已生成":" generated","⚠️ 未生成":"⚠️ Not generated","GH 登录":"GH login","已登录":"Logged in","⚠️ 未登录":"⚠️ Not logged in","SSH 配置":"SSH config"," 已配置(443)":" configured (443)","⚠️ 未配置/限制 22 端口需配置":"⚠️ Not configured / port 22 blocked — use 443","生成密钥":"Generate key","配置 SSH(config)":"Configure SSH config","配置 SSH":"Configure SSH","测试连接":"Test connection","SSH 连接成功(":"SSH connection succeeded (","已认证":"authenticated","连接失败,请确认密钥已上传到 ":"Connection failed. Make sure the key is uploaded to "," 或已登录 gh":" or that gh is logged in","测试失败:":"Test failed: ","公钥(复制上传到 ":"Public key (copy and upload to ","Gitee → 设置 → SSH 公钥":"Gitee → Settings → SSH keys",",或运行 gh auth login 自动上传):":", or run gh auth login to upload it automatically):","该目录不是 git 仓库":"This folder is not a git repository","读取令牌状态失败:":"Failed to read token status: ","已保存 Gitee 令牌(账号:":"Gitee token saved (account: ","保存令牌失败":"Failed to save token","保存令牌失败:":"Failed to save token: ","已清除 Gitee 令牌":"Gitee token cleared","清除失败":"Clear failed","清除令牌失败:":"Failed to clear token: ","已推送":"Pushed","推送失败":"Push failed","推送失败:":"Push failed: ","已拉取远程更新(本地已对齐到远程最新状态)":"Pulled remote updates (local is now aligned with the latest remote state)","拉取失败":"Pull failed","拉取失败:":"Pull failed: ","已拉取远程更新并推送更改":"Pulled remote updates and pushed changes","合并推送失败":"Merge-push failed","合并推送失败:":"Merge-push failed: ","强制推送会用本地版本覆盖远程仓库,远程上非本地的更改将被丢弃。确定继续?":"Force-push overwrites the remote repo with your local version; remote-only changes will be lost. Continue?","已强制推送,远程已更新为本地状态":"Force-pushed; the remote is now your local state","强制推送失败":"Force-push failed","强制推送失败:":"Force-push failed: ","强制拉取会把远程更新并入本地。如果本地有不想保留的内容,将按冲突处理或遗失。确定继续?":"Force-pull merges remote updates into local; anything you don't want kept may conflict or be lost. Continue?","已强制拉取远程更新":"Force-pulled remote updates","强制拉取失败":"Force-pull failed","强制拉取失败:":"Force-pull failed: ","请先加载一个有效的文件夹":"Load a valid folder first","确定把仓库 ":"Change repo "," 改为「":" visibility to「","公开":"Public","私有":"Private","」?修改可见性可能影响 Star、关注者等。":"」? Changing visibility may affect stars, watchers, etc.","已把仓库设置为「":"Repo set to「","修改可见性失败":"Failed to change visibility","修改可见性失败:":"Failed to change visibility: ","仓库已创建并推送:":"Repo created and pushed: ","创建失败":"Create failed","创建失败:":"Create failed: ","未选择目录":"No folder selected","选择失败:":"Selection failed: ","请输入或选择目录路径":"Enter or pick a folder path","添加目录失败":"Failed to add folder","添加目录失败:":"Failed to add folder: ","删除失败":"Delete failed","删除失败:":"Delete failed: ","强制对齐会用远程分支覆盖本地(丢弃本地未推送的更改与提交),确定继续?":"Force-align overwrites local with the remote branch (drops unpushed local changes and commits). Continue?","已强制对齐到远程分支":"Force-aligned to the remote branch","强制对齐失败":"Force-align failed","强制对齐失败:":"Force-align failed: ","已是 git 仓库":"Already a git repository","已创建 git 仓库,请自行拉取或推送":"Git repository created — pull or push as you wish","创建 git 失败":"Failed to init git","创建 git 失败:":"Failed to init git: ","暂存失败":"Stage failed","暂存失败:":"Stage failed: ","取消暂存失败":"Unstage failed","取消暂存失败:":"Unstage failed: ","已提交 ":"Committed "," 个文件":" file(s)","提交失败":"Commit failed","提交失败:":"Commit failed: ","已提交「":"Committed「","(自动生成)":" (auto-generated)","」并推送":"」 and pushed","已推送本地已有的提交":"Pushed existing local commits","读取分支失败":"Failed to load branches","读取分支失败:":"Failed to load branches: ","切换到分支「":"Switch to branch「","」?(若当前有未提交改动,git 会拒绝切换)":"」? (git will refuse if you have uncommitted changes)","已切换到分支 ":"Switched to branch ","切换分支失败":"Failed to switch branch","切换分支失败:":"Failed to switch branch: ","读取历史失败":"Failed to load history","读取历史失败:":"Failed to load history: ","revert 提交「":"Revert commit「","」——会生成一个反向提交,期间可能产生冲突,确定继续?":"」 — this creates a reverse commit and may cause conflicts. Continue?","已 revert 提交 ":"Reverted commit ","revert 失败":"Revert failed","revert 失败:":"Revert failed: ","cherry-pick 提交「":"Cherry-pick commit「","」到当前分支——期间可能产生冲突,确定继续?":"」 onto the current branch — this may cause conflicts. Continue?","已 cherry-pick 提交 ":"Cherry-picked commit ","cherry-pick 失败":"Cherry-pick failed","cherry-pick 失败:":"Cherry-pick failed: ","读取失败":"Failed to load","已复制":"Copied","短哈希":"short hash","完整哈希":"full hash","提交信息":"commit message","复制失败:请手动复制":"Copy failed — copy it manually","③ 代码管理":"③ Code Management","Gitee 私人令牌(OpenAPI,需 projects 权限)":"Gitee personal token (OpenAPI, requires projects permission)","✅ 已配置":"✅ Configured","(账号 ":" (account ","清除令牌":"Clear token","粘贴 Gitee 私人令牌(https://gitee.com/personal_access_tokens)":"Paste the Gitee personal token (https://gitee.com/personal_access_tokens)","保存令牌":"Save token","令牌只保存在本机 ~/.dsh/storages(0600),不会写进插件目录;需勾选个人令牌的 projects 权限。公钥需已上传到 Gitee(② 里检查)。":"The token is stored only on this machine at ~/.dsh/storages (0600), never in the plugin directory; the personal token must have projects permission. Your public key must be uploaded to Gitee (checked in ②).","选择工作区":"Workspace","选择 DSH 已登记的工作区文件夹":"Choose a workspace folder registered in DSH","(暂无可选工作区)":"(no workspaces available)","— 请选择 —":"— Select —","选择目录":"Choose folder","自定义目录:":"Custom folder: ","删除该目录记录":"Remove this folder entry","删除该目录记录(不删除实际文件夹)":"Remove this folder entry (does not delete the actual folder)","目录":"Folder","分支":"Branch","(无)":"(none)","切换分支":"Switch branch","切换":"Switch","查看提交历史":"View commit history","历史":"History","远程":"Remote","改动":"Changes","无":"None","查看改动的文件列表":"View the list of changed files","查看":"View","同步":"Sync","本地领先 ":"Ahead by "," 提交":" commit(s)","、落后 ":", behind by ","落后 ":"behind by ","与远程一致":"Up to date with remote","查看本地与远程的提交差异":"View commit differences between local and remote","⚠️ 以下 ":"⚠️ The following "," 项 >100MB(超过 GitHub 限制,推送时将自动忽略不上传):":" item(s) >100MB (over GitHub's limit — auto-ignored on push):","/(整个文件夹)":"/ (entire folder)","创建 Git":"Init Git","仅初始化 git 仓库,不拉取不推送,由你决定下一步":"Initializes a git repo only — no pull/push; you decide the next step","本地有改动且远程有更新,可直接用「强制对齐」将本地重置为远程状态":"You have local changes AND remote updates — use \"Force align\" to reset local to the remote state","强制对齐":"Force align","本地完全重置为远程分支(丢弃本地差异),解决“文件相同仍显示同步差异”的情况":"Fully resets local to the remote branch (drops local differences); fixes \"files identical but sync still shows a difference\"","推送更改":"Push changes","拉取更新":"Pull updates","✓ 已是最新":"✓ Up to date","⟳ 正在刷新状态,联网同步 GitHub/Gitee 最新数据…":"⟳ Refreshing status — syncing the latest GitHub/Gitee data…","⟳ 切换平台,正在重新检测「":"⟳ Switching platform, re-checking「","」仓库状态…":"」 repo status…","推送中…":"Pushing…","推送暂存":"Push staged","把已暂存的内容用填写的(或自动生成的)信息提交后推送到远程;不会自动暂存其他未暂存的改动":"Commits only what is staged (with your message, or auto-generated) and pushes it; does not auto-stage other changes","刷新中…":"Refreshing…","刷新状态":"Refresh status","提交信息(留空则用默认)":"Commit message (default if empty)","填写提交信息后点「提交」;留空则自动生成(chore: update <文件夹名>)":"Type a message then click \"Commit\"; leave empty to auto-generate (chore: update <folder>)","提交":"Commit","仓库名称自动取文件夹名(不可修改)":"Repo name is taken from the folder name (read-only)","(未加载文件夹)":"(no folder loaded)","仓库可见性:私有 / 公开":"Repo visibility: private / public","(修改当前仓库的可见性)":" (changes the current repo's visibility)","新建仓库并推送":"Create repo & push","修改仓库状态":"Change repo status","✓ 仓库已是「":"✓ Repo is already「","」状态,如需修改请调整左侧可见性选择。":"」 — to change it, adjust the visibility dropdown.","将把仓库从「":"Will change the repo from「","」改为「":"」 to「","」,点击「修改仓库状态」执行。":"」 — click \"Change repo status\" to apply.","⚠️ 同名仓库已经创建(无法读取当前可见性,可能未":"⚠️ A repo with this name already exists (couldn't read its visibility — maybe not ","配置 Gitee 令牌":"Gitee token configured","登录 gh":"logged into gh","✓ 同名仓库 ":"✓ No repo named "," 不存在,可在 ":" exists — you can create it on ","「新建仓库并推送」创建(可选私有/公开)。":" with \"Create repo & push\" (private or public).","已忽略未上传(":"Skipped uploads ("," 项 >100MB):":" item(s) >100MB):","已提交":"Committed"," 并推送":" and pushed","(推送省略)":" (push skipped)","查看详情":"View details","改动文件":"Changed files","提交历史":"Commit history","与远程同步差异":"Sync differences vs remote","关闭":"Close","已安装 dsh-better-sidebar,此处只列改动文件列表;具体改动内容请到 dsh-better-sidebar 的「源代码管理面板」查看。":"dsh-better-sidebar is installed — this lists changed files only; see the actual changes in its \"Source Control panel\".","点击收起":"Click to collapse","点击查看改动内容":"Click to view the diff","新文件(untracked)暂无内容 diff":"New (untracked) file — no diff yet","已暂存":"Staged","未暂存":"Unstaged","取消暂存":"Unstage","暂存":"Stage","加载中…":"Loading…","▾ 收起":"▾ Collapse","▸ 查看":"▸ View","正在加载改动内容…":"Loading changes…","(无内容差异)":"(no diff)","当前没有改动。":"No changes.","正在读取分支…":"Loading branches…","(无分支)":"(no branches)","(当前)":"(current)","正在读取历史…":"Loading history…","(暂无提交)":"(no commits)","更多操作(右键也可打开)":"More actions (right-click also works)","查看提交差异":"View commit diff","复制短哈希":"Copy short hash","复制完整哈希":"Copy full hash","复制提交信息":"Copy commit message","还原此提交":"Revert this commit","拾取此提交":"Cherry-pick this commit","正在加载提交 diff…":"Loading commit diff…","本地落后 ":"Behind by "," 个提交(远程有而本地没有):":" commit(s) (on remote, not local):"," 个提交(本地有而远程没有):":" commit(s) (on local, not remote):","✓ 已与远程同步,无差异。":"✓ In sync with remote — no differences.","选择代码目录":"Choose a code folder","可手动输入/粘贴目录绝对路径,或点击「浏览…」弹出本地文件夹选择器。确认后将添加到下方下拉并记住,下次打开无需重新选择。":"Paste an absolute folder path, or click \"Browse…\" for the native picker. Confirmed folders are added to the dropdown and remembered.","C:\\Users\\你的用户名\\项目目录":"C:\\Users\\your-name\\project","浏览…":"Browse…","取消":"Cancel","确定":"OK","新增":"Added","删除":"Deleted","重命名":"Renamed","修改":"Modified","旧版本":"Old","新版本":"New","源代码管理":"Source Control","按顺序完成:①环境检查 → ②SSH 密钥与连接 → ③代码管理。推送会自动忽略 >100MB 的文件(":"Work through in order: ① Environment → ② SSH Key & Connection → ③ Code Management. Files over 100MB are auto-ignored on push ("," 限制)并说明原因。":" limit) with the reason shown.","代码管理":"Code Management","拖动调整面板宽度":"Drag to resize the panel","二进制文件,无法查看文本内容":"Binary file — text preview unavailable","(空文件)":"(empty file)"};
|
|
183
216
|
Object.assign(EN_DICT, {
|
|
184
217
|
// ④ 克隆仓库
|
|
185
218
|
"④ 克隆仓库": "④ Clone repos",
|
|
@@ -207,47 +240,9 @@ Object.assign(EN_DICT, {
|
|
|
207
240
|
"请先登录 GitHub CLI(gh)": "Log in to GitHub CLI (gh) first",
|
|
208
241
|
"需要先配置 Gitee 私人令牌": "Configure the Gitee personal token first",
|
|
209
242
|
"克隆一个账号下的远程仓库到本地": "Clone a remote repo from your account locally",
|
|
210
|
-
// ⑤ 提交 PR
|
|
211
|
-
"⑤ 提交 PR": "⑤ Pull Request",
|
|
212
|
-
"目标仓库地址": "Target repo URL",
|
|
213
|
-
"(如 https://github.com/owner/repo 或 owner/repo)": "(e.g. https://github.com/owner/repo or owner/repo)",
|
|
214
|
-
"记录到本地": "Save locally",
|
|
215
|
-
"已记录仓库": "Saved repos",
|
|
216
|
-
"分析 PR 规则": "Analyze PR rules",
|
|
217
|
-
"重新分析(忽略缓存)": "Re-analyze (ignore cache)",
|
|
218
|
-
"AI 正在思考该仓库的 PR 规则…": "AI is analyzing this repo's PR rules…",
|
|
219
|
-
"已使用缓存规则(插件目录 rules/,无需重新分析)": "Using cached rules (plugin rules/ dir, no re-analysis needed)",
|
|
220
|
-
"AI 已生成规则并缓存到插件目录 rules/": "AI generated rules and cached them in the plugin rules/ dir",
|
|
221
|
-
"规则步骤:": "Rule steps: ",
|
|
222
|
-
"上游分支(base)": "Upstream branch (base)",
|
|
223
|
-
"PR 标题": "PR title",
|
|
224
|
-
"PR 描述": "PR description",
|
|
225
|
-
"条目文件内容(entry,可留空)": "Entry file content (entry, optional)",
|
|
226
|
-
"插件信息(供 AI 生成 PR 描述)": "Plugin info (for AI to generate the PR description)",
|
|
227
|
-
"插件名 owner/repo": "Plugin name owner/repo",
|
|
228
|
-
"分类 category": "Category",
|
|
229
|
-
"描述 en": "Description (en)",
|
|
230
|
-
"描述 zh": "Description (zh)",
|
|
231
|
-
"生成 PR 内容(AI)": "Generate PR content (AI)",
|
|
232
|
-
"已生成,请确认 / 修改后再执行": "Generated — review / edit before running",
|
|
233
|
-
"执行 PR": "Run PR",
|
|
234
|
-
"PR 执行中…": "Running PR…",
|
|
235
|
-
"PR 已创建:": "PR created: ",
|
|
236
|
-
"PR 流程已完成": "PR flow completed",
|
|
237
|
-
"PR 执行失败": "PR flow failed",
|
|
238
|
-
"PR 执行失败:": "PR flow failed: ",
|
|
239
|
-
"工作目录(fork 克隆位置,默认=默认工作区)": "Working dir (where the fork is cloned; default = default workspace)",
|
|
240
|
-
"生成失败": "Generation failed",
|
|
241
|
-
"生成失败:": "Generation failed: ",
|
|
242
|
-
"分析失败": "Analysis failed",
|
|
243
|
-
"分析失败:": "Analysis failed: ",
|
|
244
|
-
"请填写目标仓库地址": "Enter the target repo URL first",
|
|
245
|
-
"请先填写仓库并分析规则": "Enter the repo and analyze its rules first",
|
|
246
|
-
"保存失败": "Save failed",
|
|
247
|
-
"目标仓库地址会记录到本地 ~/.dsh/storages(不写入插件目录)": "Target repo URLs are saved to local ~/.dsh/storages (not the plugin dir)",
|
|
248
243
|
"删除": "Remove",
|
|
249
|
-
//
|
|
250
|
-
"
|
|
244
|
+
// ⑤ 发布 npm 包
|
|
245
|
+
"⑤ 发布 npm 包": "⑤ Publish npm package",
|
|
251
246
|
"目标目录(含 package.json)": "Target dir (contains package.json)",
|
|
252
247
|
"① 确认源(registry)": "① Check registry",
|
|
253
248
|
"② 查看认证配置(config list)": "② Auth config (config list)",
|
|
@@ -261,13 +256,14 @@ Object.assign(EN_DICT, {
|
|
|
261
256
|
"未登录 npm": "Not logged in to npm",
|
|
262
257
|
"npm 状态": "npm status",
|
|
263
258
|
"registry:": "registry: ",
|
|
259
|
+
"发布将使用官方源:": "Publishing uses the official registry: ",
|
|
264
260
|
"我已核对以上内容,确认发布到 npm registry": "I've reviewed the above — confirm publishing to the npm registry",
|
|
265
261
|
"发布完成": "Publish complete",
|
|
266
262
|
"发布失败": "Publish failed",
|
|
267
263
|
"发布失败:": "Publish failed: ",
|
|
268
264
|
"读取 npm 状态失败:": "Failed to read npm status: ",
|
|
269
265
|
"发布 npm 包五步向导:确认源 → 认证配置 → 身份 → 打包预览 → 发布": "Five-step npm publish guide: registry → auth config → identity → pack preview → publish",
|
|
270
|
-
"按顺序完成:①环境检查 → ②SSH 密钥与连接 → ③代码管理,以及 ④克隆仓库 /
|
|
266
|
+
"按顺序完成:①环境检查 → ②SSH 密钥与连接 → ③代码管理,以及 ④克隆仓库 / ⑤发布 npm 包。推送会自动忽略 >100MB 的文件(": "In order: ①Environment → ②SSH key & connection → ③Code management, plus ④Clone repos / ⑤Publish npm. Pushing auto-ignores >100MB files (",
|
|
271
267
|
" 限制)并说明原因。": " limit) with a reason.",
|
|
272
268
|
// ④ 目录选择(复用 ③ 的「选择目录」交互)
|
|
273
269
|
"克隆到目录(默认=用户主目录 home)": "Clone into (default = user home directory)",
|
|
@@ -276,18 +272,8 @@ Object.assign(EN_DICT, {
|
|
|
276
272
|
"已记住目录": "Directory remembered",
|
|
277
273
|
"记住目录失败:": "Failed to remember dir: ",
|
|
278
274
|
"目录不存在或不是文件夹:": "Directory does not exist or is not a folder: ",
|
|
279
|
-
// ⑤ 通用 PR 流程
|
|
280
|
-
"通用 PR 流程(未分析,可点击「分析 PR 规则」生成专属规则)": "Generic PR flow (not analyzed — click \"Analyze PR rules\" to generate a repo-specific one)",
|
|
281
|
-
"该仓库暂无本地规则": "No local rule for this repo yet",
|
|
282
|
-
"已加载本地规则(插件目录 rules/)": "Loaded local rule (plugin rules/ dir)",
|
|
283
|
-
"请先分析该仓库的 PR 规则再执行": "Analyze this repo's PR rules before running",
|
|
284
|
-
"已按模板生成(可编辑,未消耗 AI)": "Generated from the template (editable, no AI spent)",
|
|
285
|
-
"分析完成,规则已缓存到插件目录 rules/(下次直接使用,无需重新分析)": "Analyzed — rule cached to the plugin rules/ dir (used directly next time)",
|
|
286
|
-
"该仓库暂无本地规则,当前为通用 PR 流程": "No local rule for this repo yet — showing the generic PR flow",
|
|
287
|
-
"点击「分析 PR 规则」可让 AI 生成该仓库专属规则(并缓存到插件目录 rules/)": "Click \"Analyze PR rules\" to have AI generate a repo-specific rule (cached to the plugin rules/ dir)",
|
|
288
|
-
"已加载预置规则(插件目录 rules/)": "Preset rule loaded (plugin rules/ dir)",
|
|
289
275
|
"可手动输入/粘贴目录绝对路径,或点击「浏览…」弹出本地文件夹选择器。确认后将添加到常用目录并记住,下次打开无需重新选择。": "Type/paste an absolute path, or click \"Browse…\" to open the native folder picker. Confirming adds it to your favorite dirs so you won't re-pick it next time.",
|
|
290
|
-
//
|
|
276
|
+
// ⑤ npm 登录与目录
|
|
291
277
|
"未登录 npm,发布前需先登录": "Not logged in to npm — log in before publishing",
|
|
292
278
|
"打开终端执行 npm login": "Open a terminal to run npm login",
|
|
293
279
|
"复制登录命令": "Copy login command",
|
|
@@ -298,22 +284,7 @@ Object.assign(EN_DICT, {
|
|
|
298
284
|
"请手动在终端运行:": "Run this manually in a terminal: ",
|
|
299
285
|
"已复制:": "Copied: ",
|
|
300
286
|
"目录不存在:": "Directory does not exist: ",
|
|
301
|
-
"确保 fork": "Ensure fork",
|
|
302
|
-
"克隆 fork": "Clone fork",
|
|
303
|
-
"加上游": "Add upstream",
|
|
304
|
-
"拉取上游": "Fetch upstream",
|
|
305
|
-
"新建分支": "Create branch",
|
|
306
|
-
"写入条目": "Write entry",
|
|
307
287
|
"提交": "Commit",
|
|
308
|
-
"推送": "Push",
|
|
309
|
-
"创建 PR": "Create PR",
|
|
310
|
-
"该仓库已有规则,是否重新分析?": "A rule already exists for this repo — re-analyze?",
|
|
311
|
-
"分析 PR 规则(可选:手动填写规则或规则文件,留空则由 AI 自动查找)": "Analyze PR rules (optional: type the rules or a rule file; leave empty and AI will find them)",
|
|
312
|
-
"手动填写该仓库的 PR 规则(可选)": "Manually type this repo's PR rules (optional)",
|
|
313
|
-
"或提供规则文件(本地路径 / URL,如某仓库 README)": "Or provide a rules file (local path / URL, e.g. a repo README)",
|
|
314
|
-
"留空则由 AI 自动抓取该仓库文档(README / CONTRIBUTING / PR 模板)分析": "Leave empty and AI will auto-fetch this repo's docs (README / CONTRIBUTING / PR template)",
|
|
315
|
-
"开始分析": "Start analysis",
|
|
316
|
-
"已按你提供的规则分析并缓存到插件目录 rules/": "Analyzed with your supplied rules and cached to the plugin rules/ dir",
|
|
317
288
|
"取消": "Cancel",
|
|
318
289
|
// ④ 按地址克隆
|
|
319
290
|
"也可以按仓库地址克隆到任意位置": "You can also clone any repo by URL into any location",
|
|
@@ -322,12 +293,7 @@ Object.assign(EN_DICT, {
|
|
|
322
293
|
"请输入仓库地址": "Enter a repo URL",
|
|
323
294
|
"请选择克隆到目录": "Choose a destination directory first",
|
|
324
295
|
"无法从地址识别仓库名称": "Cannot derive a repo name from this URL",
|
|
325
|
-
// ⑤
|
|
326
|
-
"⑤ 提交 awesome-dsh-plugin 插件收录 PR": "⑤ Submit an awesome-dsh-plugin listing PR",
|
|
327
|
-
"目标仓库": "Target repo",
|
|
328
|
-
"已加载该仓库的收录规则(预置)": "Loaded this repo's listing rules (preset)",
|
|
329
|
-
"正在加载收录规则…": "Loading listing rules…",
|
|
330
|
-
// ⑥ 目录默认留空,需手动选择
|
|
296
|
+
// ⑤ 目录默认留空,需手动选择
|
|
331
297
|
"请先选择目标目录": "Choose a target directory first",
|
|
332
298
|
"请先选择目标目录(当前留空,不会自动使用默认目录)": "Choose a target directory first (left empty on purpose — it will NOT auto-use the default dir)",
|
|
333
299
|
});
|
|
@@ -447,11 +413,88 @@ function useSession() {
|
|
|
447
413
|
gh: "winget install --id GitHub.cli -e",
|
|
448
414
|
ssh: "Add-WindowsCapability -Online -Name OpenSSH.Client",
|
|
449
415
|
};
|
|
416
|
+
// 一键安装进度弹窗:实时展示分步进度 + 尾部输出 + 结果 / 重启提示。
|
|
417
|
+
function InstallDialog({ tool, logs, result, onClose }) {
|
|
418
|
+
const scrollRef = useRef(null);
|
|
419
|
+
const restartTimer = useRef(null);
|
|
420
|
+
const [restartState, setRestartState] = useState(null); // null | 'restarting' | 'error'
|
|
421
|
+
const [restartError, setRestartError] = useState(null);
|
|
422
|
+
useEffect(() => {
|
|
423
|
+
if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
|
424
|
+
}, [logs]);
|
|
425
|
+
useEffect(() => () => { if (restartTimer.current) clearTimeout(restartTimer.current); }, []);
|
|
426
|
+
/** 一键重启 dsh:服务端 detached helper 按原命令拉起新进程(机制同 dsh-update)。 */
|
|
427
|
+
const doRestart = async () => {
|
|
428
|
+
setRestartState("restarting"); setRestartError(null);
|
|
429
|
+
try {
|
|
430
|
+
const res = await fetch(API + "/restart", { method: "POST" });
|
|
431
|
+
if (!res.ok) {
|
|
432
|
+
let detail = "";
|
|
433
|
+
try { detail = await res.text(); } catch {}
|
|
434
|
+
throw new Error("HTTP " + String(res.status) + (detail !== "" ? ": " + detail : ""));
|
|
435
|
+
}
|
|
436
|
+
// 成功后本进程约 500ms 退出、helper 接管——页面会短暂断开。
|
|
437
|
+
// 若 12 秒后组件仍存活(异常情况),提示手动重启。
|
|
438
|
+
restartTimer.current = window.setTimeout(() => {
|
|
439
|
+
setRestartState((s) => (s === "restarting" ? "error" : s));
|
|
440
|
+
}, 12000);
|
|
441
|
+
} catch (e) {
|
|
442
|
+
setRestartState("error");
|
|
443
|
+
setRestartError(String(e));
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
const done = result !== null;
|
|
447
|
+
const ok = !!(result && result.ok);
|
|
448
|
+
const toolName = tool === "gh" ? "GitHub CLI" : tool === "git" ? "Git" : "SSH";
|
|
449
|
+
const needRestart = done && !!(result && result.restartNeeded);
|
|
450
|
+
return h("div", { style: { position: "fixed", inset: 0, zIndex: 9999, background: T.mask, display: "flex", alignItems: "center", justifyContent: "center" } },
|
|
451
|
+
h("div", { style: { width: 460, maxWidth: "92vw", background: T.layer1, border: "1px solid " + T.border, borderRadius: 14, padding: 16, display: "flex", flexDirection: "column", gap: 10 } },
|
|
452
|
+
h("div", { style: { fontSize: 14, fontWeight: 600, color: T.label } },
|
|
453
|
+
(done ? (ok ? "✅ " : "❌ ") : "⏳ ") + toolName + " " + (done ? t("安装完成") : t("安装中…"))),
|
|
454
|
+
h("div", { ref: scrollRef, style: { overflowY: "auto", maxHeight: 240, fontFamily: "var(--ds-font-family-code, ui-monospace, monospace)", fontSize: 12, lineHeight: 1.7 } },
|
|
455
|
+
logs.length === 0
|
|
456
|
+
? h("div", { style: { color: T.secondary } }, "…")
|
|
457
|
+
: logs.map((log, i) =>
|
|
458
|
+
h("div", { key: i, style: { display: "flex", gap: 6, alignItems: "baseline" } },
|
|
459
|
+
h("span", { style: { color: log.state === "running" ? T.brand : T.success } },
|
|
460
|
+
log.state === "running" ? "▶" : "✓"),
|
|
461
|
+
h("span", { style: { color: T.label, whiteSpace: "pre-wrap", wordBreak: "break-all" } },
|
|
462
|
+
log.label + (log.tail ? " " + log.tail : "")),
|
|
463
|
+
)
|
|
464
|
+
)),
|
|
465
|
+
done
|
|
466
|
+
? needRestart
|
|
467
|
+
? h("div", { style: { fontSize: 12, color: T.warn, lineHeight: 1.6 } },
|
|
468
|
+
t("安装完成,需要重启 DSH 生效。点击「重启 DSH」立即重启,页面会短暂断开,恢复后请刷新。"))
|
|
469
|
+
: ok
|
|
470
|
+
? h("div", { style: { fontSize: 12, color: T.success } },
|
|
471
|
+
t("安装完成") + (result.userLevel ? t("(已装到 ~/.local/bin,免 sudo)") : ""))
|
|
472
|
+
: h("div", { style: { fontSize: 12, color: T.danger } },
|
|
473
|
+
"❌ " + (result.error || t("安装失败")) + (result.command && !result.manual ? ":" + result.command : ""))
|
|
474
|
+
: h("div", { style: { fontSize: 12, color: T.secondary } }, t("安装中,请稍候…")),
|
|
475
|
+
restartState === "restarting"
|
|
476
|
+
? h("div", { style: { fontSize: 12, color: T.brand, lineHeight: 1.6 } },
|
|
477
|
+
t("正在重启 DSH,页面将短暂断开;恢复后请刷新网页。"))
|
|
478
|
+
: restartState === "error"
|
|
479
|
+
? h("div", { style: { fontSize: 12, color: T.danger, lineHeight: 1.6 } },
|
|
480
|
+
(restartError ? t("重启失败:") + restartError + " " : "") + t("重启似乎未生效,请手动重启 dsh。"))
|
|
481
|
+
: null,
|
|
482
|
+
h("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8 } },
|
|
483
|
+
needRestart
|
|
484
|
+
? h(Btn, { label: t("重启 DSH"), onClick: doRestart, tone: "primary", disabled: restartState !== null })
|
|
485
|
+
: null,
|
|
486
|
+
h(Btn, { label: t("关闭"), onClick: onClose, tone: "ghost", disabled: restartState === "restarting" })
|
|
487
|
+
)
|
|
488
|
+
)
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
|
|
450
492
|
function EnvSection() {
|
|
451
493
|
const [env, setEnv] = useState(() => cache.env);
|
|
452
494
|
const [err, setErr] = useState(null);
|
|
453
495
|
const [installing, setInstalling] = useState(null); // 'git'|'gh'|'ssh'|null
|
|
454
496
|
const [installResult, setInstallResult] = useState(null);
|
|
497
|
+
const [installLogs, setInstallLogs] = useState([]); // [{label, state:'running'|'done', tail}]
|
|
455
498
|
const load = useCallback(async () => {
|
|
456
499
|
setErr(null);
|
|
457
500
|
// 先读缓存(DSH 打开时已预取),再后台刷新保持最新
|
|
@@ -461,12 +504,28 @@ function useSession() {
|
|
|
461
504
|
}, []);
|
|
462
505
|
useEffect(() => { void preload().then(load); }, [load]);
|
|
463
506
|
|
|
464
|
-
//
|
|
507
|
+
// 一键安装:流式调用 host(NDJSON 进度事件),弹窗实时展示分步进度,成功后重新检测。
|
|
465
508
|
const doInstall = useCallback(async (tool) => {
|
|
466
|
-
setInstalling(tool); setInstallResult(null);
|
|
509
|
+
setInstalling(tool); setInstallResult(null); setInstallLogs([]);
|
|
467
510
|
try {
|
|
468
|
-
const r = await
|
|
511
|
+
const r = await jpostStream("/install-tool", { tool }, (evt) => {
|
|
512
|
+
if (evt.type === "step") {
|
|
513
|
+
setInstallLogs((logs) => [
|
|
514
|
+
...logs.map((l) => l.state === "running" ? { ...l, state: "done" } : l),
|
|
515
|
+
{ label: evt.label, state: "running", tail: "" },
|
|
516
|
+
]);
|
|
517
|
+
} else if (evt.type === "out" && typeof evt.text === "string") {
|
|
518
|
+
setInstallLogs((logs) => {
|
|
519
|
+
if (logs.length === 0) return logs;
|
|
520
|
+
const next = [...logs];
|
|
521
|
+
const last = next[next.length - 1];
|
|
522
|
+
next[next.length - 1] = { ...last, tail: (last.tail + evt.text).slice(-300) };
|
|
523
|
+
return next;
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
});
|
|
469
527
|
setInstallResult(r);
|
|
528
|
+
setInstallLogs((logs) => logs.map((l) => l.state === "running" ? { ...l, state: "done" } : l));
|
|
470
529
|
if (r.ok) void load();
|
|
471
530
|
} catch (e) { setInstallResult({ ok: false, tool, error: String(e) }); }
|
|
472
531
|
finally { setInstalling(null); }
|
|
@@ -481,7 +540,10 @@ function useSession() {
|
|
|
481
540
|
|
|
482
541
|
// 渲染单个工具行;缺失时带安装命令 + 安装按钮。
|
|
483
542
|
const toolRow = (tool, label, installed, version) => {
|
|
484
|
-
|
|
543
|
+
// 优先用 host 返回的平台自适应命令(与「一键安装」同源);
|
|
544
|
+
// Windows 兜底用内置提示;非 Windows 且 host 未返回时留空(不误导)。
|
|
545
|
+
const hint = (env && env.installHints && env.installHints[tool])
|
|
546
|
+
|| (winOs ? INSTALL_HINTS[tool] : "")
|
|
485
547
|
const status = installed
|
|
486
548
|
? h("span", { style: { color: T.label, fontSize: 13 } }, "✅ " + (version || t("已安装")))
|
|
487
549
|
: h("span", { style: { display: "inline-flex", alignItems: "center", gap: 8 } },
|
|
@@ -490,9 +552,11 @@ function useSession() {
|
|
|
490
552
|
h("button", { type: "button", title: t("一键安装(会自动选包管理器)"), disabled: installing !== null, style: { ...changeBtnStyle(), color: T.brand, fontWeight: 600 }, onClick: (e) => { e.stopPropagation(); void doInstall(tool); } }, installing === tool ? t("安装中…") : t("安装"))
|
|
491
553
|
)
|
|
492
554
|
const feedback = installResult && installResult.tool === tool
|
|
493
|
-
? (installResult.
|
|
494
|
-
? h("div", { style: { marginTop: 4, color: T.
|
|
495
|
-
:
|
|
555
|
+
? (installResult.started
|
|
556
|
+
? h("div", { style: { marginTop: 4, color: T.secondary, fontSize: 12 } }, "⏳ " + (installResult.error || ""))
|
|
557
|
+
: installResult.ok
|
|
558
|
+
? h("div", { style: { marginTop: 4, color: T.success, fontSize: 12 } }, "✅ " + (installResult.command || t("已安装")) + t(" 执行成功") + (installResult.userLevel ? t("(已装到 ~/.local/bin,免 sudo)") : "") + (installResult.needElevation ? t("(如未生效需以管理员身份重试)") : "") + (installResult.restartNeeded ? t(",需要重启 DSH 生效") : ""))
|
|
559
|
+
: h("div", { style: { marginTop: 4, color: T.danger, fontSize: 12 } }, "❌ " + (installResult.error || t("安装失败")) + (installResult.command && !installResult.manual ? ":" + installResult.command : "")))
|
|
496
560
|
: null
|
|
497
561
|
return h("div", { key: tool, style: { marginTop: 4 } },
|
|
498
562
|
h(Field, { label }, status),
|
|
@@ -509,7 +573,8 @@ function useSession() {
|
|
|
509
573
|
) : h(Field, { label: t("检测中"), value: err ? t("失败: ") + err : "…" }),
|
|
510
574
|
(!env || !env.git.installed || !env.gh.installed || !(env.ssh && env.ssh.installed)) ? h("div", { style: { marginTop: 8, fontSize: 12, color: T.secondary, lineHeight: 1.6 } },
|
|
511
575
|
t("未找到的工具可用上方「安装」按钮一键安装(") + (winOs ? t("Windows 用 winget / 内置功能") : t("用系统包管理器")) + t(",可能需管理员权限);也可手动复制安装命令执行,安装后点「重新检查」。")
|
|
512
|
-
) : h(Btn, { label: t("重新检查"), onClick: load, tone: "ghost" })
|
|
576
|
+
) : h(Btn, { label: t("重新检查"), onClick: load, tone: "ghost" }),
|
|
577
|
+
installing !== null ? h(InstallDialog, { tool: installing, logs: installLogs, result: installResult, onClose: () => { setInstalling(null); } }) : null
|
|
513
578
|
);
|
|
514
579
|
}
|
|
515
580
|
|
|
@@ -1624,7 +1689,7 @@ function useSession() {
|
|
|
1624
1689
|
};
|
|
1625
1690
|
}
|
|
1626
1691
|
|
|
1627
|
-
/** Shared text input style for the new sections (
|
|
1692
|
+
/** Shared text input style for the new sections (④⑤). */
|
|
1628
1693
|
function inputStyle() {
|
|
1629
1694
|
return {
|
|
1630
1695
|
flex: 1, minWidth: 0, font: "inherit", fontSize: 12,
|
|
@@ -1980,135 +2045,9 @@ function useSession() {
|
|
|
1980
2045
|
);
|
|
1981
2046
|
}
|
|
1982
2047
|
|
|
1983
|
-
|
|
1984
|
-
// ⑤ 固定目标仓库:awesome-dsh-plugin 插件收录 PR(隐藏地址输入 / 记录 / 规则分析)。
|
|
1985
|
-
const PR_TARGET_URL = "https://github.com/awesome-dsh-plugin/awesome-dsh-plugin";
|
|
1986
|
-
function PrSection() {
|
|
1987
|
-
const [url, setUrl] = useState(PR_TARGET_URL);
|
|
1988
|
-
const [rule, setRule] = useState(null); // 预置规则
|
|
1989
|
-
const [ruleCached, setRuleCached] = useState(false);
|
|
1990
|
-
const [ruleSource, setRuleSource] = useState(null); // 'preset' | 'cached' | null
|
|
1991
|
-
const [workdir, setWorkdir] = useState("");
|
|
1992
|
-
const [pName, setPName] = useState("");
|
|
1993
|
-
const [pCategory, setPCategory] = useState("");
|
|
1994
|
-
const [pDescEn, setPDescEn] = useState("");
|
|
1995
|
-
const [pDescZh, setPDescZh] = useState("");
|
|
1996
|
-
const [title, setTitle] = useState("");
|
|
1997
|
-
const [body, setBody] = useState("");
|
|
1998
|
-
const [entry, setEntry] = useState("");
|
|
1999
|
-
const [genBusy, setGenBusy] = useState(false);
|
|
2000
|
-
const [execBusy, setExecBusy] = useState(false);
|
|
2001
|
-
const [logs, setLogs] = useState([]);
|
|
2002
|
-
const [prUrl, setPrUrl] = useState(null);
|
|
2003
|
-
const [msg, setMsg] = useState(null);
|
|
2004
|
-
const [err, setErr] = useState(null);
|
|
2005
|
-
|
|
2006
|
-
useEffect(() => { jget("/clone/default-dir").then((r) => { if (r && r.ok && r.dir) setWorkdir(r.dir); }).catch(() => {}); }, []);
|
|
2048
|
+
|
|
2007
2049
|
|
|
2008
|
-
|
|
2009
|
-
const lookupRule = useCallback(async (u) => {
|
|
2010
|
-
const urlTxt = String(u || "").trim();
|
|
2011
|
-
try {
|
|
2012
|
-
const r = await jget("/pr/rule?url=" + encodeURIComponent(urlTxt));
|
|
2013
|
-
if (r && r.ok) {
|
|
2014
|
-
if (r.hasRule && r.rule) {
|
|
2015
|
-
setRule(r.rule);
|
|
2016
|
-
setRuleCached(true);
|
|
2017
|
-
setRuleSource(r.rule.preset ? "preset" : "cached");
|
|
2018
|
-
setErr(null);
|
|
2019
|
-
} else {
|
|
2020
|
-
setRule(null);
|
|
2021
|
-
setRuleCached(false);
|
|
2022
|
-
setRuleSource(null);
|
|
2023
|
-
}
|
|
2024
|
-
}
|
|
2025
|
-
} catch (e) { /* 静默:保持现状 */ }
|
|
2026
|
-
}, []);
|
|
2027
|
-
useEffect(() => { void lookupRule(PR_TARGET_URL); }, [lookupRule]);
|
|
2028
|
-
|
|
2029
|
-
const generate = useCallback(async () => {
|
|
2030
|
-
const u = String(url || "").trim();
|
|
2031
|
-
if (!u || !rule) { setErr(t("请先填写仓库并分析规则")); return; }
|
|
2032
|
-
setGenBusy(true); setErr(null); setMsg(null);
|
|
2033
|
-
try {
|
|
2034
|
-
const r = await jpost("/pr/generate", {
|
|
2035
|
-
url: u, rule,
|
|
2036
|
-
pluginInfo: { name: pName, category: pCategory, descriptionEn: pDescEn, descriptionZh: pDescZh },
|
|
2037
|
-
});
|
|
2038
|
-
if (r && r.ok) {
|
|
2039
|
-
setTitle(r.title || ""); setBody(r.body || ""); setEntry(r.entry || "");
|
|
2040
|
-
setMsg(r.fromTemplate ? t("已按模板生成(可编辑,未消耗 AI)") : t("已生成,请确认 / 修改后再执行"));
|
|
2041
|
-
} else setErr((r && r.error) || t("生成失败"));
|
|
2042
|
-
} catch (e) { setErr(t("生成失败:") + e); }
|
|
2043
|
-
finally { setGenBusy(false); }
|
|
2044
|
-
}, [url, rule, pName, pCategory, pDescEn, pDescZh]);
|
|
2045
|
-
|
|
2046
|
-
const execute = useCallback(async () => {
|
|
2047
|
-
const u = String(url || "").trim();
|
|
2048
|
-
if (!u || !rule) { setErr(t("请先填写仓库并分析规则")); return; }
|
|
2049
|
-
setExecBusy(true); setErr(null); setMsg(null); setLogs([]); setPrUrl(null);
|
|
2050
|
-
try {
|
|
2051
|
-
const r = await jpost("/pr/execute", {
|
|
2052
|
-
url: u, workdir, rule, entryContent: entry, title, body,
|
|
2053
|
-
base: (rule && rule.baseBranch) || "main",
|
|
2054
|
-
});
|
|
2055
|
-
setLogs((r && r.logs) || []);
|
|
2056
|
-
if (r && r.ok) {
|
|
2057
|
-
if (r.prUrl) { setPrUrl(r.prUrl); setMsg(t("PR 已创建:") + r.prUrl); }
|
|
2058
|
-
else setMsg(t("PR 流程已完成"));
|
|
2059
|
-
} else setErr((r && r.error) || t("PR 执行失败"));
|
|
2060
|
-
} catch (e) { setErr(t("PR 执行失败:") + e); }
|
|
2061
|
-
finally { setExecBusy(false); }
|
|
2062
|
-
}, [url, rule, workdir, entry, title, body]);
|
|
2063
|
-
|
|
2064
|
-
return h(Box, { title: t("⑤ 提交 awesome-dsh-plugin 插件收录 PR"), defaultCollapsed: true },
|
|
2065
|
-
h("div", { style: { fontSize: 11, color: T.secondary, marginBottom: 8, lineHeight: 1.6 } },
|
|
2066
|
-
t("目标仓库") + ": https://github.com/awesome-dsh-plugin/awesome-dsh-plugin " +
|
|
2067
|
-
(rule ? t("已加载该仓库的收录规则(预置)") : t("正在加载收录规则…"))),
|
|
2068
|
-
h("div", { style: { border: "1px solid " + T.border, borderRadius: 8, padding: 8, marginBottom: 8 } },
|
|
2069
|
-
h("div", { style: { fontSize: 12, fontWeight: 600, color: T.label, marginBottom: 6 } }, t("插件信息(供 AI 生成 PR 描述)")),
|
|
2070
|
-
h(Field, { label: t("插件名 owner/repo") }, h("input", { type: "text", value: pName, placeholder: "Zhucy123/source-code-mgmt", onChange: (e) => setPName(e.target.value), style: inputStyle() })),
|
|
2071
|
-
h(Field, { label: t("分类 category") }, h("input", { type: "text", value: pCategory, placeholder: "git", onChange: (e) => setPCategory(e.target.value), style: inputStyle() })),
|
|
2072
|
-
h(Field, { label: t("描述 en") }, h("input", { type: "text", value: pDescEn, onChange: (e) => setPDescEn(e.target.value), style: inputStyle() })),
|
|
2073
|
-
h(Field, { label: t("描述 zh") }, h("input", { type: "text", value: pDescZh, onChange: (e) => setPDescZh(e.target.value), style: inputStyle() })),
|
|
2074
|
-
h("div", { style: { display: "flex", gap: 8, marginTop: 6 } },
|
|
2075
|
-
h(Btn, { label: genBusy ? t("生成中…") : t("生成 PR 内容(AI)"), onClick: generate, disabled: genBusy || !url || !rule, tone: "primary", noBg: true }),
|
|
2076
|
-
),
|
|
2077
|
-
),
|
|
2078
|
-
h("div", { style: { marginBottom: 6 } },
|
|
2079
|
-
h("div", { style: { color: T.secondary, fontSize: 12, marginBottom: 4 } }, t("PR 标题")),
|
|
2080
|
-
h("input", { type: "text", value: title, onChange: (e) => setTitle(e.target.value), style: inputStyle() }),
|
|
2081
|
-
),
|
|
2082
|
-
h("div", { style: { marginBottom: 6 } },
|
|
2083
|
-
h("div", { style: { color: T.secondary, fontSize: 12, marginBottom: 4 } }, t("PR 描述")),
|
|
2084
|
-
h("textarea", { rows: 3, value: body, onChange: (e) => setBody(e.target.value), style: { ...inputStyle(), minHeight: 60, resize: "vertical" } }),
|
|
2085
|
-
),
|
|
2086
|
-
h("div", { style: { marginBottom: 6 } },
|
|
2087
|
-
h("div", { style: { color: T.secondary, fontSize: 12, marginBottom: 4 } }, t("条目文件内容(entry,可留空)")),
|
|
2088
|
-
h("textarea", { rows: 4, value: entry, onChange: (e) => setEntry(e.target.value), style: { ...inputStyle(), minHeight: 70, resize: "vertical", fontFamily: "var(--ds-font-family-code, ui-monospace, monospace)", fontSize: 11 } }),
|
|
2089
|
-
),
|
|
2090
|
-
h("div", { style: { marginBottom: 6 } },
|
|
2091
|
-
h("div", { style: { color: T.secondary, fontSize: 12, marginBottom: 4 } }, t("工作目录(fork 克隆位置,默认=默认工作区)")),
|
|
2092
|
-
h("input", { type: "text", value: workdir, onChange: (e) => setWorkdir(e.target.value), style: inputStyle() }),
|
|
2093
|
-
),
|
|
2094
|
-
h("div", { style: { display: "flex", gap: 8, marginTop: 6, alignItems: "center", flexWrap: "wrap" } },
|
|
2095
|
-
h(Btn, { label: execBusy ? t("PR 执行中…") : t("执行 PR"), onClick: execute, disabled: execBusy || !url || !rule, tone: "success" }),
|
|
2096
|
-
prUrl ? h("a", { href: prUrl, target: "_blank", rel: "noreferrer", style: { color: T.brand, fontSize: 12 } }, prUrl) : null,
|
|
2097
|
-
),
|
|
2098
|
-
logs.length > 0 ? h("div", { style: { marginTop: 8, maxHeight: 200, overflow: "auto", background: T.layer1, borderRadius: 8, padding: 8 } },
|
|
2099
|
-
logs.map((lg, i) =>
|
|
2100
|
-
h("div", { key: i, style: { fontSize: 11, lineHeight: 1.6 } },
|
|
2101
|
-
h("span", { style: { color: lg.ok ? T.success : T.danger, fontWeight: 600, marginRight: 6 } }, (lg.ok ? "✅" : "❌") + " " + (lg.label || "")),
|
|
2102
|
-
lg.output ? h("span", { style: { color: T.secondary } }, lg.output) : null
|
|
2103
|
-
)
|
|
2104
|
-
)
|
|
2105
|
-
) : null,
|
|
2106
|
-
msg ? h("div", { style: { color: T.success, fontSize: 12, marginTop: 6 } }, "✅ " + msg) : null,
|
|
2107
|
-
err ? h("div", { style: { color: T.danger, fontSize: 12, marginTop: 6 } }, "❌ " + err) : null,
|
|
2108
|
-
);
|
|
2109
|
-
}
|
|
2110
|
-
|
|
2111
|
-
// ---------- section 6: 发布 npm 包 ----------
|
|
2050
|
+
// ---------- section 5: 发布 npm 包 ----------
|
|
2112
2051
|
function NpmSection() {
|
|
2113
2052
|
const [dir, setDir] = useState("");
|
|
2114
2053
|
const [dirs, setDirs] = useState([]);
|
|
@@ -2132,7 +2071,7 @@ function useSession() {
|
|
|
2132
2071
|
jget("/workspaces").catch(() => null),
|
|
2133
2072
|
]);
|
|
2134
2073
|
const list = [];
|
|
2135
|
-
//
|
|
2074
|
+
// ⑤ 目标目录默认留空,由用户手动选择(不复选默认目录)。
|
|
2136
2075
|
if (def && def.dir) { if (!list.includes(def.dir)) list.push(def.dir); }
|
|
2137
2076
|
if (ws) {
|
|
2138
2077
|
if (Array.isArray(ws.workspaces)) for (const w of ws.workspaces) if (!list.includes(w)) list.push(w);
|
|
@@ -2220,7 +2159,7 @@ function useSession() {
|
|
|
2220
2159
|
{ key: "publish", label: t("⑤ 发布(publish)") },
|
|
2221
2160
|
];
|
|
2222
2161
|
|
|
2223
|
-
return h(Box, { title: t("
|
|
2162
|
+
return h(Box, { title: t("⑤ 发布 npm 包"), defaultCollapsed: true },
|
|
2224
2163
|
h("div", { style: { fontSize: 11, color: T.secondary, marginBottom: 8 } }, t("发布 npm 包五步向导:确认源 → 认证配置 → 身份 → 打包预览 → 发布")),
|
|
2225
2164
|
h("div", { style: { display: "flex", gap: 8, marginBottom: 8, alignItems: "center" } },
|
|
2226
2165
|
h("span", { style: { color: T.secondary, fontSize: 12, whiteSpace: "nowrap" } }, t("目标目录(含 package.json)")),
|
|
@@ -2247,6 +2186,7 @@ function useSession() {
|
|
|
2247
2186
|
: null,
|
|
2248
2187
|
h("div", null, (status.installed ? "" : t("npm 未安装(请先在 ① 环境检查安装)") + " ") + (status.npmPath || "npm")),
|
|
2249
2188
|
status.registry ? h("div", null, t("registry:") + status.registry) : null,
|
|
2189
|
+
status.publishRegistry ? h("div", { style: status.mirrorConfigured ? { color: T.warn } : { color: T.secondary } }, (status.mirrorConfigured ? "⚠️ " : "") + t("发布将使用官方源:") + status.publishRegistry) : null,
|
|
2250
2190
|
status.whoami
|
|
2251
2191
|
? h("div", { style: { color: T.success } }, t("已登录:") + status.whoami)
|
|
2252
2192
|
: h("div", { style: { color: T.warn } }, "⚠️ " + (status.whoamiError || t("未登录 npm"))),
|
|
@@ -2256,7 +2196,7 @@ function useSession() {
|
|
|
2256
2196
|
h("div", { style: { fontSize: 12, color: T.warn, marginBottom: 6 } }, t("未登录 npm,发布前需先登录")),
|
|
2257
2197
|
h("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" } },
|
|
2258
2198
|
h(Btn, { label: loginBusy ? t("执行中…") : t("打开终端执行 npm login"), onClick: openLogin, disabled: loginBusy || !String(dir || "").trim(), tone: "primary", noBg: true }),
|
|
2259
|
-
h(Btn, { label: t("复制登录命令"), onClick: () => { void copyText("npm login"); setMsg(t("已复制:") + "npm login"); }, noBg: true }),
|
|
2199
|
+
h(Btn, { label: t("复制登录命令"), onClick: () => { void copyText("npm login --registry=https://registry.npmjs.org"); setMsg(t("已复制:") + "npm login --registry=https://registry.npmjs.org"); }, noBg: true }),
|
|
2260
2200
|
),
|
|
2261
2201
|
) : null,
|
|
2262
2202
|
steps.map((s) =>
|
|
@@ -2321,7 +2261,7 @@ function useSession() {
|
|
|
2321
2261
|
variant === "tab" ? null : h("button", { type: "button", style: closeBtnStyle(T), "aria-label": t("关闭"), onClick: onClose }, "✕")
|
|
2322
2262
|
),
|
|
2323
2263
|
h("p", { style: { margin: "6px 0 14px", color: T.secondary, fontSize: 12, lineHeight: 1.6 } },
|
|
2324
|
-
t("按顺序完成:①环境检查 → ②SSH 密钥与连接 → ③代码管理,以及 ④克隆仓库 /
|
|
2264
|
+
t("按顺序完成:①环境检查 → ②SSH 密钥与连接 → ③代码管理,以及 ④克隆仓库 / ⑤发布 npm 包。推送会自动忽略 >100MB 的文件(") + (provider === "gitee" ? "Gitee" : "GitHub") + t(" 限制)并说明原因。")),
|
|
2325
2265
|
h(EnvSection, null),
|
|
2326
2266
|
h("div", { style: { height: 12 } }),
|
|
2327
2267
|
h(SshSection, { provider, setProvider }),
|
|
@@ -2330,8 +2270,6 @@ function useSession() {
|
|
|
2330
2270
|
h("div", { style: { height: 12 } }),
|
|
2331
2271
|
h(CloneSection, { provider, setProvider }),
|
|
2332
2272
|
h("div", { style: { height: 12 } }),
|
|
2333
|
-
h(PrSection, null),
|
|
2334
|
-
h("div", { style: { height: 12 } }),
|
|
2335
2273
|
h(NpmSection, null)
|
|
2336
2274
|
);
|
|
2337
2275
|
}
|