Shared Abstractions
This chapter summarizes fundamental data models, read-only contracts, and schema-driven configuration abstractions across Lertaro.PluginSdk.
1. Search Result Contract ISearchResult
Plugins interact with search results through the read-only ISearchResult interface:
namespace Lertaro.PluginSdk;
public interface ISearchResult
{
string Name { get; } // Display name (e.g. "Lertaro.exe")
string FullPath { get; } // Absolute physical path
string ContextDirectory { get; } // Parent folder path
bool IsDir { get; } // True if directory
bool IsApplication { get; } // True if executable / app
FileMetadata Metadata { get; } // High-precision file metadata
bool[]? GetHighlightMask(string text, string query); // Highlight bitmask
}NOTE
ISearchResult.Metadata is populated directly by the in-memory USN/MFT index. Accessing this property incurs zero disk I/O and zero IPC calls. Use FileMetadataService.GetMetadataAsync only when querying external paths not present in the active result set.
2. File Metadata Structure FileMetadata
public readonly record struct FileMetadata(
long Size,
DateTime Created,
DateTime Modified,
DateTime Accessed
);- Timestamps represent Local Time.
Metadata == defaultindicates results generated by plugins rather than physical file indexes.- Use
Metadata.Modified != defaultto differentiate unavailable metadata from valid 0-byte files.
3. Host Control Interface IPluginSearchWindow
Passed into action callbacks (e.g. ISearchResultAction.Execute) to safely trigger host window operations:
public interface IPluginSearchWindow
{
void LocateInExplorerExternal(string path); // Highlight in File Explorer
void OpenFileOrFolderExternal(string path); // Open with default app
void OpenFileOrFolderAsAdminExternal(string path);// Run with elevated admin privileges
void HideWindow(); // Hide current search window
}4. Schema-Driven Configuration IConfigurable
Implement IConfigurable to generate native configuration forms automatically under Settings → Plugins → Configure without writing XAML:
public interface IConfigurable
{
PluginConfigSchema GetConfigSchema();
}Supported Field Types ConfigFieldType
| Field Type | Visual Control & Behavior |
|---|---|
Boolean | Toggle switch or checkbox. |
Text | Text input box. Supports RequireNonEmpty to fall back to DefaultValue when cleared. |
| Text selection | SelectionStart and SelectionLength specify the zero-based initial selection in a Text field's prompt editor. |
Integer | Numeric stepper with minimum and maximum bounds. |
Choice | Dropdown selector backed by a Choices or ChoiceOptions collection. |
Hotkey | Key recording box with optional RequireModifier = true. |
FilePath / FolderPath | Text box with native Windows file/folder browse dialog picker buttons. |
StringList | Editable multi-line list box supporting addition, deletion, reordering, and soft wrapping. Real line breaks are marked visually, but the markers are not part of the setting value. |
Group | Collapsible card grouping containing nested SubFields. |
CustomControl | Mounts a custom WPF UIElement control directly. |
Button | Renders an action button and invokes the field's OnClick delegate; it stores no setting value. |
Icon fields
A text field whose schema key is Icon is rendered with an icon preview. It accepts WPF Path Data directly; when a complete SVG/XML document is pasted, the host extracts every <path d> value, combines them, and stores only the resulting WPF Path Data. Invalid icon content is cleared and reported with a themed error dialog. Empty values remain valid when no icon is desired.
PluginConfigSchema also supports OnSave and OnRollback lifecycle delegates: OnSave runs when the user clicks OK/Apply to commit changes, while OnRollback restores state when changes are cancelled or rolled back.
Localized choice labels
Use ChoiceOptions when a choice needs a localized label while keeping a stable persisted value. PluginConfigChoice.Value is stored in the plugin settings, while LabelKey is resolved for display. The legacy Choices collection remains suitable when the stored value and displayed text are the same.
new PluginConfigField
{
Key = "DisplayMode",
FieldType = ConfigFieldType.Choice,
DefaultValue = "FriendlyName",
ChoiceOptions =
[
new PluginConfigChoice
{
Value = "FriendlyName",
LabelKey = "DisplayMode_FriendlyName"
}
]
}5. Full-search file results IFullSearchFileResultProvider
Plugins that need to contribute real file or folder rows to the full search window can implement IFullSearchFileResultProvider:
public interface IFullSearchFileResultProvider : IPluginComponent
{
IReadOnlyList<InstantResultItem> GetFileResults(string query, int limit);
}The host calls GetFileResults only during the full search window's final render. Return an empty list when the provider does not handle the query. Every returned InstantResultItem must represent an existing file or folder so the full window's path, size, and type columns remain meaningful. The component is managed by the same enable/disable switch as the plugin's instant-result provider.
6. User-configured path resolution UserPathResolver
Use Lertaro.PluginSdk.Helpers.UserPathResolver whenever a plugin accepts a path from the user or its settings. It applies the same rules for environment variables and Windows Shell virtual paths before filesystem APIs are called:
string expanded = UserPathResolver.Expand(rawPath);
bool isVirtual = UserPathResolver.IsVirtualPath(expanded);
string resolved = UserPathResolver.Resolve(rawPath);Expand trims the input and expands references such as %USERPROFILE%. Resolve performs that expansion and resolves tokens such as shell:Downloads or ::{CLSID} to a physical path when possible. A virtual folder that has no physical path, such as shell:AppsFolder, resolves to its canonical ::{CLSID} name instead, so every spelling of it compares equal; that result is still virtual. Only a token the Shell cannot parse at all comes back unchanged. Test the result with IsVirtualPath before passing it to filesystem APIs. Directory indexing APIs can only enumerate a path after it resolves to a real, index-covered folder.