@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
|
@@ -117,6 +117,14 @@ module.exports = function attachSettingsPanel (win) {
|
|
|
117
117
|
/* 状态卡里的远端地址可能很长,允许在任意位置折行 */
|
|
118
118
|
.zgs-kv-url{word-break:break-all;}
|
|
119
119
|
/* 只读地址框:看起来是"展示",不是"待填" */
|
|
120
|
+
#zgs-body .zgs-auth-err{margin:10px 0 0;padding:10px 12px;border-radius:8px;font-size:12px;line-height:1.7;
|
|
121
|
+
color:#cf222e;background:rgba(207,34,46,.10);border:1px solid rgba(207,34,46,.35);word-break:break-word;}
|
|
122
|
+
:root[data-theme='dark'] #zgs-body .zgs-auth-err{color:#ff8f85;background:rgba(255,120,110,.12);border-color:rgba(255,120,110,.4);}
|
|
123
|
+
/* 抽屉内的小节(提交 / 自动同步):用细分隔线与上方仓库状态隔开 */
|
|
124
|
+
#zgs-body .zgs-sub{margin-top:16px;padding-top:14px;border-top:1px solid rgba(128,128,128,.22);}
|
|
125
|
+
#zgs-body .zgs-sub h5{font-size:12px;font-weight:600;margin:0 0 8px;letter-spacing:.02em;}
|
|
126
|
+
/* Git 未启用时,提交/自动同步整体置灰且不可交互 */
|
|
127
|
+
#zgs-body .zgp-sec.is-disabled{opacity:.45;pointer-events:none;filter:grayscale(.4);}
|
|
120
128
|
#zgs-body .zgp-in[readonly]{opacity:.8;cursor:default;background:rgba(127,127,127,.08);}
|
|
121
129
|
#zgs-body .zgp-in{padding-left:12px;padding-right:12px;height:34px;box-sizing:border-box;}
|
|
122
130
|
#zgs-body .zgp-in:focus{outline:none;border-color:rgba(120,160,255,.55);
|
|
@@ -251,6 +259,8 @@ module.exports = function attachSettingsPanel (win) {
|
|
|
251
259
|
|
|
252
260
|
// ---------- 渲染 ----------
|
|
253
261
|
async function render () {
|
|
262
|
+
// 页面重载后 <html> 上的 data-theme 有可能还没写上,这里兜一次
|
|
263
|
+
try { if (win.__CMARK_THEME && win.__CMARK_THEME.apply) win.__CMARK_THEME.apply() } catch (e) {}
|
|
254
264
|
if (!G) {
|
|
255
265
|
$('zgs-body').innerHTML = '<div class="zgp-sec"><h5>不可用</h5><div class="zgp-desc">Git 服务未加载</div></div>'
|
|
256
266
|
return
|
|
@@ -268,109 +278,68 @@ module.exports = function attachSettingsPanel (win) {
|
|
|
268
278
|
|
|
269
279
|
$('zgs-body').innerHTML = `
|
|
270
280
|
<div class="zgp-sec">
|
|
271
|
-
<h5
|
|
272
|
-
<
|
|
273
|
-
|
|
274
|
-
<button class="zgp-b" id="s-
|
|
275
|
-
<button class="zgp-b" id="s-
|
|
281
|
+
<h5>外观</h5>
|
|
282
|
+
<div class="zgp-row" id="s-theme-row">
|
|
283
|
+
<button class="zgp-b" id="s-theme-light">浅色</button>
|
|
284
|
+
<button class="zgp-b" id="s-theme-dark">深色</button>
|
|
285
|
+
<button class="zgp-b" id="s-theme-system">跟随系统</button>
|
|
276
286
|
</div>
|
|
277
|
-
<div class="zgp-desc"
|
|
287
|
+
<div class="zgp-desc">主题对所有界面生效,选择后立即应用并记住。</div>
|
|
278
288
|
</div>
|
|
279
289
|
<div class="zgp-sec">
|
|
280
|
-
<h5
|
|
281
|
-
<dl class="zgs-kv"
|
|
282
|
-
<dt
|
|
290
|
+
<h5>工作区信息</h5>
|
|
291
|
+
<dl class="zgs-kv">
|
|
292
|
+
<dt>工作区目录</dt><dd class="mono" id="s-repo-dir">—</dd>
|
|
293
|
+
<dt>笔记目录</dt><dd class="mono" id="s-ws">—</dd>
|
|
294
|
+
<dt>附件目录</dt><dd class="mono" id="s-attach-dir">—</dd>
|
|
295
|
+
<dt>附件</dt><dd id="s-attach">统计中…</dd>
|
|
283
296
|
</dl>
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
<
|
|
287
|
-
|
|
288
|
-
<button class="zgp-b" id="s-vc-adopt-btn">从远程仓库加载</button>
|
|
289
|
-
</div>
|
|
290
|
-
<div class="zgp-desc">
|
|
291
|
-
初始化:在本地建立版本历史,之后可再关联远端。<br>
|
|
292
|
-
从远程加载:把远端已有的笔记拉到本地,要求工作区为空。
|
|
293
|
-
</div>
|
|
294
|
-
</div>
|
|
295
|
-
|
|
296
|
-
<div id="s-vc-ready" style="display:none">
|
|
297
|
-
<!-- 只有本机仓库、还没绑定远端时,只露一个入口,不先摆一个空地址框 -->
|
|
298
|
-
<div id="s-vc-link-row" style="display:none">
|
|
299
|
-
<div class="zgp-row" style="margin-top:14px">
|
|
300
|
-
<button class="zgp-b primary" id="s-vc-link-btn">将本地仓库关联到远程</button>
|
|
301
|
-
</div>
|
|
302
|
-
</div>
|
|
303
|
-
<!-- 已关联远端(或点了上面的按钮)才展开地址框 -->
|
|
304
|
-
<div id="s-vc-remote-box" style="display:none">
|
|
305
|
-
<div class="zgp-row" style="margin-top:12px">
|
|
306
|
-
<button class="zgp-b danger" id="s-vc-remote-clear">移除远端</button>
|
|
307
|
-
</div>
|
|
308
|
-
</div>
|
|
297
|
+
<div class="zgp-row" style="margin-top:8px">
|
|
298
|
+
<button class="zgp-b" id="s-repo-pick">修改工作区</button>
|
|
299
|
+
<button class="zgp-b" id="s-notes-pick">选择笔记目录</button>
|
|
300
|
+
<button class="zgp-b" id="s-attach-clean" title="直接删除没有任何笔记引用的附件(不做回收站,误删可从 git 历史取回)">清理未引用附件</button>
|
|
309
301
|
</div>
|
|
302
|
+
<div class="zgp-desc">默认在 <span class="mono">文档\CMark</span>;修改后重新扫描该目录下的 Markdown 文件,文件本身不会被移动或修改。</div>
|
|
310
303
|
</div>
|
|
311
304
|
<div class="zgp-sec">
|
|
312
|
-
<
|
|
313
|
-
|
|
314
|
-
<
|
|
315
|
-
<label class="zgp-sw"><input type="checkbox" id="s-msg-custom"><i></i></label>
|
|
316
|
-
</div>
|
|
317
|
-
<div class="zgp-desc">关闭时使用默认模板与全局 git 身份;打开后可以分别自定义。</div>
|
|
305
|
+
<div style="display:flex;align-items:center;justify-content:space-between;gap:10px">
|
|
306
|
+
<h5 style="margin:0">Git 集成</h5>
|
|
307
|
+
<label class="zgp-sw"><input type="checkbox" id="s-git-enabled"><i></i></label>
|
|
318
308
|
</div>
|
|
319
|
-
<div class="zgp-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
<
|
|
323
|
-
|
|
309
|
+
<div class="zgp-desc" style="margin-top:6px">打开后可以提交历史、回溯版本,并同步到你自己的远程仓库。</div>
|
|
310
|
+
<div class="zgp-drawer" id="s-git-drawer">
|
|
311
|
+
<div id="s-git-body"></div>
|
|
312
|
+
<div class="zgs-sub" id="s-sec-auto">
|
|
313
|
+
<div style="display:flex;align-items:center;justify-content:space-between;gap:10px">
|
|
314
|
+
<h5 style="margin:0">自动同步</h5>
|
|
315
|
+
<label class="zgp-sw"><input type="checkbox" id="s-auto" ${cfg.autoSync ? 'checked' : ''}><i></i></label>
|
|
324
316
|
</div>
|
|
325
|
-
<div class="zgp-
|
|
326
|
-
|
|
327
|
-
<
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
317
|
+
<div class="zgp-desc" style="margin:6px 0 10px">按下方策略执行;只在有改动时才产生提交。</div>
|
|
318
|
+
<div class="zgp-drawer${cfg.autoSync ? ' open' : ''}" id="s-auto-drawer">
|
|
319
|
+
<div class="zgp-field">
|
|
320
|
+
<div class="zgp-lab"><span>触发方式</span></div>
|
|
321
|
+
<select class="zgp-sel" id="s-mode">
|
|
322
|
+
<option value="interval"${mode === 'interval' ? ' selected' : ''}>固定间隔</option>
|
|
323
|
+
<option value="stop-edit"${mode === 'stop-edit' ? ' selected' : ''}>停止编辑后</option>
|
|
324
|
+
</select>
|
|
325
|
+
</div>
|
|
326
|
+
<div class="zgp-field" id="s-interval-row"${mode === 'interval' ? '' : ' style="display:none"'}>
|
|
327
|
+
<div class="zgp-lab"><span>间隔时长</span></div>
|
|
328
|
+
<select class="zgp-sel" id="s-interval">${ivHtml}</select>
|
|
329
|
+
<div class="zgp-desc">跨会话生效:即使插件没开够一个间隔,下次启动会补一次。</div>
|
|
330
|
+
</div>
|
|
331
|
+
<div class="zgp-field" id="s-stop-row"${mode === 'stop-edit' ? '' : ' style="display:none"'}>
|
|
332
|
+
<div class="zgp-lab"><span>静默时长</span></div>
|
|
333
|
+
<select class="zgp-sel" id="s-stop">${stopHtml}</select>
|
|
334
|
+
<div class="zgp-desc">停止输入 N 分钟后触发一次,适合边写边自动保存。</div>
|
|
332
335
|
</div>
|
|
333
|
-
<div class="zgp-desc" id="s-vc-id-hint2"></div>
|
|
334
|
-
</div>
|
|
335
|
-
</div>
|
|
336
|
-
</div>
|
|
337
|
-
<div class="zgp-sec">
|
|
338
|
-
<h5>自动同步</h5>
|
|
339
|
-
<div class="zgp-field">
|
|
340
|
-
<div class="zgp-lab"><span>定时提交并推送</span>
|
|
341
|
-
<label class="zgp-sw"><input type="checkbox" id="s-auto" ${cfg.autoSync ? 'checked' : ''}><i></i></label>
|
|
342
336
|
</div>
|
|
343
|
-
<div class="zgp-desc">按下方策略执行;只在有改动时才产生提交。</div>
|
|
344
337
|
</div>
|
|
345
|
-
<div class="zgp-drawer${cfg.autoSync ? ' open' : ''}" id="s-auto-drawer">
|
|
346
|
-
<div class="zgp-field">
|
|
347
|
-
<div class="zgp-lab"><span>触发方式</span></div>
|
|
348
|
-
<select class="zgp-sel" id="s-mode">
|
|
349
|
-
<option value="interval"${mode === 'interval' ? ' selected' : ''}>固定间隔</option>
|
|
350
|
-
<option value="stop-edit"${mode === 'stop-edit' ? ' selected' : ''}>停止编辑后</option>
|
|
351
|
-
</select>
|
|
352
|
-
<div class="zgp-desc">两种方式互斥,只能选一种。</div>
|
|
353
|
-
</div>
|
|
354
|
-
<div class="zgp-field" id="s-interval-row"${mode === 'interval' ? '' : ' style="display:none"'}>
|
|
355
|
-
<div class="zgp-lab"><span>间隔时长</span></div>
|
|
356
|
-
<select class="zgp-sel" id="s-interval">${ivHtml}</select>
|
|
357
|
-
<div class="zgp-desc">跨会话生效:即使插件没开够一个间隔,下次启动会补一次。</div>
|
|
358
|
-
</div>
|
|
359
|
-
<div class="zgp-field" id="s-stop-row"${mode === 'stop-edit' ? '' : ' style="display:none"'}>
|
|
360
|
-
<div class="zgp-lab"><span>静默时长</span></div>
|
|
361
|
-
<select class="zgp-sel" id="s-stop">${stopHtml}</select>
|
|
362
|
-
<div class="zgp-desc">停止输入 N 分钟后触发一次,适合边写边自动保存。</div>
|
|
363
|
-
</div>
|
|
364
338
|
</div>
|
|
365
339
|
</div>
|
|
366
340
|
<div class="zgp-sec">
|
|
367
341
|
<h5>运行状态</h5>
|
|
368
342
|
<div class="zgp-desc" id="s-repo">读取中…</div>
|
|
369
|
-
<div class="zgp-desc" id="s-info"></div>
|
|
370
|
-
<div class="zgp-desc" id="s-attach" style="margin-top:8px">附件:统计中…</div>
|
|
371
|
-
<div style="margin-top:8px">
|
|
372
|
-
<button class="zgp-b" id="s-attach-clean" title="直接删除没有任何笔记引用的附件(不做回收站,误删可从 git 历史取回)">清理未引用附件</button>
|
|
373
|
-
</div>
|
|
374
343
|
</div>
|
|
375
344
|
`
|
|
376
345
|
|
|
@@ -384,19 +353,23 @@ module.exports = function attachSettingsPanel (win) {
|
|
|
384
353
|
if (v >= 1024) return (v / 1024).toFixed(0) + ' KB'
|
|
385
354
|
return v + ' B'
|
|
386
355
|
}
|
|
356
|
+
const attachDirEl = $('s-attach-dir')
|
|
387
357
|
const refreshAttachStat = function () {
|
|
358
|
+
if (attachDirEl) {
|
|
359
|
+
const ws = win.__ZNOTES_WS || ''
|
|
360
|
+
attachDirEl.textContent = ws ? (ws.replace(/[\\/]+$/, '') + '\\.attachments') : '未设置笔记目录'
|
|
361
|
+
}
|
|
388
362
|
if (!attachBox) return
|
|
389
363
|
try {
|
|
390
364
|
const api = win.__CMARK
|
|
391
365
|
const s = api && api.scanAttachments ? api.scanAttachments() : null
|
|
392
366
|
if (!s || !s.ok) {
|
|
393
|
-
attachBox.textContent = '
|
|
367
|
+
attachBox.textContent = '读取失败' + (s && s.message ? '(' + s.message + ')' : '')
|
|
394
368
|
return
|
|
395
369
|
}
|
|
396
370
|
const total = Number(s.total) || 0
|
|
397
371
|
const unused = (s.unused || []).length
|
|
398
|
-
attachBox.textContent =
|
|
399
|
-
'附件:' + total + ' 个,共 ' + fmtSize(s.allBytes) +
|
|
372
|
+
attachBox.textContent = total + ' 个,共 ' + fmtSize(s.allBytes) +
|
|
400
373
|
';未引用 ' + unused + ' 个(' + fmtSize(s.unusedBytes) + ')'
|
|
401
374
|
} catch (e) {}
|
|
402
375
|
}
|
|
@@ -425,267 +398,374 @@ module.exports = function attachSettingsPanel (win) {
|
|
|
425
398
|
}
|
|
426
399
|
}
|
|
427
400
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
401
|
+
// ---------- 外观:主题三态 ----------
|
|
402
|
+
const themeSvc = win.__CMARK_THEME
|
|
403
|
+
const curMode = (themeSvc && themeSvc.get && themeSvc.get().mode) || 'system'
|
|
404
|
+
const modeLabel = { light: '浅色', dark: '深色', system: '跟随系统' }
|
|
405
|
+
;['light', 'dark', 'system'].forEach(function (m) {
|
|
406
|
+
const btn = $('s-theme-' + m)
|
|
407
|
+
if (!btn) return
|
|
408
|
+
if (m === curMode) btn.classList.add('primary')
|
|
409
|
+
btn.onclick = function () {
|
|
410
|
+
if (!themeSvc || !themeSvc.set) return toast('主题服务未就绪', 'err')
|
|
411
|
+
themeSvc.set(m)
|
|
412
|
+
;['light', 'dark', 'system'].forEach(function (mm) {
|
|
413
|
+
const bb = $('s-theme-' + mm)
|
|
414
|
+
if (bb) bb.classList.toggle('primary', mm === m)
|
|
415
|
+
})
|
|
416
|
+
toast('已切换为' + modeLabel[m])
|
|
437
417
|
}
|
|
438
|
-
}
|
|
418
|
+
})
|
|
439
419
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
else if (win.ztools && typeof win.ztools.shellOpenExternal === 'function') win.ztools.shellOpenExternal('file://' + p)
|
|
448
|
-
} catch (e) { toast('打开失败:' + (e && e.message), 'err') }
|
|
449
|
-
}
|
|
450
|
-
}
|
|
420
|
+
// ---------- Git 集成:开关 + 按目录状态渲染 ----------
|
|
421
|
+
const fs = require('fs')
|
|
422
|
+
const GIT_KEY = 'cmark-git-enabled'
|
|
423
|
+
const gitSwitch = $('s-git-enabled')
|
|
424
|
+
const gitDrawer = $('s-git-drawer')
|
|
425
|
+
const gitBody = $('s-git-body')
|
|
426
|
+
const secAuto = $('s-sec-auto')
|
|
451
427
|
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
let st = {}
|
|
428
|
+
// 状态源:优先 preload 暴露的配置文件接口(file:// 下 localStorage 可能不可用)
|
|
429
|
+
const readGitFlag = function (fallback) {
|
|
455
430
|
try {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
st = (r && r.ok && r.data) ? r.data : ((G && typeof G.inspect === 'function') ? G.inspect() : {})
|
|
431
|
+
const v = win.__CMARK_GIT && win.__CMARK_GIT.get ? win.__CMARK_GIT.get() : undefined
|
|
432
|
+
if (v === true || v === false) return v
|
|
459
433
|
} catch (e) {}
|
|
460
|
-
const isRepo = !!(st && st.isRepo)
|
|
461
|
-
const initBox = $('s-vc-init')
|
|
462
|
-
const readyBox = $('s-vc-ready')
|
|
463
|
-
const kvEl = $('s-vc-kv')
|
|
464
|
-
const autoSw = $('s-auto')
|
|
465
|
-
if (autoSw) {
|
|
466
|
-
autoSw.disabled = !isRepo
|
|
467
|
-
if (!isRepo) autoSw.checked = false
|
|
468
|
-
}
|
|
469
|
-
// 这一段只回答"配置成什么样":启用没有、绑没绑远端。
|
|
470
|
-
// 仓库根 / 分支 / 改动数属于运行诊断,统一放在面板底部的「运行状态」里,避免两处重复。
|
|
471
|
-
let rdKv = {}
|
|
472
434
|
try {
|
|
473
|
-
const
|
|
474
|
-
|
|
435
|
+
const v = win.localStorage.getItem(GIT_KEY)
|
|
436
|
+
if (v === '1') return true
|
|
437
|
+
if (v === '0') return false
|
|
475
438
|
} catch (e) {}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
// 提交者身份沿用全局 git 配置,未启用时不再单独给输入框(要改去「提交」分区)
|
|
496
|
-
return
|
|
439
|
+
return fallback
|
|
440
|
+
}
|
|
441
|
+
const writeGitFlag = function (on) {
|
|
442
|
+
try {
|
|
443
|
+
if (win.__CMARK_GIT && win.__CMARK_GIT.set) { win.__CMARK_GIT.set(on); return } } catch (e) {}
|
|
444
|
+
try { win.localStorage.setItem(GIT_KEY, on ? '1' : '0') } catch (e) {}
|
|
445
|
+
try { win.dispatchEvent(new CustomEvent('cmark-git-changed', { detail: { enabled: !!on } })) } catch (e) {}
|
|
446
|
+
}
|
|
447
|
+
const setSectionsDisabled = function (disabled, reason) {
|
|
448
|
+
const secs = [secAuto]
|
|
449
|
+
for (let i = 0; i < secs.length; i++) {
|
|
450
|
+
const sec = secs[i]
|
|
451
|
+
if (!sec) continue
|
|
452
|
+
sec.classList.toggle('is-disabled', !!disabled)
|
|
453
|
+
// 置灰时把原因挂在容器上:disabled 的控件本身不弹 title,但祖先的 title 会生效
|
|
454
|
+
if (disabled) sec.setAttribute('title', reason || '请先配置 Git 仓库')
|
|
455
|
+
else sec.removeAttribute('title')
|
|
456
|
+
const nodes = sec.querySelectorAll('input,select,button')
|
|
457
|
+
for (let j = 0; j < nodes.length; j++) nodes[j].disabled = !!disabled
|
|
497
458
|
}
|
|
459
|
+
}
|
|
460
|
+
const openWizard = function (p) {
|
|
461
|
+
if (win.__CMARK_OPEN_WIZARD) win.__CMARK_OPEN_WIZARD(p)
|
|
462
|
+
else toast('建仓向导未加载', 'err')
|
|
463
|
+
}
|
|
464
|
+
const afterGitChange = function () {
|
|
465
|
+
try { win.dispatchEvent(new CustomEvent('cmark-git-changed')) } catch (e) {}
|
|
466
|
+
refreshGitSection().catch(function () {})
|
|
467
|
+
}
|
|
498
468
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
const
|
|
503
|
-
if (
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
const hint = $('s-vc-id-hint2')
|
|
518
|
-
if (hint) {
|
|
519
|
-
hint.textContent = (d.localName || d.localEmail)
|
|
520
|
-
? '当前使用仓库内的身份,覆盖全局配置。'
|
|
521
|
-
: '当前沿用全局 git 配置:' + ([d.globalName, d.globalEmail].filter(Boolean).join(' / ') || '未配置')
|
|
522
|
-
}
|
|
523
|
-
}
|
|
469
|
+
// 认证失败时在抽屉顶部插入提示条,并给一个直接重新授权的入口
|
|
470
|
+
const showAuthError = function (msg) {
|
|
471
|
+
if (!gitBody) return
|
|
472
|
+
const old = gitBody.querySelector('.zgs-auth-err')
|
|
473
|
+
if (old) old.remove()
|
|
474
|
+
let prov = 'github'
|
|
475
|
+
try {
|
|
476
|
+
const cur = (win.__CMARK_REMOTE && win.__CMARK_REMOTE.current) ? win.__CMARK_REMOTE.current() : null
|
|
477
|
+
if (cur && cur.provider) prov = cur.provider
|
|
478
|
+
} catch (e) {}
|
|
479
|
+
const box = doc.createElement('div')
|
|
480
|
+
box.className = 'zgs-auth-err'
|
|
481
|
+
box.innerHTML = '授权可能已失效,推送/拉取被拒绝:' + esc(String(msg || '').slice(0, 180)) +
|
|
482
|
+
'<div class="zgp-row" style="margin-top:8px"><button class="zgp-b primary" id="s-git-reauth">重新授权</button></div>'
|
|
483
|
+
gitBody.insertBefore(box, gitBody.firstChild)
|
|
484
|
+
const rb = box.querySelector('#s-git-reauth')
|
|
485
|
+
if (rb) rb.onclick = function () { openWizard(prov) }
|
|
486
|
+
toast('授权可能已失效,可点「重新授权」', 'err')
|
|
524
487
|
}
|
|
525
488
|
|
|
526
|
-
//
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
'之后可以在下面把本地仓库关联到远端。', '初始化')
|
|
538
|
-
if (!ok) return
|
|
539
|
-
initBtn.disabled = true
|
|
540
|
-
try { if (win.__znotesFlushSave) win.__znotesFlushSave() } catch (e) {}
|
|
541
|
-
let r = null
|
|
489
|
+
// 本机 git 可用性:首次检测后缓存,装完 git 可点「重新检测」刷新
|
|
490
|
+
let gitProbe = null
|
|
491
|
+
// 每次打开面板只做一次「未初始化则关开关」的校正;否则用户点开关会被立刻打回
|
|
492
|
+
let gitGateDone = false
|
|
493
|
+
const AUTH_RE = /Authentication failed|could not read Username|could not read Password|Permission denied|Authentication required|Invalid username or password|returned error: 403|403 Forbidden|support for password authentication/i
|
|
494
|
+
const isAuthError = function (msg) { return AUTH_RE.test(String(msg || '')) }
|
|
495
|
+
|
|
496
|
+
async function refreshGitSection () {
|
|
497
|
+
if (!gitBody) return
|
|
498
|
+
|
|
499
|
+
if (gitProbe === null) {
|
|
542
500
|
try {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
if (
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
501
|
+
const cp = require('child_process')
|
|
502
|
+
const out = cp.execFileSync('git', ['--version'], { encoding: 'utf8', timeout: 6000, windowsHide: true })
|
|
503
|
+
gitProbe = { ok: true, ver: String(out || '').trim() }
|
|
504
|
+
} catch (e) { gitProbe = { ok: false, ver: '' } }
|
|
505
|
+
}
|
|
506
|
+
if (!gitProbe.ok) {
|
|
507
|
+
if (gitSwitch) { gitSwitch.checked = false; gitSwitch.disabled = true }
|
|
508
|
+
if (gitDrawer) gitDrawer.classList.add('open')
|
|
509
|
+
setSectionsDisabled(true, '请先安装 Git')
|
|
510
|
+
gitBody.innerHTML =
|
|
511
|
+
'<div class="zgs-auth-err">没检测到本机安装 Git,无法使用 Git 集成。</div>' +
|
|
512
|
+
'<div class="zgp-desc">装好 Git for Windows 后点「重新检测」;如果装完仍提示未安装,重启一次 ZTools 让 PATH 生效。</div>' +
|
|
513
|
+
'<div class="zgp-row" style="margin-top:10px">' +
|
|
514
|
+
'<button class="zgp-b primary" data-git="redetect">重新检测</button>' +
|
|
515
|
+
'<button class="zgp-b" data-git="dlgit">打开下载页</button></div>'
|
|
516
|
+
const rd = gitBody.querySelector('[data-git="redetect"]')
|
|
517
|
+
if (rd) rd.onclick = function () { gitProbe = null; refreshGitSection().catch(function () {}) }
|
|
518
|
+
const dl = gitBody.querySelector('[data-git="dlgit"]')
|
|
519
|
+
if (dl) dl.onclick = async function () {
|
|
520
|
+
const ok = await zgsConfirm('即将打开外部窗口,CMark 窗口可能会被隐藏。装完 Git 后重新呼出 CMark 可继续设置。', '继续打开')
|
|
521
|
+
if (ok) { try { win.ztools.shellOpenExternal('https://git-scm.com/download/win') } catch (e) {} }
|
|
556
522
|
}
|
|
523
|
+
return
|
|
557
524
|
}
|
|
558
|
-
|
|
525
|
+
if (gitSwitch) gitSwitch.disabled = false
|
|
526
|
+
|
|
527
|
+
const insp = (G && G.inspect) ? G.inspect() : { isRepo: false }
|
|
528
|
+
let data = {}
|
|
529
|
+
try { const st = await G.state(); data = (st && st.data) || {} } catch (e) {}
|
|
530
|
+
let remote = { hasRemote: false, url: '' }
|
|
531
|
+
try { const r = await G.remoteInfo(); if (r && r.ok) remote = r.data } catch (e) {}
|
|
532
|
+
const ws = win.__ZNOTES_WS || ''
|
|
533
|
+
let count = 0
|
|
534
|
+
try {
|
|
535
|
+
if (ws && fs.existsSync(ws)) {
|
|
536
|
+
count = fs.readdirSync(ws).filter(function (n) {
|
|
537
|
+
return ['.git', '.cmark', '.attachments', 'backup'].indexOf(n) < 0
|
|
538
|
+
}).length
|
|
539
|
+
}
|
|
540
|
+
} catch (e) {}
|
|
559
541
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
542
|
+
let enabled = readGitFlag(null)
|
|
543
|
+
if (enabled === null) { enabled = !!insp.isRepo; writeGitFlag(enabled) }
|
|
544
|
+
// 每次打开面板后的首次渲染校正一次:目录还不是仓库却开着开关 → 落盘为关闭,
|
|
545
|
+
// 这样「打开开关后没继续建仓就关面板」下次进来仍是关闭。
|
|
546
|
+
if (!gitGateDone) {
|
|
547
|
+
gitGateDone = true
|
|
548
|
+
if (!insp.isRepo && enabled) { enabled = false; writeGitFlag(false) }
|
|
549
|
+
}
|
|
550
|
+
if (gitSwitch) gitSwitch.checked = enabled
|
|
551
|
+
if (gitDrawer) gitDrawer.classList.toggle('open', enabled)
|
|
552
|
+
setSectionsDisabled(
|
|
553
|
+
!enabled || !insp.isRepo,
|
|
554
|
+
!enabled ? '请先开启 Git 集成' : '请先配置 Git 仓库'
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
const branch = data.branch || 'main'
|
|
558
|
+
const ahead = data.ahead || 0
|
|
559
|
+
const behind = data.behind || 0
|
|
560
|
+
const dirty = typeof data.dirty === 'number' ? data.dirty : -1
|
|
561
|
+
|
|
562
|
+
let kind = 'empty'
|
|
563
|
+
if (insp.isRepo && remote.hasRemote) kind = 'remote'
|
|
564
|
+
else if (insp.isRepo) kind = 'local'
|
|
565
|
+
else if (count > 0) kind = 'files'
|
|
566
|
+
|
|
567
|
+
let html = ''
|
|
568
|
+
if (kind === 'empty') {
|
|
569
|
+
html = '<div class="zgp-desc">目录是空的,建一个仓库就能开始记。</div>' +
|
|
570
|
+
'<div class="zgp-row" style="margin-top:10px">' +
|
|
571
|
+
'<button class="zgp-b primary" data-git="gh">使用 GitHub 作为远程仓库</button>' +
|
|
572
|
+
'<button class="zgp-b" data-git="gt">使用 Gitee 作为远程仓库</button>' +
|
|
573
|
+
'<button class="zgp-b" data-git="adopt">从远程仓库加载</button>' +
|
|
574
|
+
'<button class="zgp-b" data-git="local">仅本地仓库</button></div>'
|
|
575
|
+
} else if (kind === 'files') {
|
|
576
|
+
html = '<div class="zgp-desc">目录里有 ' + count + ' 项内容。初始化后这些文件成为第一个提交,内容不会被改动。</div>' +
|
|
577
|
+
'<div class="zgp-row" style="margin-top:10px">' +
|
|
578
|
+
'<button class="zgp-b primary" data-git="gh">使用 GitHub 作为远程仓库</button>' +
|
|
579
|
+
'<button class="zgp-b" data-git="gt">使用 Gitee 作为远程仓库</button>' +
|
|
580
|
+
'<button class="zgp-b" data-git="local">仅本地仓库</button></div>'
|
|
581
|
+
} else if (kind === 'local') {
|
|
582
|
+
html = '<dl class="zgs-kv"><dt>仓库</dt><dd>本地仓库</dd>' +
|
|
583
|
+
'<dt>分支</dt><dd>' + esc(branch) + '</dd>' +
|
|
584
|
+
'<dt>改动</dt><dd>' + (dirty >= 0 ? dirty + ' 个文件' : '读取失败') + '</dd></dl>' +
|
|
585
|
+
'<div class="zgp-desc">还没有远程仓库,笔记只存在这台机器上。</div>' +
|
|
586
|
+
'<div class="zgp-row" style="margin-top:10px">' +
|
|
587
|
+
'<button class="zgp-b primary" data-git="gh">使用 GitHub 作为远程仓库</button>' +
|
|
588
|
+
'<button class="zgp-b" data-git="gt">使用 Gitee 作为远程仓库</button>' +
|
|
589
|
+
'<button class="zgp-b" data-git="manual">手动填远程地址</button></div>'
|
|
590
|
+
} else {
|
|
591
|
+
html = '<dl class="zgs-kv"><dt>远程</dt><dd class="mono">' + esc(remote.url || '') + '</dd>' +
|
|
592
|
+
'<dt>分支</dt><dd>' + esc(branch) + '</dd>' +
|
|
593
|
+
'<dt>状态</dt><dd>' + (dirty >= 0 ? dirty + ' 处改动' : '读取失败') +
|
|
594
|
+
(ahead ? ' · 待推送 ' + ahead : '') + (behind ? ' · 待拉取 ' + behind : '') + '</dd></dl>' +
|
|
595
|
+
'<div class="zgp-row" style="margin-top:10px">' +
|
|
596
|
+
'<button class="zgp-b" data-git="push">推送</button>' +
|
|
597
|
+
'<button class="zgp-b" data-git="pull">拉取</button>' +
|
|
598
|
+
'<button class="zgp-b danger" data-git="unbind">移除远端</button>' +
|
|
599
|
+
'<button class="zgp-b" data-git="clear-token">清除已保存凭据</button></div>'
|
|
600
|
+
}
|
|
601
|
+
gitBody.innerHTML = html
|
|
602
|
+
|
|
603
|
+
const btns = gitBody.querySelectorAll('[data-git]')
|
|
604
|
+
for (let i = 0; i < btns.length; i++) {
|
|
605
|
+
const b = btns[i]
|
|
606
|
+
b.onclick = async function () {
|
|
607
|
+
const act = b.getAttribute('data-git')
|
|
608
|
+
if (act === 'gh') return openWizard('github')
|
|
609
|
+
if (act === 'gt') return openWizard('gitee')
|
|
610
|
+
if (act === 'adopt') return loadFromRemote()
|
|
611
|
+
if (act === 'local') {
|
|
612
|
+
b.disabled = true
|
|
613
|
+
const r = await G.initRepo({ mode: 'local' })
|
|
614
|
+
b.disabled = false
|
|
615
|
+
if (r && r.ok) { toast('已创建本地仓库'); afterGitChange() } else toast((r && r.message) || '初始化失败', 'err')
|
|
616
|
+
return
|
|
617
|
+
}
|
|
618
|
+
if (act === 'manual') {
|
|
619
|
+
const bindRemote = async function (u) {
|
|
620
|
+
const r = await G.initRepo({ mode: 'remote', remoteUrl: u, push: true })
|
|
621
|
+
if (r && r.ok) { toast('已绑定远程仓库'); afterGitChange(); return { ok: true } }
|
|
622
|
+
return { ok: false, message: (r && r.message) || '绑定失败' }
|
|
572
623
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
624
|
+
// 第二步:需要授权时让用户贴令牌,存进本工作区后再探测并绑定
|
|
625
|
+
const askToken = function (u, hint) {
|
|
626
|
+
setTimeout(function () {
|
|
627
|
+
zgsPrompt(
|
|
628
|
+
'这个仓库需要授权访问\n把访问令牌粘贴到下面(GitHub PAT / Gitee 私人令牌 / 私有 GitLab token)',
|
|
629
|
+
'粘贴访问令牌(GitHub PAT / Gitee 私人令牌 / 私有 GitLab token)', '',
|
|
630
|
+
async function (tk) {
|
|
631
|
+
const t = String(tk || '').trim()
|
|
632
|
+
if (!t) return { ok: false, message: '请填写访问令牌' }
|
|
633
|
+
const saved = G.setRemoteToken(t, u)
|
|
634
|
+
if (saved && saved.ok === false) return { ok: false, message: saved.message || '保存令牌失败' }
|
|
635
|
+
const p2 = await G.probeRemote(u, t)
|
|
636
|
+
if (!p2 || !p2.ok) {
|
|
637
|
+
return { ok: false, message: '令牌无效或没有写权限:' + ((p2 && p2.message) || '未知原因') }
|
|
638
|
+
}
|
|
639
|
+
const r = await bindRemote(u)
|
|
640
|
+
if (r.ok) toast('已绑定远程仓库(凭据已保存在本机)')
|
|
641
|
+
return r
|
|
642
|
+
}
|
|
643
|
+
)
|
|
644
|
+
}, 140)
|
|
645
|
+
return { ok: true }
|
|
579
646
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
|
|
647
|
+
zgsPrompt('填写远程仓库地址', 'https://github.com/you/repo.git', '', async function (url) {
|
|
648
|
+
const u = String(url || '').trim()
|
|
649
|
+
if (!u) return { ok: false, message: '请填写远程仓库地址' }
|
|
650
|
+
// 先免授权探一下:公开仓库直接过,私有仓库会返回 auth 类错误
|
|
651
|
+
const probe = await G.probeRemote(u)
|
|
652
|
+
if (probe && probe.ok) return bindRemote(u)
|
|
653
|
+
const msg = String((probe && probe.message) || '')
|
|
654
|
+
// 注意:GitHub/Gitee 对「私有仓库无权限」和「仓库不存在」都返回 not found,
|
|
655
|
+
// 所以这里把 not found 也算进去 —— 否则私有仓库会直接走到绑定、只丢一句英文报错。
|
|
656
|
+
if (/auth|permission|denied|403|401|username|password|credentials|not found|does not exist/i.test(msg)) {
|
|
657
|
+
// 需要授权:关掉地址输入,接着弹令牌输入
|
|
658
|
+
askToken(u)
|
|
659
|
+
return { ok: true }
|
|
660
|
+
}
|
|
661
|
+
return bindRemote(u)
|
|
662
|
+
})
|
|
663
|
+
return
|
|
664
|
+
}
|
|
665
|
+
if (act === 'push' || act === 'pull') {
|
|
666
|
+
b.disabled = true
|
|
667
|
+
const r = act === 'push' ? await G.push() : await G.pull()
|
|
668
|
+
b.disabled = false
|
|
669
|
+
if (r && r.ok) { toast(act === 'push' ? '推送完成' : '拉取完成'); return }
|
|
670
|
+
const failMsg = (r && r.message) || (act === 'push' ? '推送失败' : '拉取失败')
|
|
671
|
+
if (isAuthError(failMsg)) { showAuthError(failMsg); return }
|
|
672
|
+
toast(failMsg, 'err')
|
|
673
|
+
return
|
|
674
|
+
}
|
|
675
|
+
if (act === 'clear-token') {
|
|
676
|
+
const ok = await zgsConfirm('清除本机保存的授权凭据后,推送需要重新授权;远端绑定关系保留。', '清除')
|
|
677
|
+
if (!ok) return
|
|
678
|
+
try { if (win.__CMARK_REMOTE && win.__CMARK_REMOTE.clear) win.__CMARK_REMOTE.clear() } catch (e) {}
|
|
679
|
+
toast('已清除本机保存的授权凭据')
|
|
680
|
+
afterGitChange()
|
|
681
|
+
return
|
|
682
|
+
}
|
|
683
|
+
if (act === 'unbind') {
|
|
684
|
+
const ok = await zgsConfirm('移除远端绑定后,笔记只保留在本机。', '移除')
|
|
685
|
+
if (!ok) return
|
|
686
|
+
const r = await G.setRemote('')
|
|
687
|
+
if (r && r.ok) { toast('已移除远端'); afterGitChange() } else toast((r && r.message) || '移除失败', 'err')
|
|
688
|
+
}
|
|
600
689
|
}
|
|
601
690
|
}
|
|
602
691
|
}
|
|
603
692
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
async function (v) {
|
|
613
|
-
const chk = await Promise.resolve(
|
|
614
|
-
G && typeof G.checkRemote === 'function' ? G.checkRemote(v) : { ok: true })
|
|
615
|
-
if (!chk || !chk.ok) {
|
|
616
|
-
return { ok: false, message: '仓库地址不可用:' + ((chk && chk.message) || '无法连接') }
|
|
617
|
-
}
|
|
618
|
-
remoteBranches = (chk.branches || [])
|
|
619
|
-
return { ok: true }
|
|
620
|
-
})
|
|
621
|
-
if (!url) return
|
|
622
|
-
let r = null
|
|
623
|
-
try { r = await Promise.resolve(G.setRemote(url)) } catch (e) { r = { ok: false, message: (e && e.message) || '' } }
|
|
624
|
-
if (!r || !r.ok) {
|
|
625
|
-
toast('关联失败:' + ((r && r.message) || '未知原因'), 'err')
|
|
626
|
-
return
|
|
627
|
-
}
|
|
628
|
-
try { if (win.__zgpRefresh) win.__zgpRefresh() } catch (e) {}
|
|
629
|
-
// 远端是空仓库:光写 remote 等于什么也没同步,顺手把本地历史推上去,
|
|
630
|
-
// 否则用户会以为"关联成功"但远端正本还是空的。
|
|
631
|
-
if (remoteBranches && remoteBranches.length === 0) {
|
|
632
|
-
toast('远端是空仓库,正在推送本地历史…', 'info')
|
|
633
|
-
let pr = null
|
|
634
|
-
try { pr = await Promise.resolve(G.push()) } catch (e) { pr = { ok: false, message: (e && e.message) || '' } }
|
|
635
|
-
if (pr && pr.ok) toast('已关联到远程并推送完成', 'ok')
|
|
636
|
-
else toast('已关联,但推送失败:' + ((pr && pr.message) || '未知原因'), 'err')
|
|
637
|
-
} else {
|
|
638
|
-
toast('已关联到远程', 'ok')
|
|
639
|
-
}
|
|
640
|
-
render()
|
|
693
|
+
if (gitSwitch) {
|
|
694
|
+
gitSwitch.onchange = function () {
|
|
695
|
+
writeGitFlag(!!gitSwitch.checked)
|
|
696
|
+
if (gitDrawer) gitDrawer.classList.toggle('open', !!gitSwitch.checked)
|
|
697
|
+
setSectionsDisabled(!gitSwitch.checked)
|
|
698
|
+
// 通知侧边栏操作栏与编辑区云端按钮同步置灰/隐藏
|
|
699
|
+
try { win.dispatchEvent(new CustomEvent('cmark-git-changed')) } catch (e) {}
|
|
700
|
+
refreshGitSection().catch(function () {})
|
|
641
701
|
}
|
|
642
702
|
}
|
|
703
|
+
refreshGitSection().catch(function () {})
|
|
704
|
+
|
|
705
|
+
// ---------- 工作区(仓库根)/ 笔记目录 ----------
|
|
706
|
+
const fillDirs = function () {
|
|
707
|
+
const api = win.__CMARK || {}
|
|
708
|
+
const ws = win.__ZNOTES_WS || ''
|
|
709
|
+
let root = ''
|
|
710
|
+
try { root = api.repoDir ? api.repoDir() : '' } catch (e) {}
|
|
711
|
+
if (!root) root = ws
|
|
712
|
+
const rootEl = $('s-repo-dir')
|
|
713
|
+
if (rootEl) rootEl.textContent = root || '未设置'
|
|
714
|
+
const wsEl = $('s-ws')
|
|
715
|
+
if (wsEl) wsEl.textContent = ws || '未设置'
|
|
716
|
+
}
|
|
717
|
+
fillDirs()
|
|
643
718
|
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
if (ri && ri.ok && ri.data && ri.data.hasRemote) old = ri.data.url
|
|
650
|
-
} catch (e) {}
|
|
651
|
-
if (url && old && url !== old) {
|
|
652
|
-
const ok = await zgsConfirm('把远端地址\n\n' + old + '\n\n改为\n\n' + url + '\n\n之后推送会进入新仓库。', '修改')
|
|
653
|
-
if (!ok) return
|
|
654
|
-
}
|
|
655
|
-
if (!url) {
|
|
656
|
-
const ok = await zgsConfirm('移除远端后,笔记只在本机提交,不再上传。', '移除')
|
|
657
|
-
if (!ok) return
|
|
658
|
-
}
|
|
659
|
-
if (btn) btn.disabled = true
|
|
719
|
+
const repoNames = { pick: '修改工作区', notes: '选择笔记目录' }
|
|
720
|
+
const runDirPick = function (kind) {
|
|
721
|
+
const api = win.__CMARK || {}
|
|
722
|
+
const fn = kind === 'pick' ? api.pickRepoDir : api.pickNotesDir
|
|
723
|
+
if (typeof fn !== 'function') return toast(repoNames[kind] + '功能不可用', 'err')
|
|
660
724
|
let r = null
|
|
661
|
-
try { r =
|
|
662
|
-
if (
|
|
663
|
-
if (r
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
render()
|
|
667
|
-
refreshVc().catch(function () {}) // 局部刷新,避免整块重渲染冲掉其它输入
|
|
668
|
-
} else {
|
|
669
|
-
toast('操作失败:' + ((r && r.message) || '未知原因'), 'err')
|
|
670
|
-
}
|
|
725
|
+
try { r = fn.call(api) } catch (e) { return toast('打开目录选择失败:' + (e && e.message), 'err') }
|
|
726
|
+
if (!r || r.canceled) return
|
|
727
|
+
if (!r.ok) return toast(r.message || '操作失败', 'err')
|
|
728
|
+
toast(kind === 'pick' ? '工作区已切换' : '笔记目录已切换')
|
|
729
|
+
if (r.needReload) setTimeout(function () { try { win.location.reload() } catch (e) {} }, 400)
|
|
671
730
|
}
|
|
731
|
+
const repoBtn = $('s-repo-pick')
|
|
732
|
+
if (repoBtn) repoBtn.onclick = function () { runDirPick('pick') }
|
|
733
|
+
const notesBtn = $('s-notes-pick')
|
|
734
|
+
if (notesBtn) notesBtn.onclick = function () { runDirPick('notes') }
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
// 从远程仓库加载(要求空工作区):校验连通性 + 目录状态后才拉取
|
|
738
|
+
async function loadFromRemote () {
|
|
739
|
+
const url = await zgsPrompt(
|
|
740
|
+
'从远程仓库加载\n\n输入远端仓库地址。点「检测并继续」会先确认仓库可访问、当前工作区为空,通过后才开始拉取。',
|
|
741
|
+
'https://gitee.com/账号/仓库.git', '',
|
|
742
|
+
async function (v) {
|
|
743
|
+
const chk = await Promise.resolve(G && typeof G.checkRemote === 'function' ? G.checkRemote(v) : { ok: true })
|
|
744
|
+
if (!chk || !chk.ok) return { ok: false, message: '仓库地址不可用:' + ((chk && chk.message) || '无法连接') }
|
|
745
|
+
const pre = G && typeof G.adoptPrecheck === 'function' ? G.adoptPrecheck() : null
|
|
746
|
+
if (pre && !pre.ok) {
|
|
747
|
+
const items = (pre.blocking && pre.blocking.length)
|
|
748
|
+
? ':' + pre.blocking.slice(0, 3).join('、') + (pre.blocking.length > 3 ? ' 等' : '')
|
|
749
|
+
: ''
|
|
750
|
+
return { ok: false, message: '加载需要空工作区。' + (pre.reason || '') + items }
|
|
751
|
+
}
|
|
752
|
+
return { ok: true }
|
|
753
|
+
})
|
|
754
|
+
if (!url) return
|
|
755
|
+
toast('正在从远端拉取…', 'info')
|
|
756
|
+
let r = null
|
|
757
|
+
try { r = await Promise.resolve(G.adoptRepo({ remoteUrl: url, branch: 'main' })) } catch (e) { r = { ok: false, message: (e && e.message) || String(e) } }
|
|
758
|
+
if (r && r.ok) {
|
|
759
|
+
toast('已从远端加载完成', 'ok')
|
|
760
|
+
try { if (G.rediscover) G.rediscover() } catch (e) {}
|
|
761
|
+
refreshGitSection().catch(function () {})
|
|
672
762
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
remoteClearBtn.onclick = function () { saveRemote('', remoteClearBtn) }
|
|
676
|
-
}
|
|
763
|
+
// 暴露给外部一次性监听器:每次 render 更新引用,避免重复注册 addEventListener
|
|
764
|
+
win.__zgsRefreshGitSection = refreshGitSection
|
|
677
765
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
const n = (($('s-vc-name2') || {}).value || '').trim()
|
|
682
|
-
const e = (($('s-vc-email2') || {}).value || '').trim()
|
|
683
|
-
idSaveBtn.disabled = true
|
|
684
|
-
let r = null
|
|
685
|
-
try { r = await G.setIdentity(n, e) } catch (err) { r = { ok: false, message: (err && err.message) || '' } }
|
|
686
|
-
idSaveBtn.disabled = false
|
|
687
|
-
if (r && r.ok) toast('身份已保存', 'ok')
|
|
688
|
-
else toast('保存失败:' + ((r && r.message) || '未知原因'), 'err')
|
|
766
|
+
setTimeout(function () { try { if (win.__ZNOTES_REFRESH) win.__ZNOTES_REFRESH() } catch (e) {} }, 600)
|
|
767
|
+
} else {
|
|
768
|
+
toast('加载失败:' + ((r && r.message) || '未知原因'), 'err')
|
|
689
769
|
}
|
|
690
770
|
}
|
|
691
771
|
|
|
@@ -694,30 +774,11 @@ module.exports = function attachSettingsPanel (win) {
|
|
|
694
774
|
autoSync: $('s-auto').checked,
|
|
695
775
|
autoMode: $('s-mode') ? $('s-mode').value : 'interval',
|
|
696
776
|
intervalMs: parseInt($('s-interval').value, 10),
|
|
697
|
-
onStopEditMinutes: parseInt($('s-stop').value, 10)
|
|
698
|
-
commitMessage: $('s-msg').value
|
|
777
|
+
onStopEditMinutes: parseInt($('s-stop').value, 10)
|
|
699
778
|
})
|
|
700
779
|
toast('设置已保存', 'ok')
|
|
701
780
|
}
|
|
702
781
|
|
|
703
|
-
// 自定义提交信息:开关控制抽屉展开,并决定用默认模板还是自定义模板
|
|
704
|
-
const DEFAULT_MSG = 'notes: 同步 {{datetime}}'
|
|
705
|
-
const msgCustom = $('s-msg-custom')
|
|
706
|
-
const msgDrawer = $('s-msg-drawer')
|
|
707
|
-
const msgInput = $('s-msg')
|
|
708
|
-
if (msgCustom && msgDrawer) {
|
|
709
|
-
const isCustom = !!(cfg.commitMessage && cfg.commitMessage !== DEFAULT_MSG)
|
|
710
|
-
msgCustom.checked = isCustom
|
|
711
|
-
msgDrawer.classList.toggle('open', isCustom)
|
|
712
|
-
if (!isCustom && msgInput && !msgInput.value) msgInput.value = DEFAULT_MSG
|
|
713
|
-
msgCustom.onchange = function () {
|
|
714
|
-
const on = msgCustom.checked
|
|
715
|
-
msgDrawer.classList.toggle('open', on)
|
|
716
|
-
if (!on && msgInput) msgInput.value = DEFAULT_MSG
|
|
717
|
-
save()
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
|
|
721
782
|
const autoSw = $('s-auto')
|
|
722
783
|
if (autoSw) {
|
|
723
784
|
autoSw.onchange = function () {
|
|
@@ -741,7 +802,6 @@ module.exports = function attachSettingsPanel (win) {
|
|
|
741
802
|
const el = $(id)
|
|
742
803
|
if (el) el.onchange = save
|
|
743
804
|
})
|
|
744
|
-
if ($('s-msg')) $('s-msg').onblur = save
|
|
745
805
|
|
|
746
806
|
// 仓库 / 状态信息(异步)
|
|
747
807
|
let st = {}
|
|
@@ -751,34 +811,79 @@ module.exports = function attachSettingsPanel (win) {
|
|
|
751
811
|
} catch (e) {}
|
|
752
812
|
const repoEl = $('s-repo')
|
|
753
813
|
if (repoEl) {
|
|
754
|
-
|
|
814
|
+
const inspNow = (G && G.inspect) ? G.inspect() : { isRepo: false }
|
|
815
|
+
let remoteNow = { hasRemote: false, url: '' }
|
|
816
|
+
try { const rr = await G.remoteInfo(); if (rr && rr.ok) remoteNow = rr.data } catch (e) {}
|
|
817
|
+
let lastRun = 0
|
|
818
|
+
try { const cc = G.config(); if (cc && cc.ok) lastRun = cc.data.lastRunAt || 0 } catch (e) {}
|
|
819
|
+
const fmtTime = function (t) {
|
|
820
|
+
if (!t) return ''
|
|
821
|
+
try {
|
|
822
|
+
const diff = Date.now() - t
|
|
823
|
+
if (diff < 0) return ''
|
|
824
|
+
if (diff < 60000) return '刚刚'
|
|
825
|
+
if (diff < 3600000) return Math.floor(diff / 60000) + ' 分钟前'
|
|
826
|
+
if (diff < 86400000) return Math.floor(diff / 3600000) + ' 小时前'
|
|
827
|
+
const d = new Date(t)
|
|
828
|
+
const p2 = function (n) { return n < 10 ? '0' + n : String(n) }
|
|
829
|
+
return d.getFullYear() + '-' + p2(d.getMonth() + 1) + '-' + p2(d.getDate())
|
|
830
|
+
} catch (e) { return '' }
|
|
831
|
+
}
|
|
832
|
+
const c2 = (G.config() || {}).data || {}
|
|
755
833
|
let rows = ''
|
|
834
|
+
rows += '<dt>本地仓库</dt><dd>' + (inspNow.isRepo ? '已绑定' + (st.branch ? ' · ' + esc(st.branch) : '') : '未绑定') + '</dd>'
|
|
835
|
+
rows += '<dt>远程仓库</dt><dd>' + (remoteNow.hasRemote ? '已绑定' : '未绑定') + '</dd>'
|
|
836
|
+
if (inspNow.isRepo) {
|
|
837
|
+
rows += '<dt>上次同步</dt><dd>' + (c2.lastRunAt ? new Date(c2.lastRunAt).toLocaleString('zh-CN') : '从未执行') + '</dd>'
|
|
838
|
+
if (c2.lastAutoResult) {
|
|
839
|
+
const ar = c2.lastAutoResult
|
|
840
|
+
rows += '<dt>最近结果</dt><dd>' + (ar.ok ? '成功' : '失败') + (ar.message ? ' · ' + esc(ar.message) : '') + '</dd>'
|
|
841
|
+
if (ar.error) rows += '<dt>错误</dt><dd>' + esc(ar.error) + '</dd>'
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
if (typeof st.dirty === 'number') rows += '<dt>未提交</dt><dd>' + st.dirty + ' 个文件</dd>'
|
|
756
845
|
if (G.scopeRel) rows += '<dt>笔记子目录</dt><dd class="mono">' + esc(G.scopeRel) + '</dd>'
|
|
757
|
-
if (st.branch) rows += '<dt>当前分支</dt><dd class="mono">' + esc(st.branch) + '</dd>'
|
|
758
|
-
if (typeof st.dirty === 'number') rows += '<dt>未提交改动</dt><dd>' + st.dirty + ' 个文件</dd>'
|
|
759
846
|
repoEl.innerHTML = '<dl class="zgs-kv">' + rows + '</dl>'
|
|
760
847
|
}
|
|
761
|
-
const c2 = (G.config() || {}).data || {}
|
|
762
|
-
let irows = '<dt>上次同步</dt><dd>' + (c2.lastRunAt ? new Date(c2.lastRunAt).toLocaleString('zh-CN') : '从未执行') + '</dd>'
|
|
763
|
-
if (c2.lastAutoResult) {
|
|
764
|
-
const ar = c2.lastAutoResult
|
|
765
|
-
irows += '<dt>最近结果</dt><dd>' + (ar.ok ? '成功' : '失败') + (ar.message ? ' · ' + esc(ar.message) : '') + '</dd>'
|
|
766
|
-
if (ar.error) irows += '<dt>错误</dt><dd>' + esc(ar.error) + '</dd>'
|
|
767
|
-
if (ar.at) irows += '<dt>发生时间</dt><dd>' + new Date(ar.at).toLocaleString('zh-CN') + '</dd>'
|
|
768
|
-
}
|
|
769
|
-
const infoEl = $('s-info')
|
|
770
|
-
if (infoEl) infoEl.innerHTML = '<dl class="zgs-kv">' + irows + '</dl>'
|
|
771
848
|
}
|
|
772
849
|
|
|
773
850
|
// ---------- 开关 ----------
|
|
851
|
+
// ZTools 的窗口策略是失焦即隐藏:打开资源管理器 / 外部授权页 / 原生目录选择框之后,
|
|
852
|
+
// 插件窗口会被宿主收起来。这里用 sessionStorage 记住面板是否开着,
|
|
853
|
+
// 窗口重新可见(或插件被重新呼出)时自动恢复,用户不用每次都重新点一遍。
|
|
854
|
+
const OPEN_KEY = 'cmark-settings-open'
|
|
855
|
+
function readOpenFlag () {
|
|
856
|
+
try { return win.sessionStorage.getItem(OPEN_KEY) === '1' } catch (e) { return false }
|
|
857
|
+
}
|
|
858
|
+
function writeOpenFlag (on) {
|
|
859
|
+
try { win.sessionStorage.setItem(OPEN_KEY, on ? '1' : '0') } catch (e) {}
|
|
860
|
+
}
|
|
861
|
+
|
|
774
862
|
function open () {
|
|
775
863
|
if (root.classList.contains('open')) return
|
|
776
864
|
root.classList.add('open')
|
|
865
|
+
writeOpenFlag(true)
|
|
777
866
|
render()
|
|
778
867
|
}
|
|
779
868
|
function close () {
|
|
780
869
|
root.classList.remove('open')
|
|
870
|
+
writeOpenFlag(false)
|
|
781
871
|
}
|
|
872
|
+
|
|
873
|
+
// 窗口从隐藏变回可见时恢复面板
|
|
874
|
+
try {
|
|
875
|
+
doc.addEventListener('visibilitychange', function () {
|
|
876
|
+
if (doc.visibilityState === 'visible' && readOpenFlag() && !root.classList.contains('open')) open()
|
|
877
|
+
})
|
|
878
|
+
} catch (e) {}
|
|
879
|
+
// 补一条:宿主重新进入插件时也恢复(部分场景不触发 visibilitychange)
|
|
880
|
+
try {
|
|
881
|
+
if (win.ztools && typeof win.ztools.onPluginEnter === 'function') {
|
|
882
|
+
win.ztools.onPluginEnter(function () { if (readOpenFlag()) open() })
|
|
883
|
+
}
|
|
884
|
+
} catch (e) {}
|
|
885
|
+
// 首次挂载时若上次会话面板是开的,直接打开
|
|
886
|
+
if (readOpenFlag()) setTimeout(function () { try { open() } catch (e) {} }, 80)
|
|
782
887
|
function toggle () {
|
|
783
888
|
if (root.classList.contains('open')) close()
|
|
784
889
|
else open()
|
|
@@ -842,6 +947,13 @@ module.exports = function attachSettingsPanel (win) {
|
|
|
842
947
|
win.__zgsOpen = open
|
|
843
948
|
win.__zgsClose = close
|
|
844
949
|
win.__zgsIsOpen = function () { return root.classList.contains('open') }
|
|
950
|
+
// 建仓向导完成 / 开关变更时重算仓库状态(只注册一次;render 会被反复调用)
|
|
951
|
+
try {
|
|
952
|
+
win.addEventListener('cmark-git-changed', function () {
|
|
953
|
+
try { if (typeof win.__zgsRefreshGitSection === 'function') win.__zgsRefreshGitSection() } catch (e) {}
|
|
954
|
+
})
|
|
955
|
+
} catch (e) {}
|
|
956
|
+
|
|
845
957
|
win.__zgsRender = render
|
|
846
958
|
logBoot('done, btn=' + !!doc.getElementById('znotes-sbtn'))
|
|
847
959
|
}
|