@arshdelight/practi 0.9.4 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -1
- package/dist/cmd/clone.js +2 -3
- package/dist/cmd/clone.js.map +1 -1
- package/dist/cmd/edit.js +2 -3
- package/dist/cmd/edit.js.map +1 -1
- package/dist/cmd/new.js +2 -3
- package/dist/cmd/new.js.map +1 -1
- package/dist/cmd/pull.js +2 -3
- package/dist/cmd/pull.js.map +1 -1
- package/dist/cmd/repair.js +34 -0
- package/dist/cmd/repair.js.map +1 -0
- package/dist/cmd/skill.js +2 -3
- package/dist/cmd/skill.js.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/state.js +30 -0
- package/dist/state.js.map +1 -1
- package/dist/web.js +305 -139
- package/dist/web.js.map +1 -1
- package/dist/workspace.js +10 -0
- package/dist/workspace.js.map +1 -1
- package/package.json +3 -2
- package/web-default/app.js +106 -0
- package/web-default/detail.html +32 -0
- package/web-default/detail.js +531 -0
- package/web-default/index.html +28 -0
- package/web-default/lang.js +169 -0
- package/web-default/marked.min.js +69 -0
- package/web-default/style.css +182 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// practi web 默认前端逻辑:拉 /api/directs 渲染目录表格(客户端分页,每页 20 条)。
|
|
2
|
+
// 数据与展示分离:本文件只消费 JSON 数据窗,改样式改结构都在文件里随便改,
|
|
3
|
+
// 保存即触发 live reload(服务端注入的 /_lr.js 负责自动刷新)。
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
var PAGE_SIZE = 20;
|
|
7
|
+
var docs = [];
|
|
8
|
+
var page = 1;
|
|
9
|
+
|
|
10
|
+
function escapeHtml(s) {
|
|
11
|
+
return String(s).replace(/[&<>"']/g, function (c) {
|
|
12
|
+
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 空态书本图标(lucide book-open,同 hub /me 空态)
|
|
17
|
+
var BOOK_ICON =
|
|
18
|
+
'<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
|
|
19
|
+
'<path d="M12 7v14"/>' +
|
|
20
|
+
'<path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/>' +
|
|
21
|
+
'</svg>';
|
|
22
|
+
|
|
23
|
+
function plural(n, unit) {
|
|
24
|
+
return n + ' ' + unit + (n === 1 ? '' : 's');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 本地时刻、精确到分钟:YYYY-MM-DD HH:mm(等宽数字,列内不截断)
|
|
28
|
+
function fmtDateTime(iso) {
|
|
29
|
+
var d = new Date(iso);
|
|
30
|
+
if (isNaN(d.getTime())) return '';
|
|
31
|
+
var pad = function (n) { return (n < 10 ? '0' : '') + n; };
|
|
32
|
+
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Added 单元格:claimedAt=声明值(认领盖戳);无 claimedAt 而 addedAt 存在=文件 mtime
|
|
36
|
+
// 推测值(弱化 + 悬浮说明);两者皆无=空白
|
|
37
|
+
function addedCell(d) {
|
|
38
|
+
if (!d.addedAt) return '<td class="doc-date"></td>';
|
|
39
|
+
if (!d.claimedAt) {
|
|
40
|
+
return '<td class="doc-date derived" title="file time (unstamped claim)">' + escapeHtml(fmtDateTime(d.addedAt)) + '</td>';
|
|
41
|
+
}
|
|
42
|
+
return '<td class="doc-date">' + escapeHtml(fmtDateTime(d.addedAt)) + '</td>';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function render() {
|
|
46
|
+
var t = POP_I18N.t;
|
|
47
|
+
var app = document.getElementById('app');
|
|
48
|
+
if (docs.length === 0) {
|
|
49
|
+
app.innerHTML =
|
|
50
|
+
'<div class="empty">' + BOOK_ICON +
|
|
51
|
+
'<p class="lead">' + t('emptyLead') + '</p>' +
|
|
52
|
+
'<p class="hint">' + t('emptyHint') + '</p></div>';
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
var totalPages = Math.max(1, Math.ceil(docs.length / PAGE_SIZE));
|
|
56
|
+
if (page > totalPages) page = totalPages;
|
|
57
|
+
var rows = docs
|
|
58
|
+
.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE)
|
|
59
|
+
.map(function (d) {
|
|
60
|
+
return '<tr>' +
|
|
61
|
+
'<td><a class="doc-name" href="/pop/' + encodeURIComponent(d.hash) + '">' + escapeHtml(d.name) + '</a>' +
|
|
62
|
+
(d.description ? '<p class="doc-desc">' + escapeHtml(d.description) + '</p>' : '') + '</td>' +
|
|
63
|
+
'<td class="doc-steps">' + d.steps + '</td>' +
|
|
64
|
+
addedCell(d) +
|
|
65
|
+
'</tr>';
|
|
66
|
+
})
|
|
67
|
+
.join('\n');
|
|
68
|
+
var pager = '';
|
|
69
|
+
if (page > 1 || page < totalPages) {
|
|
70
|
+
pager = '<div class="pager">' +
|
|
71
|
+
(page > 1 ? '<button type="button" data-page="' + (page - 1) + '">' + t('pagerPrev') + '</button>' : '') +
|
|
72
|
+
(page < totalPages ? '<button type="button" data-page="' + (page + 1) + '">' + t('pagerNext') + '</button>' : '') +
|
|
73
|
+
'</div>';
|
|
74
|
+
}
|
|
75
|
+
app.innerHTML =
|
|
76
|
+
'<h3 class="page-title">' + t('pageTitle') + '</h3>' +
|
|
77
|
+
'<table class="doc-table">' +
|
|
78
|
+
'<thead><tr><th>' + t('thDocument') + '</th><th class="col-steps">' + t('thSteps') + '</th><th class="col-added">' + t('thAdded') + '</th></tr></thead>' +
|
|
79
|
+
'<tbody>' + rows + '</tbody></table>' +
|
|
80
|
+
'<div class="count-row"><span>' + t('countDocs', docs.length) + ' · ' + t('pageOf', page, totalPages) + '</span>' + pager + '</div>';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 翻页走事件委托:innerHTML 重渲染不需要重复绑监听
|
|
84
|
+
document.addEventListener('click', function (e) {
|
|
85
|
+
var btn = e.target.closest('[data-page]');
|
|
86
|
+
if (!btn) return;
|
|
87
|
+
page = Number(btn.getAttribute('data-page'));
|
|
88
|
+
render();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// 语言切换:整页文案重渲染
|
|
92
|
+
POP_I18N.onChange(render);
|
|
93
|
+
|
|
94
|
+
fetch('/api/directs')
|
|
95
|
+
.then(function (r) { return r.json(); })
|
|
96
|
+
.then(function (data) {
|
|
97
|
+
// 按 added 倒序:后发的在前;无 addedAt(时间未知)垫底
|
|
98
|
+
docs = (data.docs || []).slice().sort(function (a, b) {
|
|
99
|
+
return new Date(b.addedAt || 0) - new Date(a.addedAt || 0);
|
|
100
|
+
});
|
|
101
|
+
render();
|
|
102
|
+
})
|
|
103
|
+
.catch(function () {
|
|
104
|
+
document.getElementById('app').innerHTML =
|
|
105
|
+
'<div class="empty"><p class="lead">' + POP_I18N.t('loadFail') + '</p></div>';
|
|
106
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>practi — detail</title>
|
|
7
|
+
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%2267.5%2075%20459%20459%22%20fill%3D%22none%22%20stroke%3D%22%23111827%22%20stroke-width%3D%2227%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%0A%20%20%20%20%3Cpath%20d%3D%22M%20301.0%20252.0%20C%20300.5%20246.2%20299.6%20228.7%20298.0%20217.2%20C%20296.4%20205.7%20294.8%20194.1%20291.4%20183.0%20C%20287.9%20171.8%20283.5%20160.2%20277.3%20150.3%20C%20271.1%20140.4%20263.1%20130.9%20254.1%20123.7%20C%20245.1%20116.5%20234.0%20110.2%20223.1%20107.1%20C%20212.2%20104.1%20199.5%20103.2%20188.5%20105.4%20C%20177.5%20107.5%20165.6%20112.8%20157.2%20119.9%20C%20148.8%20127.1%20142.8%20137.8%20138.0%20148.2%20C%20133.3%20158.7%20129.8%20171.1%20128.8%20182.7%20C%20127.7%20194.3%20127.4%20208.0%20131.8%20218.0%20C%20136.2%20228.0%20145.9%20236.1%20155.2%20242.8%20C%20164.4%20249.6%20176.1%20254.3%20187.3%20258.4%20C%20198.4%20262.5%20210.4%20264.9%20222.0%20267.2%20C%20233.7%20269.5%20256.5%20269.2%20257.2%20272.3%20C%20257.8%20275.5%20236.5%20281.6%20225.8%20286.2%20C%20215.0%20290.7%20203.3%20294.4%20192.6%20299.5%20C%20181.8%20304.7%20171.2%20310.6%20161.2%20317.1%20C%20151.2%20323.6%20140.8%20330.3%20132.6%20338.6%20C%20124.3%20346.9%20115.6%20356.3%20111.5%20366.7%20C%20107.4%20377.1%20103.6%20391.8%20107.9%20400.9%20C%20112.1%20410.0%20126.3%20418.0%20137.0%20421.5%20C%20147.7%20425.0%20160.5%20423.7%20172.0%20422.0%20C%20183.5%20420.2%20195.4%20416.3%20206.0%20411.3%20C%20216.6%20406.2%20226.8%20399.5%20235.6%20391.8%20C%20244.4%20384.1%20251.5%20374.1%20259.0%20365.0%20C%20266.5%20355.9%20274.3%20345.5%20280.7%20337.4%20C%20287.0%20329.2%20294.3%20319.6%20297.0%20316.0%22%20%2F%3E%0A%20%20%20%20%3Cpath%20d%3D%22M%20381.0%20210.0%20C%20386.8%20209.4%20404.2%20205.8%20415.6%20206.3%20C%20426.9%20206.8%20438.9%20208.3%20449.1%20212.9%20C%20459.2%20217.4%20469.8%20224.7%20476.4%20233.4%20C%20483.1%20242.2%20487.7%20254.4%20489.1%20265.4%20C%20490.6%20276.5%20489.7%20289.4%20485.2%20299.6%20C%20480.7%20309.8%20471.5%20320.6%20462.1%20326.7%20C%20452.7%20332.7%20440.2%20335.1%20428.8%20336.0%20C%20417.4%20336.9%20404.9%20334.7%20393.6%20332.1%20C%20382.3%20329.4%20369.0%20323.4%20361.0%20320.1%20C%20353.1%20316.7%20348.5%20313.3%20346.0%20312.0%22%20%2F%3E%0A%20%20%20%20%3Cpath%20d%3D%22M%20310.0%20303.0%20C%20311.1%20308.7%20315.4%20325.9%20316.8%20337.4%20C%20318.1%20349.0%20319.1%20360.6%20318.0%20372.1%20C%20316.8%20383.7%20313.7%20395.6%20309.9%20406.8%20C%20306.1%20418.1%20300.9%20429.2%20295.1%20439.5%20C%20289.3%20449.9%20283.2%20458.2%20275.0%20469.1%20C%20266.9%20480.1%20250.8%20499.0%20246.0%20505.0%22%20%2F%3E%0A%20%20%3C%2Fsvg%3E">
|
|
8
|
+
<link rel="stylesheet" href="/style.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<header>
|
|
12
|
+
<!-- 站点图标:与 hub 的 LogoMark 同一组贝塞尔路径 -->
|
|
13
|
+
<a class="brand-link" href="/" aria-label="practi — all practices">
|
|
14
|
+
<svg class="logo" viewBox="67.5 75 459 459" fill="none" stroke="currentColor" stroke-width="27" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
15
|
+
<path d="M 301.0 252.0 C 300.5 246.2 299.6 228.7 298.0 217.2 C 296.4 205.7 294.8 194.1 291.4 183.0 C 287.9 171.8 283.5 160.2 277.3 150.3 C 271.1 140.4 263.1 130.9 254.1 123.7 C 245.1 116.5 234.0 110.2 223.1 107.1 C 212.2 104.1 199.5 103.2 188.5 105.4 C 177.5 107.5 165.6 112.8 157.2 119.9 C 148.8 127.1 142.8 137.8 138.0 148.2 C 133.3 158.7 129.8 171.1 128.8 182.7 C 127.7 194.3 127.4 208.0 131.8 218.0 C 136.2 228.0 145.9 236.1 155.2 242.8 C 164.4 249.6 176.1 254.3 187.3 258.4 C 198.4 262.5 210.4 264.9 222.0 267.2 C 233.7 269.5 256.5 269.2 257.2 272.3 C 257.8 275.5 236.5 281.6 225.8 286.2 C 215.0 290.7 203.3 294.4 192.6 299.5 C 181.8 304.7 171.2 310.6 161.2 317.1 C 151.2 323.6 140.8 330.3 132.6 338.6 C 124.3 346.9 115.6 356.3 111.5 366.7 C 107.4 377.1 103.6 391.8 107.9 400.9 C 112.1 410.0 126.3 418.0 137.0 421.5 C 147.7 425.0 160.5 423.7 172.0 422.0 C 183.5 420.2 195.4 416.3 206.0 411.3 C 216.6 406.2 226.8 399.5 235.6 391.8 C 244.4 384.1 251.5 374.1 259.0 365.0 C 266.5 355.9 274.3 345.5 280.7 337.4 C 287.0 329.2 294.3 319.6 297.0 316.0" />
|
|
16
|
+
<path d="M 381.0 210.0 C 386.8 209.4 404.2 205.8 415.6 206.3 C 426.9 206.8 438.9 208.3 449.1 212.9 C 459.2 217.4 469.8 224.7 476.4 233.4 C 483.1 242.2 487.7 254.4 489.1 265.4 C 490.6 276.5 489.7 289.4 485.2 299.6 C 480.7 309.8 471.5 320.6 462.1 326.7 C 452.7 332.7 440.2 335.1 428.8 336.0 C 417.4 336.9 404.9 334.7 393.6 332.1 C 382.3 329.4 369.0 323.4 361.0 320.1 C 353.1 316.7 348.5 313.3 346.0 312.0" />
|
|
17
|
+
<path d="M 310.0 303.0 C 311.1 308.7 315.4 325.9 316.8 337.4 C 318.1 349.0 319.1 360.6 318.0 372.1 C 316.8 383.7 313.7 395.6 309.9 406.8 C 306.1 418.1 300.9 429.2 295.1 439.5 C 289.3 449.9 283.2 458.2 275.0 469.1 C 266.9 480.1 250.8 499.0 246.0 505.0" />
|
|
18
|
+
</svg>
|
|
19
|
+
<span class="brand">practi</span>
|
|
20
|
+
</a>
|
|
21
|
+
</header>
|
|
22
|
+
<div class="shell">
|
|
23
|
+
<aside class="side" id="side" aria-label="outline"></aside>
|
|
24
|
+
<main id="app" class="detail">
|
|
25
|
+
<div class="empty"><p class="lead">…</p></div>
|
|
26
|
+
</main>
|
|
27
|
+
</div>
|
|
28
|
+
<script src="/lang.js" defer></script>
|
|
29
|
+
<script src="/marked.min.js"></script>
|
|
30
|
+
<script src="/detail.js" defer></script>
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|