5.6 KiB
ResultTable system: overview and usage
This document explains the ResultTable system used across the CLI: how tables
are built, how plugins integrate with them, and how @N selection, row replay,
and plugin selectors work.
TL;DR
ResultTableis the unified object used to render tabular results and drive@Nbehavior.- Plugins return
SearchResultobjects or dicts and can either supply row selection args or implement aselector()method. - Rows can also carry an explicit
selection_action, which the CLI replays verbatim. - Table metadata helps plugins attach context. Some metadata keys still use older
providernames internally.
Key concepts
ResultTable
SYS/result_table.py renders rows as a rich table and stores metadata used for
selection expansion.
Important APIs:
add_result()set_table()set_source_command()set_row_selection_args()set_row_selection_action()set_table_metadata()
ResultRow
A row holds columns, payload data, selection args, and optionally a full
selection_action token list. When selection_action is present, @N uses it
instead of reconstructing a command from the source command and row args.
Plugin selector
If a plugin implements selector(selected_items, ctx=..., stage_is_last=True),
that selector runs first when @N is used. If the selector returns True, it
has handled the selection, for example by publishing a nested table.
Table metadata
ResultTable.set_table_metadata(dict) attaches plugin-specific context for
selectors and follow-up stages. You will still see legacy keys such as
provider in some metadata because parts of the runtime still consume them.
Building a table
Typical plugin flow:
from SYS.result_table import ResultTable
table = ResultTable("Plugin: X results").set_preserve_order(True)
table.set_table("plugin_name")
table.set_table_metadata({"plugin": "plugin_name", "view": "folders"})
table.set_source_command("search-file", ["-plugin", "plugin_name", "query"])
for result in results:
table.add_result(result)
ctx.set_last_result_table(table, payloads)
ctx.set_current_stage_table(table)
Notes:
- To drive a direct replay, call
table.set_row_selection_args(row_index, ["-open", "<id>"]). - For drill-in or interactive behavior, implement
plugin.selector()and returnTruewhen handled. - For exact row replay, call
table.set_row_selection_action(row_index, tokens).
Selection flow
When a user enters @N:
- The CLI chooses the active table.
- It gathers the selected payloads.
PipelineExecutor._maybe_run_class_selector()runs pluginselector()hooks for the plugin inferred from the table or payloads.- If no selector handles the row, the CLI checks
row.selection_actionfirst. - If no explicit row action exists, the CLI expands
source_command + source_args + row_selection_args. - Multi-selection results are piped downstream instead of being collapsed to one row replay.
Columns and display
- Plugins can pass a
columnslist in the result dict orSearchResultto control column order and display. - Otherwise,
ResultTableuses sensible defaults. - Rendering helpers such as
to_rich,format_json, andformat_compactsupport different CLI display paths.
Plugin-specific examples
AllDebrid
AllDebrid exposes a list of magnets as folder rows and the files inside each
magnet as file rows. The plugin uses selector() to drill into a magnet and
publish a new ResultTable of files.
Example commands:
search-file -plugin alldebrid "*"
@3
Illustrative magnet row:
SearchResult(
table="alldebrid",
title="My Magnet Title",
path="alldebrid:magnet:123",
media_kind="folder",
full_metadata={
"magnet_id": 123,
"plugin": "alldebrid",
"plugin_view": "folders",
},
)
Illustrative file row:
SearchResult(
table="alldebrid",
title="Episode 01.mkv",
path="https://.../unlocked_direct_url",
media_kind="file",
full_metadata={
"magnet_id": 123,
"plugin": "alldebrid",
"plugin_view": "files",
},
)
Selection and download behavior:
@3on a magnet row runs the plugin selector and shows a file table.@2 | download-filedownloads the selected file row.@1 | add-file -plugin local -instance <dest>triggers add-file's plugin-aware handoff flow.
Configure the AllDebrid plugin in .config plugins and add its API key before
testing these flows.
Bandcamp
Bandcamp search supports artist: queries. The Bandcamp plugin implements a
selector() that detects artist rows and expands them into a discography table.
Example:
search-file -plugin bandcamp "artist:radiohead"
@1
Plugin selectors are the right tool when you need to replace one table with another, such as artist to discography drill-in.
Plugin author checklist
- Implement
search(query, limit, filters)and returnSearchResultobjects or dicts. - Include useful
full_metadatavalues for drill-in and selection replay. - Implement
download(result, output_dir)when the plugin can materialize file bytes. - Implement
selector(...)for drill-in or interactive transforms. - Add tests that cover search, select, and download flows.
Debugging tips
- Use
ctx.set_last_result_table(table, payloads)to publish a table while developing a selector. - Add
log(...)messages in plugin code to capture fail points. - Inspect
full_metadatawhen a selector or replay path is missing required context.
Quick reference
- ResultTable implementation:
SYS/result_table.py - Pipeline helpers:
SYS/pipeline.py - Row replay and selection flow:
SYS/pipeline.py - Plugin authoring guide:
docs/plugin_authoring.md