Files
dsh-desktop/patches/dsh-client-ui-plugin-manager@0.1.7-rc.1.patch

224 lines
11 KiB
Diff

diff --git a/lib/client.js b/lib/client.js
index 31ae9a6..3c2ee39 100644
--- a/lib/client.js
+++ b/lib/client.js
@@ -28,6 +28,8 @@ window.__ModuleLoader__.load({
return `${bundle}#${rowId}`;
}
const SLOTS = [
+ "plugins.bundle.hidden",
+ "plugins.item.hidden",
"plugins.item",
"plugins.bundle.config",
"plugins.row.config"
@@ -42,6 +44,8 @@ window.__ModuleLoader__.load({
let revision = -1;
let ledger = {
items: [],
+ hiddenBundles: new Set(),
+ hiddenItems: new Set(),
bundles: /* @__PURE__ */ new Set(),
rows: /* @__PURE__ */ new Set()
};
@@ -59,6 +63,8 @@ window.__ModuleLoader__.load({
id: entry.options.id ?? "",
label: (0, _deepseek_ai_dsh_client_ui_slots.resolveSlotLabel)(entry.options.label) ?? ""
})),
+ hiddenBundles: keysOf("plugins.bundle.hidden"),
+ hiddenItems: keysOf("plugins.item.hidden"),
bundles: keysOf("plugins.bundle.config"),
rows: keysOf("plugins.row.config")
};
@@ -1589,9 +1595,15 @@ window.__ModuleLoader__.load({
]
});
}
+ /** Sidebar control in native window chrome, independent of the scrolling page header. */
+ function PluginSidebarControl({ renderSlot }) {
+ if (!(0, _deepseek_ai_dsh_client_ui_primitives.isDarwinDesktop)()) return null;
+ return (0, react_jsx_runtime.jsx)("div", { "data-plugin-sidebar-control": "", children: renderSlot("plugins.header.leading", {}) });
+ }
/** The top every page shares: the crumb that leads back, then the icon with the page's actions at its right. */
function DetailTop({ crumbLabel, crumbText, onBack, icon, actions }) {
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("button", {
+ "data-plugin-page-header": "detail",
type: "button",
className: PluginManagerPage_module_css_default.crumb,
"aria-label": crumbLabel,
@@ -1610,7 +1622,7 @@ window.__ModuleLoader__.load({
})] });
}
/** One package as a card that opens its page: its name, its one-liner, its tags, and its bundle switch. */
- function PackageCard({ pkg, t, resolveText, busy, highlighted, onOpen, onSetEnabled }) {
+ function PackageCard({ pkg, t, resolveText, busy, highlighted, onOpen, onSetEnabled, renderSlot }) {
const { title, description, beta } = packageText(pkg, resolveText);
const status = packageStatus(pkg);
return (0, react_jsx_runtime.jsxs)("li", {
@@ -1633,13 +1645,15 @@ window.__ModuleLoader__.load({
children: t("statusProblem")
}) : null] }),
description,
- end: (0, react_jsx_runtime.jsx)(EnableSwitch, {
+ end: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
+ renderSlot("plugins.bundle.actions", { subject: { kind: "bundle", pkg: packageRef(pkg) } }, { entryKey: pkg.name }),
+ (0, react_jsx_runtime.jsx)(EnableSwitch, {
pkg,
title,
t,
busy,
onSetEnabled
- })
+ })] })
}), (0, react_jsx_runtime.jsx)(MetadataError, {
error: pkg.meta?.error,
t
@@ -2656,11 +2670,12 @@ window.__ModuleLoader__.load({
};
}, [highlight, clearHighlight]);
const noticeLine = state.notice === null ? null : noticeText(state.notice, t);
- const listed = state.packages.filter((pkg) => !BUILTIN_PROFILE_BUNDLES.has(pkg.name) && (pkg.installed || pkg.optional || pkg.error !== void 0));
+ const available = state.packages.filter((pkg) => !BUILTIN_PROFILE_BUNDLES.has(pkg.name) && (pkg.installed || pkg.optional || pkg.error !== void 0));
+ const listed = available.filter((pkg) => !ledger.hiddenBundles?.has(pkg.name));
const mine = listed.filter((pkg) => pkg.installed || !pkg.optional);
const official = listed.filter((pkg) => pkg.optional && !pkg.installed);
const loaded = state.status === "ready" || state.status === "error";
- const openPkg = view.kind === "package" || view.kind === "row" ? listed.find((pkg) => pkg.name === view.name) : void 0;
+ const openPkg = view.kind === "package" || view.kind === "row" ? available.find((pkg) => pkg.name === view.name) : void 0;
const openItem = view.kind === "item" ? ledger.items.find((item) => item.id === view.id) : void 0;
const openRow = view.kind === "row" && openPkg !== void 0 ? openPkg.rows.find((row) => row.rowId === view.rowId) : void 0;
const showsCards = openPkg === void 0 && openItem === void 0;
@@ -2681,6 +2696,7 @@ window.__ModuleLoader__.load({
});
const packageCard = (pkg) => (0, react_jsx_runtime.jsx)(PackageCard, {
pkg,
+ renderSlot,
t,
resolveText,
busy: state.busy.includes(pkg.name),
@@ -2697,7 +2713,7 @@ window.__ModuleLoader__.load({
props.setEnabled(pkg.name, enabled);
}
}, pkg.name);
- const officialCards = [...official.map(packageCard), ...ledger.items.map((item) => (0, react_jsx_runtime.jsx)(ItemCard, {
+ const officialCards = [...official.map(packageCard), ...ledger.items.filter((item) => !ledger.hiddenItems?.has(item.id)).map((item) => (0, react_jsx_runtime.jsx)(ItemCard, {
item,
t,
renderSlot,
@@ -2732,8 +2748,10 @@ window.__ModuleLoader__.load({
"data-plugin-panel": true,
"aria-busy": state.status === "loading",
children: [
+ (0, react_jsx_runtime.jsx)(PluginSidebarControl, { renderSlot }),
showsCards ? (0, react_jsx_runtime.jsxs)("header", {
className: PluginManagerPage_module_css_default.pageHead,
+ "data-plugin-page-header": "list",
children: [(0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsx)("h1", {
className: PluginManagerPage_module_css_default.pageTitle,
children: t("title")
@@ -2765,6 +2783,18 @@ window.__ModuleLoader__.load({
})]
})]
}) : null,
+ showsCards && loaded ? renderSlot("plugins.overview", {
+ onOpenBundle: (name) => {
+ if (!available.some((pkg) => pkg.name === name)) return;
+ setActivation(null);
+ setView({ kind: "package", name });
+ },
+ onOpenItem: (id) => {
+ if (!ledger.items.some((item) => item.id === id)) return;
+ setActivation(null);
+ setView({ kind: "item", id });
+ }
+ }) : null,
showsCards && state.status === "loading" ? (0, react_jsx_runtime.jsxs)("p", {
className: `${PluginManagerPage_module_css_default.status} ${PluginManagerPage_module_css_default.statusWithDot}`,
role: "status",
@@ -3349,6 +3379,11 @@ window.__ModuleLoader__.load({
locale: NS,
inject: () => controller.inject(configLedger, (text) => ctx.locale.resolveText(text)),
children: {
+ "plugins.header.leading": { kind: "single", scope: "root" },
+ "plugins.overview": { kind: "list", scope: "root" },
+ "plugins.bundle.hidden": { kind: "keyed", scope: "root" },
+ "plugins.item.hidden": { kind: "keyed", scope: "root" },
+ "plugins.bundle.actions": { kind: "keyed", scope: "root" },
"plugins.item": {
kind: "list",
scope: "root"
@@ -3396,4 +3431,4 @@ window.__ModuleLoader__.load({
}
});
-//# sourceMappingURL=client.js.map
\ No newline at end of file
+//# sourceMappingURL=client.js.map
diff --git a/lib/types/client/PluginManagerPage.d.ts b/lib/types/client/PluginManagerPage.d.ts
index d9e8e74..08042a4 100644
--- a/lib/types/client/PluginManagerPage.d.ts
+++ b/lib/types/client/PluginManagerPage.d.ts
@@ -12,7 +12,7 @@ import { type ReactNode } from 'react';
import type { InjectFace, PropsLocale, PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
import { type PluginManagerFace } from './manager-store.ts';
/** Full component props assembled by the main slot renderer. */
-export type PluginManagerPageProps = PropsRuntime<'main'> & PropsLocale<'pluginManager'> & PropsRenderSlots<'plugins.item' | 'plugins.bundle.config' | 'plugins.row.config' | 'plugins.bundle.activation' | 'plugins.detail.actions' | 'plugins.detail.badge' | 'plugins.detail.section'> & InjectFace<PluginManagerFace>;
+export type PluginManagerPageProps = PropsRuntime<'main'> & PropsLocale<'pluginManager'> & PropsRenderSlots<'plugins.header.leading' | 'plugins.overview' | 'plugins.item' | 'plugins.bundle.actions' | 'plugins.bundle.config' | 'plugins.row.config' | 'plugins.bundle.activation' | 'plugins.detail.actions' | 'plugins.detail.badge' | 'plugins.detail.section'> & InjectFace<PluginManagerFace>;
/** Render the plugin manager: the official plugins and installed bundles, their pages, the install dialog, and the confirmation. */
export declare function PluginManagerPage(props: PluginManagerPageProps): ReactNode;
-//# sourceMappingURL=PluginManagerPage.d.ts.map
\ No newline at end of file
+//# sourceMappingURL=PluginManagerPage.d.ts.map
diff --git a/lib/types/client/config-ledger.d.ts b/lib/types/client/config-ledger.d.ts
index 3abc24e..8f49351 100644
--- a/lib/types/client/config-ledger.d.ts
+++ b/lib/types/client/config-ledger.d.ts
@@ -19,6 +19,8 @@ export interface OfficialItem {
*/
export interface ConfigLedger {
readonly items: readonly OfficialItem[];
+ readonly hiddenBundles?: ReadonlySet<string>;
+ readonly hiddenItems?: ReadonlySet<string>;
readonly bundles: ReadonlySet<string>;
readonly rows: ReadonlySet<string>;
}
@@ -35,4 +37,4 @@ export declare function rowConfigKey(bundle: string, rowId: string): string;
* @returns the ledger source; its snapshot changes only when a ledger or the locale does.
*/
export declare function configLedgerSource(ctx: ClientContext): HostObservable<ConfigLedger>;
-//# sourceMappingURL=config-ledger.d.ts.map
\ No newline at end of file
+//# sourceMappingURL=config-ledger.d.ts.map
diff --git a/lib/types/client/slot-contract.d.ts b/lib/types/client/slot-contract.d.ts
index f7ba8cc..de2bd3d 100644
--- a/lib/types/client/slot-contract.d.ts
+++ b/lib/types/client/slot-contract.d.ts
@@ -72,6 +72,21 @@ export interface PluginActivationOwnerProps {
}
declare module '@deepseek-ai/dsh-client-ui-slots' {
interface SlotMap {
+ /** Native sidebar control, separate from the scrolling page header. */
+ 'plugins.header.leading': { kind: 'single'; scope: 'root'; };
+ /** Controls above the ordinary plugin cards. */
+ 'plugins.overview': { kind: 'list'; scope: 'root'; owner: {
+ /** Open the manager's existing bundle detail, including an overview-owned bundle. */
+ onOpenBundle(name: string): void;
+ /** Open a registered item's existing configuration detail. */
+ onOpenItem(id: string): void;
+ }; };
+ /** Bundle names managed by overview controls instead of ordinary cards. */
+ 'plugins.bundle.hidden': { kind: 'keyed'; scope: 'root'; };
+ /** Item ids presented by overview controls, omitted only from ordinary cards and counts. */
+ 'plugins.item.hidden': { kind: 'keyed'; scope: 'root'; };
+ /** Desktop-owned actions before a bundle card's switch, keyed by package name. */
+ 'plugins.bundle.actions': { kind: 'keyed'; scope: 'root'; owner: PluginDetailProps; };
/** Optional guidance after the user enables a bundle from the list, keyed by npm package name. */
'plugins.bundle.activation': {
kind: 'keyed';
@@ -153,4 +168,4 @@ export interface ConfigPageForm {
/** Submit all field edits together with the revision the editor read. */
readonly mutate: ConfigForm<Record<string, unknown>>['mutate'];
}
-//# sourceMappingURL=slot-contract.d.ts.map
\ No newline at end of file
+//# sourceMappingURL=slot-contract.d.ts.map