describePanel method
Declares feeds, states, knobs and actions onto panel, each with the code
that serves it.
Called once per mount. The plugin keeps panel if it has anything to emit
or announce later.
Implementation
@override
void describePanel(Panel panel) {
_panel = panel;
var watchable = adapter.watch != null || adapter.updates != null;
panel.state(
'schema',
'Schema',
description:
'Every table and view, its columns and its row count, read live '
'from sqlite_master.',
read: _schema,
);
panel.action(
PluginAction(
'query',
'Run a query',
description:
'Runs one statement and answers with its rows, inline. The reply '
'is capped at `limit` rows and says so when it truncates — put a '
'LIMIT in the SQL to page through a big table.',
parameters: [
const ActionParameter('sql', 'SQL'),
const ActionParameter(
'args',
'Bind values',
required: false,
description: 'A JSON array, bound to the ? placeholders in order.',
),
ActionParameter(
'limit',
'Row cap',
kind: ActionParameterKind.integer,
required: false,
defaultValue: '$databaseQueryDefaultLimit',
description: 'How many rows the reply carries at most.',
),
],
),
_query,
);
if (adapter.updates != null) {
panel.feed(
'changes',
'Changes',
description:
'Which tables changed, per write transaction, coalesced over '
'${(coalesceWindow ?? databaseCoalesceWindow).inMilliseconds}ms '
'so a burst reads as one event.',
fields: const [
FieldDescriptor('tables', 'Tables', primary: true),
FieldDescriptor(
'transactions',
'Transactions',
kind: FieldKind.number,
),
],
);
_updatesSubscription = adapter.updates!.listen(_onTick);
}
if (watchable) {
panel.feed(
'watch',
'Watched queries',
description:
'Every result snapshot of every watched query — the history of '
'what the query answered, not a mutating table. Rows ride in the '
'details.',
fields: const [
FieldDescriptor('sql', 'SQL', primary: true),
FieldDescriptor('watch', 'Watch', kind: FieldKind.number),
FieldDescriptor('rows', 'Rows', kind: FieldKind.number),
FieldDescriptor('error', 'Error'),
],
);
panel.itemAction(
'watch',
const PluginAction(
'explain',
'Explain',
description: "EXPLAIN QUERY PLAN for this snapshot's query.",
),
_explain,
);
panel.action(
const PluginAction(
'watch',
'Watch a query',
description:
'Re-runs the query on every relevant change and reports each '
'result on the `watch` feed. Answers with the watch id '
'`unwatch` takes.',
parameters: [ActionParameter('sql', 'SQL')],
),
_watch,
);
panel.action(
const PluginAction(
'unwatch',
'Stop watching',
parameters: [
ActionParameter(
'id',
'Watch id',
kind: ActionParameterKind.integer,
),
],
),
_unwatch,
);
}
if (adapter.sync == DatabaseSync.powersync) {
panel.state(
'sync',
'Sync',
description:
'PowerSync, from its own tables: this client, what waits to '
'upload, when it last synced, and how far each bucket is.',
read: _powerSyncState,
);
if (adapter.updates != null) {
panel.feed(
'records',
'Records',
description:
'Every record this app wrote locally, as it joins the upload '
'queue, and every record a checkpoint brought in, with its '
'operation — what joins a change on one device to its arrival '
'on another — and every bucket it subscribes to or lets go '
'of.',
fields: const [
FieldDescriptor('key', 'Record', primary: true),
FieldDescriptor('table', 'Table'),
FieldDescriptor('change', 'Change'),
FieldDescriptor('op', 'Operation', kind: FieldKind.number),
FieldDescriptor('bucket', 'Bucket'),
],
);
_recordsSubscription = _coalescedTicks.stream.listen(
(_) => _readRecords(),
);
unawaited(_readRecords());
}
}
if (adapter.execute != null) {
panel.action(
const PluginAction(
'execute',
'Execute SQL',
danger: true,
description:
'Runs a write statement against the live database. Exists only '
'because this app opted in by providing an execute function.',
parameters: [
ActionParameter('sql', 'SQL'),
ActionParameter(
'args',
'Bind values',
required: false,
description:
'A JSON array, bound to the ? placeholders in order.',
),
],
),
_execute,
);
}
}