feat: unify plugin workspace navigation

This commit is contained in:
2026-09-03 13:39:55 +08:00
parent 959e2faf03
commit 1b1f206dd8
27 changed files with 3097 additions and 1100 deletions

View File

@@ -0,0 +1,54 @@
import type {
PluginWorkspaceFilters,
PluginWorkspaceKey,
PluginWorkspaceScope,
PluginWorkspaceSource,
PluginWorkspaceState,
} from './plugin-workspace-model';
const SCOPES: readonly PluginWorkspaceScope[] = ['all', 'project'];
const SOURCES: readonly PluginWorkspaceSource[] = ['all', 'official', 'local'];
const STATES: readonly PluginWorkspaceState[] = [
'all', 'available', 'mine', 'enabled', 'update', 'unavailable',
];
function member<T extends string>(value: string | null, values: readonly T[]): value is T {
return value !== null && values.includes(value as T);
}
function pluginKey(value: string | null): PluginWorkspaceKey | null {
if (!value) return null;
if (value.startsWith('official:') && value.length > 'official:'.length) return value as `official:${string}`;
if (value.startsWith('local:') && value.length > 'local:'.length) return value as `local:${string}`;
if (value.startsWith('retained:') && value.length > 'retained:'.length) return value as `retained:${string}`;
return null;
}
export function parsePluginWorkspaceSearch(
search: URLSearchParams,
hasActiveProject: boolean,
): PluginWorkspaceFilters {
const defaultScope: PluginWorkspaceScope = hasActiveProject ? 'project' : 'all';
const requestedScope = search.get('scope');
const requestedSource = search.get('source');
const requestedState = search.get('state');
const parsedScope = member(requestedScope, SCOPES) ? requestedScope : defaultScope;
return {
scope: parsedScope === 'project' && !hasActiveProject ? 'all' : parsedScope,
source: member(requestedSource, SOURCES) ? requestedSource : 'all',
state: member(requestedState, STATES) ? requestedState : 'all',
search: search.get('q')?.trim() ?? '',
selectedKey: pluginKey(search.get('plugin')),
};
}
export function serializePluginWorkspaceSearch(filters: PluginWorkspaceFilters): URLSearchParams {
const search = new URLSearchParams();
search.set('scope', filters.scope);
search.set('source', filters.source);
search.set('state', filters.state);
const query = filters.search.trim();
if (query) search.set('q', query);
if (filters.selectedKey) search.set('plugin', filters.selectedKey);
return search;
}