@lancc/cmark 1.1.0 → 1.2.0
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/CHANGELOG.md +19 -0
- package/README.md +37 -5
- package/assets/index-B17NCAw4.js +758 -0
- package/assets/index-eB_q_VA3.css +1 -0
- package/export-file/github-markdown.css +549 -549
- package/export-file/index.html +32 -32
- package/index.html +2 -2
- package/logo.png +0 -0
- package/package.json +1 -1
- package/plugin.json +8 -218
- package/preload/branch-bar.js +44 -2
- package/preload/export-css.js +59 -0
- package/preload/git-panel.js +73 -18
- package/preload/git-service.js +64 -4
- package/preload/host.js +130 -0
- package/preload/remote-setup.js +392 -0
- package/preload/remote-wizard.js +328 -0
- package/preload/rename-note.js +4 -5
- package/preload/services.js +6 -15
- package/preload/settings-panel.js +468 -356
- package/preload/ws-prefs.js +55 -0
- package/preload.js +261 -6
- package/assets/index-BLONSItV.css +0 -1
- package/assets/index-Cz6qaZbV.js +0 -744
package/preload/git-panel.js
CHANGED
|
@@ -42,8 +42,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
42
42
|
--zgp-hunk:#ddf4ff; --zgp-hunk-fg:#0969da;
|
|
43
43
|
--zgp-shadow:0 8px 32px rgba(31,35,40,.14),0 2px 8px rgba(31,35,40,.08);
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
:root{
|
|
45
|
+
:root[data-theme='dark']{
|
|
47
46
|
--zgp-bg:#161b22; --zgp-bg2:#1c2128; --zgp-bg3:#22272e;
|
|
48
47
|
--zgp-fg:#e6edf3; --zgp-fg2:#9aa4b2; --zgp-fg3:#6e7681;
|
|
49
48
|
--zgp-bd:#30363d; --zgp-bd2:#3d444d;
|
|
@@ -53,7 +52,6 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
53
52
|
--zgp-del:#2d1214; --zgp-del-fg:#f85149;
|
|
54
53
|
--zgp-hunk:#0c2d4a; --zgp-hunk-fg:#58a6ff;
|
|
55
54
|
--zgp-shadow:0 8px 32px rgba(0,0,0,.6),0 2px 8px rgba(0,0,0,.4);
|
|
56
|
-
}
|
|
57
55
|
}
|
|
58
56
|
#zgp-btn{display:inline-flex;align-items:center;gap:5px;font-size:12px;font-weight:500;padding:3px 10px;
|
|
59
57
|
border:1px solid var(--zgp-bd2);border-radius:6px;background:var(--zgp-bg);color:var(--zgp-fg2);
|
|
@@ -128,7 +126,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
128
126
|
.zgp-st.D{background:var(--zgp-del);color:var(--zgp-del-fg);}
|
|
129
127
|
.zgp-st.Q{background:var(--zgp-bg3);color:var(--zgp-fg3);}
|
|
130
128
|
.zgp-st.U{background:var(--zgp-del);color:var(--zgp-del-fg);}
|
|
131
|
-
|
|
129
|
+
:root[data-theme='dark'] .zgp-st.M{background:#3a2d00;color:#d29922;}
|
|
132
130
|
.zgp-fp{flex:1;min-width:0;font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
133
131
|
font-family:Consolas,Menlo,monospace;}
|
|
134
132
|
.zgp-fp span{unicode-bidi:normal;}
|
|
@@ -219,7 +217,8 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
219
217
|
.zgp-jumpbar button{padding:3px 10px;font-size:11.5px;border-radius:6px;cursor:pointer;
|
|
220
218
|
border:1px solid var(--zgp-bd2);background:var(--zgp-bg);color:var(--zgp-fg2);}
|
|
221
219
|
.zgp-jumpbar button:hover{background:var(--zgp-acc-bg);border-color:var(--zgp-acc);color:var(--zgp-acc);}
|
|
222
|
-
.zgp-dl.zgp-flash{outline:
|
|
220
|
+
.zgp-dl.zgp-flash{outline:none !important;border:none !important;background:rgba(9,105,218,.16) !important;}
|
|
221
|
+
:root[data-theme='dark'] .zgp-dl.zgp-flash{background:rgba(88,166,255,.30) !important;}
|
|
223
222
|
|
|
224
223
|
.zgp-cf-ov{position:absolute;inset:0;background:rgba(0,0,0,.38);display:flex;align-items:center;
|
|
225
224
|
justify-content:center;z-index:20;padding:20px;}
|
|
@@ -458,6 +457,38 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
458
457
|
})
|
|
459
458
|
}
|
|
460
459
|
|
|
460
|
+
/** 找到元素最近的可滚动祖先(弹窗是 vbody 自己滚,内嵌是面板 body 滚) */
|
|
461
|
+
function scrollHostOf (el) {
|
|
462
|
+
var n = el
|
|
463
|
+
while (n && n !== doc.body) {
|
|
464
|
+
var cs = win.getComputedStyle(n)
|
|
465
|
+
if (/(auto|scroll)/.test(cs.overflowY) && n.scrollHeight > n.clientHeight + 4) return n
|
|
466
|
+
n = n.parentElement
|
|
467
|
+
}
|
|
468
|
+
return el
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* diff 打开后默认落到第一处改动:把第一行 add/del 滚到视口约 1/3 高度并闪一下,
|
|
473
|
+
* 免得长文件每次都要自己往下翻。
|
|
474
|
+
*/
|
|
475
|
+
function focusFirstChange (body) {
|
|
476
|
+
if (!body) return
|
|
477
|
+
var first = body.querySelector('.zgp-dl.add, .zgp-dl.del')
|
|
478
|
+
if (!first) return
|
|
479
|
+
var host = scrollHostOf(body)
|
|
480
|
+
try {
|
|
481
|
+
var hr = host.getBoundingClientRect()
|
|
482
|
+
var er = first.getBoundingClientRect()
|
|
483
|
+
var delta = er.top - hr.top
|
|
484
|
+
host.scrollTop = Math.max(0, host.scrollTop + delta - Math.round(host.clientHeight / 3))
|
|
485
|
+
} catch (e) {
|
|
486
|
+
try { first.scrollIntoView({ block: 'center' }) } catch (e2) {}
|
|
487
|
+
}
|
|
488
|
+
first.classList.add('zgp-flash')
|
|
489
|
+
win.setTimeout(function () { first.classList.remove('zgp-flash') }, 1200)
|
|
490
|
+
}
|
|
491
|
+
|
|
461
492
|
// diff 渲染:带行号
|
|
462
493
|
function renderDiffLines (lines) {
|
|
463
494
|
let oldNo = 0, newNo = 0
|
|
@@ -512,7 +543,12 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
512
543
|
body.innerHTML = '<span class="zgp-dl">' + esc(content || '(空)') + '</span>'
|
|
513
544
|
}
|
|
514
545
|
view.classList.add('on')
|
|
515
|
-
|
|
546
|
+
if (isDiff) {
|
|
547
|
+
// 必须先让弹窗可见再算位置,隐藏时 rect 全是 0
|
|
548
|
+
focusFirstChange(body)
|
|
549
|
+
} else {
|
|
550
|
+
body.scrollTop = 0
|
|
551
|
+
}
|
|
516
552
|
}
|
|
517
553
|
|
|
518
554
|
// ============ 当前笔记 ============
|
|
@@ -574,7 +610,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
574
610
|
if (!ok) return
|
|
575
611
|
const r = await G.abortInProgress()
|
|
576
612
|
toast(r.ok ? ('已中止 ' + st.inProgress) : ('中止失败:' + r.message), r.ok ? 'ok' : 'err')
|
|
577
|
-
|
|
613
|
+
refreshAfterAction()
|
|
578
614
|
}
|
|
579
615
|
wrap.appendChild(b)
|
|
580
616
|
}
|
|
@@ -585,7 +621,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
585
621
|
b2.onclick = async () => {
|
|
586
622
|
const r = await G.clearStaleLock()
|
|
587
623
|
toast(r.ok ? '已清理陈旧 index.lock' : ('清理失败:' + r.message), r.ok ? 'ok' : 'err')
|
|
588
|
-
|
|
624
|
+
refreshAfterAction()
|
|
589
625
|
}
|
|
590
626
|
wrap.appendChild(b2)
|
|
591
627
|
}
|
|
@@ -720,7 +756,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
720
756
|
if (r.ok) {
|
|
721
757
|
toast('已推送:' + (r.data.steps.join(' · ') || '无改动'), 'ok')
|
|
722
758
|
logCache = null
|
|
723
|
-
|
|
759
|
+
refreshAfterAction()
|
|
724
760
|
} else toast('推送失败:' + hintOf(r), 'err')
|
|
725
761
|
}
|
|
726
762
|
}
|
|
@@ -732,7 +768,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
732
768
|
toast('已还原到最近一次提交', 'ok')
|
|
733
769
|
logCache = null
|
|
734
770
|
try { if (win.__znotesReloadNote) win.__znotesReloadNote(rel) } catch (e) {}
|
|
735
|
-
|
|
771
|
+
refreshAfterAction()
|
|
736
772
|
} else if (rr.kind !== 'cancelled') {
|
|
737
773
|
toast('还原失败:' + hintOf(rr), 'err')
|
|
738
774
|
}
|
|
@@ -758,6 +794,8 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
758
794
|
const lines = stripDiffHeader(diff)
|
|
759
795
|
body.innerHTML = renderDiffLines(lines)
|
|
760
796
|
mountJumpBar(body)
|
|
797
|
+
// 首次渲染才跳;后续轮询刷新(内容没变)不打断用户当前阅读位置
|
|
798
|
+
if (!existedBody) focusFirstChange(body)
|
|
761
799
|
}
|
|
762
800
|
|
|
763
801
|
function renderChanges(st) {
|
|
@@ -806,7 +844,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
806
844
|
// 编辑器内部可能残留旧内容并触发自动保存,把还原结果又写回磁盘;
|
|
807
845
|
// 整页刷新是唯一可靠的做法(还原属低频操作)
|
|
808
846
|
try { if (win.__znotesReloadNote) win.__znotesReloadNote(f.path) } catch (err) {}
|
|
809
|
-
|
|
847
|
+
refreshAfterAction()
|
|
810
848
|
} else if (r.kind !== 'cancelled') {
|
|
811
849
|
toast('还原失败:' + hintOf(r), 'err')
|
|
812
850
|
}
|
|
@@ -938,7 +976,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
938
976
|
+ '<button class="zgp-ic" data-cmpprev="' + i + '" title="与提交前比对:这一次提交本身对这篇笔记做了什么改动">' + ICON.cmpPrev + '</button>'
|
|
939
977
|
+ '<button class="zgp-ic" data-view="' + i + '" title="查看该版本:显示这次提交时这篇笔记的完整内容">' + ICON.eye + '</button>'
|
|
940
978
|
+ '<span style="flex:1"></span>'
|
|
941
|
-
+ '<button class="zgp-ic danger" data-back="' + i + '" title="
|
|
979
|
+
+ '<button class="zgp-ic danger" data-back="' + i + '" title="回溯到此版本:把这篇笔记恢复成这个版本的内容">' + ICON.undo + '</button>'
|
|
942
980
|
+ '</div>'
|
|
943
981
|
: ''
|
|
944
982
|
return '<div class="zgp-cm" data-c="' + i + '">'
|
|
@@ -987,7 +1025,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
987
1025
|
toast('已回溯到 ' + c.short, 'ok')
|
|
988
1026
|
logCache = null
|
|
989
1027
|
try { if (win.__znotesReloadNote) win.__znotesReloadNote(onlyFile) } catch (e) {}
|
|
990
|
-
|
|
1028
|
+
refreshAfterAction()
|
|
991
1029
|
} else if (r.kind !== 'cancelled') {
|
|
992
1030
|
toast('回溯失败:' + hintOf(r), 'err')
|
|
993
1031
|
}
|
|
@@ -1019,7 +1057,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
1019
1057
|
+ '<span class="zgp-fs ' + (f.status || '').charAt(0) + '">' + (f.status || '').charAt(0) + '</span>'
|
|
1020
1058
|
+ '<span style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + esc(f.path) + '">' + esc(f.path) + '</span>'
|
|
1021
1059
|
+ '<button class="zgp-ic" title="查看该次改动">' + ICON.diff + '</button>'
|
|
1022
|
-
+ '<button class="zgp-ic danger" data-restore="1" title="
|
|
1060
|
+
+ '<button class="zgp-ic danger" data-restore="1" title="回溯到该版本">' + ICON.undo + '</button>'
|
|
1023
1061
|
+ '</div>'
|
|
1024
1062
|
}).join('')
|
|
1025
1063
|
}
|
|
@@ -1042,7 +1080,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
1042
1080
|
toast('已回溯到 ' + hash.slice(0, 7), 'ok')
|
|
1043
1081
|
logCache = null
|
|
1044
1082
|
try { if (win.__znotesReloadNote) win.__znotesReloadNote(p) } catch (err) {}
|
|
1045
|
-
|
|
1083
|
+
refreshAfterAction()
|
|
1046
1084
|
} else if (r3.kind !== 'cancelled') {
|
|
1047
1085
|
toast('回溯失败:' + hintOf(r3), 'err')
|
|
1048
1086
|
}
|
|
@@ -1101,6 +1139,23 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
1101
1139
|
}
|
|
1102
1140
|
}
|
|
1103
1141
|
|
|
1142
|
+
/**
|
|
1143
|
+
* 操作(提交/推送/拉取/还原/中止)完成后的刷新。
|
|
1144
|
+
*
|
|
1145
|
+
* 不能只调 refresh():① 它带签名去重,读到和上次一样的中间状态会直接返回;
|
|
1146
|
+
* ② git 命令返回时工作区/索引可能还没完全落稳,第一次读到的往往还是旧状态。
|
|
1147
|
+
* 所以先强制刷一次,再补几次重试,同时把历史页和顶部分支条一起拉新。
|
|
1148
|
+
*/
|
|
1149
|
+
function refreshAfterAction() {
|
|
1150
|
+
try { refresh(true) } catch (e) {}
|
|
1151
|
+
;[120, 400, 1000, 2200].forEach(function (ms) {
|
|
1152
|
+
setTimeout(function () { try { refresh(true) } catch (e) {} }, ms)
|
|
1153
|
+
})
|
|
1154
|
+
try { logCache = null } catch (e) {}
|
|
1155
|
+
try { if (activeTab === 'history') renderHistory(true) } catch (e) {}
|
|
1156
|
+
try { if (win.__zbbRefresh) win.__zbbRefresh() } catch (e) {}
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1104
1159
|
// ============ 事件 ============
|
|
1105
1160
|
let activeTab = 'changes'
|
|
1106
1161
|
let panelMode = 'global' // global:全部改动/历史;file:只看当前笔记
|
|
@@ -1212,7 +1267,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
1212
1267
|
} else {
|
|
1213
1268
|
toast('同步失败:' + hintOf(r), 'err')
|
|
1214
1269
|
}
|
|
1215
|
-
|
|
1270
|
+
refreshAfterAction()
|
|
1216
1271
|
}
|
|
1217
1272
|
|
|
1218
1273
|
const pushBtn = $('zgp-push')
|
|
@@ -1221,7 +1276,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
1221
1276
|
const r = await G.push()
|
|
1222
1277
|
busyUI(false)
|
|
1223
1278
|
toast(r.ok ? '已推送' : ('推送失败:' + hintOf(r)), r.ok ? 'ok' : 'err')
|
|
1224
|
-
|
|
1279
|
+
refreshAfterAction()
|
|
1225
1280
|
}
|
|
1226
1281
|
|
|
1227
1282
|
const pullBtn = $('zgp-pull')
|
|
@@ -1231,7 +1286,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
1231
1286
|
busyUI(false)
|
|
1232
1287
|
toast(r.ok ? '已拉取' : ('拉取失败:' + hintOf(r)), r.ok ? 'ok' : 'err')
|
|
1233
1288
|
logCache = null
|
|
1234
|
-
|
|
1289
|
+
refreshAfterAction()
|
|
1235
1290
|
}
|
|
1236
1291
|
}
|
|
1237
1292
|
bindStatusButtons()
|
package/preload/git-service.js
CHANGED
|
@@ -20,6 +20,7 @@ const fs = require('fs')
|
|
|
20
20
|
const path = require('path')
|
|
21
21
|
const os = require('os')
|
|
22
22
|
const crypto = require('crypto')
|
|
23
|
+
const wsPrefs = require('./ws-prefs.js')
|
|
23
24
|
|
|
24
25
|
const DEFAULTS = {
|
|
25
26
|
autoSync: false,
|
|
@@ -30,7 +31,8 @@ const DEFAULTS = {
|
|
|
30
31
|
pullBeforePush: true,
|
|
31
32
|
pushEnabled: true,
|
|
32
33
|
commitScope: 'notes',
|
|
33
|
-
|
|
34
|
+
// 提交信息固定,不开放给用户配置(少一个配置项,也避免有人把模板改坏导致历史不可读)
|
|
35
|
+
commitMessage: 'cmark sync:{{datetime}}',
|
|
34
36
|
lastRunAt: 0,
|
|
35
37
|
failCount: 0
|
|
36
38
|
}
|
|
@@ -241,10 +243,42 @@ module.exports = function installGitService(win, argvPath) {
|
|
|
241
243
|
// git 会弹出「选择凭据助手」的 GUI 窗口,在插件里就是卡死。
|
|
242
244
|
const BASE_ARGS = ['-c', 'core.quotepath=false', '-c', 'credential.helper=', '-c', 'credential.helper=manager']
|
|
243
245
|
|
|
246
|
+
// ---- 远端凭据(一键建仓用)----
|
|
247
|
+
// token 存在 ~/.cmark-config.json 的 remoteToken 里,网络命令用内联 credential helper
|
|
248
|
+
// 从环境变量读取:不写进 .git/config,也不出现在 git 的命令行参数里。
|
|
249
|
+
const NET_CFG_FILE = path.join(os.homedir(), '.cmark-config.json')
|
|
250
|
+
let netCfgCache = { t: -1, data: null }
|
|
251
|
+
function readNetCfg () {
|
|
252
|
+
try {
|
|
253
|
+
const st = fs.statSync(NET_CFG_FILE)
|
|
254
|
+
if (netCfgCache.data && netCfgCache.t === st.mtimeMs) return netCfgCache.data
|
|
255
|
+
const d = JSON.parse(fs.readFileSync(NET_CFG_FILE, 'utf8'))
|
|
256
|
+
netCfgCache = { t: st.mtimeMs, data: d }
|
|
257
|
+
return d
|
|
258
|
+
} catch (e) { return null }
|
|
259
|
+
}
|
|
260
|
+
const NET_CMDS = { push: 1, pull: 1, fetch: 1, 'ls-remote': 1, clone: 1 }
|
|
261
|
+
function netToken (args) {
|
|
262
|
+
if (!args.some(function (a) { return NET_CMDS[a] })) return null
|
|
263
|
+
// 先取本工作区的凭据,再回退到旧版全局配置(迁移兼容)
|
|
264
|
+
try {
|
|
265
|
+
const p = wsPrefs.read(WS)
|
|
266
|
+
if (p.remoteToken && p.remoteToken.token) return p.remoteToken.token
|
|
267
|
+
} catch (e) {}
|
|
268
|
+
const c = readNetCfg()
|
|
269
|
+
const t = c && c.remoteToken
|
|
270
|
+
return t && t.token ? t.token : null
|
|
271
|
+
}
|
|
272
|
+
const TOKEN_HELPER = '!f() { echo username=x-access-token; echo password=$CMARK_GIT_TOKEN; }; f'
|
|
273
|
+
|
|
244
274
|
function exec(args, opt) {
|
|
245
275
|
const o = opt || {}
|
|
276
|
+
const token = o.token || netToken(args)
|
|
277
|
+
const pre = token
|
|
278
|
+
? ['-c', 'core.quotepath=false', '-c', 'credential.helper=', '-c', 'credential.helper=' + TOKEN_HELPER]
|
|
279
|
+
: BASE_ARGS
|
|
246
280
|
return new Promise((resolve) => {
|
|
247
|
-
execFile('git',
|
|
281
|
+
execFile('git', pre.concat(args), {
|
|
248
282
|
cwd: repoRoot || WS,
|
|
249
283
|
encoding: o.buffer ? 'buffer' : 'utf8',
|
|
250
284
|
maxBuffer: 32 * 1024 * 1024,
|
|
@@ -254,7 +288,8 @@ module.exports = function installGitService(win, argvPath) {
|
|
|
254
288
|
GCM_INTERACTIVE: 'never',
|
|
255
289
|
GIT_TERMINAL_PROMPT: '0',
|
|
256
290
|
GIT_LITERAL_PATHSPECS: '1',
|
|
257
|
-
LC_ALL: 'C'
|
|
291
|
+
LC_ALL: 'C',
|
|
292
|
+
CMARK_GIT_TOKEN: token || ''
|
|
258
293
|
})
|
|
259
294
|
}, (err, stdout, stderr) => {
|
|
260
295
|
if (err) {
|
|
@@ -688,7 +723,8 @@ module.exports = function installGitService(win, argvPath) {
|
|
|
688
723
|
const names = staged.data.split('\0').filter(Boolean)
|
|
689
724
|
if (!names.length) return { ok: false, kind: 'nothing', message: ERR_TEXT.nothing }
|
|
690
725
|
|
|
691
|
-
|
|
726
|
+
// 固定使用内置模板:cmark sync:YYYY-MM-DD HH:mm:ss
|
|
727
|
+
const msg = formatMessage(DEFAULTS.commitMessage, { count: names.length })
|
|
692
728
|
const r = await runGit(['commit', '-m', msg], { label: 'commit', timeout: 90000 })
|
|
693
729
|
if (!r.ok) return r
|
|
694
730
|
return { ok: true, data: { message: msg, files: names.length } }
|
|
@@ -1280,6 +1316,29 @@ module.exports = function installGitService(win, argvPath) {
|
|
|
1280
1316
|
}
|
|
1281
1317
|
}
|
|
1282
1318
|
|
|
1319
|
+
// ---- 手填远程地址的支持 ----
|
|
1320
|
+
// 远程地址可能是需要授权的私有仓库(GitHub/Gitee/自建 GitLab 等)。
|
|
1321
|
+
// 这里给两个能力:先探测(看能不能免授权访问),再用显式 token 探测/绑定。
|
|
1322
|
+
async function probeRemote (url, token) {
|
|
1323
|
+
const u = String(url || '').trim()
|
|
1324
|
+
if (!u) return { ok: false, kind: 'badrev', message: '请填写远程仓库地址' }
|
|
1325
|
+
if (!/^(https?:\/\/|git@|ssh:\/\/|file:\/\/|[A-Za-z]:[\\/]|\\\\|\/)/.test(u)) {
|
|
1326
|
+
return { ok: false, kind: 'badrev', message: '远程地址格式不正确:' + u }
|
|
1327
|
+
}
|
|
1328
|
+
const r = await runGit(['ls-remote', '--heads', u], { label: 'probe', timeout: 60000, token: token || undefined })
|
|
1329
|
+
if (r.ok) return { ok: true, data: { hasRefs: !!String(r.data || '').trim() } }
|
|
1330
|
+
return { ok: false, kind: r.kind, message: r.message }
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
/** 保存手填地址用的访问令牌(按工作区存,记录地址用于展示) */
|
|
1334
|
+
function setRemoteToken (token, url) {
|
|
1335
|
+
const t = String(token || '').trim()
|
|
1336
|
+
if (!t) return { ok: false, message: '令牌为空' }
|
|
1337
|
+
const res = wsPrefs.patch(WS, { remoteToken: { provider: 'custom', token: t, url: String(url || '').trim() } })
|
|
1338
|
+
if (res && res.ok === false) return res
|
|
1339
|
+
return { ok: true }
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1283
1342
|
async function setRemote(url) {
|
|
1284
1343
|
const pre = precheck()
|
|
1285
1344
|
if (!pre.ok) return pre
|
|
@@ -1357,6 +1416,7 @@ module.exports = function installGitService(win, argvPath) {
|
|
|
1357
1416
|
stage, unstage, discard, restoreTo,
|
|
1358
1417
|
commitAll, push, pull, sync,
|
|
1359
1418
|
remoteInfo: getRemoteInfo, setRemote,
|
|
1419
|
+
probeRemote, setRemoteToken,
|
|
1360
1420
|
identity: getIdentity, setIdentity,
|
|
1361
1421
|
abortInProgress,
|
|
1362
1422
|
clearStaleLock,
|
package/preload/host.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 宿主适配:uTools / ZTools / 浏览器
|
|
3
|
+
*
|
|
4
|
+
* 两个宿主的 API 名字一致,但注入的全局对象不同:
|
|
5
|
+
* uTools 注入 window.utools
|
|
6
|
+
* ZTools 注入 window.ztools
|
|
7
|
+
*
|
|
8
|
+
* 两个坑必须一起处理:
|
|
9
|
+
* 1) preload 可能在宿主注入全局对象「之前」执行(uTools 尤其如此),
|
|
10
|
+
* 这时候直接读 window.utools 是 undefined,若把 undefined 当结论,
|
|
11
|
+
* 后面所有 window.ztools.xxx 都会炸,preload 直接中断 —— 表现就是
|
|
12
|
+
* 设置面板、Git 面板全都打不开。
|
|
13
|
+
* 2) 两个宿主的 API 并非完全同集,缺某个方法时不能让调用方 TypeError。
|
|
14
|
+
*
|
|
15
|
+
* 所以这里不写死别名,而是装一个惰性代理:读取时才去 window.utools /
|
|
16
|
+
* window.ztools 找真实对象,找不到就返回 no-op 并记一笔,等宿主注入后
|
|
17
|
+
* 同一个代理立刻恢复正常。
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const RAW_U = (typeof window !== 'undefined' && window.utools) || null
|
|
21
|
+
const RAW_Z = (typeof window !== 'undefined' && window.ztools) || null
|
|
22
|
+
const PROXY_TAG = '__cmark_host_proxy__'
|
|
23
|
+
|
|
24
|
+
/** 真实的宿主对象(排除我们自己的代理) */
|
|
25
|
+
function target() {
|
|
26
|
+
try {
|
|
27
|
+
const u = window.utools
|
|
28
|
+
const z = window.ztools
|
|
29
|
+
if (u && !u[PROXY_TAG]) return u
|
|
30
|
+
if (z && !z[PROXY_TAG]) return z
|
|
31
|
+
return null
|
|
32
|
+
} catch (e) {
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function makeProxy() {
|
|
38
|
+
const missing = new Set()
|
|
39
|
+
const p = new Proxy(
|
|
40
|
+
{},
|
|
41
|
+
{
|
|
42
|
+
get(_, prop) {
|
|
43
|
+
if (prop === PROXY_TAG) return true
|
|
44
|
+
const t = target() || RAW_U || RAW_Z
|
|
45
|
+
if (!t) {
|
|
46
|
+
// 宿主还没注入:函数类先给 no-op,避免 preload 在早期就崩掉
|
|
47
|
+
return function () { return undefined }
|
|
48
|
+
}
|
|
49
|
+
const v = t[prop]
|
|
50
|
+
if (typeof v === 'function') return v.bind(t)
|
|
51
|
+
if (v === undefined) {
|
|
52
|
+
missing.add(String(prop))
|
|
53
|
+
return function () { return undefined }
|
|
54
|
+
}
|
|
55
|
+
return v
|
|
56
|
+
},
|
|
57
|
+
has(_, prop) {
|
|
58
|
+
const t = target() || RAW_U || RAW_Z
|
|
59
|
+
return !!t && prop in t
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
try {
|
|
64
|
+
Object.defineProperty(p, '__missing', { value: missing, enumerable: false })
|
|
65
|
+
} catch (e) {}
|
|
66
|
+
return p
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let proxy = null
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 装 shim:缺哪个补哪个(只在缺失时补,不覆盖宿主原对象)。
|
|
73
|
+
* 返回值就是当前识别到的宿主名。
|
|
74
|
+
*/
|
|
75
|
+
function applyHostShim() {
|
|
76
|
+
try {
|
|
77
|
+
if (typeof window === 'undefined') return 'node'
|
|
78
|
+
if (!window.utools && !window.ztools) {
|
|
79
|
+
proxy = makeProxy()
|
|
80
|
+
window.ztools = proxy
|
|
81
|
+
window.utools = proxy
|
|
82
|
+
} else {
|
|
83
|
+
if (!window.utools) window.utools = window.ztools
|
|
84
|
+
if (!window.ztools) window.ztools = window.utools
|
|
85
|
+
}
|
|
86
|
+
// 每次读取时重新判断,宿主晚注入也能正确反映
|
|
87
|
+
try {
|
|
88
|
+
Object.defineProperty(window, '__CMARK_HOST', {
|
|
89
|
+
configurable: true,
|
|
90
|
+
get() { return detect() },
|
|
91
|
+
})
|
|
92
|
+
} catch (e) {
|
|
93
|
+
window.__CMARK_HOST = detect()
|
|
94
|
+
}
|
|
95
|
+
return detect()
|
|
96
|
+
} catch (e) {
|
|
97
|
+
return 'browser'
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function detect() {
|
|
102
|
+
// 关键:shim 之后 window.utools 与 window.ztools 是同一个对象,
|
|
103
|
+
// 不能拿「是谁」判断,必须用 shim 之前的快照 RAW_U / RAW_Z。
|
|
104
|
+
if (RAW_U && !RAW_Z) return 'utools'
|
|
105
|
+
if (RAW_Z && !RAW_U) return 'ztools'
|
|
106
|
+
// 宿主晚注入的情形:看实时注入的对象里哪个是真实的(非我们的代理)
|
|
107
|
+
const u = window.utools
|
|
108
|
+
const z = window.ztools
|
|
109
|
+
const realU = u && !u[PROXY_TAG]
|
|
110
|
+
const realZ = z && !z[PROXY_TAG]
|
|
111
|
+
if (realU && !realZ) return 'utools'
|
|
112
|
+
if (realZ && !realU) return 'ztools'
|
|
113
|
+
return 'browser'
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function hostApi() {
|
|
117
|
+
return target() || RAW_U || RAW_Z || null
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function isInHost() {
|
|
121
|
+
return !!hostApi()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** 哪些方法宿主没提供过(用于排查,不抛错) */
|
|
125
|
+
function missingMethods() {
|
|
126
|
+
if (proxy && proxy.__missing) return Array.from(proxy.__missing)
|
|
127
|
+
return []
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = { detect, applyHostShim, hostApi, isInHost, missingMethods, target }
|