dsh-plugin 1.6.11 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/client/client.js +705 -803
- package/client/client.js.map +1 -1
- package/lib/http/catalog.js +2 -3
- package/lib/http/diagnostics.js +6 -7
- package/lib/http/install.js +47 -213
- package/lib/http/logs.js +1 -2
- package/lib/http/npm-release-age.js +42 -0
- package/lib/http/routes.js +8 -7
- package/lib/http/settings.js +2 -2
- package/lib/http/shared.js +2 -2
- package/lib/http/task.js +1 -2
- package/lib/index.js +5 -6
- package/lib/services/install/install-types.js +4 -4
- package/lib/services/install/install.js +1 -1
- package/lib/services/install/npm-check.js +3 -2
- package/lib/services/install/npm-resolve.js +9 -96
- package/lib/services/install/package-entry.js +6 -11
- package/lib/services/install/registry-check.js +5 -16
- package/lib/services/install/release-target.js +8 -9
- package/lib/services/install/task-queue.js +74 -57
- package/lib/services/npm-release-age.js +119 -0
- package/lib/services/probe.js +4 -4
- package/lib/services/profile/installed-versions.js +15 -21
- package/lib/services/profile/profile.js +16 -44
- package/lib/services/profile/progress.js +8 -11
- package/lib/services/settings.js +2 -5
- package/lib/types/http/catalog.d.ts +1 -1
- package/lib/types/http/install.d.ts +2 -2
- package/lib/types/http/npm-release-age.d.ts +2 -0
- package/lib/types/http/routes.d.ts +10 -12
- package/lib/types/http/shared.d.ts +5 -6
- package/lib/types/index.d.ts +7 -8
- package/lib/types/services/install/install-types.d.ts +12 -13
- package/lib/types/services/install/install.d.ts +1 -1
- package/lib/types/services/install/npm-resolve.d.ts +4 -16
- package/lib/types/services/install/package-entry.d.ts +6 -11
- package/lib/types/services/install/release-target.d.ts +7 -8
- package/lib/types/services/install/task-queue.d.ts +32 -31
- package/lib/types/services/npm-release-age.d.ts +15 -0
- package/lib/types/services/probe.d.ts +4 -4
- package/lib/types/services/profile/installed-versions.d.ts +5 -7
- package/lib/types/services/profile/profile.d.ts +14 -18
- package/lib/types/services/profile/progress.d.ts +7 -10
- package/lib/types/services/settings.d.ts +0 -4
- package/package.json +1 -1
- package/src/client/components/PluginHubSection.tsx +23 -27
- package/src/client/components/catalog/CatalogList.tsx +2 -4
- package/src/client/components/catalog/PluginCard.tsx +2 -3
- package/src/client/components/layout/CatalogControls.tsx +5 -6
- package/src/client/components/layout/CatalogHeader.tsx +1 -2
- package/src/client/components/layout/CategoryTabs.tsx +2 -3
- package/src/client/components/layout/SectionTabs.tsx +3 -5
- package/src/client/components/modals/ConfirmDialog.tsx +4 -6
- package/src/client/components/modals/InstalledDetailModal.tsx +3 -6
- package/src/client/components/modals/LogsModal.tsx +7 -12
- package/src/client/components/modals/NotificationsModal.tsx +52 -24
- package/src/client/components/modals/ProgressView.tsx +3 -4
- package/src/client/components/modals/modals.tsx +58 -30
- package/src/client/components/ui/Dropdown.tsx +4 -6
- package/src/client/components/ui/Toggle.tsx +2 -3
- package/src/client/components/ui/icons.tsx +1 -2
- package/src/client/components/views/CustomInstallView.tsx +20 -178
- package/src/client/components/views/DiagnosticsView.tsx +3 -6
- package/src/client/components/views/InstalledView.tsx +6 -11
- package/src/client/components/views/MarketView.tsx +2 -4
- package/src/client/components/views/SettingsView.tsx +9 -32
- package/src/client/css-modules.d.ts +1 -1
- package/src/client/data/catalog.ts +2 -3
- package/src/client/data/host.ts +26 -3
- package/src/client/data/hub.ts +2 -3
- package/src/client/hooks/useCatalog.ts +79 -24
- package/src/client/hooks/useIncrementalList.ts +3 -5
- package/src/client/hooks/useLanguage.ts +4 -6
- package/src/client/hooks/useSettings.ts +4 -11
- package/src/client/hooks/useTaskQueue.ts +36 -42
- package/src/client/index.tsx +2 -3
- package/src/client/locales.ts +96 -123
- package/src/client/logic/constants.ts +1 -2
- package/src/client/logic/ensure-plugin-css.ts +8 -12
- package/src/client/logic/failures.ts +41 -28
- package/src/client/logic/format.ts +1 -1
- package/src/client/logic/install-command.ts +14 -35
- package/src/client/logic/installed.ts +15 -10
- package/src/client/logic/normalize.ts +3 -3
- package/src/client/logic/release-age.ts +19 -0
- package/src/client/logic/urls.ts +55 -50
- package/src/client/styles/CustomInstallView.module.css +3 -30
- package/src/client/styles/DiagnosticsView.module.css +3 -5
- package/src/client/styles/Dropdown.module.css +2 -4
- package/src/client/styles/Header.module.css +6 -7
- package/src/client/styles/InstalledView.module.css +4 -7
- package/src/client/styles/List.module.css +4 -5
- package/src/client/styles/LogsView.module.css +2 -3
- package/src/client/styles/Modal.module.css +4 -5
- package/src/client/styles/SectionTabs.module.css +2 -4
- package/src/client/styles/SettingsView.module.css +3 -5
- package/src/client/styles/Toggle.module.css +2 -4
- package/src/client/styles/tokens.module.css +14 -18
- package/src/client/types.ts +11 -11
- package/src/server/http/catalog.ts +2 -3
- package/src/server/http/diagnostics.ts +6 -7
- package/src/server/http/install.ts +47 -208
- package/src/server/http/logs.ts +1 -2
- package/src/server/http/npm-release-age.ts +42 -0
- package/src/server/http/routes.ts +12 -12
- package/src/server/http/settings.ts +2 -2
- package/src/server/http/shared.ts +5 -6
- package/src/server/http/task.ts +1 -2
- package/src/server/index.ts +7 -8
- package/src/server/services/install/install-types.ts +12 -13
- package/src/server/services/install/install.ts +0 -1
- package/src/server/services/install/npm-check.ts +3 -2
- package/src/server/services/install/npm-resolve.ts +9 -99
- package/src/server/services/install/package-entry.ts +6 -11
- package/src/server/services/install/registry-check.ts +5 -16
- package/src/server/services/install/release-target.ts +8 -9
- package/src/server/services/install/task-queue.ts +79 -63
- package/src/server/services/npm-release-age.ts +107 -0
- package/src/server/services/probe.ts +4 -4
- package/src/server/services/profile/installed-versions.ts +16 -22
- package/src/server/services/profile/profile.ts +16 -42
- package/src/server/services/profile/progress.ts +8 -11
- package/src/server/services/settings.ts +2 -9
- package/lib/services/install/preflight.js +0 -178
- package/lib/types/services/install/preflight.d.ts +0 -23
- package/src/server/services/install/preflight.ts +0 -184
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Kept visually distinct from the category chips so the hierarchy reads:
|
|
10
|
-
* section → category → plugin.
|
|
6
|
+
* 一级导航:紧凑分段控件,在四个顶层板块间切换 —— 市场(浏览)、已安装(管理)、
|
|
7
|
+
* 自定义(手动命令安装)与设置(网络源与偏好)。与分类 chip 视觉上区分开,
|
|
8
|
+
* 让层级清晰:板块 → 分类 → 插件。
|
|
11
9
|
*/
|
|
12
10
|
import { createElement as h } from 'react'
|
|
13
11
|
import styles from '../../styles/SectionTabs.module.css'
|
|
@@ -3,13 +3,11 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* in-button double-click.
|
|
6
|
+
* 通用确认弹窗 —— 应用里所有「确定吗?」问题的唯一入口。规则:任何询问都必须是
|
|
7
|
+
* 弹窗,绝不用按钮内双击。
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* action glyph for destructive ones (clear / delete / uninstall / reset).
|
|
9
|
+
* 中间的图标一眼说明类型:常规确认(安装/更新/重启/忽略)用蓝色问号,
|
|
10
|
+
* 危险操作(清空/删除/卸载/重置)用红色动作图形。
|
|
13
11
|
*/
|
|
14
12
|
import { createElement as h } from 'react'
|
|
15
13
|
import type { MouseEvent } from 'react'
|
|
@@ -3,12 +3,9 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* latest version) merged with the host runtime facts (install path, installed
|
|
10
|
-
* version, install time, last update). Update / uninstall actions are
|
|
11
|
-
* offered from here too, mirroring the row actions.
|
|
6
|
+
* 已安装插件详情弹窗:从「已安装」视图打开(行点击或详情按钮),展示我们对某个已安装项
|
|
7
|
+
* 掌握的全部信息 —— 目录元数据(分类、收录入口、Star/Fork、最新版本)与宿主运行时事实
|
|
8
|
+
* (安装路径、已装版本、安装时间、最后更新)合并呈现。此处的更新/卸载动作与行内动作一致。
|
|
12
9
|
*/
|
|
13
10
|
import { createElement as h } from 'react'
|
|
14
11
|
import type { MouseEvent } from 'react'
|
|
@@ -3,20 +3,15 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* ~/.dsh/profiles/<profile>/hub.log) straight from the server, one page at
|
|
9
|
-
* a time.
|
|
6
|
+
* 内置日志查看器弹窗:无需外部软件 —— 应用自己开窗,从服务端直接分页加载本地系统日志
|
|
7
|
+
* (JSONL,位于 ~/.dsh/profiles/<profile>/hub.log)。
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* say, only install errors — so the exact incompatible step of an install
|
|
15
|
-
* (or uninstall) is easy to spot. Search narrows by event code or message.
|
|
9
|
+
* 每行双重归类:功能域(安装/卸载/更新/诊断/设置/系统)与严重级别(debug/info/success/
|
|
10
|
+
* warn/error)。两轴筛选可只盯某类,比如只看安装错误 —— 便于定位安装(或卸载)中真正
|
|
11
|
+
* 卡住的那一步;搜索按事件码或消息缩小范围。
|
|
16
12
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* real file location and reveals it in the system file manager.
|
|
13
|
+
* 服务端分页:GET /dsh-plugin-hub/logs?offset=&limit=&category=&level=&query=
|
|
14
|
+
* → { entries, total, hasMore, path }。底部展示日志真实路径并可在系统文件管理器中定位。
|
|
20
15
|
*/
|
|
21
16
|
import { createElement as h, useCallback, useEffect, useRef, useState } from 'react'
|
|
22
17
|
import type { MouseEvent } from 'react'
|
|
@@ -3,22 +3,18 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* remove task — successes and failures alike.
|
|
6
|
+
* 通知中心弹窗:每个已结算的安装/卸载任务(成功与失败)的持久化清单。
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* success, red cross for failure) with white glyph and message text;
|
|
13
|
-
* failures keep their copy / fix / file-an-issue actions. Opened from the
|
|
14
|
-
* header entry button (after the Settings tab).
|
|
8
|
+
* 记录在结算时写入 localStorage(见 logic/failures.ts),即使弹窗被关闭或用户离开,
|
|
9
|
+
* 结果也不会丢失。每条带圆形状态徽标(成功绿勾、失败红叉,白色图形)与消息文本;
|
|
10
|
+
* 失败项保留复制/修复/提 Issue 动作。由头部入口按钮(「设置」tab 之后)打开。
|
|
15
11
|
*/
|
|
16
12
|
import { createElement as h, useState } from 'react'
|
|
17
13
|
import type { MouseEvent } from 'react'
|
|
18
14
|
import styles from '../../styles/Modal.module.css'
|
|
19
15
|
import type { EnvInfo, Translate } from '../../types.ts'
|
|
20
16
|
import type { NotificationRecord } from '../../logic/failures.ts'
|
|
21
|
-
import { classifyFailure, missingRegistryPackageOf, npmTooLowVersion, pnpmPolicyHintOf, pnpmPolicyHintParams, registryMissingHintOf, unreachableTargetOf, registryHostOf } from '../../logic/failures.ts'
|
|
17
|
+
import { actionOfRecord, classifyFailure, missingRegistryPackageOf, npmTooLowVersion, pnpmPolicyHintOf, pnpmPolicyHintParams, registryMissingHintOf, unreachableTargetOf, registryHostOf } from '../../logic/failures.ts'
|
|
22
18
|
import type { PendingRestart, QueueTask } from '../../hooks/useTaskQueue.ts'
|
|
23
19
|
import { pluginIssueUrl, pluginSiteUrl, hubIssueUrl } from '../../logic/urls.ts'
|
|
24
20
|
import { CloseIcon } from '../ui/icons.tsx'
|
|
@@ -255,6 +251,9 @@ export function NotificationsModal({ records, tasks, pendingRestarts, t, env, on
|
|
|
255
251
|
// 卡片不展示错误日志预览,完整日志靠「复制完整日志」按钮带走,避免每条卡片被日志撑高
|
|
256
252
|
!r.ok && (() => {
|
|
257
253
|
const kind = classifyFailure(r.message)
|
|
254
|
+
// 动作词:该条提示与上报正文按真实动作取词
|
|
255
|
+
const action = actionOfRecord(r)
|
|
256
|
+
const actionWord = t(action === 'uninstall' ? 'actionUninstall' : action === 'update' ? 'actionUpdate' : 'actionInstall')
|
|
258
257
|
if (kind === 'npmTooOld') {
|
|
259
258
|
// npm 内部崩溃(edgesOut)+ 本机 npm 版本过低:npm 自身已知缺陷,不是插件问题 ——
|
|
260
259
|
// 直接给升级指引,不引导提 Issue
|
|
@@ -324,7 +323,7 @@ export function NotificationsModal({ records, tasks, pendingRestarts, t, env, on
|
|
|
324
323
|
h('div', { className: styles.failPrepareHint }, t('failPluginMissingDepHint')),
|
|
325
324
|
r.repo && shownRepo ? h('a', {
|
|
326
325
|
className: styles.failBigIssue,
|
|
327
|
-
href: pluginIssueUrl(shownRepo, r.message, env, r.command, r.attempts),
|
|
326
|
+
href: pluginIssueUrl(shownRepo, r.message, env, r.command, r.attempts, action),
|
|
328
327
|
target: '_blank',
|
|
329
328
|
rel: 'noopener noreferrer',
|
|
330
329
|
title: t('failIssueHint'),
|
|
@@ -333,7 +332,7 @@ export function NotificationsModal({ records, tasks, pendingRestarts, t, env, on
|
|
|
333
332
|
}
|
|
334
333
|
if (kind === 'registryVersionStale') {
|
|
335
334
|
// 要装的版本在「实际执行安装的那个源」里查不到(ERR_PNPM_NO_MATCHING_VERSION / ETARGET):
|
|
336
|
-
// 常见于镜像滞后 ——
|
|
335
|
+
// 常见于镜像滞后 —— 实际安装沿用本机 ~/.npmrc 的源;
|
|
337
336
|
// 也可能只是版本号写错。与插件作者无关 —— 给对齐源 / 显式走官方源 / 离线包指引,不引导提 Issue
|
|
338
337
|
return h('div', { className: styles.failPrepareHint }, t('failRegistryVersionStaleHint'))
|
|
339
338
|
}
|
|
@@ -364,7 +363,7 @@ export function NotificationsModal({ records, tasks, pendingRestarts, t, env, on
|
|
|
364
363
|
return h('div', { className: styles.failPrepareHint }, t('failOriginRejectedHint', { profile: env?.profile ?? '<profile>' }))
|
|
365
364
|
}
|
|
366
365
|
if (kind === 'network') {
|
|
367
|
-
//
|
|
366
|
+
// 安装前网络探测 / 安装日志里的连接失败([network] / git fetch 超时 / DNS / TLS):
|
|
368
367
|
// 是本机网络不通或代理有问题,不是插件问题 —— 提示检查网络 + 直达「系统诊断」,
|
|
369
368
|
// 不引导提 Issue
|
|
370
369
|
return h('div', null, [
|
|
@@ -373,7 +372,7 @@ export function NotificationsModal({ records, tasks, pendingRestarts, t, env, on
|
|
|
373
372
|
// registry 被指向内网/自定义源(tarball 从非官方源下载失败):直接给出换源指引,
|
|
374
373
|
// 否则用户外网是通的会困惑「为什么连不上」
|
|
375
374
|
(() => { const host = registryHostOf(r.message); return host ? h('div', { className: styles.failPrepareHint }, t('failNetworkRegistryHint', { host })) : null })(),
|
|
376
|
-
h('div', { className: styles.failPrepareHint }, t('failNetworkHint')),
|
|
375
|
+
h('div', { className: styles.failPrepareHint }, t('failNetworkHint', { action: actionWord })),
|
|
377
376
|
onRunDiagnostics ? h('button', {
|
|
378
377
|
className: styles.failDiagBtn,
|
|
379
378
|
onClick: onRunDiagnostics,
|
|
@@ -381,24 +380,24 @@ export function NotificationsModal({ records, tasks, pendingRestarts, t, env, on
|
|
|
381
380
|
])
|
|
382
381
|
}
|
|
383
382
|
if (kind === 'pluginPrepare') {
|
|
384
|
-
// [packaging]
|
|
383
|
+
// [packaging](装后校验拦截:分发缺产物)与
|
|
385
384
|
// prepare 构建脚本实际执行失败:唯一确有证据的插件打包分发问题 —— 引导到插件仓库提 Issue
|
|
386
385
|
return h('div', null, [
|
|
387
386
|
h('div', { className: styles.failPrepareHint }, /\[packaging\]/i.test(r.message) ? t('failPackagingHint') : t('failPrepareHint')),
|
|
388
387
|
r.repo && shownRepo ? h('a', {
|
|
389
388
|
className: styles.failBigIssue,
|
|
390
|
-
href: pluginIssueUrl(shownRepo, r.message, env, r.command, r.attempts),
|
|
389
|
+
href: pluginIssueUrl(shownRepo, r.message, env, r.command, r.attempts, action),
|
|
391
390
|
target: '_blank',
|
|
392
391
|
rel: 'noopener noreferrer',
|
|
393
392
|
title: t('failIssueHint'),
|
|
394
393
|
}, t('failIssueBig')) : null,
|
|
395
|
-
// 次要入口:这条分支的判据([packaging]
|
|
394
|
+
// 次要入口:这条分支的判据([packaging] 装后校验、prepare 摘要)是插件中心
|
|
396
395
|
// 自己产出的,不是 pnpm 原文 —— 判错时用户会被送去作者仓库、我们永远收不到反馈
|
|
397
396
|
r.repo && shownRepo ? h('div', null, [
|
|
398
397
|
h('div', { className: styles.failPrepareHint }, t('failMisjudgeHint')),
|
|
399
398
|
h('a', {
|
|
400
399
|
className: styles.failMisjudgeLink,
|
|
401
|
-
href: hubIssueUrl(shownRepo, r.message, env, r.command, r.attempts, 'misjudge'),
|
|
400
|
+
href: hubIssueUrl(shownRepo, r.message, env, r.command, r.attempts, 'misjudge', action),
|
|
402
401
|
target: '_blank',
|
|
403
402
|
rel: 'noopener noreferrer',
|
|
404
403
|
title: t('failHubIssueHint'),
|
|
@@ -409,7 +408,7 @@ export function NotificationsModal({ records, tasks, pendingRestarts, t, env, on
|
|
|
409
408
|
if (kind === 'hubState') {
|
|
410
409
|
// 插件中心的列表状态已过期(安装器自身守卫消息):非插件问题、也不是 bug,只给可行动提示,不引导提 Issue
|
|
411
410
|
return h('div', null, [
|
|
412
|
-
h('div', { className: styles.failPrepareHint }, t('failHubStateHint')),
|
|
411
|
+
h('div', { className: styles.failPrepareHint }, t('failHubStateHint', { action: actionWord })),
|
|
413
412
|
])
|
|
414
413
|
}
|
|
415
414
|
if (kind === 'timeout') {
|
|
@@ -421,17 +420,46 @@ export function NotificationsModal({ records, tasks, pendingRestarts, t, env, on
|
|
|
421
420
|
if (kind === 'profileLock') {
|
|
422
421
|
// DSH 写 profile 的锁超时(同一 profile 被别的 dsh/安装任务占着):只给可行动提示,不引导提 Issue
|
|
423
422
|
return h('div', null, [
|
|
424
|
-
h('div', { className: styles.failPrepareHint }, t('failProfileLockHint')),
|
|
423
|
+
h('div', { className: styles.failPrepareHint }, t('failProfileLockHint', { action: actionWord })),
|
|
425
424
|
])
|
|
426
425
|
}
|
|
427
|
-
|
|
426
|
+
if (kind === 'hostCliSpawn') {
|
|
427
|
+
// 宿主自身入口起不来:环境问题,给提示、不引导提 Issue
|
|
428
|
+
return h('div', null, [
|
|
429
|
+
h('div', { className: styles.failPrepareHint }, t('failHostCliSpawnHint')),
|
|
430
|
+
])
|
|
431
|
+
}
|
|
432
|
+
if (kind === 'pnpmIgnoredBuild') {
|
|
433
|
+
// 插件的依赖需要在用户机器上编译原生模块,pnpm 默认拦住;我们已自动放行
|
|
434
|
+
// allowBuilds 并重试过一次,仍失败说明任何用户都装不上 —— 属作者侧(发预构建产物 / 走 release 包)
|
|
435
|
+
return h('div', null, [
|
|
436
|
+
h('div', { className: styles.failPrepareHint }, t('failIgnoredBuild')),
|
|
437
|
+
r.repo && shownRepo ? h('a', {
|
|
438
|
+
className: styles.failBigIssue,
|
|
439
|
+
href: pluginIssueUrl(shownRepo, r.message, env, r.command, r.attempts, action),
|
|
440
|
+
target: '_blank',
|
|
441
|
+
rel: 'noopener noreferrer',
|
|
442
|
+
title: t('failIssueHint'),
|
|
443
|
+
}, t('failIssueBig')) : null,
|
|
444
|
+
// 这条判定里也有我们的动作(自动放行过 allowBuilds),判错时得留一条回到插件中心的路
|
|
445
|
+
r.repo && shownRepo ? h('div', null, [
|
|
446
|
+
h('div', { className: styles.failPrepareHint }, t('failMisjudgeHint')),
|
|
447
|
+
h('a', {
|
|
448
|
+
className: styles.failMisjudgeLink,
|
|
449
|
+
href: hubIssueUrl(shownRepo, r.message, env, r.command, r.attempts, 'misjudge', action),
|
|
450
|
+
target: '_blank',
|
|
451
|
+
rel: 'noopener noreferrer',
|
|
452
|
+
title: t('failHubIssueHint'),
|
|
453
|
+
}, t('failMisjudgeLink')),
|
|
454
|
+
]) : null,
|
|
455
|
+
])
|
|
456
|
+
}
|
|
457
|
+
// 未知失败:不归到插件作者,改为引导到插件中心仓库排查
|
|
428
458
|
return h('div', null, [
|
|
429
|
-
|
|
430
|
-
? h('div', { className: styles.failPrepareHint }, t('failIgnoredBuild'))
|
|
431
|
-
: h('div', { className: styles.failPrepareHint }, t('failUnclassifiedHint')),
|
|
459
|
+
h('div', { className: styles.failPrepareHint }, t('failUnclassifiedHint', { action: actionWord })),
|
|
432
460
|
h('a', {
|
|
433
461
|
className: styles.failBigIssue,
|
|
434
|
-
href: hubIssueUrl(shownRepo || r.repo, r.message, env, r.command, r.attempts),
|
|
462
|
+
href: hubIssueUrl(shownRepo || r.repo, r.message, env, r.command, r.attempts, 'triage', action),
|
|
435
463
|
target: '_blank',
|
|
436
464
|
rel: 'noopener noreferrer',
|
|
437
465
|
title: t('failHubIssueHint'),
|
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* empty for most of the run; failures surface their full output in ErrorModal.
|
|
6
|
+
* 安装与卸载弹窗共用的内联进度块:真实百分比进度条(服务端估算、客户端轮询)。
|
|
7
|
+
* 不做实时终端窗口 —— pnpm 在 git clone/构建期间几乎无输出,日志面板大部分时间是空的;
|
|
8
|
+
* 失败时完整输出在 ErrorModal 里展示。
|
|
10
9
|
*/
|
|
11
10
|
import { createElement as h } from 'react'
|
|
12
11
|
import styles from '../../styles/Modal.module.css'
|
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* the common case (the page refreshes on close so the change applies), or a
|
|
10
|
-
* single "restart now" for the rare changes a refresh cannot pick up.
|
|
6
|
+
* 插件中心的弹窗层:安装确认弹窗、卸载弹窗与全局 Toast。两个弹窗在变更执行期间
|
|
7
|
+
* 自我锁定,随后切到只有一个动作的结果视图 —— 常见情况给「完成」(关闭时刷新页面即生效),
|
|
8
|
+
* 刷新解决不了的少数情况才给单个「立即重启」。
|
|
11
9
|
*/
|
|
12
10
|
import { createElement as h } from 'react'
|
|
13
11
|
import type { MouseEvent } from 'react'
|
|
@@ -18,6 +16,7 @@ import { ProgressView } from './ProgressView.tsx'
|
|
|
18
16
|
import { installCommandOf } from '../../logic/install-command.ts'
|
|
19
17
|
import { pluginDetailUrl, pluginIssueUrl, pluginSiteUrl, hubIssueUrl } from '../../logic/urls.ts'
|
|
20
18
|
import { classifyFailure, missingRegistryPackageOf, npmTooLowVersion, pnpmPolicyHintOf, pnpmPolicyHintParams, registryMissingHintOf, unreachableTargetOf, registryHostOf } from '../../logic/failures.ts'
|
|
19
|
+
import type { FailureAction } from '../../logic/failures.ts'
|
|
21
20
|
|
|
22
21
|
/** 完成结果视图:绿色对勾 + 标题/描述;
|
|
23
22
|
* 终态只有两种:
|
|
@@ -336,14 +335,13 @@ export function UninstallModal(props: UninstallModalProps) {
|
|
|
336
335
|
)
|
|
337
336
|
}
|
|
338
337
|
|
|
339
|
-
/**
|
|
340
|
-
|
|
341
|
-
export function ErrorModal({ message, repo, kind, command, attempts, t, env, onCopy, onClose, onRunDiagnostics }: {
|
|
338
|
+
/** 安装/更新/卸载失败弹窗:标题、徽标、提示文案与 issue 正文都按传入的动作取词。 */
|
|
339
|
+
export function ErrorModal({ message, repo, action, command, attempts, t, env, onCopy, onClose, onRunDiagnostics }: {
|
|
342
340
|
message: string
|
|
343
341
|
repo: string | null
|
|
344
|
-
|
|
342
|
+
action: FailureAction
|
|
345
343
|
command?: string
|
|
346
|
-
/**
|
|
344
|
+
/** 实际执行/重试过的安装命令:issue 预填一并贴给作者 */
|
|
347
345
|
attempts?: string[]
|
|
348
346
|
t: Translate
|
|
349
347
|
env: EnvInfo | null
|
|
@@ -352,8 +350,10 @@ export function ErrorModal({ message, repo, kind, command, attempts, t, env, onC
|
|
|
352
350
|
/** 网络不通时的「去系统诊断」直达按钮:跳到设置 → 系统诊断跑连通性检测(由宿主提供) */
|
|
353
351
|
onRunDiagnostics?: () => void
|
|
354
352
|
}) {
|
|
355
|
-
//
|
|
353
|
+
// 失败归类:装不上 = 插件分发/依赖的问题,一律引导提 Issue
|
|
356
354
|
const failureKind = classifyFailure(message)
|
|
355
|
+
// 动作词:标题、徽标与提示里的 {action} 统一取这一个词
|
|
356
|
+
const actionWord = t(action === 'uninstall' ? 'actionUninstall' : action === 'update' ? 'actionUpdate' : 'actionInstall')
|
|
357
357
|
// 服务端 [npm-too-low] 标记里带的本机 npm 版本(无标记为 null,提示文案降级为「怀疑」)
|
|
358
358
|
const npmVersion = npmTooLowVersion(message)
|
|
359
359
|
// 复制完整错误:实际执行/尝试过的安装命令(如有)+ 完整错误正文。
|
|
@@ -372,8 +372,9 @@ export function ErrorModal({ message, repo, kind, command, attempts, t, env, onC
|
|
|
372
372
|
},
|
|
373
373
|
h('div', { className: styles.errorModal, role: 'dialog', 'aria-modal': 'true' },
|
|
374
374
|
h('div', { className: styles.modalHead },
|
|
375
|
-
//
|
|
376
|
-
h('div', { className: styles.errorTitle },
|
|
375
|
+
// 标题按动作明确区分:安装失败 / 更新失败 / 卸载失败
|
|
376
|
+
h('div', { className: styles.errorTitle },
|
|
377
|
+
t(action === 'uninstall' ? 'errorTitleUninstall' : action === 'update' ? 'errorTitleUpdate' : 'errorTitleInstall')),
|
|
377
378
|
h('button', {
|
|
378
379
|
className: styles.modalClose,
|
|
379
380
|
'aria-label': t('errorClose'),
|
|
@@ -385,8 +386,8 @@ export function ErrorModal({ message, repo, kind, command, attempts, t, env, onC
|
|
|
385
386
|
h('div', { className: styles.failRow },
|
|
386
387
|
h('div', { className: styles.failHead },
|
|
387
388
|
h('span', {
|
|
388
|
-
className:
|
|
389
|
-
},
|
|
389
|
+
className: action === 'install' ? styles.failKindInstall : styles.failKindUninstall,
|
|
390
|
+
}, actionWord),
|
|
390
391
|
// 仓库地址(用户名/仓库)统一显示为可点击超链接,跳转到官网详情页(含插件收录信息),而非直接跳 GitHub
|
|
391
392
|
repo ? h('a', {
|
|
392
393
|
className: styles.failRepo,
|
|
@@ -458,7 +459,7 @@ export function ErrorModal({ message, repo, kind, command, attempts, t, env, onC
|
|
|
458
459
|
h('div', { className: styles.failPrepareHint }, t('failPluginMissingDepHint')),
|
|
459
460
|
repo ? h('a', {
|
|
460
461
|
className: styles.failBigIssue,
|
|
461
|
-
href: pluginIssueUrl(repo, message, env, command, attempts),
|
|
462
|
+
href: pluginIssueUrl(repo, message, env, command, attempts, action),
|
|
462
463
|
target: '_blank',
|
|
463
464
|
rel: 'noopener noreferrer',
|
|
464
465
|
title: t('failIssueHint'),
|
|
@@ -466,7 +467,7 @@ export function ErrorModal({ message, repo, kind, command, attempts, t, env, onC
|
|
|
466
467
|
])
|
|
467
468
|
: failureKind === 'registryVersionStale'
|
|
468
469
|
// 要装的版本在「实际执行安装的那个源」里查不到(ERR_PNPM_NO_MATCHING_VERSION / ETARGET):
|
|
469
|
-
// 常见于镜像滞后 ——
|
|
470
|
+
// 常见于镜像滞后 —— 实际安装沿用本机 ~/.npmrc 的源,
|
|
470
471
|
// 官方源刚发布而镜像还没同步;也可能只是版本号写错。与插件作者无关 ——
|
|
471
472
|
// 给「对齐源 / 那一次显式走官方源 / 用离线包」的指引,不引导提 Issue
|
|
472
473
|
? h('div', { className: styles.failPrepareHint }, t('failRegistryVersionStaleHint'))
|
|
@@ -489,7 +490,7 @@ export function ErrorModal({ message, repo, kind, command, attempts, t, env, onC
|
|
|
489
490
|
// 安装流程,与插件无关 —— 引导从本机地址重新打开市场,不引导提 Issue
|
|
490
491
|
? h('div', { className: styles.failPrepareHint }, t('failOriginRejectedHint', { profile: env?.profile ?? '<profile>' }))
|
|
491
492
|
: failureKind === 'network'
|
|
492
|
-
//
|
|
493
|
+
// 安装前网络探测 / 安装日志里的连接失败([network] / git fetch 超时 / DNS / TLS):
|
|
493
494
|
// 是本机网络不通或代理有问题,不是插件问题 —— 提示检查网络 + 直达「系统诊断」,
|
|
494
495
|
// 不引导提 Issue
|
|
495
496
|
? h('div', null, [
|
|
@@ -498,7 +499,7 @@ export function ErrorModal({ message, repo, kind, command, attempts, t, env, onC
|
|
|
498
499
|
// registry 被指向内网/自定义源(tarball 从非官方源下载失败):直接给出换源指引,
|
|
499
500
|
// 否则用户外网是通的会困惑「为什么连不上」
|
|
500
501
|
(() => { const host = registryHostOf(message); return host ? h('div', { className: styles.failPrepareHint }, t('failNetworkRegistryHint', { host })) : null })(),
|
|
501
|
-
h('div', { className: styles.failPrepareHint }, t('failNetworkHint')),
|
|
502
|
+
h('div', { className: styles.failPrepareHint }, t('failNetworkHint', { action: actionWord })),
|
|
502
503
|
onRunDiagnostics ? h('button', {
|
|
503
504
|
className: styles.failDiagBtn,
|
|
504
505
|
onClick: onRunDiagnostics,
|
|
@@ -506,24 +507,24 @@ export function ErrorModal({ message, repo, kind, command, attempts, t, env, onC
|
|
|
506
507
|
])
|
|
507
508
|
: failureKind === 'pluginPrepare'
|
|
508
509
|
? h('div', null, [
|
|
509
|
-
// [packaging]
|
|
510
|
+
// [packaging](装后校验拦截:分发缺产物)与
|
|
510
511
|
// prepare 构建脚本实际执行失败:唯一确有证据的插件打包分发问题 —— 引导到插件仓库提 Issue
|
|
511
512
|
h('div', { className: styles.failPrepareHint }, /\[packaging\]/i.test(message) ? t('failPackagingHint') : t('failPrepareHint')),
|
|
512
513
|
repo ? h('a', {
|
|
513
514
|
className: styles.failBigIssue,
|
|
514
|
-
href: pluginIssueUrl(repo, message, env, command, attempts),
|
|
515
|
+
href: pluginIssueUrl(repo, message, env, command, attempts, action),
|
|
515
516
|
target: '_blank',
|
|
516
517
|
rel: 'noopener noreferrer',
|
|
517
518
|
title: t('failIssueHint'),
|
|
518
519
|
}, t('failIssueBig')) : null,
|
|
519
|
-
// 次要入口:这条分支的判据([packaging]
|
|
520
|
+
// 次要入口:这条分支的判据([packaging] 装后校验、prepare 摘要)是插件中心
|
|
520
521
|
// 自己产出的,不是 pnpm 原文 —— 判错时用户会被送去作者仓库、我们永远收不到反馈,
|
|
521
522
|
// 故另给一条「可能是市场误判」的反馈通道(正文自我说明它来自哪条分支)
|
|
522
523
|
repo ? h('div', null, [
|
|
523
524
|
h('div', { className: styles.failPrepareHint }, t('failMisjudgeHint')),
|
|
524
525
|
h('a', {
|
|
525
526
|
className: styles.failMisjudgeLink,
|
|
526
|
-
href: hubIssueUrl(repo, message, env, command, attempts, 'misjudge'),
|
|
527
|
+
href: hubIssueUrl(repo, message, env, command, attempts, 'misjudge', action),
|
|
527
528
|
target: '_blank',
|
|
528
529
|
rel: 'noopener noreferrer',
|
|
529
530
|
title: t('failHubIssueHint'),
|
|
@@ -533,7 +534,7 @@ export function ErrorModal({ message, repo, kind, command, attempts, t, env, onC
|
|
|
533
534
|
: failureKind === 'hubState'
|
|
534
535
|
? h('div', null, [
|
|
535
536
|
// 插件中心的列表状态已过期(安装器自身守卫消息):不是插件问题、也不是 bug,只给可行动提示,不引导提 Issue
|
|
536
|
-
h('div', { className: styles.failPrepareHint }, t('failHubStateHint')),
|
|
537
|
+
h('div', { className: styles.failPrepareHint }, t('failHubStateHint', { action: actionWord })),
|
|
537
538
|
])
|
|
538
539
|
: failureKind === 'timeout'
|
|
539
540
|
? h('div', null, [
|
|
@@ -543,16 +544,43 @@ export function ErrorModal({ message, repo, kind, command, attempts, t, env, onC
|
|
|
543
544
|
: failureKind === 'profileLock'
|
|
544
545
|
? h('div', null, [
|
|
545
546
|
// DSH 写 profile 的锁超时(同一 profile 被别的 dsh/安装任务占着):给可行动提示,不引导提 Issue
|
|
546
|
-
h('div', { className: styles.failPrepareHint }, t('failProfileLockHint')),
|
|
547
|
+
h('div', { className: styles.failPrepareHint }, t('failProfileLockHint', { action: actionWord })),
|
|
548
|
+
])
|
|
549
|
+
: failureKind === 'hostCliSpawn'
|
|
550
|
+
? h('div', null, [
|
|
551
|
+
// 宿主自身入口起不来:环境问题,给提示、不引导提 Issue
|
|
552
|
+
h('div', { className: styles.failPrepareHint }, t('failHostCliSpawnHint')),
|
|
553
|
+
])
|
|
554
|
+
: failureKind === 'pnpmIgnoredBuild'
|
|
555
|
+
// 该类型的插件依赖需要在用户机器上编译原生模块,pnpm 默认拦住;我们已自动放行
|
|
556
|
+
// allowBuilds 并重试过一次,仍失败说明任何用户都装不上 —— 属作者侧(发预构建产物 / 走 release 包)。
|
|
557
|
+
? h('div', null, [
|
|
558
|
+
h('div', { className: styles.failPrepareHint }, t('failIgnoredBuild')),
|
|
559
|
+
repo ? h('a', {
|
|
560
|
+
className: styles.failBigIssue,
|
|
561
|
+
href: pluginIssueUrl(repo, message, env, command, attempts, action),
|
|
562
|
+
target: '_blank',
|
|
563
|
+
rel: 'noopener noreferrer',
|
|
564
|
+
title: t('failIssueHint'),
|
|
565
|
+
}, t('failIssueBig')) : null,
|
|
566
|
+
// 这条判定里也有我们的动作(我们自动放行过 allowBuilds),判错时得留一条回到插件中心的路
|
|
567
|
+
repo ? h('div', null, [
|
|
568
|
+
h('div', { className: styles.failPrepareHint }, t('failMisjudgeHint')),
|
|
569
|
+
h('a', {
|
|
570
|
+
className: styles.failMisjudgeLink,
|
|
571
|
+
href: hubIssueUrl(repo, message, env, command, attempts, 'misjudge', action),
|
|
572
|
+
target: '_blank',
|
|
573
|
+
rel: 'noopener noreferrer',
|
|
574
|
+
title: t('failHubIssueHint'),
|
|
575
|
+
}, t('failMisjudgeLink')),
|
|
576
|
+
]) : null,
|
|
547
577
|
])
|
|
548
578
|
: h('div', null, [
|
|
549
|
-
//
|
|
550
|
-
|
|
551
|
-
? h('div', { className: styles.failPrepareHint }, t('failIgnoredBuild'))
|
|
552
|
-
: h('div', { className: styles.failPrepareHint }, t('failUnclassifiedHint')),
|
|
579
|
+
// 未分类(hub):不归到插件作者,改为引导到插件中心仓库分诊
|
|
580
|
+
h('div', { className: styles.failPrepareHint }, t('failUnclassifiedHint', { action: actionWord })),
|
|
553
581
|
h('a', {
|
|
554
582
|
className: styles.failBigIssue,
|
|
555
|
-
href: hubIssueUrl(repo, message, env, command, attempts),
|
|
583
|
+
href: hubIssueUrl(repo, message, env, command, attempts, 'triage', action),
|
|
556
584
|
target: '_blank',
|
|
557
585
|
rel: 'noopener noreferrer',
|
|
558
586
|
title: t('failHubIssueHint'),
|
|
@@ -3,13 +3,11 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* 按钮式下拉框(替代原生 <select>)。
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* outside or pressing Esc closes the panel; options get a hover fill and
|
|
12
|
-
* the active one a brand highlight, all driven by the --hub-* tokens.
|
|
8
|
+
* 触发器是紧凑的工具栏按钮 —— 细边框 + 淡灰填充 + 当前值 + 箭头 —— 与其它工具栏按钮
|
|
9
|
+
* 并排自然,只占一格横向空间。点击外部或按 Esc 关闭面板;选项有 hover 填充、
|
|
10
|
+
* 激活项为品牌高亮,全部由 --hub-* 变量驱动。
|
|
13
11
|
*/
|
|
14
12
|
import { createElement as h, useEffect, useRef, useState } from 'react'
|
|
15
13
|
import type { JSX } from 'react'
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* by the --hub-* tokens (on = brand fill, off = neutral track).
|
|
6
|
+
* 设置行用的紧凑开关。基于原生 button,可键盘聚焦并通过 aria-checked 播报状态;
|
|
7
|
+
* 样式由 --hub-* 变量驱动(开 = 品牌填充,关 = 中性轨道)。
|
|
9
8
|
*/
|
|
10
9
|
import { createElement as h } from 'react'
|
|
11
10
|
import styles from '../../styles/Toggle.module.css'
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* Website: https://dsh-plugin.org
|
|
4
4
|
* GitHub: https://github.com/dshplugin/dsh-plugin-hub
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* they adapt to themes and disabled states automatically.
|
|
6
|
+
* 客户端各处使用的内联 SVG 图标。图标继承 currentColor,自动适配主题与禁用态。
|
|
8
7
|
*/
|
|
9
8
|
import { createElement as h } from 'react'
|
|
10
9
|
import headerStyles from '../../styles/Header.module.css'
|