dsh-lighthouse 0.0.0-stage → 0.3.4
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/LICENSE +21 -0
- package/README.md +167 -2
- package/bin/dsh-lighthouse.mjs +133 -0
- package/client.js +388 -0
- package/cordis.patch.yml +4 -0
- package/lib/collect.js +484 -0
- package/lib/fleet.js +139 -0
- package/lib/index.js +385 -0
- package/lib/render.js +415 -0
- package/lib/status-route.js +126 -0
- package/package.json +93 -6
package/client.js
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-lighthouse · 客户端半 —— 「作品观察区」。
|
|
3
|
+
*
|
|
4
|
+
* 一句话:**给用户一个点开就能看的窗口** —— 每个作品现在怎么样了:
|
|
5
|
+
* 发布了没有(✅ / ⏳ / ❌)、多少星、多少人关注、有没有人留言、收录 PR 排到哪。
|
|
6
|
+
*
|
|
7
|
+
* 它只画,不算:数据全来自 host 半的只读接口 `GET /dsh-lighthouse/status`
|
|
8
|
+
* (同一个数字在面板、工具、卡片里必须一致)。
|
|
9
|
+
*
|
|
10
|
+
* 座位:
|
|
11
|
+
* · `sidebar.panellist` —— 侧栏那个图标(组件收到 `{ size, active }`);
|
|
12
|
+
* · `main`(keyed = 本插件 id)—— 点图标后中间出现的那一屏。
|
|
13
|
+
* 两者的 id 必须一致 —— 侧栏就是靠这个 id 找到要切到哪个 main 面板的。
|
|
14
|
+
*
|
|
15
|
+
* 形态:Plain ModuleLoader bundle(无构建步骤),与官方插件的客户端半同构。
|
|
16
|
+
*/
|
|
17
|
+
window.__ModuleLoader__.load({ id: "dsh-lighthouse", factory: (require) => {
|
|
18
|
+
var module = { exports: {} }
|
|
19
|
+
var exports = module.exports
|
|
20
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" })
|
|
21
|
+
|
|
22
|
+
const react = require("react")
|
|
23
|
+
const h = react.createElement
|
|
24
|
+
const { useCallback, useEffect, useRef, useState } = react
|
|
25
|
+
|
|
26
|
+
const name = "dsh-lighthouse"
|
|
27
|
+
const inject = ["slots"]
|
|
28
|
+
const PANEL_ID = "dsh-lighthouse"
|
|
29
|
+
const ROUTE = "/dsh-lighthouse/status"
|
|
30
|
+
|
|
31
|
+
/* ------------------------------------------------------------ 小工具 */
|
|
32
|
+
|
|
33
|
+
/** 一次性注入样式,重复 apply 不叠加。 */
|
|
34
|
+
function installStyles() {
|
|
35
|
+
const id = "dsh-lighthouse-styles"
|
|
36
|
+
if (document.getElementById(id)) return
|
|
37
|
+
const node = document.createElement("style")
|
|
38
|
+
node.id = id
|
|
39
|
+
node.textContent = `
|
|
40
|
+
.lh-root { padding: 18px 22px 40px; max-width: 900px; margin: 0 auto; color: inherit;
|
|
41
|
+
box-sizing: border-box; height: 100%; overflow-y: auto; }
|
|
42
|
+
.lh-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
|
|
43
|
+
.lh-title { font-size: 18px; font-weight: 650; margin: 0; }
|
|
44
|
+
.lh-sub { font-size: 12px; opacity: .62; }
|
|
45
|
+
.lh-btn { margin-left: auto; font-size: 12px; padding: 4px 10px; border-radius: 7px; cursor: pointer;
|
|
46
|
+
border: 1px solid rgba(128,128,128,.35); background: rgba(128,128,128,.08); color: inherit; }
|
|
47
|
+
.lh-btn:hover { background: rgba(128,128,128,.16); }
|
|
48
|
+
.lh-btn:disabled { opacity: .5; cursor: default; }
|
|
49
|
+
.lh-tiles { display: flex; gap: 10px; flex-wrap: wrap; margin: 14px 0 18px; }
|
|
50
|
+
.lh-tile { flex: 1 1 96px; min-width: 96px; border: 1px solid rgba(128,128,128,.22); border-radius: 10px; padding: 9px 12px; }
|
|
51
|
+
.lh-tile b { display: block; font-size: 19px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.25; }
|
|
52
|
+
.lh-tile span { font-size: 11px; opacity: .62; }
|
|
53
|
+
.lh-card { border: 1px solid rgba(128,128,128,.22); border-left-width: 3px; border-radius: 10px;
|
|
54
|
+
padding: 11px 14px; margin-bottom: 10px; }
|
|
55
|
+
.lh-card.ok { border-left-color: #3fb950; }
|
|
56
|
+
.lh-card.wait { border-left-color: #d29922; }
|
|
57
|
+
.lh-card.bad { border-left-color: #f85149; }
|
|
58
|
+
.lh-row { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
|
|
59
|
+
.lh-name { font-size: 13.5px; font-weight: 650; font-family: ui-monospace, Consolas, monospace; }
|
|
60
|
+
.lh-zh { font-size: 11.5px; opacity: .72; }
|
|
61
|
+
.lh-link { color: inherit; text-decoration: none; border-bottom: 1px dashed rgba(128,128,128,.55); }
|
|
62
|
+
.lh-link:hover { color: #58a6ff; border-bottom-color: #58a6ff; }
|
|
63
|
+
.lh-tag { font-size: 10.5px; opacity: .58; border: 1px solid rgba(128,128,128,.3); border-radius: 999px; padding: 0 7px; }
|
|
64
|
+
.lh-line { font-size: 12.5px; margin-top: 4px; display: flex; gap: 12px; flex-wrap: wrap; }
|
|
65
|
+
.lh-k { opacity: .58; }
|
|
66
|
+
.lh-num { font-variant-numeric: tabular-nums; font-weight: 600; }
|
|
67
|
+
.lh-state { font-size: 12px; }
|
|
68
|
+
.lh-dim { opacity: .55; }
|
|
69
|
+
.lh-err { color: #f85149; font-size: 12.5px; margin: 8px 0; }
|
|
70
|
+
.lh-empty { opacity: .6; font-size: 13px; padding: 24px 0; text-align: center; }
|
|
71
|
+
.lh-fx { position: fixed; inset: 0; width: 100vw; height: 100vh;
|
|
72
|
+
pointer-events: none; z-index: 9999; }
|
|
73
|
+
`
|
|
74
|
+
document.head.appendChild(node)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** 小时 → 人话。 */
|
|
78
|
+
function hoursText(hours) {
|
|
79
|
+
if (!Number.isFinite(hours)) return "?"
|
|
80
|
+
if (hours < 1) return Math.round(hours * 60) + " 分钟"
|
|
81
|
+
if (hours < 48) return hours.toFixed(1) + " 小时"
|
|
82
|
+
return (hours / 24).toFixed(1) + " 天"
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** ISO 时间 → MM-DD HH:mm。 */
|
|
86
|
+
function shortTime(iso) {
|
|
87
|
+
if (typeof iso !== "string" || iso === "") return "—"
|
|
88
|
+
const d = new Date(iso)
|
|
89
|
+
if (Number.isNaN(d.getTime())) return "—"
|
|
90
|
+
const p = (n) => String(n).padStart(2, "0")
|
|
91
|
+
return p(d.getMonth() + 1) + "-" + p(d.getDate()) + " " + p(d.getHours()) + ":" + p(d.getMinutes())
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* --------------------------------------------------------------- 取数 */
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 拉一次状态。只读接口,失败就把原因原样显示出来(不编数字)。
|
|
98
|
+
*/
|
|
99
|
+
function useStatus() {
|
|
100
|
+
const [state, setState] = useState({ loading: true, report: null, error: null, at: 0 })
|
|
101
|
+
const load = useCallback(function () {
|
|
102
|
+
setState(function (prev) { return { ...prev, loading: true } })
|
|
103
|
+
fetch(ROUTE, { credentials: "same-origin", cache: "no-store" })
|
|
104
|
+
.then(function (res) { return res.json() })
|
|
105
|
+
.then(function (json) {
|
|
106
|
+
if (json && json.ok === true) {
|
|
107
|
+
setState({ loading: false, report: json.report, error: null, at: Date.now() })
|
|
108
|
+
} else {
|
|
109
|
+
const why = (json && json.errors ? json.errors.join(";") : (json && json.error) || "读不到")
|
|
110
|
+
setState({ loading: false, report: null, error: why, at: Date.now() })
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
.catch(function (err) {
|
|
114
|
+
setState({ loading: false, report: null, error: String((err && err.message) || err), at: Date.now() })
|
|
115
|
+
})
|
|
116
|
+
}, [])
|
|
117
|
+
useEffect(function () { load() }, [load])
|
|
118
|
+
return { ...state, reload: load }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* --------------------------------------------------------------- 组件 */
|
|
122
|
+
|
|
123
|
+
/** 侧栏图标:一座灯塔。 */
|
|
124
|
+
function PanelIcon() {
|
|
125
|
+
return h("svg", {
|
|
126
|
+
width: 16, height: 16, viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true",
|
|
127
|
+
},
|
|
128
|
+
h("path", { d: "M7.1 1.2h1.8v1.5l1.1.6.7 2.9H5.3l.7-2.9 1.1-.6V1.2z" }),
|
|
129
|
+
h("path", { d: "M1.6 4.3 4 5.5l-.5 1.1L1 5.5l.6-1.2zm12.8 0 .6 1.2-2.5 1.1-.5-1.1 2.4-1.2z" }),
|
|
130
|
+
h("path", { d: "M5.1 7.4h5.8l1.3 7.4H3.8l1.3-7.4z" }),
|
|
131
|
+
h("path", { d: "M6.2 9.2h3.6v1.1H6.2V9.2z", fill: "rgba(0,0,0,.35)" })
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** 一个数字格。 */
|
|
136
|
+
function Tile(props) {
|
|
137
|
+
return h("div", { className: "lh-tile" },
|
|
138
|
+
h("b", null, String(props.value)),
|
|
139
|
+
h("span", null, props.label)
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 一个作品一张卡。**状态用图标说话**:
|
|
145
|
+
* 已发布 ✅;收录/PR 还在路上 ⏳;读不到 ❌(并写出原因)。
|
|
146
|
+
*/
|
|
147
|
+
function WorkCard(props) {
|
|
148
|
+
const item = props.item
|
|
149
|
+
const work = item.work || {}
|
|
150
|
+
const local = item.local || {}
|
|
151
|
+
const gh = item.github || {}
|
|
152
|
+
const market = item.market || {}
|
|
153
|
+
const released = gh.exists === true
|
|
154
|
+
const listed = market.listed === true
|
|
155
|
+
const pr = gh.pr
|
|
156
|
+
const cls = listed ? "lh-card ok" : (released ? "lh-card wait" : "lh-card bad")
|
|
157
|
+
|
|
158
|
+
const stateLine = []
|
|
159
|
+
stateLine.push(h("span", { key: "rel", className: "lh-state" },
|
|
160
|
+
released ? "✅ 已发布" : (gh.exists === false ? "❌ 仓库不存在" : "❌ 读不到仓库")))
|
|
161
|
+
stateLine.push(h("span", { key: "mk", className: "lh-state" },
|
|
162
|
+
listed
|
|
163
|
+
? "✅ 市场已收录" + (market.downloads === null || market.downloads === undefined ? "(下载数暂无)" : "(下载 " + market.downloads + ")")
|
|
164
|
+
: (market.available === true ? "⏳ 收录中" : "❌ 市场读不到")))
|
|
165
|
+
if (pr) {
|
|
166
|
+
const waited = (Date.now() - Date.parse(pr.createdAt)) / 3600000
|
|
167
|
+
stateLine.push(h("span", { key: "pr", className: "lh-state" },
|
|
168
|
+
pr.mergedAt
|
|
169
|
+
? "✅ PR #" + pr.number + " 已合并"
|
|
170
|
+
: (pr.state === "open" ? "⏳ PR #" + pr.number + " 等 " + hoursText(waited) : "· PR #" + pr.number + " " + pr.state)))
|
|
171
|
+
} else {
|
|
172
|
+
stateLine.push(h("span", { key: "nopr", className: "lh-state lh-dim" }, "· 没有收录 PR"))
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 中文名(名册 nameZh 优先)+ 直达链接(GitHub;有市场条目页就再给一个)
|
|
176
|
+
const zh = (work && work.nameZh) || (local && local.displayName) || ""
|
|
177
|
+
const link = (item.links && item.links.github) || null
|
|
178
|
+
const marketLink = (item.links && item.links.market) || null
|
|
179
|
+
|
|
180
|
+
return h("div", { className: cls },
|
|
181
|
+
h("div", { className: "lh-row" },
|
|
182
|
+
link
|
|
183
|
+
? h("a", { className: "lh-name lh-link", href: link, target: "_blank", rel: "noreferrer" }, work.name || "?")
|
|
184
|
+
: h("span", { className: "lh-name" }, work.name || "?"),
|
|
185
|
+
zh ? h("span", { className: "lh-zh" }, zh) : null,
|
|
186
|
+
work.category ? h("span", { className: "lh-tag" }, work.category) : null,
|
|
187
|
+
marketLink
|
|
188
|
+
? h("a", { className: "lh-zh lh-link", href: marketLink, target: "_blank", rel: "noreferrer", title: "市场条目页" }, "市场 →")
|
|
189
|
+
: null,
|
|
190
|
+
h("span", { className: "lh-sub", style: { marginLeft: "auto" } },
|
|
191
|
+
"本地 " + (local.available === true ? (local.version || "?") + " · " + (local.files || 0) + " 文件" : "读不到"))
|
|
192
|
+
),
|
|
193
|
+
h("div", { className: "lh-line" },
|
|
194
|
+
h("span", null, h("span", { className: "lh-k" }, "⭐ 星 "), h("span", { className: "lh-num" }, String(gh.stars || 0))),
|
|
195
|
+
h("span", null, h("span", { className: "lh-k" }, "👁 关注 "), h("span", { className: "lh-num" }, String(gh.watchers || 0))),
|
|
196
|
+
h("span", null, h("span", { className: "lh-k" }, "💬 留言 "), h("span", { className: "lh-num" }, String(gh.openIssues || 0))),
|
|
197
|
+
h("span", null, h("span", { className: "lh-k" }, "最后推送 "), h("span", null, shortTime(gh.pushedAt)))
|
|
198
|
+
),
|
|
199
|
+
h("div", { className: "lh-line" }, stateLine)
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** 观察区主体。 */
|
|
204
|
+
function WatchPanel() {
|
|
205
|
+
const { loading, report, error, at, reload } = useStatus()
|
|
206
|
+
const s = (report && report.summary) || null
|
|
207
|
+
|
|
208
|
+
/* ---------------- 刷新粒子特效:跟重启坞同款的粒子海浪 ---------------- */
|
|
209
|
+
const [fxOn, setFxOn] = useState(false)
|
|
210
|
+
const fxEndRef = useRef(null)
|
|
211
|
+
const fxCanvasRef = useRef(null)
|
|
212
|
+
|
|
213
|
+
function playFx(after) {
|
|
214
|
+
fxEndRef.current = typeof after === "function" ? after : null
|
|
215
|
+
setFxOn(true)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 调试入口:不用点刷新也能放一次特效
|
|
219
|
+
useEffect(function () {
|
|
220
|
+
const trigger = function () { playFx() }
|
|
221
|
+
window.__dshLighthouseFx = trigger
|
|
222
|
+
return function () {
|
|
223
|
+
if (window.__dshLighthouseFx === trigger) delete window.__dshLighthouseFx
|
|
224
|
+
}
|
|
225
|
+
}, [])
|
|
226
|
+
|
|
227
|
+
useEffect(function () {
|
|
228
|
+
if (!fxOn) return undefined
|
|
229
|
+
const canvas = fxCanvasRef.current
|
|
230
|
+
if (!canvas) { setFxOn(false); return undefined }
|
|
231
|
+
const dpr = Math.min(window.devicePixelRatio || 1, 2)
|
|
232
|
+
const W = window.innerWidth
|
|
233
|
+
const H = window.innerHeight
|
|
234
|
+
canvas.width = Math.round(W * dpr)
|
|
235
|
+
canvas.height = Math.round(H * dpr)
|
|
236
|
+
const ctx = canvas.getContext("2d")
|
|
237
|
+
if (!ctx) { setFxOn(false); return undefined }
|
|
238
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
|
|
239
|
+
|
|
240
|
+
const DURATION = 2000
|
|
241
|
+
const TAU = Math.PI * 2
|
|
242
|
+
const diag = Math.hypot(W, H)
|
|
243
|
+
const dirX = W / diag
|
|
244
|
+
const dirY = H / diag
|
|
245
|
+
const STEP = 22
|
|
246
|
+
const KEEP = 0.22
|
|
247
|
+
const parts = []
|
|
248
|
+
for (let y = -STEP; y < H + STEP; y += STEP) {
|
|
249
|
+
for (let x = -STEP; x < W + STEP; x += STEP) {
|
|
250
|
+
if (Math.random() > KEEP) continue
|
|
251
|
+
parts.push({
|
|
252
|
+
x: x + (Math.random() - 0.5) * STEP * 0.9,
|
|
253
|
+
y: y + (Math.random() - 0.5) * STEP * 0.9,
|
|
254
|
+
s: (x * dirX + y * dirY) / diag,
|
|
255
|
+
jitter: 0.55 + Math.random() * 0.45,
|
|
256
|
+
hue: 194 + Math.random() * 42,
|
|
257
|
+
})
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
let raf = 0
|
|
262
|
+
let finished = false
|
|
263
|
+
const begin = performance.now()
|
|
264
|
+
|
|
265
|
+
const frame = function (now) {
|
|
266
|
+
if (finished) return
|
|
267
|
+
const t = Math.min(1, (now - begin) / DURATION)
|
|
268
|
+
if (t >= 1) {
|
|
269
|
+
finished = true
|
|
270
|
+
ctx.clearRect(0, 0, W, H)
|
|
271
|
+
const cb = fxEndRef.current
|
|
272
|
+
fxEndRef.current = null
|
|
273
|
+
setFxOn(false)
|
|
274
|
+
if (typeof cb === "function") cb()
|
|
275
|
+
return
|
|
276
|
+
}
|
|
277
|
+
ctx.clearRect(0, 0, W, H)
|
|
278
|
+
ctx.globalCompositeOperation = "lighter"
|
|
279
|
+
const travel = t * 3
|
|
280
|
+
for (const q of parts) {
|
|
281
|
+
const phase = q.s * TAU * 2.2 - travel * TAU * 0.55
|
|
282
|
+
const wave =
|
|
283
|
+
Math.sin(phase) * 0.62 +
|
|
284
|
+
Math.sin(phase * 1.93 + 1.7) * 0.26 +
|
|
285
|
+
Math.sin(phase * 0.47 + 3.1) * 0.12
|
|
286
|
+
if (wave <= 0) continue
|
|
287
|
+
const lum = Math.pow(wave, 1.9) * q.jitter
|
|
288
|
+
if (lum <= 0.02) continue
|
|
289
|
+
const depth = 0.75 + q.s * 0.55
|
|
290
|
+
const size = (1.1 + lum * 6.4) * depth
|
|
291
|
+
const light = 58 + lum * 38
|
|
292
|
+
const arm = size * (1.7 + lum * 1.7)
|
|
293
|
+
const thick = Math.max(1.0, size * 0.46)
|
|
294
|
+
if (lum > 0.35) {
|
|
295
|
+
ctx.globalAlpha = Math.min(0.34, lum * 0.3)
|
|
296
|
+
ctx.fillStyle = "hsl(" + q.hue + " 100% 78%)"
|
|
297
|
+
ctx.beginPath()
|
|
298
|
+
ctx.arc(q.x, q.y, arm * 0.85, 0, TAU)
|
|
299
|
+
ctx.fill()
|
|
300
|
+
}
|
|
301
|
+
ctx.globalAlpha = Math.min(1, lum * 1.75)
|
|
302
|
+
ctx.fillStyle = "hsl(" + q.hue + " 96% " + light + "%)"
|
|
303
|
+
ctx.fillRect(q.x - arm, q.y - thick / 2, arm * 2, thick)
|
|
304
|
+
ctx.fillRect(q.x - thick / 2, q.y - arm, thick, arm * 2)
|
|
305
|
+
ctx.beginPath()
|
|
306
|
+
ctx.arc(q.x, q.y, thick * 1.15, 0, TAU)
|
|
307
|
+
ctx.fill()
|
|
308
|
+
}
|
|
309
|
+
ctx.globalAlpha = 1
|
|
310
|
+
ctx.globalCompositeOperation = "source-over"
|
|
311
|
+
raf = requestAnimationFrame(frame)
|
|
312
|
+
}
|
|
313
|
+
raf = requestAnimationFrame(frame)
|
|
314
|
+
return function () {
|
|
315
|
+
finished = true
|
|
316
|
+
cancelAnimationFrame(raf)
|
|
317
|
+
}
|
|
318
|
+
}, [fxOn])
|
|
319
|
+
|
|
320
|
+
const body = []
|
|
321
|
+
if (error !== null) {
|
|
322
|
+
body.push(h("div", { className: "lh-err", key: "err" }, "读不到:" + error))
|
|
323
|
+
}
|
|
324
|
+
if (s !== null) {
|
|
325
|
+
body.push(h("div", { className: "lh-tiles", key: "tiles" },
|
|
326
|
+
h(Tile, { key: "w", value: s.works, label: "作品" }),
|
|
327
|
+
h(Tile, { key: "p", value: s.published, label: "已发布" }),
|
|
328
|
+
h(Tile, { key: "l", value: s.listed, label: "市场已收录" }),
|
|
329
|
+
h(Tile, { key: "d", value: s.totalDownloads, label: "总下载" }),
|
|
330
|
+
h(Tile, { key: "pr", value: s.openPrs, label: "待合 PR" })
|
|
331
|
+
))
|
|
332
|
+
const list = Array.isArray(report.works) ? report.works : []
|
|
333
|
+
for (const item of list) body.push(h(WorkCard, { key: item.work && item.work.name, item }))
|
|
334
|
+
if (list.length === 0) body.push(h("div", { className: "lh-empty", key: "empty" }, "名册里还没有作品"))
|
|
335
|
+
}
|
|
336
|
+
if (report === null && error === null && loading === true) {
|
|
337
|
+
body.push(h("div", { className: "lh-empty", key: "loading" }, "读取中…"))
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return h("div", { className: "lh-root" },
|
|
341
|
+
fxOn ? h("canvas", { className: "lh-fx", ref: fxCanvasRef }) : null,
|
|
342
|
+
h("div", { className: "lh-head" },
|
|
343
|
+
h("h1", { className: "lh-title" }, "🚢 作品观察区"),
|
|
344
|
+
h("span", { className: "lh-sub" },
|
|
345
|
+
at ? "更新于 " + shortTime(new Date(at).toISOString())
|
|
346
|
+
: (report && report.generatedAt ? "数据 " + shortTime(report.generatedAt) : "")),
|
|
347
|
+
h("button", {
|
|
348
|
+
className: "lh-btn",
|
|
349
|
+
onClick: function () { if (loading === true) return; playFx(reload) },
|
|
350
|
+
disabled: loading === true,
|
|
351
|
+
type: "button",
|
|
352
|
+
},
|
|
353
|
+
loading === true ? "刷新中…" : "刷新")
|
|
354
|
+
),
|
|
355
|
+
h("div", { className: "lh-sub", style: { marginTop: "6px" } },
|
|
356
|
+
report && report.market
|
|
357
|
+
? "市场登记表:" + (report.market.available === true
|
|
358
|
+
? (report.market.count || "?") + " 条(更新于 " + (report.market.updated || "?") + ")"
|
|
359
|
+
: "读不到(" + (report.market.reason || "?") + ")")
|
|
360
|
+
: "全部数字来自 GitHub API 与本机市场接口,不经模型生成。"),
|
|
361
|
+
body
|
|
362
|
+
)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/* --------------------------------------------------------------- 挂载 */
|
|
366
|
+
|
|
367
|
+
function apply(ctx) {
|
|
368
|
+
installStyles()
|
|
369
|
+
// 侧栏图标:id 就是 main 面板的 key,两者必须一致
|
|
370
|
+
ctx.slots.inject("sidebar.panellist", function () {
|
|
371
|
+
return ctx.slots.register({
|
|
372
|
+
name: "sidebar.panellist",
|
|
373
|
+
id: PANEL_ID,
|
|
374
|
+
order: 60,
|
|
375
|
+
label: function () { return "作品观察区" },
|
|
376
|
+
}, PanelIcon)
|
|
377
|
+
})
|
|
378
|
+
// 中间那一屏
|
|
379
|
+
ctx.slots.inject("main", function () {
|
|
380
|
+
return ctx.slots.register({ name: "main", key: PANEL_ID }, WatchPanel)
|
|
381
|
+
})
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
exports.name = name
|
|
385
|
+
exports.inject = inject
|
|
386
|
+
exports.apply = apply
|
|
387
|
+
return module.exports
|
|
388
|
+
}})
|
package/cordis.patch.yml
ADDED