@north-light/crouter 0.3.239 → 0.3.241
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clients/attach/viewer.js +400 -400
- package/dist/commands/node/bash.js +69 -5
- package/dist/commands/node/inspect.js +1 -1
- package/dist/commands/sys/context/admin/detail-panel.d.ts +3 -0
- package/dist/commands/sys/context/admin/detail-panel.js +32 -3
- package/dist/commands/sys/context/admin/docs-panel.d.ts +23 -13
- package/dist/commands/sys/context/admin/docs-panel.js +101 -103
- package/dist/commands/sys/context/admin/list-view.d.ts +53 -0
- package/dist/commands/sys/context/admin/list-view.js +125 -0
- package/dist/commands/sys/context/admin/model.d.ts +11 -0
- package/dist/commands/sys/context/admin/model.js +28 -1
- package/dist/commands/sys/context/admin/rail-panel.d.ts +46 -0
- package/dist/commands/sys/context/admin/{axes-panel.js → rail-panel.js} +64 -21
- package/dist/commands/sys/context/admin/shell.d.ts +19 -2
- package/dist/commands/sys/context/admin/shell.js +142 -46
- package/dist/commands/sys/context/admin.js +4 -2
- package/dist/commands/sys/context/resolve.js +5 -1
- package/dist/core/bash-jobs.d.ts +23 -0
- package/dist/core/bash-jobs.js +44 -2
- package/dist/core/canvas/browse/model.d.ts +0 -9
- package/dist/core/canvas/browse/model.js +1 -37
- package/dist/core/canvas/browse/render.js +2 -1
- package/dist/core/runtime/tmux-bindings.js +4 -3
- package/dist/core/tui/fuzzy.d.ts +9 -0
- package/dist/core/tui/fuzzy.js +38 -0
- package/dist/core/wake.d.ts +3 -0
- package/dist/core/wake.js +3 -2
- package/dist/daemon/crtrd.js +3 -0
- package/dist/daemon/reconcilers/bash-deadline.d.ts +17 -0
- package/dist/daemon/reconcilers/bash-deadline.js +73 -0
- package/dist/pi-extensions/canvas-bash-valve.js +15 -2
- package/dist/shared/env.d.ts +4 -0
- package/dist/shared/env.js +7 -0
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
- package/dist/commands/sys/context/admin/axes-panel.d.ts +0 -21
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// The list's view model: which documents a question selects, how they group,
|
|
2
|
+
// and what each group costs. Pure — no terminal, no colour, no width — so the
|
|
3
|
+
// rail's counts and the list's rows are computed from the same call.
|
|
4
|
+
import { fuzzyMatch } from '../../../../core/tui/fuzzy.js';
|
|
5
|
+
import { deliveredCost, rungCostsOf } from './model.js';
|
|
6
|
+
export const NO_FILTERS = { rung: 'any', origin: 'any', kind: 'any', state: 'any', query: '' };
|
|
7
|
+
/** Documents whose canonical name carries no `/` share one leading section
|
|
8
|
+
* rather than becoming a crowd of one-member groups. */
|
|
9
|
+
const LEADING_KEY = '';
|
|
10
|
+
const LEADING_LABEL = 'top level';
|
|
11
|
+
export function groupKeyOf(name) {
|
|
12
|
+
const cut = name.indexOf('/');
|
|
13
|
+
return cut < 0 ? LEADING_KEY : name.slice(0, cut);
|
|
14
|
+
}
|
|
15
|
+
/** Every group key the rows can produce, so collapse state can drop the keys a
|
|
16
|
+
* re-plan removed instead of accumulating them. */
|
|
17
|
+
export function groupKeysOf(rows) {
|
|
18
|
+
return new Set(rows.map((row) => groupKeyOf(row.name)));
|
|
19
|
+
}
|
|
20
|
+
/** The lines to draw, in corpus order: the leading section, then each group in
|
|
21
|
+
* order of first appearance. A live query force-expands every group holding a
|
|
22
|
+
* match without touching `expanded`, so clearing it restores what the user
|
|
23
|
+
* collapsed. */
|
|
24
|
+
export function buildListView(rows, filters, expanded, event) {
|
|
25
|
+
const groups = new Map();
|
|
26
|
+
const order = [];
|
|
27
|
+
for (const row of rows) {
|
|
28
|
+
if (!passes(row, row.records.get(event), filters))
|
|
29
|
+
continue;
|
|
30
|
+
const key = groupKeyOf(row.name);
|
|
31
|
+
const held = groups.get(key);
|
|
32
|
+
if (held === undefined) {
|
|
33
|
+
groups.set(key, [row]);
|
|
34
|
+
order.push(key);
|
|
35
|
+
}
|
|
36
|
+
else
|
|
37
|
+
held.push(row);
|
|
38
|
+
}
|
|
39
|
+
if (groups.has(LEADING_KEY)) {
|
|
40
|
+
order.splice(order.indexOf(LEADING_KEY), 1);
|
|
41
|
+
order.unshift(LEADING_KEY);
|
|
42
|
+
}
|
|
43
|
+
const lines = [];
|
|
44
|
+
for (const key of order) {
|
|
45
|
+
const members = groups.get(key);
|
|
46
|
+
const rungs = { name: 0, preview: 0, content: 0 };
|
|
47
|
+
let cost = 0;
|
|
48
|
+
for (const row of members) {
|
|
49
|
+
const record = row.records.get(event);
|
|
50
|
+
if (record !== undefined && record.finalRung !== 'none')
|
|
51
|
+
rungs[record.finalRung] += 1;
|
|
52
|
+
cost += deliveredCost(record);
|
|
53
|
+
}
|
|
54
|
+
const open = filters.query !== '' || expanded.has(key);
|
|
55
|
+
lines.push({
|
|
56
|
+
kind: 'group',
|
|
57
|
+
key,
|
|
58
|
+
label: key === LEADING_KEY ? LEADING_LABEL : `${key}/`,
|
|
59
|
+
docs: members.length,
|
|
60
|
+
rungs,
|
|
61
|
+
cost,
|
|
62
|
+
expanded: open,
|
|
63
|
+
first: members[0],
|
|
64
|
+
});
|
|
65
|
+
if (!open)
|
|
66
|
+
continue;
|
|
67
|
+
for (const row of members) {
|
|
68
|
+
lines.push({
|
|
69
|
+
kind: 'doc',
|
|
70
|
+
key: row.key,
|
|
71
|
+
row,
|
|
72
|
+
label: key === LEADING_KEY ? row.name : row.name.slice(key.length + 1),
|
|
73
|
+
cost: deliveredCost(row.records.get(event)),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return lines;
|
|
78
|
+
}
|
|
79
|
+
/** What the whole filtered set costs, split by the rung it arrives at. */
|
|
80
|
+
export function costTotals(rows, event) {
|
|
81
|
+
const byRung = { name: 0, preview: 0, content: 0 };
|
|
82
|
+
let total = 0;
|
|
83
|
+
for (const row of rows) {
|
|
84
|
+
const record = row.records.get(event);
|
|
85
|
+
if (record === undefined || record.finalRung === 'none')
|
|
86
|
+
continue;
|
|
87
|
+
const spend = rungCostsOf(record)[record.finalRung];
|
|
88
|
+
byRung[record.finalRung] += spend;
|
|
89
|
+
total += spend;
|
|
90
|
+
}
|
|
91
|
+
return { total, byRung };
|
|
92
|
+
}
|
|
93
|
+
function passes(row, record, filters) {
|
|
94
|
+
if (filters.rung !== 'any') {
|
|
95
|
+
const rung = record?.finalRung ?? 'none';
|
|
96
|
+
if (filters.rung === 'silent' ? rung !== 'none' : rung !== filters.rung)
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
if (filters.origin !== 'any' && row.anchor.scope !== filters.origin)
|
|
100
|
+
return false;
|
|
101
|
+
if (filters.kind !== 'any' && row.anchor.kind !== filters.kind)
|
|
102
|
+
return false;
|
|
103
|
+
if (filters.state !== 'any' && !inState(record, filters.state))
|
|
104
|
+
return false;
|
|
105
|
+
if (filters.query !== '' && !fuzzyMatch(filters.query, row.name))
|
|
106
|
+
return false;
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
/** `gated` is the document's own gate — the one predicate that can silence a
|
|
110
|
+
* document in every event. An entry gate failure belongs to a single entry and
|
|
111
|
+
* reads in the dossier's verdict, not here. */
|
|
112
|
+
function inState(record, state) {
|
|
113
|
+
if (record === undefined)
|
|
114
|
+
return state === 'no-entry';
|
|
115
|
+
switch (state) {
|
|
116
|
+
case 'delivering':
|
|
117
|
+
return record.finalRung !== 'none';
|
|
118
|
+
case 'gated':
|
|
119
|
+
return !record.docGate.pass;
|
|
120
|
+
case 'shadowed':
|
|
121
|
+
return record.shadowedBy !== null;
|
|
122
|
+
case 'no-entry':
|
|
123
|
+
return record.authoredEntries.length === 0;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -73,3 +73,14 @@ export declare function surfaceEntryToFlag(entry: SurfaceEntry): string;
|
|
|
73
73
|
/** One authored entry as a single readable row. */
|
|
74
74
|
export declare function describeSurfaceEntry(entry: SurfaceEntry): string;
|
|
75
75
|
export declare function plural(count: number, one: string, many: string): string;
|
|
76
|
+
/** What one document would cost at each rung, in approximate tokens. The
|
|
77
|
+
* renderers decide the payload: `content` emits the body, `preview` emits the
|
|
78
|
+
* catalog entry plus the routing line, `name` emits the entry alone. The
|
|
79
|
+
* estimate is characters ÷ 4 — it ranks documents correctly and totals
|
|
80
|
+
* approximately, and every place it renders says so. */
|
|
81
|
+
export type RungCosts = Record<Rung, number>;
|
|
82
|
+
export declare function rungCostsOf(record: DeliveryRecord): RungCosts;
|
|
83
|
+
/** What this document actually spends in the event the record belongs to. */
|
|
84
|
+
export declare function deliveredCost(record: DeliveryRecord | undefined): number;
|
|
85
|
+
/** Approximate tokens in at most six columns, always marked as an estimate. */
|
|
86
|
+
export declare function formatCost(count: number): string;
|
|
@@ -5,7 +5,7 @@ import { resolve as resolvePath } from 'node:path';
|
|
|
5
5
|
import { theme } from '../../../../core/tui/panel.js';
|
|
6
6
|
import { realpathOrSelf } from '../../../../core/fs-utils.js';
|
|
7
7
|
import { planDelivery, } from '../../../../core/substrate/plan.js';
|
|
8
|
-
import { RUNGS, SURFACE_EVENTS, rungRank, } from '../../../../core/substrate/schema.js';
|
|
8
|
+
import { RUNGS, SURFACE_EVENTS, previewLine, rungRank, } from '../../../../core/substrate/schema.js';
|
|
9
9
|
import { scopeForCwd, profileNameFor } from '../../../../core/substrate/subject-fields.js';
|
|
10
10
|
import { resolveMemoryDocForTarget } from '../../../../core/memory-resolver.js';
|
|
11
11
|
export function subjectOf(snapshot) {
|
|
@@ -207,3 +207,30 @@ export function describeSurfaceEntry(entry) {
|
|
|
207
207
|
export function plural(count, one, many) {
|
|
208
208
|
return `${count} ${count === 1 ? one : many}`;
|
|
209
209
|
}
|
|
210
|
+
export function rungCostsOf(record) {
|
|
211
|
+
const entry = record.name.length;
|
|
212
|
+
const routing = previewLine(record.doc).length;
|
|
213
|
+
return {
|
|
214
|
+
none: 0,
|
|
215
|
+
name: tokens(entry),
|
|
216
|
+
preview: tokens(entry + routing),
|
|
217
|
+
content: tokens(record.doc.body.length),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/** What this document actually spends in the event the record belongs to. */
|
|
221
|
+
export function deliveredCost(record) {
|
|
222
|
+
return record === undefined ? 0 : rungCostsOf(record)[record.finalRung];
|
|
223
|
+
}
|
|
224
|
+
function tokens(characters) {
|
|
225
|
+
return Math.ceil(characters / 4);
|
|
226
|
+
}
|
|
227
|
+
/** Approximate tokens in at most six columns, always marked as an estimate. */
|
|
228
|
+
export function formatCost(count) {
|
|
229
|
+
if (count <= 0)
|
|
230
|
+
return '';
|
|
231
|
+
if (count < 1000)
|
|
232
|
+
return `~${count}`;
|
|
233
|
+
if (count < 10_000)
|
|
234
|
+
return `~${(count / 1000).toFixed(1)}k`;
|
|
235
|
+
return `~${Math.round(count / 1000)}k`;
|
|
236
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type Snapshot } from './model.js';
|
|
2
|
+
import type { Filters } from './list-view.js';
|
|
3
|
+
export type AxisId = 'event' | 'payload' | 'kind' | 'mode' | 'lifecycle' | 'manager' | 'depth' | 'profile' | 'cwd';
|
|
4
|
+
export interface Axis {
|
|
5
|
+
id: AxisId;
|
|
6
|
+
label: string;
|
|
7
|
+
value: string;
|
|
8
|
+
/** Free-text axes open a field; the rest cycle in place. */
|
|
9
|
+
typed: boolean;
|
|
10
|
+
}
|
|
11
|
+
export type FacetId = 'rung' | 'origin' | 'kind' | 'state' | 'query';
|
|
12
|
+
export interface Facet {
|
|
13
|
+
id: FacetId;
|
|
14
|
+
label: string;
|
|
15
|
+
value: string;
|
|
16
|
+
/** The query is typed; every other facet cycles through a closed set. */
|
|
17
|
+
typed: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** The FILTERS rows for one filter value. `doc kind` is spelled out because the
|
|
20
|
+
* snapshot above it dials the NODE's kind. */
|
|
21
|
+
export declare function facetsOf(filters: Filters): Facet[];
|
|
22
|
+
export declare function cycleFacet(filters: Filters, id: FacetId, delta: number): Filters;
|
|
23
|
+
/** The rail's rows for a snapshot. The payload row appears only for the events
|
|
24
|
+
* that read one, because on boot there is nothing it could change. */
|
|
25
|
+
export declare function axesOf(snapshot: Snapshot, profileName: string): Axis[];
|
|
26
|
+
/** Cycle one enum axis, returning the changed snapshot. A typed axis is never
|
|
27
|
+
* cycled — the shell opens a field for it instead. */
|
|
28
|
+
export declare function cycleAxis(snapshot: Snapshot, id: AxisId, delta: number): Snapshot;
|
|
29
|
+
/** Apply a typed axis's submitted value. */
|
|
30
|
+
export declare function setTypedAxis(snapshot: Snapshot, id: AxisId, value: string): Snapshot;
|
|
31
|
+
/** The value a typed axis's field is seeded with. */
|
|
32
|
+
export declare function typedAxisValue(snapshot: Snapshot, id: AxisId): string;
|
|
33
|
+
/** Both sections as one column. `index` runs over the axes and then the facets
|
|
34
|
+
* — the order the shell routes a cycle in — while the section headings and the
|
|
35
|
+
* blank rows between them are drawn but never selectable. */
|
|
36
|
+
export declare function renderRail(opts: {
|
|
37
|
+
axes: Axis[];
|
|
38
|
+
facets: Facet[];
|
|
39
|
+
index: number;
|
|
40
|
+
width: number;
|
|
41
|
+
height: number;
|
|
42
|
+
focused: boolean;
|
|
43
|
+
/** Documents the filters select, out of the whole corpus. */
|
|
44
|
+
matched: number;
|
|
45
|
+
total: number;
|
|
46
|
+
}): string[];
|
|
@@ -1,11 +1,45 @@
|
|
|
1
|
-
// The
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// The rail: two sections in one row grammar. SNAPSHOT holds the configuration
|
|
2
|
+
// axes the page plans against — changing one re-plans every event. FILTERS holds
|
|
3
|
+
// the question asked of that plan — changing one only changes what is shown.
|
|
4
|
+
// An enum cycles in place; a free-text row opens a field.
|
|
4
5
|
import { truncateToWidth } from '@earendil-works/pi-tui';
|
|
5
|
-
import { padAnsi, theme
|
|
6
|
-
import { SURFACE_EVENTS } from '../../../../core/substrate/schema.js';
|
|
6
|
+
import { padAnsi, theme } from '../../../../core/tui/panel.js';
|
|
7
|
+
import { KINDS, RUNGS, SURFACE_EVENTS } from '../../../../core/substrate/schema.js';
|
|
7
8
|
import { listProfiles } from '../../../../core/profiles/manifest.js';
|
|
8
9
|
import { payloadAxisFor } from './model.js';
|
|
10
|
+
const RUNG_FACETS = ['any', ...RUNGS.filter((rung) => rung !== 'none'), 'silent'];
|
|
11
|
+
const ORIGIN_FACETS = ['any', 'project', 'user', 'profile', 'builtin', 'node'];
|
|
12
|
+
const KIND_FACETS = ['any', ...KINDS];
|
|
13
|
+
const STATE_FACETS = ['any', 'delivering', 'gated', 'shadowed', 'no-entry'];
|
|
14
|
+
/** The FILTERS rows for one filter value. `doc kind` is spelled out because the
|
|
15
|
+
* snapshot above it dials the NODE's kind. */
|
|
16
|
+
export function facetsOf(filters) {
|
|
17
|
+
return [
|
|
18
|
+
{ id: 'rung', label: 'rung', value: filters.rung, typed: false },
|
|
19
|
+
{ id: 'origin', label: 'origin', value: filters.origin, typed: false },
|
|
20
|
+
{ id: 'kind', label: 'doc kind', value: filters.kind, typed: false },
|
|
21
|
+
{ id: 'state', label: 'state', value: filters.state, typed: false },
|
|
22
|
+
{ id: 'query', label: 'search', value: filters.query === '' ? 'any' : filters.query, typed: true },
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
export function cycleFacet(filters, id, delta) {
|
|
26
|
+
const step = (values, current) => {
|
|
27
|
+
const at = values.indexOf(current);
|
|
28
|
+
return values[(Math.max(0, at) + delta + values.length) % values.length];
|
|
29
|
+
};
|
|
30
|
+
switch (id) {
|
|
31
|
+
case 'rung':
|
|
32
|
+
return { ...filters, rung: step(RUNG_FACETS, filters.rung) };
|
|
33
|
+
case 'origin':
|
|
34
|
+
return { ...filters, origin: step(ORIGIN_FACETS, filters.origin) };
|
|
35
|
+
case 'kind':
|
|
36
|
+
return { ...filters, kind: step(KIND_FACETS, filters.kind) };
|
|
37
|
+
case 'state':
|
|
38
|
+
return { ...filters, state: step(STATE_FACETS, filters.state) };
|
|
39
|
+
case 'query':
|
|
40
|
+
return filters;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
9
43
|
function tildify(path) {
|
|
10
44
|
const home = process.env['HOME'];
|
|
11
45
|
return home !== undefined && home !== '' && path.startsWith(home) ? `~${path.slice(home.length)}` : path;
|
|
@@ -92,26 +126,35 @@ export function typedAxisValue(snapshot, id) {
|
|
|
92
126
|
return axis === 'file' ? snapshot.file : axis === 'docName' ? snapshot.docName : axis === 'command' ? snapshot.command : '';
|
|
93
127
|
}
|
|
94
128
|
const LABEL_WIDTH = 10;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
129
|
+
/** Both sections as one column. `index` runs over the axes and then the facets
|
|
130
|
+
* — the order the shell routes a cycle in — while the section headings and the
|
|
131
|
+
* blank rows between them are drawn but never selectable. */
|
|
132
|
+
export function renderRail(opts) {
|
|
133
|
+
const { axes, facets, index, width, height, focused, matched, total } = opts;
|
|
134
|
+
const lines = [];
|
|
135
|
+
let cursorLine = 0;
|
|
136
|
+
const push = (at, label, value) => {
|
|
100
137
|
const room = Math.max(1, width - LABEL_WIDTH - 2);
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
const body = ` ${
|
|
138
|
+
const padded = padAnsi(truncateToWidth(label, LABEL_WIDTH), LABEL_WIDTH);
|
|
139
|
+
const shown = truncateToWidth(value, room);
|
|
140
|
+
const body = ` ${padded} ${shown}`;
|
|
141
|
+
if (at === index)
|
|
142
|
+
cursorLine = lines.length;
|
|
104
143
|
if (at === index && focused)
|
|
105
|
-
|
|
144
|
+
lines.push(theme.bg('selectedBg', padAnsi(body, width)));
|
|
106
145
|
else if (at === index)
|
|
107
|
-
|
|
146
|
+
lines.push(`${theme.fg('accent', '▌')}${padAnsi(theme.fg('accent', body.slice(1)), width - 1)}`);
|
|
108
147
|
else
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
148
|
+
lines.push(padAnsi(` ${theme.fg('dim', padded)} ${shown}`, width));
|
|
149
|
+
};
|
|
150
|
+
lines.push(theme.fg('dim', ' SNAPSHOT'), '');
|
|
151
|
+
axes.forEach((axis, at) => push(at, axis.label, axis.value));
|
|
152
|
+
lines.push('', theme.fg('dim', ` FILTERS · ${matched} of ${total}`), '');
|
|
153
|
+
facets.forEach((facet, at) => push(axes.length + at, facet.label, facet.value));
|
|
154
|
+
const maxStart = Math.max(0, lines.length - height);
|
|
155
|
+
const start = Math.min(Math.max(0, cursorLine - Math.floor(height / 2)), maxStart);
|
|
156
|
+
const out = lines.slice(start, start + height);
|
|
114
157
|
while (out.length < height)
|
|
115
158
|
out.push('');
|
|
116
|
-
return out
|
|
159
|
+
return out;
|
|
117
160
|
}
|
|
@@ -14,7 +14,11 @@ export declare class ContextAdminShell implements Component, Focusable {
|
|
|
14
14
|
private snapshot;
|
|
15
15
|
private plans;
|
|
16
16
|
private zone;
|
|
17
|
-
private
|
|
17
|
+
private railIndex;
|
|
18
|
+
private filters;
|
|
19
|
+
private rows;
|
|
20
|
+
/** The search field owns the keyboard while it is open. */
|
|
21
|
+
private searching;
|
|
18
22
|
private readonly docs;
|
|
19
23
|
private readonly detail;
|
|
20
24
|
private modal;
|
|
@@ -35,6 +39,9 @@ export declare class ContextAdminShell implements Component, Focusable {
|
|
|
35
39
|
/** Recompute the focused event's plan, refresh the panels from it, and let
|
|
36
40
|
* the remaining events fill in behind it. */
|
|
37
41
|
private rebuild;
|
|
42
|
+
/** A filter never re-plans: it is a question about the plan already computed,
|
|
43
|
+
* which is what keeps a facet change instant on a corpus this size. */
|
|
44
|
+
private setFilters;
|
|
38
45
|
private syncDetail;
|
|
39
46
|
private setSnapshot;
|
|
40
47
|
private matches;
|
|
@@ -47,8 +54,14 @@ export declare class ContextAdminShell implements Component, Focusable {
|
|
|
47
54
|
* drawn against, so it earns the page-level gesture. */
|
|
48
55
|
private cyclesEvent;
|
|
49
56
|
handleInput(data: string): void;
|
|
50
|
-
|
|
57
|
+
/** The rail's rows are the snapshot axes followed by the filter facets, so
|
|
58
|
+
* one cursor and one gesture drive both sections. */
|
|
59
|
+
private handleRail;
|
|
51
60
|
private handleDocs;
|
|
61
|
+
private openSearch;
|
|
62
|
+
/** Typing filters live, so the field is the list's own heading rather than a
|
|
63
|
+
* modal over the rows it is selecting. */
|
|
64
|
+
private handleSearch;
|
|
52
65
|
private handleDetail;
|
|
53
66
|
/** What the focused dossier row does when opened — the same write its letter
|
|
54
67
|
* key starts, so a user who never learns the letters can still edit. */
|
|
@@ -78,5 +91,9 @@ export declare class ContextAdminShell implements Component, Focusable {
|
|
|
78
91
|
private renderModal;
|
|
79
92
|
private footer;
|
|
80
93
|
render(width: number): string[];
|
|
94
|
+
/** What the delivering documents spend on this event. An estimate, and a
|
|
95
|
+
* floor on the rendered prompt: boot wraps these documents in its own intro
|
|
96
|
+
* prose and catalog tree. */
|
|
97
|
+
private costMeta;
|
|
81
98
|
}
|
|
82
99
|
export declare function runContextAdmin(seed: Snapshot): Promise<AdminResult | undefined>;
|