@lancc/cmark 1.1.0 → 1.2.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/CHANGELOG.md +27 -0
- package/LICENSE +21 -21
- package/README.md +54 -82
- package/assets/abnfDiagram-WEYOIA3C-CKV_7Upb.js +1 -0
- package/assets/architectureDiagram-ZHVYKT53-BvLudDvk.js +1 -0
- package/assets/blockDiagram-RJAE7QA3--gzkMGi2.js +1 -0
- package/assets/c4Diagram-XDPPPE54-ByfRiJGR.js +1 -0
- package/assets/chunk-2E7WHVMD-TfbC_dmh.js +1 -0
- package/assets/chunk-2Q5K7J3B-0YJgChVo.js +1 -0
- package/assets/chunk-4EMNKR2T-DHoYW7g5.js +1 -0
- package/assets/chunk-55XCU76K-Cq25Yw_G.js +1 -0
- package/assets/chunk-5VM5RSS4-CJaRXFHK.js +1 -0
- package/assets/chunk-JWPE2WC7-BP2iXBhe.js +1 -0
- package/assets/chunk-VQ5W62QI-B95zur4F.js +1 -0
- package/assets/chunk-XXDRQBXY-Ct0MhvQo.js +1 -0
- package/assets/chunk-ZCHMQSOR-C5VhW-wL.js +2 -0
- package/assets/classDiagram-v2-4MYWR3UA-C1G-Vkj0.js +1 -0
- package/assets/cose-bilkent-JH36ORCC-qIQoIBv0.js +1 -0
- package/assets/cynefin-EF2NZ3EQ-CR1E3YiZ.js +1 -0
- package/assets/cynefinDiagram-WF24UDRP-DaJFvZ3b.js +1 -0
- package/assets/cytoscape.esm-SjxgSg6I.js +9 -0
- package/assets/dagre-NK4GIZVX-BFRQhpii.js +1 -0
- package/assets/diagram-CBK3MWTC-DlueL6XZ.js +1 -0
- package/assets/diagram-I5XIIV2D-bITyafYX.js +1 -0
- package/assets/diagram-ODIL2UCW-Dlb4hg_-.js +1 -0
- package/assets/diagram-PFBEJUXE-CRfen0O7.js +1 -0
- package/assets/diagram-RJA3I4VV-CK-FlhGV.js +1 -0
- package/assets/diagram-TYUNFSNR-DmgVsxqa.js +1 -0
- package/assets/ebnfDiagram-V4P3TTPT-B9KGI9eQ.js +1 -0
- package/assets/echarts-CytsvCdp.js +25 -0
- package/assets/elk-QONTBCTQ-VgDftvsU.js +1 -0
- package/assets/erDiagram-KYQIGQAA-DkpP7sS4.js +1 -0
- package/assets/flowDiagram-IUGLLWBU-CRXa9Kxy.js +1 -0
- package/assets/ganttDiagram-R7GPNTJY-Cu2I55Wb.js +1 -0
- package/assets/gitGraphDiagram-PDYL7XHY-CwSo663I.js +1 -0
- package/assets/graph-Dz7AM1Q5.js +1 -0
- package/assets/index-BJ-NdQc4.js +100 -0
- package/assets/index-eB_q_VA3.css +1 -0
- package/assets/infoDiagram-MVDRKAX5-D3Qknzuo.js +1 -0
- package/assets/ishikawaDiagram-HPEHV2GE-9qYT75tk.js +1 -0
- package/assets/journeyDiagram-BOSHW7O7-DeSrJg9o.js +1 -0
- package/assets/kanban-definition-FNSA4HC5-DINkB6G7.js +1 -0
- package/assets/katex-DN8cTsI3.js +1 -0
- package/assets/markdown-S2mWvoE-.js +5 -0
- package/assets/mermaid-T_6Ze_cT.js +7 -0
- package/assets/mindmap-definition-6OY57UG3-DW5hqiAe.js +1 -0
- package/assets/pegDiagram-ECIUJNBX-BWy_tkzy.js +1 -0
- package/assets/pieDiagram-GNMPY74T-CniJbr6l.js +1 -0
- package/assets/quadrantDiagram-D57K4MC3-BVnDJkCv.js +1 -0
- package/assets/railroadDiagram-Y72NRBL7-BNKZpJJb.js +1 -0
- package/assets/requirementDiagram-EQYSW5RA-BETipHj0.js +1 -0
- package/assets/sankeyDiagram-SY24JHO5-ByEYqT7T.js +1 -0
- package/assets/sequenceDiagram-BLEHNHQU-BqCJ2tKG.js +1 -0
- package/assets/sizeCapture-INFHLROL-DlhrWFj-.js +1 -0
- package/assets/stateDiagram-v2-A6IESKWZ-DJUBWLTP.js +1 -0
- package/assets/swimlanes-B3ENRKFQ-D7k7tF5U.js +1 -0
- package/assets/swimlanesDiagram-AYQRV4AK-DoQBgB9o.js +1 -0
- package/assets/timeline-definition-JXYKGLGB-BCNhwnn6.js +1 -0
- package/assets/usecaseDiagram-WPOLBNNX-DRa_Rgl3.js +1 -0
- package/assets/vennDiagram-Q3Y3IDCW-Ba2eaO68.js +1 -0
- package/assets/wardleyDiagram-IVAIDKWA-W_i9vzY-.js +1 -0
- package/assets/xychartDiagram-4URNHDU4-ByfaGjvc.js +1 -0
- package/export-file/github-markdown.css +549 -549
- package/export-file/index.html +32 -32
- package/index.html +4 -4
- 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 +102 -23
- package/preload/git-service.js +99 -6
- package/preload/host.js +130 -0
- package/preload/remote-setup.js +425 -0
- package/preload/remote-wizard.js +330 -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 +291 -6
- package/assets/abnfDiagram-WEYOIA3C-BgXsTF7a.js +0 -1
- package/assets/architectureDiagram-ZHVYKT53-BsCWa_qC.js +0 -36
- package/assets/blockDiagram-RJAE7QA3-wbIxEiYN.js +0 -140
- package/assets/c4Diagram-XDPPPE54-5zdyxFvA.js +0 -38
- package/assets/chunk-2E7WHVMD-F85Lca-i.js +0 -88
- package/assets/chunk-2Q5K7J3B-CjpAZ0OI.js +0 -1
- package/assets/chunk-4EMNKR2T-C8Kbh83c.js +0 -1
- package/assets/chunk-55XCU76K-BINcFiLN.js +0 -1
- package/assets/chunk-5VM5RSS4-Diy5h2LS.js +0 -15
- package/assets/chunk-JWPE2WC7-BXnjDdh1.js +0 -1
- package/assets/chunk-VQ5W62QI-BM7ZZo1I.js +0 -1
- package/assets/chunk-XXDRQBXY-CW8CjB1w.js +0 -1
- package/assets/chunk-ZCHMQSOR-C7ATz1L1.js +0 -2
- package/assets/classDiagram-v2-4MYWR3UA-CMoqNk-b.js +0 -217
- package/assets/cose-bilkent-JH36ORCC-6rqR5iJk.js +0 -1
- package/assets/cynefin-EF2NZ3EQ-DypLIJxl.js +0 -141
- package/assets/cynefinDiagram-WF24UDRP-8b2CEBlR.js +0 -62
- package/assets/cytoscape.esm-CjX6Hyah.js +0 -331
- package/assets/dagre-NK4GIZVX-Dv6vuUbR.js +0 -4
- package/assets/diagram-CBK3MWTC-DJgMDeYd.js +0 -24
- package/assets/diagram-I5XIIV2D-BB9cMoRW.js +0 -24
- package/assets/diagram-ODIL2UCW-BVcRtsdy.js +0 -30
- package/assets/diagram-PFBEJUXE-BbGSOtJx.js +0 -3
- package/assets/diagram-RJA3I4VV-CzkY_8Fo.js +0 -41
- package/assets/diagram-TYUNFSNR-YTl5QdfL.js +0 -200
- package/assets/ebnfDiagram-V4P3TTPT-COVjPAjE.js +0 -1
- package/assets/echarts-BAPDkhe3.js +0 -61
- package/assets/elk-QONTBCTQ-DHsjY8Z_.js +0 -27
- package/assets/erDiagram-KYQIGQAA-CUAbTUl1.js +0 -99
- package/assets/flowDiagram-IUGLLWBU-CWsiqTQC.js +0 -213
- package/assets/ganttDiagram-R7GPNTJY-DetBW0Wm.js +0 -292
- package/assets/gitGraphDiagram-PDYL7XHY-ByrdgJmw.js +0 -106
- package/assets/graph-D8UmR1vK.js +0 -1
- package/assets/index-BLONSItV.css +0 -1
- package/assets/index-Cz6qaZbV.js +0 -744
- package/assets/infoDiagram-MVDRKAX5-BM48fjhU.js +0 -2
- package/assets/ishikawaDiagram-HPEHV2GE-DtHYywHZ.js +0 -70
- package/assets/journeyDiagram-BOSHW7O7-DJ1LiRh3.js +0 -139
- package/assets/kanban-definition-FNSA4HC5-DPPt8pwJ.js +0 -89
- package/assets/katex-CQL9J12G.js +0 -257
- package/assets/markdown-DVehI1Sg.js +0 -314
- package/assets/mermaid-C_q9pug0.js +0 -312
- package/assets/mindmap-definition-6OY57UG3-zbqYOEY6.js +0 -98
- package/assets/pegDiagram-ECIUJNBX-e7QG7TMq.js +0 -1
- package/assets/pieDiagram-GNMPY74T-Duf6YP0N.js +0 -39
- package/assets/quadrantDiagram-D57K4MC3-B2eYoUUy.js +0 -7
- package/assets/railroadDiagram-Y72NRBL7-BWCzPcI_.js +0 -1
- package/assets/requirementDiagram-EQYSW5RA-HhPMEqB1.js +0 -84
- package/assets/sankeyDiagram-SY24JHO5-CMUTwFBB.js +0 -40
- package/assets/sequenceDiagram-BLEHNHQU-C9wR2ll7.js +0 -169
- package/assets/sizeCapture-INFHLROL-DXfz37S6.js +0 -1
- package/assets/stateDiagram-v2-A6IESKWZ-D_gtonhE.js +0 -291
- package/assets/swimlanes-B3ENRKFQ-DdJOuuMV.js +0 -1
- package/assets/swimlanesDiagram-AYQRV4AK-BWt3HxHV.js +0 -8
- package/assets/timeline-definition-JXYKGLGB-DQsdwJe-.js +0 -120
- package/assets/usecaseDiagram-WPOLBNNX-DTRyFq7d.js +0 -370
- package/assets/vennDiagram-Q3Y3IDCW-dihYszm0.js +0 -34
- package/assets/wardleyDiagram-IVAIDKWA-BoPpgYfK.js +0 -78
- package/assets/xychartDiagram-4URNHDU4-DoLjYxeB.js +0 -7
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
|
// ============ 当前笔记 ============
|
|
@@ -531,7 +567,12 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
531
567
|
ensureStatusContainers()
|
|
532
568
|
const box = $('zgp-status')
|
|
533
569
|
if (!st || !st.isRepo) {
|
|
534
|
-
|
|
570
|
+
// 只换 chips 的内容,绝不整体覆盖 .zgp-status:
|
|
571
|
+
// 整体覆盖会把 chips/acts/fileact 三个容器一起删掉,之后自愈重建时
|
|
572
|
+
// 散落在状态条里的旧节点会留下来,表现为「工作区变更记录」里混着
|
|
573
|
+
// 单个文档的 chip(两套状态条叠在一起)
|
|
574
|
+
const chipsBox0 = box.querySelector('#zgp-chips')
|
|
575
|
+
if (chipsBox0) chipsBox0.innerHTML = '<span class="zgp-chip warn">尚未初始化 git 仓库</span>'
|
|
535
576
|
const foot0 = doc.querySelector('.zgp-acts')
|
|
536
577
|
const oldE = doc.getElementById('zgp-foot-extra')
|
|
537
578
|
if (oldE) oldE.remove()
|
|
@@ -574,7 +615,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
574
615
|
if (!ok) return
|
|
575
616
|
const r = await G.abortInProgress()
|
|
576
617
|
toast(r.ok ? ('已中止 ' + st.inProgress) : ('中止失败:' + r.message), r.ok ? 'ok' : 'err')
|
|
577
|
-
|
|
618
|
+
refreshAfterAction()
|
|
578
619
|
}
|
|
579
620
|
wrap.appendChild(b)
|
|
580
621
|
}
|
|
@@ -585,7 +626,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
585
626
|
b2.onclick = async () => {
|
|
586
627
|
const r = await G.clearStaleLock()
|
|
587
628
|
toast(r.ok ? '已清理陈旧 index.lock' : ('清理失败:' + r.message), r.ok ? 'ok' : 'err')
|
|
588
|
-
|
|
629
|
+
refreshAfterAction()
|
|
589
630
|
}
|
|
590
631
|
wrap.appendChild(b2)
|
|
591
632
|
}
|
|
@@ -664,7 +705,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
664
705
|
if (!rel) {
|
|
665
706
|
const chips0 = box.querySelector('#zgp-chips')
|
|
666
707
|
if (chips0) chips0.innerHTML = '<span class="zgp-chip warn">没有打开的笔记</span>'
|
|
667
|
-
else
|
|
708
|
+
else ensureStatusContainers()
|
|
668
709
|
const pane = $('zgp-pane-changes')
|
|
669
710
|
if (pane) pane.innerHTML = '<div class="zgp-empty"><b>没有打开的笔记</b>打开一篇笔记后,这里显示它的改动与历史</div>'
|
|
670
711
|
const hp = $('zgp-pane-history')
|
|
@@ -677,10 +718,11 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
677
718
|
const chipsHtml = '<span class="zgp-chip acc">' + esc(displayPath(rel)) + '</span>'
|
|
678
719
|
+ (file ? '<span class="zgp-chip warn">有未提交改动</span>' : '<span class="zgp-chip ok">与最近提交一致</span>')
|
|
679
720
|
+ (readErr ? '<span class="zgp-chip err">' + esc(readErr.slice(0, 40)) + '</span>' : '')
|
|
680
|
-
// 只更新 chips
|
|
721
|
+
// 只更新 chips,不要覆盖整个状态条(否则会删掉右侧操作区与容器)。
|
|
722
|
+
// 容器缺失时先自愈重建,而不是退回「直接写 .zgp-status」那种会留残骸的兜底。
|
|
723
|
+
ensureStatusContainers()
|
|
681
724
|
const chipsBoxF = box.querySelector('#zgp-chips')
|
|
682
725
|
if (chipsBoxF) chipsBoxF.innerHTML = chipsHtml
|
|
683
|
-
else box.innerHTML = chipsHtml
|
|
684
726
|
renderFileChanges(rel, file)
|
|
685
727
|
if (activeTab === 'history') renderHistory(true)
|
|
686
728
|
}
|
|
@@ -720,7 +762,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
720
762
|
if (r.ok) {
|
|
721
763
|
toast('已推送:' + (r.data.steps.join(' · ') || '无改动'), 'ok')
|
|
722
764
|
logCache = null
|
|
723
|
-
|
|
765
|
+
refreshAfterAction()
|
|
724
766
|
} else toast('推送失败:' + hintOf(r), 'err')
|
|
725
767
|
}
|
|
726
768
|
}
|
|
@@ -732,7 +774,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
732
774
|
toast('已还原到最近一次提交', 'ok')
|
|
733
775
|
logCache = null
|
|
734
776
|
try { if (win.__znotesReloadNote) win.__znotesReloadNote(rel) } catch (e) {}
|
|
735
|
-
|
|
777
|
+
refreshAfterAction()
|
|
736
778
|
} else if (rr.kind !== 'cancelled') {
|
|
737
779
|
toast('还原失败:' + hintOf(rr), 'err')
|
|
738
780
|
}
|
|
@@ -758,6 +800,8 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
758
800
|
const lines = stripDiffHeader(diff)
|
|
759
801
|
body.innerHTML = renderDiffLines(lines)
|
|
760
802
|
mountJumpBar(body)
|
|
803
|
+
// 首次渲染才跳;后续轮询刷新(内容没变)不打断用户当前阅读位置
|
|
804
|
+
if (!existedBody) focusFirstChange(body)
|
|
761
805
|
}
|
|
762
806
|
|
|
763
807
|
function renderChanges(st) {
|
|
@@ -806,7 +850,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
806
850
|
// 编辑器内部可能残留旧内容并触发自动保存,把还原结果又写回磁盘;
|
|
807
851
|
// 整页刷新是唯一可靠的做法(还原属低频操作)
|
|
808
852
|
try { if (win.__znotesReloadNote) win.__znotesReloadNote(f.path) } catch (err) {}
|
|
809
|
-
|
|
853
|
+
refreshAfterAction()
|
|
810
854
|
} else if (r.kind !== 'cancelled') {
|
|
811
855
|
toast('还原失败:' + hintOf(r), 'err')
|
|
812
856
|
}
|
|
@@ -938,7 +982,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
938
982
|
+ '<button class="zgp-ic" data-cmpprev="' + i + '" title="与提交前比对:这一次提交本身对这篇笔记做了什么改动">' + ICON.cmpPrev + '</button>'
|
|
939
983
|
+ '<button class="zgp-ic" data-view="' + i + '" title="查看该版本:显示这次提交时这篇笔记的完整内容">' + ICON.eye + '</button>'
|
|
940
984
|
+ '<span style="flex:1"></span>'
|
|
941
|
-
+ '<button class="zgp-ic danger" data-back="' + i + '" title="
|
|
985
|
+
+ '<button class="zgp-ic danger" data-back="' + i + '" title="回溯到此版本:把这篇笔记恢复成这个版本的内容">' + ICON.undo + '</button>'
|
|
942
986
|
+ '</div>'
|
|
943
987
|
: ''
|
|
944
988
|
return '<div class="zgp-cm" data-c="' + i + '">'
|
|
@@ -987,7 +1031,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
987
1031
|
toast('已回溯到 ' + c.short, 'ok')
|
|
988
1032
|
logCache = null
|
|
989
1033
|
try { if (win.__znotesReloadNote) win.__znotesReloadNote(onlyFile) } catch (e) {}
|
|
990
|
-
|
|
1034
|
+
refreshAfterAction()
|
|
991
1035
|
} else if (r.kind !== 'cancelled') {
|
|
992
1036
|
toast('回溯失败:' + hintOf(r), 'err')
|
|
993
1037
|
}
|
|
@@ -1019,7 +1063,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
1019
1063
|
+ '<span class="zgp-fs ' + (f.status || '').charAt(0) + '">' + (f.status || '').charAt(0) + '</span>'
|
|
1020
1064
|
+ '<span style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + esc(f.path) + '">' + esc(f.path) + '</span>'
|
|
1021
1065
|
+ '<button class="zgp-ic" title="查看该次改动">' + ICON.diff + '</button>'
|
|
1022
|
-
+ '<button class="zgp-ic danger" data-restore="1" title="
|
|
1066
|
+
+ '<button class="zgp-ic danger" data-restore="1" title="回溯到该版本">' + ICON.undo + '</button>'
|
|
1023
1067
|
+ '</div>'
|
|
1024
1068
|
}).join('')
|
|
1025
1069
|
}
|
|
@@ -1042,7 +1086,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
1042
1086
|
toast('已回溯到 ' + hash.slice(0, 7), 'ok')
|
|
1043
1087
|
logCache = null
|
|
1044
1088
|
try { if (win.__znotesReloadNote) win.__znotesReloadNote(p) } catch (err) {}
|
|
1045
|
-
|
|
1089
|
+
refreshAfterAction()
|
|
1046
1090
|
} else if (r3.kind !== 'cancelled') {
|
|
1047
1091
|
toast('回溯失败:' + hintOf(r3), 'err')
|
|
1048
1092
|
}
|
|
@@ -1101,6 +1145,23 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
1101
1145
|
}
|
|
1102
1146
|
}
|
|
1103
1147
|
|
|
1148
|
+
/**
|
|
1149
|
+
* 操作(提交/推送/拉取/还原/中止)完成后的刷新。
|
|
1150
|
+
*
|
|
1151
|
+
* 不能只调 refresh():① 它带签名去重,读到和上次一样的中间状态会直接返回;
|
|
1152
|
+
* ② git 命令返回时工作区/索引可能还没完全落稳,第一次读到的往往还是旧状态。
|
|
1153
|
+
* 所以先强制刷一次,再补几次重试,同时把历史页和顶部分支条一起拉新。
|
|
1154
|
+
*/
|
|
1155
|
+
function refreshAfterAction() {
|
|
1156
|
+
try { refresh(true) } catch (e) {}
|
|
1157
|
+
;[120, 400, 1000, 2200].forEach(function (ms) {
|
|
1158
|
+
setTimeout(function () { try { refresh(true) } catch (e) {} }, ms)
|
|
1159
|
+
})
|
|
1160
|
+
try { logCache = null } catch (e) {}
|
|
1161
|
+
try { if (activeTab === 'history') renderHistory(true) } catch (e) {}
|
|
1162
|
+
try { if (win.__zbbRefresh) win.__zbbRefresh() } catch (e) {}
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1104
1165
|
// ============ 事件 ============
|
|
1105
1166
|
let activeTab = 'changes'
|
|
1106
1167
|
let panelMode = 'global' // global:全部改动/历史;file:只看当前笔记
|
|
@@ -1131,6 +1192,10 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
1131
1192
|
const fAct = root.querySelector('#zgp-fileact')
|
|
1132
1193
|
if (fAct) fAct.style.display = isFile ? 'flex' : 'none'
|
|
1133
1194
|
if (!isFile && fAct) fAct.innerHTML = ''
|
|
1195
|
+
// 切模式时先清一遍 chips:refresh 有签名去重,命中时会直接 return,
|
|
1196
|
+
// 上一模式的 chip(全局的「分支 main/工作区干净」或单文件的「路径」)就会留在新面板里
|
|
1197
|
+
const chipsEl = doc.getElementById('zgp-chips')
|
|
1198
|
+
if (chipsEl) chipsEl.innerHTML = ''
|
|
1134
1199
|
}
|
|
1135
1200
|
|
|
1136
1201
|
doc.querySelectorAll('.zgp-tab').forEach((t) => {
|
|
@@ -1212,7 +1277,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
1212
1277
|
} else {
|
|
1213
1278
|
toast('同步失败:' + hintOf(r), 'err')
|
|
1214
1279
|
}
|
|
1215
|
-
|
|
1280
|
+
refreshAfterAction()
|
|
1216
1281
|
}
|
|
1217
1282
|
|
|
1218
1283
|
const pushBtn = $('zgp-push')
|
|
@@ -1221,7 +1286,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
1221
1286
|
const r = await G.push()
|
|
1222
1287
|
busyUI(false)
|
|
1223
1288
|
toast(r.ok ? '已推送' : ('推送失败:' + hintOf(r)), r.ok ? 'ok' : 'err')
|
|
1224
|
-
|
|
1289
|
+
refreshAfterAction()
|
|
1225
1290
|
}
|
|
1226
1291
|
|
|
1227
1292
|
const pullBtn = $('zgp-pull')
|
|
@@ -1231,7 +1296,7 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
1231
1296
|
busyUI(false)
|
|
1232
1297
|
toast(r.ok ? '已拉取' : ('拉取失败:' + hintOf(r)), r.ok ? 'ok' : 'err')
|
|
1233
1298
|
logCache = null
|
|
1234
|
-
|
|
1299
|
+
refreshAfterAction()
|
|
1235
1300
|
}
|
|
1236
1301
|
}
|
|
1237
1302
|
bindStatusButtons()
|
|
@@ -1241,6 +1306,15 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
1241
1306
|
const status = $('zgp-status')
|
|
1242
1307
|
if (!status) return
|
|
1243
1308
|
let changed = false
|
|
1309
|
+
// 先清掉不属于三个容器的散落节点:历史上 renderStatus/renderFileMode 都走过
|
|
1310
|
+
// 「直接给 .zgp-status 写 innerHTML」的兜底,留下的裸 chip 会和重建的容器
|
|
1311
|
+
// 并排显示(全局 chip + 单文件 chip 同时出现),必须一次性清干净
|
|
1312
|
+
const kids = Array.prototype.slice.call(status.children)
|
|
1313
|
+
for (const el of kids) {
|
|
1314
|
+
if (el.id === 'zgp-chips' || el.id === 'zgp-fileact') continue
|
|
1315
|
+
if (el.classList && el.classList.contains('zgp-acts')) continue
|
|
1316
|
+
try { status.removeChild(el); changed = true } catch (e) {}
|
|
1317
|
+
}
|
|
1244
1318
|
if (!$('zgp-chips')) {
|
|
1245
1319
|
const d = doc.createElement('div')
|
|
1246
1320
|
d.className = 'zgp-chips'
|
|
@@ -1297,7 +1371,12 @@ module.exports = function installGitPanel(win, WS) {
|
|
|
1297
1371
|
refresh()
|
|
1298
1372
|
}
|
|
1299
1373
|
}
|
|
1300
|
-
win.__zgpOpen = function (mode) {
|
|
1374
|
+
win.__zgpOpen = function (mode) {
|
|
1375
|
+
const m = mode === 'file' ? 'file' : 'global'
|
|
1376
|
+
// 面板已经打开时也要切模式:否则从「文档变更记录」再点侧边栏的
|
|
1377
|
+
// 「工作区变更记录」会被静默忽略,标题与内容停在上一层,看起来就是错乱
|
|
1378
|
+
if (!root.classList.contains('open') || m !== panelMode) win.__zgpToggle(m)
|
|
1379
|
+
}
|
|
1301
1380
|
win.__zgpIsOpen = function () { return root.classList.contains('open') }
|
|
1302
1381
|
|
|
1303
1382
|
// 面板打开时定时刷新(3 秒);历史页不做轮询,避免反复重绘导致闪烁
|
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
|
}
|
|
@@ -201,7 +203,7 @@ module.exports = function installGitService(win, argvPath) {
|
|
|
201
203
|
{ kind: 'detached', re: /detached HEAD|not currently on a branch/i },
|
|
202
204
|
{ kind: 'norepo', re: /not a git repository/i },
|
|
203
205
|
{ kind: 'noUpstream', re: /no upstream configured|has no upstream branch|couldn't find remote ref/i },
|
|
204
|
-
{ kind: 'auth', re: /(Authentication failed|Permission denied \(publickey|invalid username or password|could not read Username|could not read Password|No such identity)/i },
|
|
206
|
+
{ kind: 'auth', re: /(Authentication failed|Permission denied \(publickey|invalid username or password|could not read Username|could not read Password|No such identity|token username invalid|Incorrect username or password|returned error:\s*40[13]|HTTP 40[13])/i },
|
|
205
207
|
{ kind: 'network', re: /could not resolve host|Unable to access|Connection (refused|reset|timed out)|Failed to connect|network is unreachable|Operation timed out/i },
|
|
206
208
|
{ kind: 'perm', re: /Permission denied|EACCES|Read-only file system|No space left/i },
|
|
207
209
|
{ kind: 'dirty', re: /local changes would be lost|Your local changes|uncommitted changes/i }
|
|
@@ -209,7 +211,7 @@ module.exports = function installGitService(win, argvPath) {
|
|
|
209
211
|
|
|
210
212
|
const ERR_TEXT = {
|
|
211
213
|
network: '网络不通或远端不可达',
|
|
212
|
-
auth: '
|
|
214
|
+
auth: '认证失败:令牌无效或用户名不匹配(Gitee/自建服务重新授权一次即可)',
|
|
213
215
|
conflict: '存在冲突,需要先解决',
|
|
214
216
|
lock: 'git 被其他进程占用(index.lock)',
|
|
215
217
|
detached: 'HEAD 处于游离状态,请先切回分支',
|
|
@@ -241,10 +243,73 @@ 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
|
+
|
|
262
|
+
// Gitee 对 HTTPS 的用户名是**强校验**的:用户名必须与令牌所有者一致,否则直接回
|
|
263
|
+
// 403「The token username invalid」(实测:用 x-access-token 当用户名,Gitee 连
|
|
264
|
+
// 假令牌都明确回 username 相关错误)。x-access-token 这种占位用户名只有 GitHub 认。
|
|
265
|
+
// 所以按远端决定用户名:
|
|
266
|
+
// https://gitee.com/<owner>/<repo>.git → owner(个人仓库的 owner 就是令牌所有者)
|
|
267
|
+
// 其它(GitHub 等接受任意用户名) → x-access-token
|
|
268
|
+
const SAFE_USER = /^[A-Za-z0-9._-]{1,64}$/
|
|
269
|
+
function userForUrl (url) {
|
|
270
|
+
const m = /^https?:\/\/(?:[^@/]+@)?([^/:]+)(?::\d+)?\/([^/]+)\//.exec(String(url || ''))
|
|
271
|
+
if (m && /(^|\.)gitee\.com$/i.test(m[1]) && SAFE_USER.test(m[2])) return m[2]
|
|
272
|
+
return 'x-access-token'
|
|
273
|
+
}
|
|
274
|
+
// 从 .git/config 里同步取 origin 的地址(exec 是同步拼参数的场合,不能等 git 命令)
|
|
275
|
+
function originUrl () {
|
|
276
|
+
try {
|
|
277
|
+
const txt = fs.readFileSync(path.join(repoRoot || WS, '.git', 'config'), 'utf8')
|
|
278
|
+
const tail = txt.split(/\[remote "origin"\]/)[1] || ''
|
|
279
|
+
const m = /^[ \t]*url[ \t]*=[ \t]*(.+)$/m.exec(tail)
|
|
280
|
+
return m ? m[1].trim() : ''
|
|
281
|
+
} catch (e) { return '' }
|
|
282
|
+
}
|
|
283
|
+
function tokenHelper (username) {
|
|
284
|
+
const u = SAFE_USER.test(String(username || '')) ? String(username) : 'x-access-token'
|
|
285
|
+
return '!f() { echo username=' + u + '; echo password=$CMARK_GIT_TOKEN; }; f'
|
|
286
|
+
}
|
|
287
|
+
function netCred (args) {
|
|
288
|
+
if (!args.some(function (a) { return NET_CMDS[a] })) return null
|
|
289
|
+
// 先取本工作区的凭据,再回退到旧版全局配置(迁移兼容)
|
|
290
|
+
let rec = null
|
|
291
|
+
try { rec = wsPrefs.read(WS).remoteToken || null } catch (e) {}
|
|
292
|
+
if (!(rec && rec.token)) {
|
|
293
|
+
const c = readNetCfg()
|
|
294
|
+
rec = (c && c.remoteToken) || null
|
|
295
|
+
}
|
|
296
|
+
if (!rec || !rec.token) return null
|
|
297
|
+
// 用户名以配置为准;老配置没有 username 字段就用远端地址推断,
|
|
298
|
+
// 这样已经用旧版本绑过 Gitee 的用户升级后不用重新授权
|
|
299
|
+
return { token: rec.token, username: rec.username || userForUrl(rec.url || originUrl()) }
|
|
300
|
+
}
|
|
301
|
+
|
|
244
302
|
function exec(args, opt) {
|
|
245
303
|
const o = opt || {}
|
|
304
|
+
const cred = o.token
|
|
305
|
+
? { token: o.token, username: o.username || userForUrl(o.url || originUrl()) }
|
|
306
|
+
: netCred(args)
|
|
307
|
+
const token = cred && cred.token
|
|
308
|
+
const pre = token
|
|
309
|
+
? ['-c', 'core.quotepath=false', '-c', 'credential.helper=', '-c', 'credential.helper=' + tokenHelper(cred.username)]
|
|
310
|
+
: BASE_ARGS
|
|
246
311
|
return new Promise((resolve) => {
|
|
247
|
-
execFile('git',
|
|
312
|
+
execFile('git', pre.concat(args), {
|
|
248
313
|
cwd: repoRoot || WS,
|
|
249
314
|
encoding: o.buffer ? 'buffer' : 'utf8',
|
|
250
315
|
maxBuffer: 32 * 1024 * 1024,
|
|
@@ -254,7 +319,8 @@ module.exports = function installGitService(win, argvPath) {
|
|
|
254
319
|
GCM_INTERACTIVE: 'never',
|
|
255
320
|
GIT_TERMINAL_PROMPT: '0',
|
|
256
321
|
GIT_LITERAL_PATHSPECS: '1',
|
|
257
|
-
LC_ALL: 'C'
|
|
322
|
+
LC_ALL: 'C',
|
|
323
|
+
CMARK_GIT_TOKEN: token || ''
|
|
258
324
|
})
|
|
259
325
|
}, (err, stdout, stderr) => {
|
|
260
326
|
if (err) {
|
|
@@ -688,7 +754,8 @@ module.exports = function installGitService(win, argvPath) {
|
|
|
688
754
|
const names = staged.data.split('\0').filter(Boolean)
|
|
689
755
|
if (!names.length) return { ok: false, kind: 'nothing', message: ERR_TEXT.nothing }
|
|
690
756
|
|
|
691
|
-
|
|
757
|
+
// 固定使用内置模板:cmark sync:YYYY-MM-DD HH:mm:ss
|
|
758
|
+
const msg = formatMessage(DEFAULTS.commitMessage, { count: names.length })
|
|
692
759
|
const r = await runGit(['commit', '-m', msg], { label: 'commit', timeout: 90000 })
|
|
693
760
|
if (!r.ok) return r
|
|
694
761
|
return { ok: true, data: { message: msg, files: names.length } }
|
|
@@ -1280,6 +1347,31 @@ module.exports = function installGitService(win, argvPath) {
|
|
|
1280
1347
|
}
|
|
1281
1348
|
}
|
|
1282
1349
|
|
|
1350
|
+
// ---- 手填远程地址的支持 ----
|
|
1351
|
+
// 远程地址可能是需要授权的私有仓库(GitHub/Gitee/自建 GitLab 等)。
|
|
1352
|
+
// 这里给两个能力:先探测(看能不能免授权访问),再用显式 token 探测/绑定。
|
|
1353
|
+
async function probeRemote (url, token) {
|
|
1354
|
+
const u = String(url || '').trim()
|
|
1355
|
+
if (!u) return { ok: false, kind: 'badrev', message: '请填写远程仓库地址' }
|
|
1356
|
+
if (!/^(https?:\/\/|git@|ssh:\/\/|file:\/\/|[A-Za-z]:[\\/]|\\\\|\/)/.test(u)) {
|
|
1357
|
+
return { ok: false, kind: 'badrev', message: '远程地址格式不正确:' + u }
|
|
1358
|
+
}
|
|
1359
|
+
const r = await runGit(['ls-remote', '--heads', u], { label: 'probe', timeout: 60000, token: token || undefined, url: u })
|
|
1360
|
+
if (r.ok) return { ok: true, data: { hasRefs: !!String(r.data || '').trim() } }
|
|
1361
|
+
return { ok: false, kind: r.kind, message: r.message }
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
/** 保存手填地址用的访问令牌(按工作区存,记录地址用于展示) */
|
|
1365
|
+
function setRemoteToken (token, url) {
|
|
1366
|
+
const t = String(token || '').trim()
|
|
1367
|
+
if (!t) return { ok: false, message: '令牌为空' }
|
|
1368
|
+
const u = String(url || '').trim()
|
|
1369
|
+
// 记下按远端推断出的用户名:Gitee 要求它与令牌所有者一致,见 userForUrl
|
|
1370
|
+
const res = wsPrefs.patch(WS, { remoteToken: { provider: 'custom', token: t, url: u, username: userForUrl(u) } })
|
|
1371
|
+
if (res && res.ok === false) return res
|
|
1372
|
+
return { ok: true }
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1283
1375
|
async function setRemote(url) {
|
|
1284
1376
|
const pre = precheck()
|
|
1285
1377
|
if (!pre.ok) return pre
|
|
@@ -1357,6 +1449,7 @@ module.exports = function installGitService(win, argvPath) {
|
|
|
1357
1449
|
stage, unstage, discard, restoreTo,
|
|
1358
1450
|
commitAll, push, pull, sync,
|
|
1359
1451
|
remoteInfo: getRemoteInfo, setRemote,
|
|
1452
|
+
probeRemote, setRemoteToken,
|
|
1360
1453
|
identity: getIdentity, setIdentity,
|
|
1361
1454
|
abortInProgress,
|
|
1362
1455
|
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 }
|