feat: surface plugin settings as search sub-results
This commit is contained in:
@@ -264,6 +264,9 @@ function flattenStrings(node: unknown, sink: string[]): void {
|
||||
interface SubResult {
|
||||
label: string;
|
||||
description?: string;
|
||||
// For plugin setting fields: the id of the plugin whose card needs to be
|
||||
// expanded before the field becomes visible in the DOM.
|
||||
pluginId?: string;
|
||||
}
|
||||
|
||||
// Walk a translation subtree and emit sub-results for renderable settings.
|
||||
@@ -377,12 +380,29 @@ export default function SettingsPage() {
|
||||
haystacks[tabId] = strings.join(' ').toLowerCase();
|
||||
}
|
||||
if (installedPlugins.length) {
|
||||
const text = installedPlugins.map((p) => `${p.name} ${p.description} ${p.author}`).join(' ');
|
||||
haystacks.plugins = `${haystacks.plugins ?? ''} ${text}`.toLowerCase();
|
||||
subs.plugins = [
|
||||
...(subs.plugins ?? []),
|
||||
...installedPlugins.map((p) => ({ label: p.name, description: p.description })),
|
||||
];
|
||||
const haystackText = installedPlugins.map((p) => {
|
||||
const fieldText = p.settingsSchema
|
||||
? Object.values(p.settingsSchema)
|
||||
.map((s) => `${s.label} ${s.description ?? ''}`)
|
||||
.join(' ')
|
||||
: '';
|
||||
return `${p.name} ${p.description} ${p.author} ${fieldText}`;
|
||||
}).join(' ');
|
||||
haystacks.plugins = `${haystacks.plugins ?? ''} ${haystackText}`.toLowerCase();
|
||||
const pluginSubs: SubResult[] = installedPlugins.flatMap((p) => {
|
||||
const items: SubResult[] = [{ label: p.name, description: p.description }];
|
||||
if (p.settingsSchema) {
|
||||
for (const schema of Object.values(p.settingsSchema)) {
|
||||
items.push({
|
||||
label: schema.label,
|
||||
description: schema.description,
|
||||
pluginId: p.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
return items;
|
||||
});
|
||||
subs.plugins = [...(subs.plugins ?? []), ...pluginSubs];
|
||||
}
|
||||
if (installedThemes.length) {
|
||||
const text = installedThemes.map((th) => `${th.name} ${th.description} ${th.author}`).join(' ');
|
||||
@@ -606,9 +626,14 @@ export default function SettingsPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubResultSelect = (tabId: Tab, label: string) => {
|
||||
const handleSubResultSelect = (tabId: Tab, sub: SubResult) => {
|
||||
handleTabSelect(tabId);
|
||||
setPendingHighlight({ tab: tabId, label });
|
||||
if (sub.pluginId && typeof window !== 'undefined') {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('settings-plugin-expand', { detail: { pluginId: sub.pluginId } })
|
||||
);
|
||||
}
|
||||
setPendingHighlight({ tab: tabId, label: sub.label });
|
||||
};
|
||||
|
||||
const activeTabLabel = tabs.find((tab) => tab.id === effectiveActiveTab)?.label ?? '';
|
||||
@@ -753,7 +778,7 @@ export default function SettingsPage() {
|
||||
{subs.map((sub) => (
|
||||
<button
|
||||
key={`${tab.id}:${sub.label}`}
|
||||
onClick={() => handleSubResultSelect(tab.id, sub.label)}
|
||||
onClick={() => handleSubResultSelect(tab.id, sub)}
|
||||
className="w-full flex items-center pl-12 pr-5 py-2 text-xs text-muted-foreground hover:bg-muted hover:text-foreground transition-colors duration-150 text-left"
|
||||
>
|
||||
<span className="truncate">{sub.label}</span>
|
||||
@@ -895,7 +920,7 @@ export default function SettingsPage() {
|
||||
{subs.map((sub) => (
|
||||
<button
|
||||
key={`${tab.id}:${sub.label}`}
|
||||
onClick={() => handleSubResultSelect(tab.id, sub.label)}
|
||||
onClick={() => handleSubResultSelect(tab.id, sub)}
|
||||
className="w-full text-left pl-9 pr-3 py-1.5 rounded-md text-xs text-muted-foreground hover:bg-muted hover:text-foreground transition-colors duration-150"
|
||||
>
|
||||
<span className="truncate block">{sub.label}</span>
|
||||
|
||||
@@ -32,6 +32,18 @@ export function PluginsSettings() {
|
||||
initializePlugins();
|
||||
}, [fetchPolicy, initializePlugins, loaded]);
|
||||
|
||||
// Listen for "expand this plugin" events fired by the settings search when
|
||||
// the user clicks a plugin-setting sub-result. Expanding the card mounts
|
||||
// the per-field rows so the highlight effect can find them.
|
||||
useEffect(() => {
|
||||
const handler = (e: Event) => {
|
||||
const id = (e as CustomEvent<{ pluginId: string }>).detail?.pluginId;
|
||||
if (id) setExpandedPlugin(id);
|
||||
};
|
||||
window.addEventListener('settings-plugin-expand', handler);
|
||||
return () => window.removeEventListener('settings-plugin-expand', handler);
|
||||
}, []);
|
||||
|
||||
if (!isFeatureEnabled('pluginsEnabled')) {
|
||||
return null;
|
||||
}
|
||||
@@ -312,7 +324,7 @@ function PluginSettingField({ schema, value, onChange }: PluginSettingFieldProps
|
||||
switch (schema.type) {
|
||||
case 'boolean':
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div data-search-label={schema.label} className="flex items-center justify-between">
|
||||
<div>
|
||||
<span className="text-xs text-foreground">{schema.label}</span>
|
||||
{schema.description && <p className="text-[10px] text-muted-foreground">{schema.description}</p>}
|
||||
@@ -323,7 +335,7 @@ function PluginSettingField({ schema, value, onChange }: PluginSettingFieldProps
|
||||
|
||||
case 'select':
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div data-search-label={schema.label} className="flex items-center justify-between">
|
||||
<div>
|
||||
<span className="text-xs text-foreground">{schema.label}</span>
|
||||
{schema.description && <p className="text-[10px] text-muted-foreground">{schema.description}</p>}
|
||||
@@ -342,7 +354,7 @@ function PluginSettingField({ schema, value, onChange }: PluginSettingFieldProps
|
||||
|
||||
case 'string':
|
||||
return (
|
||||
<div>
|
||||
<div data-search-label={schema.label}>
|
||||
<span className="text-xs text-foreground">{schema.label}</span>
|
||||
{schema.description && <p className="text-[10px] text-muted-foreground">{schema.description}</p>}
|
||||
<input
|
||||
@@ -356,7 +368,7 @@ function PluginSettingField({ schema, value, onChange }: PluginSettingFieldProps
|
||||
|
||||
case 'number':
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div data-search-label={schema.label} className="flex items-center justify-between">
|
||||
<div>
|
||||
<span className="text-xs text-foreground">{schema.label}</span>
|
||||
{schema.description && <p className="text-[10px] text-muted-foreground">{schema.description}</p>}
|
||||
|
||||
Reference in New Issue
Block a user