dshmarket 1.66.26 → 1.66.28
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/client/client.js +135 -25
- package/package.json +1 -1
- package/src/client/MarketSection.tsx +65 -13
- package/src/client/locales.ts +4 -2
- package/src/client/market-data.ts +36 -0
package/client/client.js
CHANGED
|
@@ -510,6 +510,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
510
510
|
confirmInstall: "确认安装",
|
|
511
511
|
cmdDetails: "安装命令",
|
|
512
512
|
cmdCopy: "复制安装命令",
|
|
513
|
+
cmdDesktopUnavailable: "桌面端的插件由应用自己安装(用上面的「安装」按钮);`dsh plugin` 不接受桌面端这个 profile。",
|
|
513
514
|
cmdCopied: "已复制",
|
|
514
515
|
catsMore: "更多分类",
|
|
515
516
|
catsLess: "收起",
|
|
@@ -523,7 +524,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
523
524
|
hostDetecting: "正在识别宿主版本…",
|
|
524
525
|
hostUnknown: "宿主版本未知",
|
|
525
526
|
hostFilterLoading: "正在检查宿主要求 {0}/{1};未声明或无法确认的插件仍会显示",
|
|
526
|
-
hostFilterActive: "
|
|
527
|
+
hostFilterActive: "已隐藏 {1} 个与 DSH {0} 不兼容的插件;未声明版本的仍在列表里",
|
|
527
528
|
hostRequirement: "DSH {0}",
|
|
528
529
|
hostRequirementLoading: "正在读取宿主要求…",
|
|
529
530
|
hostRequirementUndeclared: "未声明宿主要求",
|
|
@@ -1238,6 +1239,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
1238
1239
|
confirmInstall: "Confirm install",
|
|
1239
1240
|
cmdDetails: "Install command",
|
|
1240
1241
|
cmdCopy: "Copy install command",
|
|
1242
|
+
cmdDesktopUnavailable: "The desktop app installs plugins itself — use the Install button above; `dsh plugin` refuses this profile.",
|
|
1241
1243
|
cmdCopied: "Copied",
|
|
1242
1244
|
catsMore: "More categories",
|
|
1243
1245
|
catsLess: "Fewer categories",
|
|
@@ -1251,7 +1253,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
1251
1253
|
hostDetecting: "Detecting host version…",
|
|
1252
1254
|
hostUnknown: "Host version unknown",
|
|
1253
1255
|
hostFilterLoading: "Checking host requirements {0}/{1}; undeclared or unavailable plugins remain visible",
|
|
1254
|
-
hostFilterActive: "
|
|
1256
|
+
hostFilterActive: "Hid {1} plugins incompatible with DSH {0}; those declaring no version stay listed",
|
|
1255
1257
|
hostRequirement: "DSH {0}",
|
|
1256
1258
|
hostRequirementLoading: "Reading host requirement…",
|
|
1257
1259
|
hostRequirementUndeclared: "Host requirement undeclared",
|
|
@@ -1901,6 +1903,53 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
1901
1903
|
if (state.supervisor !== null) return "restartHintSupervised";
|
|
1902
1904
|
return "restartHint";
|
|
1903
1905
|
}
|
|
1906
|
+
/**
|
|
1907
|
+
* The install command shown on a plugin's card, corrected for THIS profile
|
|
1908
|
+
* (#872).
|
|
1909
|
+
*
|
|
1910
|
+
* awesome-dsh-plugin generates the string once, for the catalog — always
|
|
1911
|
+
* `dsh plugin --profile web add <spec>` — which is right on a `web` profile
|
|
1912
|
+
* and wrong on every other one. On a desktop profile it is more than wrong:
|
|
1913
|
+
* the DSH CLI refuses a profile by that name, so the command as printed
|
|
1914
|
+
* cannot install anything there at all. Two outcomes, and the caller renders
|
|
1915
|
+
* them differently because the user's next step differs:
|
|
1916
|
+
*
|
|
1917
|
+
* - `command`: the text to show and copy, with the profile it names corrected;
|
|
1918
|
+
* - `unusable`: show no command at all (the market's own button is the way),
|
|
1919
|
+
* because handing over a command that cannot work is worse than handing
|
|
1920
|
+
* over none.
|
|
1921
|
+
*
|
|
1922
|
+
* A command with no `--profile` token is passed through untouched: it is not
|
|
1923
|
+
* this function's business to add one.
|
|
1924
|
+
*/
|
|
1925
|
+
function installCommandFor(command, profile) {
|
|
1926
|
+
if (typeof command !== "string" || command.trim() === "") return {
|
|
1927
|
+
command: null,
|
|
1928
|
+
unusable: false
|
|
1929
|
+
};
|
|
1930
|
+
const named = /--profile(?:=|\s+)(\S+)/u.exec(command);
|
|
1931
|
+
if (named === null) return {
|
|
1932
|
+
command,
|
|
1933
|
+
unusable: false
|
|
1934
|
+
};
|
|
1935
|
+
const wanted = typeof profile === "string" ? profile.trim() : "";
|
|
1936
|
+
if (wanted === "") return {
|
|
1937
|
+
command,
|
|
1938
|
+
unusable: false
|
|
1939
|
+
};
|
|
1940
|
+
if (wanted.toLowerCase() === "desktop") return {
|
|
1941
|
+
command: null,
|
|
1942
|
+
unusable: true
|
|
1943
|
+
};
|
|
1944
|
+
if (named[1] === wanted) return {
|
|
1945
|
+
command,
|
|
1946
|
+
unusable: false
|
|
1947
|
+
};
|
|
1948
|
+
return {
|
|
1949
|
+
command: command.replace(named[0], `--profile ${wanted}`),
|
|
1950
|
+
unusable: false
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1904
1953
|
function groupSwitchState(members, disabled) {
|
|
1905
1954
|
const list = members ?? [];
|
|
1906
1955
|
if (list.length === 0) return "empty";
|
|
@@ -8258,6 +8307,21 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
8258
8307
|
const names = body.runningAgents.map(String).filter((name) => name !== "");
|
|
8259
8308
|
return names.length === 0 ? void 0 : names;
|
|
8260
8309
|
}
|
|
8310
|
+
/**
|
|
8311
|
+
* Whether the "compatible with this host" filter starts on, and remembers the
|
|
8312
|
+
* answer once the user disagrees with the default (#872).
|
|
8313
|
+
*
|
|
8314
|
+
* Storage may be unavailable (private mode, a locked-down webview); the
|
|
8315
|
+
* default is what matters, so a failure to read it is not a failure.
|
|
8316
|
+
*/
|
|
8317
|
+
const HOST_FILTER_KEY = "dshm-host-filter";
|
|
8318
|
+
function readHostFilterPreference() {
|
|
8319
|
+
try {
|
|
8320
|
+
return localStorage.getItem(HOST_FILTER_KEY) !== "all";
|
|
8321
|
+
} catch {
|
|
8322
|
+
return true;
|
|
8323
|
+
}
|
|
8324
|
+
}
|
|
8261
8325
|
function MarketSection(props) {
|
|
8262
8326
|
const t = props.t;
|
|
8263
8327
|
const initialWebdav = (0, react.useMemo)(savedWebdav, []);
|
|
@@ -8811,7 +8875,16 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
8811
8875
|
/** Undefined while the first catalog response is pending; null means the host cannot be located. */
|
|
8812
8876
|
const [hostVersion, setHostVersion] = (0, react.useState)(void 0);
|
|
8813
8877
|
/** v1 is deliberately opt-in: undeclared/unknown entries stay visible even when enabled. */
|
|
8814
|
-
const [compatibleWithHost,
|
|
8878
|
+
const [compatibleWithHost, setCompatibleWithHostState] = (0, react.useState)(readHostFilterPreference);
|
|
8879
|
+
const setCompatibleWithHost = (0, react.useCallback)((next) => {
|
|
8880
|
+
setCompatibleWithHostState(next);
|
|
8881
|
+
try {
|
|
8882
|
+
localStorage.setItem(HOST_FILTER_KEY, next ? "compatible" : "all");
|
|
8883
|
+
} catch {}
|
|
8884
|
+
}, []);
|
|
8885
|
+
/** The profile this page is running in, for the manual command's own
|
|
8886
|
+
* `--profile` token (#872). The catalog's is always `web`. */
|
|
8887
|
+
const [profileName, setProfileName] = (0, react.useState)(null);
|
|
8815
8888
|
const [hostCompatibility, setHostCompatibility] = (0, react.useState)({});
|
|
8816
8889
|
const [hostCompatibilityPending, setHostCompatibilityPending] = (0, react.useState)(0);
|
|
8817
8890
|
/** Names already resolved or in flight; failed/unavailable names are released for an explicit retry. */
|
|
@@ -8880,6 +8953,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
8880
8953
|
}
|
|
8881
8954
|
if (body.notes !== null && typeof body.notes === "object" && !Array.isArray(body.notes)) setNotes(body.notes);
|
|
8882
8955
|
if (Array.isArray(body.patchDisabled)) setPatchDisabledNames(body.patchDisabled);
|
|
8956
|
+
if (typeof body.profile === "string") setProfileName(body.profile);
|
|
8883
8957
|
if (Array.isArray(body.unbundled)) setUnbundledNames(body.unbundled);
|
|
8884
8958
|
setConfigNamespaceIssues(Array.isArray(body.configNamespaces) ? body.configNamespaces.filter((item) => typeof item === "object" && item !== null && typeof item.name === "string" && Array.isArray(item.namespaces)) : []);
|
|
8885
8959
|
if (body.groups && typeof body.groups === "object") setGroups(body.groups);
|
|
@@ -9125,6 +9199,10 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
9125
9199
|
(0, react.useEffect)(() => {
|
|
9126
9200
|
if (busyUrl === null && updatingName === null) {
|
|
9127
9201
|
setHostBusy(false);
|
|
9202
|
+
statusRef.current = {
|
|
9203
|
+
...statusRef.current,
|
|
9204
|
+
busy: false
|
|
9205
|
+
};
|
|
9128
9206
|
setProgressLine(null);
|
|
9129
9207
|
setProgressPhase(null);
|
|
9130
9208
|
setProgressCurrent(null);
|
|
@@ -9250,16 +9328,37 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
9250
9328
|
qInstalled,
|
|
9251
9329
|
installedView
|
|
9252
9330
|
]);
|
|
9253
|
-
const pluginsAll = (0, react.useMemo)(() =>
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9331
|
+
const { plugins: pluginsAll, hostHiddenCount } = (0, react.useMemo)(() => {
|
|
9332
|
+
if (data === null) return {
|
|
9333
|
+
plugins: [],
|
|
9334
|
+
hostHiddenCount: 0
|
|
9335
|
+
};
|
|
9336
|
+
const query = {
|
|
9337
|
+
category: cat,
|
|
9338
|
+
query: q,
|
|
9339
|
+
lang,
|
|
9340
|
+
categories: data.categories,
|
|
9341
|
+
sort: `${sortField}-${sortDir}`,
|
|
9342
|
+
sinceDays: timeRange === "all" ? void 0 : TIME_RANGE_DAYS[timeRange],
|
|
9343
|
+
hostCompatibility
|
|
9344
|
+
};
|
|
9345
|
+
const unfiltered = visiblePlugins(data.plugins, {
|
|
9346
|
+
...query,
|
|
9347
|
+
compatibleWithHost: false
|
|
9348
|
+
});
|
|
9349
|
+
if (!compatibleWithHost) return {
|
|
9350
|
+
plugins: unfiltered,
|
|
9351
|
+
hostHiddenCount: 0
|
|
9352
|
+
};
|
|
9353
|
+
const filtered = visiblePlugins(data.plugins, {
|
|
9354
|
+
...query,
|
|
9355
|
+
compatibleWithHost: true
|
|
9356
|
+
});
|
|
9357
|
+
return {
|
|
9358
|
+
plugins: filtered,
|
|
9359
|
+
hostHiddenCount: unfiltered.length - filtered.length
|
|
9360
|
+
};
|
|
9361
|
+
}, [
|
|
9263
9362
|
data,
|
|
9264
9363
|
q,
|
|
9265
9364
|
cat,
|
|
@@ -12871,7 +12970,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
12871
12970
|
})]
|
|
12872
12971
|
}), compatibleWithHost && typeof hostVersion === "string" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
12873
12972
|
className: Market_module_css_default.hostFilterNote,
|
|
12874
|
-
children: hostCompatibilityPending > 0 || loadedHostPackages < allHostPackages.length ? t("hostFilterLoading").replace("{0}", String(loadedHostPackages)).replace("{1}", String(allHostPackages.length)) : t("hostFilterActive").replace("{0}", hostVersion)
|
|
12973
|
+
children: hostCompatibilityPending > 0 || loadedHostPackages < allHostPackages.length ? t("hostFilterLoading").replace("{0}", String(loadedHostPackages)).replace("{1}", String(allHostPackages.length)) : t("hostFilterActive").replace("{0}", hostVersion).replace("{1}", String(hostHiddenCount))
|
|
12875
12974
|
})]
|
|
12876
12975
|
})]
|
|
12877
12976
|
}),
|
|
@@ -14124,17 +14223,28 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
14124
14223
|
title: t("cmdDetails"),
|
|
14125
14224
|
open: cmdOpen,
|
|
14126
14225
|
onToggle: () => setCmdOpen((o) => !o),
|
|
14127
|
-
children:
|
|
14128
|
-
|
|
14129
|
-
|
|
14130
|
-
className: Market_module_css_default.
|
|
14131
|
-
children:
|
|
14132
|
-
|
|
14133
|
-
|
|
14134
|
-
|
|
14135
|
-
|
|
14136
|
-
|
|
14137
|
-
|
|
14226
|
+
children: (() => {
|
|
14227
|
+
const shown = installCommandFor(confirming.install, profileName);
|
|
14228
|
+
if (shown.unusable) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
14229
|
+
className: Market_module_css_default.confirmCmd,
|
|
14230
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
14231
|
+
className: Market_module_css_default.cmd,
|
|
14232
|
+
children: t("cmdDesktopUnavailable")
|
|
14233
|
+
})
|
|
14234
|
+
});
|
|
14235
|
+
if (shown.command === null) return null;
|
|
14236
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
14237
|
+
className: Market_module_css_default.confirmCmd,
|
|
14238
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
14239
|
+
className: Market_module_css_default.cmd,
|
|
14240
|
+
children: shown.command
|
|
14241
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfirmCopyButton, {
|
|
14242
|
+
text: shown.command,
|
|
14243
|
+
label: t("cmdCopy"),
|
|
14244
|
+
copiedLabel: t("cmdCopied")
|
|
14245
|
+
})]
|
|
14246
|
+
});
|
|
14247
|
+
})()
|
|
14138
14248
|
})]
|
|
14139
14249
|
}),
|
|
14140
14250
|
(looksTerminal(confirming, lang) || confirming.deprecated === true) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dshmarket",
|
|
3
3
|
"description": "Visual plugin market inside DeepSeek Harness — browse, search, and one-click install community plugins. · DSH 可视化插件市场:逛一逛,点一下,装好。",
|
|
4
|
-
"version": "1.66.
|
|
4
|
+
"version": "1.66.28",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/types/index.d.ts",
|
|
@@ -51,6 +51,7 @@ import type { OperationRecord } from './operations.ts'
|
|
|
51
51
|
import { Diagnostics } from './Diagnostics.tsx'
|
|
52
52
|
import { exportMarketLog } from './self-check.ts'
|
|
53
53
|
import {
|
|
54
|
+
installCommandFor,
|
|
54
55
|
api, applyGithubRouting, avatarColor, catalogEntryForInstalled, entryForDep, failureText, githubRouteCandidates, groupSwitchState, installedForCatalog, isGenerationSpec, isInstalled, localizeBilingual, localizeBilingualList, looksTerminal, matchInstalledName, orderedCategories, pluginCategories,
|
|
55
56
|
formatCount, pageItems, pluginName, blockAliases, pluginScreenshotCandidates, pluginScreenshots, pluginsForFavorites, queuedRowApplies, rankThemeScreenshots, readSession, releaseNotesHttpsImage, rememberGithubRoute, resetScreenshotsCache, resolveCatalogRestore, safeScreenshots, sanitizeReleaseNotesBody, staleFavoriteUrls, themePlugins as themePluginsOf, themeSwatch, TIME_RANGE_DAYS, visiblePlugins, restartHintKey,
|
|
56
57
|
} from './market-data.ts'
|
|
@@ -1723,6 +1724,18 @@ function blockedSessions(body: { runningAgents?: unknown }): string[] | undefine
|
|
|
1723
1724
|
return names.length === 0 ? undefined : names
|
|
1724
1725
|
}
|
|
1725
1726
|
|
|
1727
|
+
/**
|
|
1728
|
+
* Whether the "compatible with this host" filter starts on, and remembers the
|
|
1729
|
+
* answer once the user disagrees with the default (#872).
|
|
1730
|
+
*
|
|
1731
|
+
* Storage may be unavailable (private mode, a locked-down webview); the
|
|
1732
|
+
* default is what matters, so a failure to read it is not a failure.
|
|
1733
|
+
*/
|
|
1734
|
+
const HOST_FILTER_KEY = 'dshm-host-filter'
|
|
1735
|
+
function readHostFilterPreference(): boolean {
|
|
1736
|
+
try { return localStorage.getItem(HOST_FILTER_KEY) !== 'all' } catch { return true }
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1726
1739
|
export function MarketSection(props: MarketSectionProps) {
|
|
1727
1740
|
const t = props.t
|
|
1728
1741
|
const initialWebdav = useMemo(savedWebdav, [])
|
|
@@ -2320,7 +2333,20 @@ export function MarketSection(props: MarketSectionProps) {
|
|
|
2320
2333
|
/** Undefined while the first catalog response is pending; null means the host cannot be located. */
|
|
2321
2334
|
const [hostVersion, setHostVersion] = useState<string | null | undefined>(undefined)
|
|
2322
2335
|
/** v1 is deliberately opt-in: undeclared/unknown entries stay visible even when enabled. */
|
|
2323
|
-
|
|
2336
|
+
// #872: ON by default. The report measured it on a live 0.2.0-rc.2 host —
|
|
2337
|
+
// 175 discovered plugins, 45 of them declaring that host — so the default
|
|
2338
|
+
// list was ~74% installable-in-name-only, with nothing at list level saying
|
|
2339
|
+
// so. That is the wrong default per this project's own rule: the default has
|
|
2340
|
+
// to be the one that is right without reading anything. Turning it off is
|
|
2341
|
+
// one click in the filter menu, and the choice sticks.
|
|
2342
|
+
const [compatibleWithHost, setCompatibleWithHostState] = useState(readHostFilterPreference)
|
|
2343
|
+
const setCompatibleWithHost = useCallback((next: boolean) => {
|
|
2344
|
+
setCompatibleWithHostState(next)
|
|
2345
|
+
try { localStorage.setItem(HOST_FILTER_KEY, next ? 'compatible' : 'all') } catch { /* storage unavailable — the choice just won't survive a reload */ }
|
|
2346
|
+
}, [])
|
|
2347
|
+
/** The profile this page is running in, for the manual command's own
|
|
2348
|
+
* `--profile` token (#872). The catalog's is always `web`. */
|
|
2349
|
+
const [profileName, setProfileName] = useState<string | null>(null)
|
|
2324
2350
|
const [hostCompatibility, setHostCompatibility] = useState<HostCompatibilityMap>({})
|
|
2325
2351
|
const [hostCompatibilityPending, setHostCompatibilityPending] = useState(0)
|
|
2326
2352
|
/** Names already resolved or in flight; failed/unavailable names are released for an explicit retry. */
|
|
@@ -2405,6 +2431,7 @@ export function MarketSection(props: MarketSectionProps) {
|
|
|
2405
2431
|
setNotes(body.notes as Record<string, string>)
|
|
2406
2432
|
}
|
|
2407
2433
|
if (Array.isArray(body.patchDisabled)) setPatchDisabledNames(body.patchDisabled)
|
|
2434
|
+
if (typeof body.profile === 'string') setProfileName(body.profile)
|
|
2408
2435
|
if (Array.isArray(body.unbundled)) setUnbundledNames(body.unbundled)
|
|
2409
2436
|
setConfigNamespaceIssues(Array.isArray(body.configNamespaces)
|
|
2410
2437
|
? body.configNamespaces.filter((item: unknown): item is { name: string; namespaces: string[]; ownEntryIds: string[] } =>
|
|
@@ -2700,6 +2727,12 @@ export function MarketSection(props: MarketSectionProps) {
|
|
|
2700
2727
|
// that stale sample; the guarded restart route still handles the small
|
|
2701
2728
|
// post-response lock-release window with its existing 409 retry.
|
|
2702
2729
|
setHostBusy(false)
|
|
2730
|
+
// The same sample is what `runQueuedNow` reads to decide whether the
|
|
2731
|
+
// host is free (#845 review). Left in place it answered 「另一个安装、
|
|
2732
|
+
// 更新或卸载还在进行」 about an idle host, for the up to 2 s until the
|
|
2733
|
+
// drain's next tick — and the idle poll that used to correct it is
|
|
2734
|
+
// exactly what #845 removed, so with no queued row it never came back.
|
|
2735
|
+
statusRef.current = { ...statusRef.current, busy: false }
|
|
2703
2736
|
setProgressLine(null)
|
|
2704
2737
|
setProgressPhase(null)
|
|
2705
2738
|
setProgressCurrent(null)
|
|
@@ -2830,14 +2863,23 @@ export function MarketSection(props: MarketSectionProps) {
|
|
|
2830
2863
|
setShowTop(false)
|
|
2831
2864
|
}, [tab, q, cat, sortField, sortDir, timeRange, compatibleWithHost, qThemes, themeSortField, themeSortDir, themeTimeRange, qFavorites, favSortField, favSortDir, favTimeRange, qInstalled, installedView])
|
|
2832
2865
|
|
|
2833
|
-
|
|
2834
|
-
|
|
2866
|
+
// The list, plus how many entries the host filter took out of it (#872).
|
|
2867
|
+
// Both come from one pass over the same query, so the count is exactly
|
|
2868
|
+
// "what this filter is hiding from what you were looking at" and the chip
|
|
2869
|
+
// can say it without a second trip through the catalog.
|
|
2870
|
+
const { plugins: pluginsAll, hostHiddenCount } = useMemo(() => {
|
|
2871
|
+
if (data === null) return { plugins: [] as RegistryPlugin[], hostHiddenCount: 0 }
|
|
2872
|
+
const query = {
|
|
2835
2873
|
category: cat, query: q, lang, categories: data.categories,
|
|
2836
|
-
sort: `${sortField}-${sortDir}`,
|
|
2874
|
+
sort: `${sortField}-${sortDir}` as `${typeof sortField}-${typeof sortDir}`,
|
|
2837
2875
|
sinceDays: timeRange === 'all' ? undefined : TIME_RANGE_DAYS[timeRange],
|
|
2838
2876
|
hostCompatibility,
|
|
2839
|
-
|
|
2840
|
-
})
|
|
2877
|
+
}
|
|
2878
|
+
const unfiltered = visiblePlugins(data.plugins, { ...query, compatibleWithHost: false })
|
|
2879
|
+
if (!compatibleWithHost) return { plugins: unfiltered, hostHiddenCount: 0 }
|
|
2880
|
+
const filtered = visiblePlugins(data.plugins, { ...query, compatibleWithHost: true })
|
|
2881
|
+
return { plugins: filtered, hostHiddenCount: unfiltered.length - filtered.length }
|
|
2882
|
+
},
|
|
2841
2883
|
[data, q, cat, lang, sortField, sortDir, timeRange, hostCompatibility, compatibleWithHost])
|
|
2842
2884
|
// One plugin can be stored under the installed name or the catalog name.
|
|
2843
2885
|
// Both have to hide the card and skip Update all (#657).
|
|
@@ -6234,7 +6276,9 @@ export function MarketSection(props: MarketSectionProps) {
|
|
|
6234
6276
|
? t('hostFilterLoading')
|
|
6235
6277
|
.replace('{0}', String(loadedHostPackages))
|
|
6236
6278
|
.replace('{1}', String(allHostPackages.length))
|
|
6237
|
-
: t('hostFilterActive')
|
|
6279
|
+
: t('hostFilterActive')
|
|
6280
|
+
.replace('{0}', hostVersion)
|
|
6281
|
+
.replace('{1}', String(hostHiddenCount))}
|
|
6238
6282
|
</div>
|
|
6239
6283
|
)}
|
|
6240
6284
|
</div>
|
|
@@ -7328,12 +7372,20 @@ export function MarketSection(props: MarketSectionProps) {
|
|
|
7328
7372
|
open={cmdOpen}
|
|
7329
7373
|
onToggle={() => setCmdOpen(o => !o)}
|
|
7330
7374
|
>
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
)
|
|
7336
|
-
|
|
7375
|
+
{(() => {
|
|
7376
|
+
// #872: the catalog's command names the `web` profile, which is
|
|
7377
|
+
// a different profile from the one this page is running in —
|
|
7378
|
+
// and on a desktop profile the CLI refuses that name outright.
|
|
7379
|
+
const shown = installCommandFor(confirming.install, profileName)
|
|
7380
|
+
if (shown.unusable) return <div className={css.confirmCmd}><div className={css.cmd}>{t('cmdDesktopUnavailable')}</div></div>
|
|
7381
|
+
if (shown.command === null) return null
|
|
7382
|
+
return (
|
|
7383
|
+
<div className={css.confirmCmd}>
|
|
7384
|
+
<div className={css.cmd}>{shown.command}</div>
|
|
7385
|
+
<ConfirmCopyButton text={shown.command} label={t('cmdCopy')} copiedLabel={t('cmdCopied')} />
|
|
7386
|
+
</div>
|
|
7387
|
+
)
|
|
7388
|
+
})()}
|
|
7337
7389
|
</ConfirmFold>
|
|
7338
7390
|
</div>
|
|
7339
7391
|
{(looksTerminal(confirming, lang) || confirming.deprecated === true) && (
|
package/src/client/locales.ts
CHANGED
|
@@ -316,6 +316,7 @@ export const zh = {
|
|
|
316
316
|
confirmInstall: '确认安装',
|
|
317
317
|
cmdDetails: '安装命令',
|
|
318
318
|
cmdCopy: '复制安装命令',
|
|
319
|
+
cmdDesktopUnavailable: '桌面端的插件由应用自己安装(用上面的「安装」按钮);`dsh plugin` 不接受桌面端这个 profile。',
|
|
319
320
|
cmdCopied: '已复制',
|
|
320
321
|
catsMore: '更多分类',
|
|
321
322
|
catsLess: '收起',
|
|
@@ -329,7 +330,7 @@ export const zh = {
|
|
|
329
330
|
hostDetecting: '正在识别宿主版本…',
|
|
330
331
|
hostUnknown: '宿主版本未知',
|
|
331
332
|
hostFilterLoading: '正在检查宿主要求 {0}/{1};未声明或无法确认的插件仍会显示',
|
|
332
|
-
hostFilterActive: '
|
|
333
|
+
hostFilterActive: '已隐藏 {1} 个与 DSH {0} 不兼容的插件;未声明版本的仍在列表里',
|
|
333
334
|
hostRequirement: 'DSH {0}',
|
|
334
335
|
hostRequirementLoading: '正在读取宿主要求…',
|
|
335
336
|
hostRequirementUndeclared: '未声明宿主要求',
|
|
@@ -1065,6 +1066,7 @@ export const en: Record<MarketKey, string> = {
|
|
|
1065
1066
|
confirmInstall: 'Confirm install',
|
|
1066
1067
|
cmdDetails: 'Install command',
|
|
1067
1068
|
cmdCopy: 'Copy install command',
|
|
1069
|
+
cmdDesktopUnavailable: 'The desktop app installs plugins itself — use the Install button above; `dsh plugin` refuses this profile.',
|
|
1068
1070
|
cmdCopied: 'Copied',
|
|
1069
1071
|
catsMore: 'More categories',
|
|
1070
1072
|
catsLess: 'Fewer categories',
|
|
@@ -1078,7 +1080,7 @@ export const en: Record<MarketKey, string> = {
|
|
|
1078
1080
|
hostDetecting: 'Detecting host version…',
|
|
1079
1081
|
hostUnknown: 'Host version unknown',
|
|
1080
1082
|
hostFilterLoading: 'Checking host requirements {0}/{1}; undeclared or unavailable plugins remain visible',
|
|
1081
|
-
hostFilterActive: '
|
|
1083
|
+
hostFilterActive: 'Hid {1} plugins incompatible with DSH {0}; those declaring no version stay listed',
|
|
1082
1084
|
hostRequirement: 'DSH {0}',
|
|
1083
1085
|
hostRequirementLoading: 'Reading host requirement…',
|
|
1084
1086
|
hostRequirementUndeclared: 'Host requirement undeclared',
|
|
@@ -318,6 +318,42 @@ export interface ActivationInfo {
|
|
|
318
318
|
loadedByProfile?: true
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
+
/**
|
|
322
|
+
* The install command shown on a plugin's card, corrected for THIS profile
|
|
323
|
+
* (#872).
|
|
324
|
+
*
|
|
325
|
+
* awesome-dsh-plugin generates the string once, for the catalog — always
|
|
326
|
+
* `dsh plugin --profile web add <spec>` — which is right on a `web` profile
|
|
327
|
+
* and wrong on every other one. On a desktop profile it is more than wrong:
|
|
328
|
+
* the DSH CLI refuses a profile by that name, so the command as printed
|
|
329
|
+
* cannot install anything there at all. Two outcomes, and the caller renders
|
|
330
|
+
* them differently because the user's next step differs:
|
|
331
|
+
*
|
|
332
|
+
* - `command`: the text to show and copy, with the profile it names corrected;
|
|
333
|
+
* - `unusable`: show no command at all (the market's own button is the way),
|
|
334
|
+
* because handing over a command that cannot work is worse than handing
|
|
335
|
+
* over none.
|
|
336
|
+
*
|
|
337
|
+
* A command with no `--profile` token is passed through untouched: it is not
|
|
338
|
+
* this function's business to add one.
|
|
339
|
+
*/
|
|
340
|
+
export function installCommandFor(
|
|
341
|
+
command: unknown,
|
|
342
|
+
profile: string | null | undefined,
|
|
343
|
+
): { command: string | null; unusable: boolean } {
|
|
344
|
+
if (typeof command !== 'string' || command.trim() === '') return { command: null, unusable: false }
|
|
345
|
+
const named = /--profile(?:=|\s+)(\S+)/u.exec(command)
|
|
346
|
+
if (named === null) return { command, unusable: false }
|
|
347
|
+
const wanted = typeof profile === 'string' ? profile.trim() : ''
|
|
348
|
+
if (wanted === '') return { command, unusable: false }
|
|
349
|
+
// The CLI refuses the desktop profile by name — inside the running app it
|
|
350
|
+
// can never be used, whatever the install layout is (src/index.ts states
|
|
351
|
+
// the same rule where it chooses the plugin manager).
|
|
352
|
+
if (wanted.toLowerCase() === 'desktop') return { command: null, unusable: true }
|
|
353
|
+
if (named[1] === wanted) return { command, unusable: false }
|
|
354
|
+
return { command: command.replace(named[0], `--profile ${wanted}`), unusable: false }
|
|
355
|
+
}
|
|
356
|
+
|
|
321
357
|
/** The /dsh-market/installed payload (fields the market UI consumes). */
|
|
322
358
|
export interface InstalledPayload {
|
|
323
359
|
profile?: string
|