@hediet/linkrpc-cli 0.0.3-next.20260922.2 → 0.0.3-next.20260924.1
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/README.md +164 -6
- package/dist/chunks/{cli-FDKN1ydZ.js → cli-CY5WVWEQ.js} +3552 -2015
- package/dist/chunks/cli-CY5WVWEQ.js.map +1 -0
- package/dist/chunks/dist-BOcSU4-O.js +1500 -0
- package/dist/chunks/dist-BOcSU4-O.js.map +1 -0
- package/dist/chunks/{runApprovalUi-CTyhiDpa.js → runApprovalUi-C4yIUu8Y.js} +5 -4
- package/dist/chunks/{runApprovalUi-CTyhiDpa.js.map → runApprovalUi-C4yIUu8Y.js.map} +1 -1
- package/dist/chunks/{runComplete-Du5wmOdB.js → runComplete-F95L0neY.js} +19 -17
- package/dist/chunks/runComplete-F95L0neY.js.map +1 -0
- package/dist/chunks/{runUi-CuJr8_tY.js → runUi-Dp2P3aNN.js} +639 -92
- package/dist/chunks/runUi-Dp2P3aNN.js.map +1 -0
- package/dist/chunks/{scroll--AVfTs5W.js → scroll-CY9Rcshb.js} +2 -2
- package/dist/chunks/{scroll--AVfTs5W.js.map → scroll-CY9Rcshb.js.map} +1 -1
- package/dist/hub.js +1 -1
- package/dist/index.js +1 -1
- package/dist/linkrpc.js +1 -1
- package/dist/rpc.js +1 -1
- package/package.json +6 -6
- package/dist/chunks/cli-FDKN1ydZ.js.map +0 -1
- package/dist/chunks/runComplete-Du5wmOdB.js.map +0 -1
- package/dist/chunks/runUi-CuJr8_tY.js.map +0 -1
|
@@ -1,12 +1,292 @@
|
|
|
1
|
-
import { D as formatPrincipalSource, L as validateValueAgainstSchema, S as fetchSchema, T as walkHubDetailed, b as setupSigning, k as connect, x as fetchDefaults } from "./runComplete-
|
|
2
|
-
import { a as
|
|
3
|
-
import { n as
|
|
1
|
+
import { D as formatPrincipalSource, L as validateValueAgainstSchema, S as fetchSchema, T as walkHubDetailed, b as setupSigning, k as connect, r as withStaticHubReflection, x as fetchDefaults } from "./runComplete-F95L0neY.js";
|
|
2
|
+
import { a as formatCommandLegend, c as safeTerminalText, d as interfaceTarget, f as matchesTarget, i as dispatchHostKey, l as resolveViewTarget, n as views, o as hostCommands, p as targetId, r as ViewFrame, s as matchesKey, u as formatCondition } from "./cli-CY5WVWEQ.js";
|
|
3
|
+
import { a as derived, i as autorun, n as ObservablePromise, o as Disposable, r as observableValue, t as ObservableLazyPromise } from "./dist-BOcSU4-O.js";
|
|
4
|
+
import { n as useObservable, t as useScroll } from "./scroll-CY9Rcshb.js";
|
|
4
5
|
import { ChannelConnector, RpcError, SigningSender } from "@hediet/linkrpc";
|
|
5
6
|
import { isHubEndpoint, openHubChannel } from "@hediet/linkrpc/node";
|
|
7
|
+
import { stdin } from "node:process";
|
|
8
|
+
import { emitKeypressEvents } from "node:readline";
|
|
6
9
|
import React from "react";
|
|
7
|
-
import { Box, Text, render, useApp, useInput, useStdout } from "ink";
|
|
8
|
-
import TextInput from "ink-text-input";
|
|
10
|
+
import { Box, Text, measureElement, render, useApp, useInput, useStdout } from "ink";
|
|
9
11
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
import TextInput from "ink-text-input";
|
|
13
|
+
//#region src/ui/SchemaSession.tsx
|
|
14
|
+
/** Built-in read-only view; uses the same viewport and command contract as contributions. */
|
|
15
|
+
var SchemaSession = class {
|
|
16
|
+
schema;
|
|
17
|
+
element = /* @__PURE__ */ jsx(SchemaPanel, { session: this });
|
|
18
|
+
viewport = observableValue(this, {
|
|
19
|
+
rows: 24,
|
|
20
|
+
columns: 80,
|
|
21
|
+
top: 0,
|
|
22
|
+
left: 0,
|
|
23
|
+
disposed: false
|
|
24
|
+
});
|
|
25
|
+
constructor(schema) {
|
|
26
|
+
this.schema = schema;
|
|
27
|
+
}
|
|
28
|
+
lines = derived(this, (reader) => {
|
|
29
|
+
const value = this.schema.read(reader);
|
|
30
|
+
return (value.kind === "loaded" ? JSON.stringify(value.schema, null, 2) : value.kind === "error" ? String(value.error) : value.kind === "none" ? "Select an interface to inspect its schema." : "Loading schema…").split("\n").map(safeTerminalText);
|
|
31
|
+
});
|
|
32
|
+
commands = derived(this, (reader) => {
|
|
33
|
+
const viewport = this.viewport.read(reader);
|
|
34
|
+
if (viewport.disposed) return [];
|
|
35
|
+
const lines = this.lines.read(reader);
|
|
36
|
+
const page = Math.max(1, viewport.rows - 2);
|
|
37
|
+
const maxTop = Math.max(0, lines.length - page);
|
|
38
|
+
const maxLeft = Math.max(0, lines.reduce((width, line) => Math.max(width, [...line].length), 0) - viewport.columns);
|
|
39
|
+
const top = Math.min(viewport.top, maxTop), left = Math.min(viewport.left, maxLeft);
|
|
40
|
+
const move = (vertical, horizontal = 0) => this.viewport.set({
|
|
41
|
+
...this.viewport.get(),
|
|
42
|
+
top: Math.max(0, Math.min(maxTop, top + vertical)),
|
|
43
|
+
left: Math.max(0, Math.min(maxLeft, left + horizontal))
|
|
44
|
+
}, void 0);
|
|
45
|
+
const command = (id, title, keys, execute, enabled) => ({
|
|
46
|
+
id: `schema.${id}`,
|
|
47
|
+
title,
|
|
48
|
+
keybindings: keys.map((key) => ({ key })),
|
|
49
|
+
execute,
|
|
50
|
+
enabled,
|
|
51
|
+
context: "Schema"
|
|
52
|
+
});
|
|
53
|
+
return [
|
|
54
|
+
command("up", "Scroll up", ["up", "k"], () => move(-1), top > 0),
|
|
55
|
+
command("down", "Scroll down", ["down", "j"], () => move(1), top < maxTop),
|
|
56
|
+
command("pageUp", "Page up", ["pageup"], () => move(-page), top > 0),
|
|
57
|
+
command("pageDown", "Page down", ["pagedown"], () => move(page), top < maxTop),
|
|
58
|
+
command("first", "First line", ["home", "g"], () => move(-maxTop), top > 0),
|
|
59
|
+
command("last", "Last line", ["end", "G"], () => move(maxTop), top < maxTop),
|
|
60
|
+
command("left", "Scroll left", ["left", "h"], () => move(0, -20), left > 0),
|
|
61
|
+
command("right", "Scroll right", ["right", "l"], () => move(0, 20), left < maxLeft)
|
|
62
|
+
];
|
|
63
|
+
});
|
|
64
|
+
setViewport(rows, columns = this.viewport.get().columns) {
|
|
65
|
+
const state = this.viewport.get();
|
|
66
|
+
if (rows !== state.rows || columns !== state.columns) this.viewport.set({
|
|
67
|
+
...state,
|
|
68
|
+
rows,
|
|
69
|
+
columns
|
|
70
|
+
}, void 0);
|
|
71
|
+
}
|
|
72
|
+
captureState() {
|
|
73
|
+
const { top, left } = this.viewport.get();
|
|
74
|
+
return {
|
|
75
|
+
top,
|
|
76
|
+
left
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
restoreState(value) {
|
|
80
|
+
const state = value;
|
|
81
|
+
if (state) this.viewport.set({
|
|
82
|
+
...this.viewport.get(),
|
|
83
|
+
top: state.top ?? 0,
|
|
84
|
+
left: state.left ?? 0
|
|
85
|
+
}, void 0);
|
|
86
|
+
}
|
|
87
|
+
dispose() {
|
|
88
|
+
this.viewport.set({
|
|
89
|
+
...this.viewport.get(),
|
|
90
|
+
disposed: true
|
|
91
|
+
}, void 0);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var SchemaPanel = class extends React.Component {
|
|
95
|
+
state = { revision: 0 };
|
|
96
|
+
subscription;
|
|
97
|
+
componentDidMount() {
|
|
98
|
+
this.subscription = autorun((reader) => {
|
|
99
|
+
this.props.session.viewport.read(reader);
|
|
100
|
+
this.props.session.lines.read(reader);
|
|
101
|
+
this.setState((previous) => ({ revision: previous.revision + 1 }));
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
componentWillUnmount() {
|
|
105
|
+
this.subscription?.dispose();
|
|
106
|
+
}
|
|
107
|
+
render() {
|
|
108
|
+
const { session } = this.props;
|
|
109
|
+
const { rows, columns, top } = session.viewport.get();
|
|
110
|
+
const lines = session.lines.get();
|
|
111
|
+
const left = Math.min(session.viewport.get().left, Math.max(0, lines.reduce((width, line) => Math.max(width, [...line].length), 0) - columns));
|
|
112
|
+
const capacity = Math.max(1, rows - 2);
|
|
113
|
+
const start = Math.min(top, Math.max(0, lines.length - capacity));
|
|
114
|
+
const schema = session.schema.get();
|
|
115
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
116
|
+
flexDirection: "column",
|
|
117
|
+
width: columns,
|
|
118
|
+
height: rows,
|
|
119
|
+
flexShrink: 0,
|
|
120
|
+
overflow: "hidden",
|
|
121
|
+
children: [
|
|
122
|
+
rows > 1 && /* @__PURE__ */ jsx(Box, {
|
|
123
|
+
height: 1,
|
|
124
|
+
flexShrink: 0,
|
|
125
|
+
overflow: "hidden",
|
|
126
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
127
|
+
bold: true,
|
|
128
|
+
color: "cyan",
|
|
129
|
+
wrap: "truncate-end",
|
|
130
|
+
children: schema.kind === "loaded" ? `SCHEMA ${safeTerminalText(schema.schema.id)}` : "SCHEMA"
|
|
131
|
+
})
|
|
132
|
+
}),
|
|
133
|
+
Array.from({ length: capacity }, (_, offset) => /* @__PURE__ */ jsx(Box, {
|
|
134
|
+
height: 1,
|
|
135
|
+
flexShrink: 0,
|
|
136
|
+
overflow: "hidden",
|
|
137
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
138
|
+
wrap: "truncate-end",
|
|
139
|
+
children: [...lines[start + offset] ?? ""].slice(left).join("") || " "
|
|
140
|
+
})
|
|
141
|
+
}, offset)),
|
|
142
|
+
rows > 2 && /* @__PURE__ */ jsx(Box, {
|
|
143
|
+
height: 1,
|
|
144
|
+
flexShrink: 0,
|
|
145
|
+
overflow: "hidden",
|
|
146
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
147
|
+
dimColor: true,
|
|
148
|
+
wrap: "truncate-end",
|
|
149
|
+
children: `Lines ${start + 1}–${Math.min(lines.length, start + capacity)}/${lines.length} · column ${left + 1}`
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
]
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region src/ui/ViewController.ts
|
|
158
|
+
var ViewController = class extends Disposable {
|
|
159
|
+
channel;
|
|
160
|
+
selection;
|
|
161
|
+
schema;
|
|
162
|
+
listings;
|
|
163
|
+
tab = observableValue(this, "methods");
|
|
164
|
+
kind = observableValue(this, "interface");
|
|
165
|
+
session = observableValue(this, void 0);
|
|
166
|
+
error = observableValue(this, void 0);
|
|
167
|
+
connectionGeneration = observableValue(this, 0);
|
|
168
|
+
connected = observableValue(this, true);
|
|
169
|
+
generation = 0;
|
|
170
|
+
savedState;
|
|
171
|
+
sessionKey;
|
|
172
|
+
pendingCleanup = /* @__PURE__ */ new Set();
|
|
173
|
+
constructor(channel, selection, schema, listings) {
|
|
174
|
+
super();
|
|
175
|
+
this.channel = channel;
|
|
176
|
+
this.selection = selection;
|
|
177
|
+
this.schema = schema;
|
|
178
|
+
this.listings = listings;
|
|
179
|
+
this._register(autorun((reader) => {
|
|
180
|
+
const tab = this.tab.read(reader);
|
|
181
|
+
const target = this.target.read(reader);
|
|
182
|
+
this.connectionGeneration.read(reader);
|
|
183
|
+
const connected = this.connected.read(reader);
|
|
184
|
+
const generation = ++this.generation;
|
|
185
|
+
const previous = this.session.get();
|
|
186
|
+
if (previous && this.sessionKey) this.savedState = {
|
|
187
|
+
key: this.sessionKey,
|
|
188
|
+
state: previous.captureState?.()
|
|
189
|
+
};
|
|
190
|
+
this.stopSession(previous);
|
|
191
|
+
this.session.set(void 0, void 0);
|
|
192
|
+
this.error.set(void 0, void 0);
|
|
193
|
+
if (tab === "schema") {
|
|
194
|
+
const key = `schema:${target?.id ?? ""}`;
|
|
195
|
+
const session = new SchemaSession(this.schema);
|
|
196
|
+
if (this.savedState?.key === key) session.restoreState(this.savedState.state);
|
|
197
|
+
this.sessionKey = key;
|
|
198
|
+
this.session.set(session, void 0);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const view = views.find((value) => value.id === tab);
|
|
202
|
+
if (!view || !target || !matchesTarget(target, view) || !connected) return;
|
|
203
|
+
const key = `${tab}:${target.id}`;
|
|
204
|
+
resolveViewTarget(channel, view, target.id).then((context) => {
|
|
205
|
+
if (generation !== this.generation) return;
|
|
206
|
+
const session = view.createSession(context);
|
|
207
|
+
if (this.savedState?.key === key) session.restoreState?.(this.savedState.state);
|
|
208
|
+
this.sessionKey = key;
|
|
209
|
+
this.session.set(session, void 0);
|
|
210
|
+
}).catch((error) => {
|
|
211
|
+
if (generation === this.generation) this.error.set(String(error), void 0);
|
|
212
|
+
});
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
target = derived(this, (reader) => {
|
|
216
|
+
const selection = this.selection.read(reader);
|
|
217
|
+
if (!selection) return void 0;
|
|
218
|
+
const state = this.schema.read(reader);
|
|
219
|
+
const all = this.listings();
|
|
220
|
+
const listing = all.find((value) => value.serviceId === selection.serviceId && value.interfaceId === selection.interfaceId && !!value.isDefault === !!selection.isDefault);
|
|
221
|
+
const selected = {
|
|
222
|
+
...selection,
|
|
223
|
+
discoveredFrom: listing?.discoveredFrom ?? selection.serviceId,
|
|
224
|
+
hash: listing?.hash,
|
|
225
|
+
tags: state.kind === "loaded" ? state.schema.tags : listing?.tags
|
|
226
|
+
};
|
|
227
|
+
if (this.kind.read(reader) === "interface") return interfaceTarget(selected);
|
|
228
|
+
const interfaces = [];
|
|
229
|
+
for (const value of [selected, ...all.filter((value) => value.serviceId === selected.serviceId && value.discoveredFrom === selected.discoveredFrom)]) if (!interfaces.some((item) => item.interfaceId === value.interfaceId)) interfaces.push(value);
|
|
230
|
+
return {
|
|
231
|
+
id: targetId("service", selected),
|
|
232
|
+
kind: "service",
|
|
233
|
+
serviceId: selected.serviceId,
|
|
234
|
+
discoveredFrom: selected.discoveredFrom,
|
|
235
|
+
interfaces
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
tabs = derived(this, (reader) => {
|
|
239
|
+
const target = this.target.read(reader);
|
|
240
|
+
return [
|
|
241
|
+
{
|
|
242
|
+
id: "methods",
|
|
243
|
+
title: "Methods",
|
|
244
|
+
condition: "interface:any | service:any"
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
id: "schema",
|
|
248
|
+
title: "Schema",
|
|
249
|
+
condition: "interface:any | service:any"
|
|
250
|
+
},
|
|
251
|
+
...views.filter((view) => target && matchesTarget(target, view)).map((view) => ({
|
|
252
|
+
id: view.id,
|
|
253
|
+
title: view.title,
|
|
254
|
+
condition: view.conditions.map(formatCondition).join(" | ")
|
|
255
|
+
}))
|
|
256
|
+
];
|
|
257
|
+
});
|
|
258
|
+
cycleTab() {
|
|
259
|
+
const tabs = this.tabs.get();
|
|
260
|
+
const index = tabs.findIndex((tab) => tab.id === this.tab.get());
|
|
261
|
+
this.tab.set(tabs[(index + 1) % tabs.length].id, void 0);
|
|
262
|
+
}
|
|
263
|
+
toggleKind() {
|
|
264
|
+
this.kind.set(this.kind.get() === "interface" ? "service" : "interface", void 0);
|
|
265
|
+
}
|
|
266
|
+
disconnect() {
|
|
267
|
+
this.connected.set(false, void 0);
|
|
268
|
+
}
|
|
269
|
+
reconnect() {
|
|
270
|
+
if (!this.connected.get()) this.connected.set(true, void 0);
|
|
271
|
+
else this.connectionGeneration.set(this.connectionGeneration.get() + 1, void 0);
|
|
272
|
+
}
|
|
273
|
+
stopSession(session) {
|
|
274
|
+
session?.dispose();
|
|
275
|
+
const cleanup = session?.disposeAsync?.();
|
|
276
|
+
if (!cleanup) return;
|
|
277
|
+
this.pendingCleanup.add(cleanup);
|
|
278
|
+
cleanup.then(() => this.pendingCleanup.delete(cleanup), () => this.pendingCleanup.delete(cleanup));
|
|
279
|
+
}
|
|
280
|
+
async waitForIdle() {
|
|
281
|
+
await Promise.allSettled([...this.pendingCleanup]);
|
|
282
|
+
}
|
|
283
|
+
dispose() {
|
|
284
|
+
++this.generation;
|
|
285
|
+
this.stopSession(this.session.get());
|
|
286
|
+
super.dispose();
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
//#endregion
|
|
10
290
|
//#region src/ui/UiModel.ts
|
|
11
291
|
/**
|
|
12
292
|
* View model for the TUI. Owns the live channel and an in-memory schema cache
|
|
@@ -15,6 +295,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
15
295
|
*/
|
|
16
296
|
var UiModel = class extends Disposable {
|
|
17
297
|
_channel;
|
|
298
|
+
views;
|
|
18
299
|
/** Initial directory fetch — kicked off in the constructor. */
|
|
19
300
|
servicesPromise;
|
|
20
301
|
/** Non-fatal discovery failures for services that could not be inspected. */
|
|
@@ -63,6 +344,7 @@ var UiModel = class extends Disposable {
|
|
|
63
344
|
return result.services;
|
|
64
345
|
});
|
|
65
346
|
this.servicesPromise.promise.catch(() => {});
|
|
347
|
+
this.views = this._register(new ViewController(_channel, this.selection, this.currentSchemaState, () => this.servicesPromise.promiseResult.get()?.data ?? []));
|
|
66
348
|
this._register(autorun((reader) => {
|
|
67
349
|
const sel = this.selection.read(reader);
|
|
68
350
|
if (!sel) return;
|
|
@@ -617,24 +899,171 @@ function pad(text, width) {
|
|
|
617
899
|
return text.slice(0, width - 1) + "…";
|
|
618
900
|
}
|
|
619
901
|
//#endregion
|
|
902
|
+
//#region src/ui/ViewArea.tsx
|
|
903
|
+
/** Class binding keeps contributed views hook-free. Models own their sessions. */
|
|
904
|
+
var ViewArea = class extends React.Component {
|
|
905
|
+
state = { revision: 0 };
|
|
906
|
+
subscription;
|
|
907
|
+
componentDidMount() {
|
|
908
|
+
this.subscription = autorun((reader) => {
|
|
909
|
+
const model = this.props.model;
|
|
910
|
+
model.views.tab.read(reader);
|
|
911
|
+
model.views.kind.read(reader);
|
|
912
|
+
model.views.tabs.read(reader);
|
|
913
|
+
model.views.session.read(reader);
|
|
914
|
+
model.views.error.read(reader);
|
|
915
|
+
model.focusedColumn.read(reader);
|
|
916
|
+
model.currentSchemaState.read(reader);
|
|
917
|
+
this.setState((previous) => ({ revision: previous.revision + 1 }));
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
componentWillUnmount() {
|
|
921
|
+
this.subscription?.dispose();
|
|
922
|
+
}
|
|
923
|
+
render() {
|
|
924
|
+
const model = this.props.model;
|
|
925
|
+
const views = model.views;
|
|
926
|
+
const selected = views.tab.get();
|
|
927
|
+
const tabs = views.tabs.get();
|
|
928
|
+
const current = tabs.find((tab) => tab.id === selected);
|
|
929
|
+
const chromeRows = this.props.chromeRows ?? 2;
|
|
930
|
+
const contentRows = Math.max(1, this.props.height - chromeRows);
|
|
931
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
932
|
+
flexDirection: "column",
|
|
933
|
+
flexGrow: 1,
|
|
934
|
+
flexBasis: 0,
|
|
935
|
+
minWidth: 0,
|
|
936
|
+
height: this.props.height,
|
|
937
|
+
overflow: "hidden",
|
|
938
|
+
children: [
|
|
939
|
+
chromeRows > 0 && /* @__PURE__ */ jsx(Box, {
|
|
940
|
+
height: 1,
|
|
941
|
+
flexShrink: 0,
|
|
942
|
+
children: /* @__PURE__ */ jsxs(Text, {
|
|
943
|
+
wrap: "truncate-end",
|
|
944
|
+
children: [
|
|
945
|
+
views.kind.get(),
|
|
946
|
+
": ",
|
|
947
|
+
tabs.map((tab) => tab.id === selected ? `[${tab.title}]` : tab.title).join(" ")
|
|
948
|
+
]
|
|
949
|
+
})
|
|
950
|
+
}),
|
|
951
|
+
chromeRows > 1 && /* @__PURE__ */ jsx(Box, {
|
|
952
|
+
height: 1,
|
|
953
|
+
flexShrink: 0,
|
|
954
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
955
|
+
dimColor: true,
|
|
956
|
+
wrap: "truncate-end",
|
|
957
|
+
children: safeTerminalText(current?.condition ?? "Unavailable for this target")
|
|
958
|
+
})
|
|
959
|
+
}),
|
|
960
|
+
selected === "methods" ? /* @__PURE__ */ jsx(Box, {
|
|
961
|
+
height: contentRows,
|
|
962
|
+
flexShrink: 0,
|
|
963
|
+
overflow: "hidden",
|
|
964
|
+
children: this.props.children
|
|
965
|
+
}) : views.error.get() ? /* @__PURE__ */ jsx(Text, {
|
|
966
|
+
color: "red",
|
|
967
|
+
children: safeTerminalText(views.error.get())
|
|
968
|
+
}) : views.session.get() ? /* @__PURE__ */ jsx(ViewFrame, {
|
|
969
|
+
session: views.session.get(),
|
|
970
|
+
rows: contentRows,
|
|
971
|
+
maxColumns: this.props.columns,
|
|
972
|
+
tabbed: true,
|
|
973
|
+
active: model.focusedColumn.get() > 0
|
|
974
|
+
}) : /* @__PURE__ */ jsx(Text, { children: current ? "Loading view…" : "View unavailable; press tab" })
|
|
975
|
+
]
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
var ViewFooter = class extends React.Component {
|
|
980
|
+
state = { revision: 0 };
|
|
981
|
+
subscription;
|
|
982
|
+
componentDidMount() {
|
|
983
|
+
this.subscription = autorun((reader) => {
|
|
984
|
+
this.props.model.views.tab.read(reader);
|
|
985
|
+
this.props.model.views.session.read(reader);
|
|
986
|
+
this.props.model.focusedColumn.read(reader);
|
|
987
|
+
this.props.model.formEditing.read(reader);
|
|
988
|
+
this.setState((previous) => ({ revision: previous.revision + 1 }));
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
componentWillUnmount() {
|
|
992
|
+
this.subscription?.dispose();
|
|
993
|
+
}
|
|
994
|
+
render() {
|
|
995
|
+
const model = this.props.model;
|
|
996
|
+
if (model.views.session.get()) return /* @__PURE__ */ jsx(Text, {
|
|
997
|
+
dimColor: true,
|
|
998
|
+
children: model.focusedColumn.get() === 0 ? "Enter/→: focus view" : "View commands shown above"
|
|
999
|
+
});
|
|
1000
|
+
const noop = () => {};
|
|
1001
|
+
const commands = hostCommands({
|
|
1002
|
+
quit: noop,
|
|
1003
|
+
nextTab: noop,
|
|
1004
|
+
toggleScope: noop,
|
|
1005
|
+
back: model.views.tab.get() !== "methods" ? noop : void 0
|
|
1006
|
+
}, model.formEditing.get());
|
|
1007
|
+
return /* @__PURE__ */ jsx(Text, {
|
|
1008
|
+
dimColor: true,
|
|
1009
|
+
wrap: "truncate-end",
|
|
1010
|
+
children: (model.views.tab.get() === "methods" ? "←/→: switch column · ↑/↓: move · Enter: edit/submit · Esc: cancel edit · " : "") + formatCommandLegend(commands)
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
//#endregion
|
|
1015
|
+
//#region src/views/TerminalKeyInput.tsx
|
|
1016
|
+
/** Readline preserves named keys such as Home/End that Ink's Key type omits. */
|
|
1017
|
+
var TerminalKeyInput = class extends React.Component {
|
|
1018
|
+
onKey = (input, key) => this.props.onKey({
|
|
1019
|
+
...key,
|
|
1020
|
+
input
|
|
1021
|
+
});
|
|
1022
|
+
componentDidMount() {
|
|
1023
|
+
emitKeypressEvents(stdin);
|
|
1024
|
+
stdin.on("keypress", this.onKey);
|
|
1025
|
+
}
|
|
1026
|
+
componentWillUnmount() {
|
|
1027
|
+
stdin.off("keypress", this.onKey);
|
|
1028
|
+
}
|
|
1029
|
+
render() {
|
|
1030
|
+
return null;
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
//#endregion
|
|
620
1034
|
//#region src/ui/App.tsx
|
|
621
|
-
const IDENTITY_HEADER_H = 1;
|
|
622
|
-
const FOOTER_H = 1;
|
|
623
|
-
const RESULT_H = 8;
|
|
624
1035
|
/** Column chrome that is not list rows: top border + bottom border + title. */
|
|
625
1036
|
const COLUMN_CHROME_H = 3;
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
const
|
|
629
|
-
const
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
1037
|
+
function uiLayout(columns, rows, focusedColumn, warningCount = 0) {
|
|
1038
|
+
const width = Math.max(1, Math.floor(columns));
|
|
1039
|
+
const height = Math.max(1, Math.floor(rows));
|
|
1040
|
+
const compact = width < 60;
|
|
1041
|
+
const headerRows = height > 2 ? 1 + Math.min(warningCount, Math.max(0, height - 10)) : 0;
|
|
1042
|
+
const footerRows = height > 1 ? 1 : 0;
|
|
1043
|
+
const bodyRows = Math.max(1, height - headerRows - footerRows);
|
|
1044
|
+
const sidebarWidth = compact ? focusedColumn === 0 ? width : 0 : Math.min(28, Math.max(18, Math.floor(width * .22)));
|
|
1045
|
+
const mainWidth = width - sidebarWidth;
|
|
1046
|
+
const viewHeaderRows = Math.min(bodyRows - 1, height < 20 ? 1 : 2);
|
|
1047
|
+
const contentRows = bodyRows - viewHeaderRows;
|
|
1048
|
+
const resultRows = Math.min(Math.max(0, contentRows - 1), Math.max(3, Math.min(6, Math.floor(contentRows / 3))));
|
|
1049
|
+
const methodRows = contentRows - resultRows;
|
|
1050
|
+
const splitMethods = mainWidth >= 65;
|
|
1051
|
+
return {
|
|
1052
|
+
width,
|
|
1053
|
+
height,
|
|
1054
|
+
compact,
|
|
1055
|
+
headerRows,
|
|
1056
|
+
footerRows,
|
|
1057
|
+
bodyRows,
|
|
1058
|
+
sidebarWidth,
|
|
1059
|
+
mainWidth,
|
|
1060
|
+
viewHeaderRows,
|
|
1061
|
+
contentRows,
|
|
1062
|
+
resultRows,
|
|
1063
|
+
methodRows,
|
|
1064
|
+
splitMethods,
|
|
1065
|
+
methodWidth: splitMethods ? Math.min(30, Math.max(22, Math.floor(mainWidth * .35))) : mainWidth
|
|
1066
|
+
};
|
|
638
1067
|
}
|
|
639
1068
|
/**
|
|
640
1069
|
* Miller-column TUI: three columns left → right (services, methods, form)
|
|
@@ -643,67 +1072,162 @@ function useTerminalRows() {
|
|
|
643
1072
|
* 0 / 1 immediately previews into the column to its right.
|
|
644
1073
|
*/
|
|
645
1074
|
const App = ({ model }) => {
|
|
646
|
-
const focusedColumn = useObservable(model.focusedColumn);
|
|
647
|
-
const editing = useObservable(model.formEditing);
|
|
648
|
-
const discoveryWarnings = useObservable(model.discoveryWarnings);
|
|
649
|
-
const termRows = useTerminalRows();
|
|
650
1075
|
const { exit } = useApp();
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
1076
|
+
const { stdout } = useStdout();
|
|
1077
|
+
useInput(() => {});
|
|
1078
|
+
return /* @__PURE__ */ jsx(AppLayout, {
|
|
1079
|
+
model,
|
|
1080
|
+
exit,
|
|
1081
|
+
stdout
|
|
1082
|
+
});
|
|
1083
|
+
};
|
|
1084
|
+
var AppLayout = class extends React.Component {
|
|
1085
|
+
state = { revision: 0 };
|
|
1086
|
+
measuredColumns = observableValue(this, void 0);
|
|
1087
|
+
root = React.createRef();
|
|
1088
|
+
mounted = false;
|
|
1089
|
+
subscription;
|
|
1090
|
+
invalidate = () => this.setState((previous) => ({ revision: previous.revision + 1 }));
|
|
1091
|
+
componentDidMount() {
|
|
1092
|
+
this.mounted = true;
|
|
1093
|
+
this.props.stdout.prependListener("resize", this.invalidate);
|
|
1094
|
+
this.subscription = autorun((reader) => {
|
|
1095
|
+
const { model } = this.props;
|
|
1096
|
+
model.focusedColumn.read(reader);
|
|
1097
|
+
model.formEditing.read(reader);
|
|
1098
|
+
model.discoveryWarnings.read(reader);
|
|
1099
|
+
model.views.tab.read(reader);
|
|
1100
|
+
this.measuredColumns.read(reader);
|
|
1101
|
+
this.invalidate();
|
|
1102
|
+
});
|
|
1103
|
+
this.measure();
|
|
1104
|
+
}
|
|
1105
|
+
componentDidUpdate() {
|
|
1106
|
+
this.measure();
|
|
1107
|
+
}
|
|
1108
|
+
measure() {
|
|
1109
|
+
queueMicrotask(() => {
|
|
1110
|
+
if (!this.mounted || !this.root.current) return;
|
|
1111
|
+
const width = Math.max(1, Math.floor(measureElement(this.root.current).width));
|
|
1112
|
+
if (width !== this.measuredColumns.get()) this.measuredColumns.set(width, void 0);
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
componentWillUnmount() {
|
|
1116
|
+
this.mounted = false;
|
|
1117
|
+
this.subscription?.dispose();
|
|
1118
|
+
this.props.stdout.off("resize", this.invalidate);
|
|
1119
|
+
}
|
|
1120
|
+
onKey = (event) => {
|
|
1121
|
+
const { model, exit } = this.props;
|
|
1122
|
+
const focusedColumn = model.focusedColumn.get();
|
|
1123
|
+
if (model.formEditing.get()) {
|
|
1124
|
+
if (matchesKey({
|
|
1125
|
+
key: "c",
|
|
1126
|
+
ctrl: true
|
|
1127
|
+
}, event)) exit();
|
|
655
1128
|
return;
|
|
656
1129
|
}
|
|
657
|
-
|
|
1130
|
+
const inView = model.views.tab.get() !== "methods" && focusedColumn > 0;
|
|
1131
|
+
if (dispatchHostKey(inView ? model.views.session.get() : void 0, event, {
|
|
1132
|
+
quit: exit,
|
|
1133
|
+
nextTab: () => model.views.cycleTab(),
|
|
1134
|
+
toggleScope: () => model.views.toggleKind(),
|
|
1135
|
+
back: inView ? () => model.focusColumn(0) : void 0
|
|
1136
|
+
})) return;
|
|
1137
|
+
if (inView) return;
|
|
1138
|
+
if (event.name === "left" && focusedColumn > 0) {
|
|
658
1139
|
model.focusColumn(focusedColumn - 1);
|
|
659
1140
|
return;
|
|
660
1141
|
}
|
|
661
|
-
if (
|
|
1142
|
+
if (event.name === "right" && focusedColumn < 2) {
|
|
662
1143
|
model.focusColumn(focusedColumn + 1);
|
|
663
1144
|
return;
|
|
664
1145
|
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}),
|
|
686
|
-
/* @__PURE__ */ jsx(MethodsColumn, {
|
|
687
|
-
model,
|
|
688
|
-
focused: focusedColumn === 1,
|
|
689
|
-
height: bodyHeight,
|
|
690
|
-
listHeight
|
|
691
|
-
}),
|
|
692
|
-
/* @__PURE__ */ jsx(FormColumn, {
|
|
1146
|
+
};
|
|
1147
|
+
render() {
|
|
1148
|
+
const { model, stdout } = this.props;
|
|
1149
|
+
const focusedColumn = model.focusedColumn.get();
|
|
1150
|
+
const warnings = model.discoveryWarnings.get();
|
|
1151
|
+
const layout = uiLayout(Math.min(this.measuredColumns.get() ?? 1, stdout.columns ?? 80), stdout.rows ?? 30, focusedColumn, warnings.length);
|
|
1152
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1153
|
+
ref: this.root,
|
|
1154
|
+
flexDirection: "column",
|
|
1155
|
+
width: "100%",
|
|
1156
|
+
minWidth: 0,
|
|
1157
|
+
height: layout.height,
|
|
1158
|
+
overflow: "hidden",
|
|
1159
|
+
children: [
|
|
1160
|
+
/* @__PURE__ */ jsx(TerminalKeyInput, { onKey: this.onKey }),
|
|
1161
|
+
layout.headerRows > 0 && /* @__PURE__ */ jsx(Box, {
|
|
1162
|
+
height: layout.headerRows,
|
|
1163
|
+
flexShrink: 0,
|
|
1164
|
+
overflow: "hidden",
|
|
1165
|
+
children: /* @__PURE__ */ jsx(Header, {
|
|
693
1166
|
model,
|
|
694
|
-
|
|
695
|
-
height: bodyHeight,
|
|
696
|
-
listHeight
|
|
1167
|
+
warnings: warnings.slice(0, layout.headerRows - 1)
|
|
697
1168
|
})
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
1169
|
+
}),
|
|
1170
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
1171
|
+
width: layout.width,
|
|
1172
|
+
height: layout.bodyRows,
|
|
1173
|
+
flexShrink: 0,
|
|
1174
|
+
overflow: "hidden",
|
|
1175
|
+
children: [layout.sidebarWidth > 0 && /* @__PURE__ */ jsx(ServicesColumn, {
|
|
1176
|
+
model,
|
|
1177
|
+
focused: focusedColumn === 0,
|
|
1178
|
+
width: layout.sidebarWidth,
|
|
1179
|
+
height: layout.bodyRows,
|
|
1180
|
+
listHeight: Math.max(1, layout.bodyRows - COLUMN_CHROME_H)
|
|
1181
|
+
}), layout.mainWidth > 0 && /* @__PURE__ */ jsx(Box, {
|
|
1182
|
+
width: layout.mainWidth,
|
|
1183
|
+
height: layout.bodyRows,
|
|
1184
|
+
flexShrink: 0,
|
|
1185
|
+
overflow: "hidden",
|
|
1186
|
+
children: /* @__PURE__ */ jsx(ViewArea, {
|
|
1187
|
+
model,
|
|
1188
|
+
height: layout.bodyRows,
|
|
1189
|
+
columns: layout.mainWidth,
|
|
1190
|
+
chromeRows: layout.viewHeaderRows,
|
|
1191
|
+
children: /* @__PURE__ */ jsxs(Box, {
|
|
1192
|
+
flexDirection: "column",
|
|
1193
|
+
height: layout.contentRows,
|
|
1194
|
+
width: layout.mainWidth,
|
|
1195
|
+
flexShrink: 0,
|
|
1196
|
+
overflow: "hidden",
|
|
1197
|
+
children: [/* @__PURE__ */ jsxs(Box, {
|
|
1198
|
+
height: layout.methodRows,
|
|
1199
|
+
width: layout.mainWidth,
|
|
1200
|
+
flexShrink: 0,
|
|
1201
|
+
overflow: "hidden",
|
|
1202
|
+
children: [(layout.splitMethods || focusedColumn !== 2) && /* @__PURE__ */ jsx(MethodsColumn, {
|
|
1203
|
+
model,
|
|
1204
|
+
focused: focusedColumn === 1,
|
|
1205
|
+
width: layout.methodWidth,
|
|
1206
|
+
height: layout.methodRows,
|
|
1207
|
+
listHeight: Math.max(1, layout.methodRows - COLUMN_CHROME_H)
|
|
1208
|
+
}), (layout.splitMethods || focusedColumn === 2) && /* @__PURE__ */ jsx(FormColumn, {
|
|
1209
|
+
model,
|
|
1210
|
+
focused: focusedColumn === 2,
|
|
1211
|
+
height: layout.methodRows,
|
|
1212
|
+
listHeight: Math.max(1, layout.methodRows - COLUMN_CHROME_H)
|
|
1213
|
+
})]
|
|
1214
|
+
}), layout.resultRows > 0 && /* @__PURE__ */ jsx(ResultPane, {
|
|
1215
|
+
model,
|
|
1216
|
+
height: layout.resultRows
|
|
1217
|
+
})]
|
|
1218
|
+
})
|
|
1219
|
+
})
|
|
1220
|
+
})]
|
|
1221
|
+
}),
|
|
1222
|
+
layout.footerRows > 0 && /* @__PURE__ */ jsx(Box, {
|
|
1223
|
+
height: layout.footerRows,
|
|
1224
|
+
flexShrink: 0,
|
|
1225
|
+
overflow: "hidden",
|
|
1226
|
+
children: /* @__PURE__ */ jsx(ViewFooter, { model })
|
|
1227
|
+
})
|
|
1228
|
+
]
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
707
1231
|
};
|
|
708
1232
|
/** "▲ N more" / "▼ N more" indicator row, shown only when items are hidden. */
|
|
709
1233
|
const ScrollIndicator = ({ direction, count }) => {
|
|
@@ -717,17 +1241,24 @@ const Header = ({ model, warnings }) => {
|
|
|
717
1241
|
const identity = useObservable(model.identity);
|
|
718
1242
|
return /* @__PURE__ */ jsxs(Box, {
|
|
719
1243
|
flexDirection: "column",
|
|
720
|
-
children: [/* @__PURE__ */ jsxs(Box, {
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
1244
|
+
children: [/* @__PURE__ */ jsxs(Box, {
|
|
1245
|
+
height: 1,
|
|
1246
|
+
flexShrink: 0,
|
|
1247
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
1248
|
+
dimColor: true,
|
|
1249
|
+
children: "identity: "
|
|
1250
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
1251
|
+
wrap: "truncate-end",
|
|
1252
|
+
children: identity ?? "(resolving…)"
|
|
1253
|
+
})]
|
|
1254
|
+
}), warnings.map((warning) => /* @__PURE__ */ jsxs(Text, {
|
|
724
1255
|
color: "yellow",
|
|
725
1256
|
wrap: "truncate-end",
|
|
726
1257
|
children: ["! ", warning]
|
|
727
1258
|
}, warning))]
|
|
728
1259
|
});
|
|
729
1260
|
};
|
|
730
|
-
const ServicesColumn = ({ model, focused, height, listHeight }) => {
|
|
1261
|
+
const ServicesColumn = ({ model, focused, width, height, listHeight }) => {
|
|
731
1262
|
const result = useObservable(model.servicesPromise.promiseResult);
|
|
732
1263
|
const selection = useObservable(model.selection);
|
|
733
1264
|
useInput((_input, key) => {
|
|
@@ -741,7 +1272,7 @@ const ServicesColumn = ({ model, focused, height, listHeight }) => {
|
|
|
741
1272
|
return /* @__PURE__ */ jsx(Column, {
|
|
742
1273
|
title: "Services",
|
|
743
1274
|
focused,
|
|
744
|
-
width
|
|
1275
|
+
width,
|
|
745
1276
|
height,
|
|
746
1277
|
children: !result ? /* @__PURE__ */ jsx(Text, {
|
|
747
1278
|
dimColor: true,
|
|
@@ -775,7 +1306,7 @@ const ServicesColumn = ({ model, focused, height, listHeight }) => {
|
|
|
775
1306
|
] })
|
|
776
1307
|
});
|
|
777
1308
|
};
|
|
778
|
-
const MethodsColumn = ({ model, focused, height, listHeight }) => {
|
|
1309
|
+
const MethodsColumn = ({ model, focused, width, height, listHeight }) => {
|
|
779
1310
|
const selection = useObservable(model.selection);
|
|
780
1311
|
const schemaState = useObservable(model.currentSchemaState);
|
|
781
1312
|
const methods = useObservable(model.currentMethods);
|
|
@@ -789,7 +1320,7 @@ const MethodsColumn = ({ model, focused, height, listHeight }) => {
|
|
|
789
1320
|
return /* @__PURE__ */ jsx(Column, {
|
|
790
1321
|
title: "Methods",
|
|
791
1322
|
focused,
|
|
792
|
-
width
|
|
1323
|
+
width,
|
|
793
1324
|
height,
|
|
794
1325
|
children: !selection ? /* @__PURE__ */ jsx(Text, {
|
|
795
1326
|
dimColor: true,
|
|
@@ -938,7 +1469,7 @@ const MethodForm = ({ model, method, focused, listHeight }) => {
|
|
|
938
1469
|
children: [
|
|
939
1470
|
/* @__PURE__ */ jsx(Text, {
|
|
940
1471
|
bold: true,
|
|
941
|
-
children: (method.result === void 0 ? "notify " : "request ") +
|
|
1472
|
+
children: (method.result === void 0 ? "notify " : "request ") + method.name
|
|
942
1473
|
}),
|
|
943
1474
|
method.summary ? /* @__PURE__ */ jsx(Text, {
|
|
944
1475
|
dimColor: true,
|
|
@@ -993,6 +1524,7 @@ const Column = ({ title, focused, children, width, flexGrow, height }) => {
|
|
|
993
1524
|
width,
|
|
994
1525
|
flexGrow,
|
|
995
1526
|
height,
|
|
1527
|
+
minWidth: 0,
|
|
996
1528
|
overflow: "hidden",
|
|
997
1529
|
borderStyle: "single",
|
|
998
1530
|
borderColor: focused ? "cyan" : void 0,
|
|
@@ -1003,13 +1535,14 @@ const Column = ({ title, focused, children, width, flexGrow, height }) => {
|
|
|
1003
1535
|
}), children]
|
|
1004
1536
|
});
|
|
1005
1537
|
};
|
|
1006
|
-
const ResultPane = ({ model }) => {
|
|
1538
|
+
const ResultPane = ({ model, height }) => {
|
|
1007
1539
|
const promise = useObservable(model.lastCall);
|
|
1008
1540
|
const result = useObservable(promise ? promise.promiseResult : NO_RESULT);
|
|
1009
1541
|
const chunks = useObservable(model.streamChunks);
|
|
1010
1542
|
return /* @__PURE__ */ jsxs(Box, {
|
|
1011
1543
|
flexDirection: "column",
|
|
1012
|
-
height
|
|
1544
|
+
height,
|
|
1545
|
+
flexShrink: 0,
|
|
1013
1546
|
overflow: "hidden",
|
|
1014
1547
|
borderStyle: "single",
|
|
1015
1548
|
paddingX: 1,
|
|
@@ -1056,20 +1589,21 @@ function formatError(e) {
|
|
|
1056
1589
|
//#endregion
|
|
1057
1590
|
//#region src/ui/runUi.tsx
|
|
1058
1591
|
async function runUi(opts) {
|
|
1059
|
-
if (isHubEndpoint(opts.endpoint)) await _runUiReconnecting(opts.endpoint, opts.principalSpec);
|
|
1060
|
-
else await _runUiOnce(opts.endpoint, opts.principalSpec);
|
|
1592
|
+
if (isHubEndpoint(opts.endpoint) && opts.profile !== "rpc") await _runUiReconnecting(opts.endpoint, opts.principalSpec, opts.schema);
|
|
1593
|
+
else await _runUiOnce(opts.endpoint, opts.principalSpec, opts.schema, opts.profile);
|
|
1061
1594
|
}
|
|
1062
1595
|
/** Non-Hub endpoints: a single connection, no redial. */
|
|
1063
|
-
async function _runUiOnce(endpoint, principalSpec) {
|
|
1596
|
+
async function _runUiOnce(endpoint, principalSpec, schema, profile) {
|
|
1064
1597
|
const conn = await connect(endpoint);
|
|
1065
|
-
const identity = _isRawEndpoint(endpoint) ? "unsigned (raw endpoint)" : _formatIdentity(await setupSigning(conn.channel, conn.signing, principalSpec, { negotiateHubCaps: false }));
|
|
1066
|
-
const model = new UiModel(conn.channel);
|
|
1598
|
+
const identity = _isRawEndpoint(endpoint) || profile === "rpc" ? "unsigned (raw endpoint)" : _formatIdentity(await setupSigning(conn.channel, conn.signing, principalSpec, { negotiateHubCaps: false }));
|
|
1599
|
+
const model = new UiModel(schema ? withStaticHubReflection(conn.channel, schema) : conn.channel);
|
|
1067
1600
|
model.identity.set(identity, void 0);
|
|
1068
|
-
const instance = render(/* @__PURE__ */ jsx(App, { model }));
|
|
1601
|
+
const instance = render(/* @__PURE__ */ jsx(App, { model }), { exitOnCtrlC: false });
|
|
1069
1602
|
try {
|
|
1070
1603
|
await instance.waitUntilExit();
|
|
1071
1604
|
} finally {
|
|
1072
1605
|
model.dispose();
|
|
1606
|
+
await model.views.waitForIdle();
|
|
1073
1607
|
conn.close();
|
|
1074
1608
|
}
|
|
1075
1609
|
}
|
|
@@ -1082,7 +1616,7 @@ function _isRawEndpoint(endpoint) {
|
|
|
1082
1616
|
* open a fresh hub channel, install signing, and point the swappable sender at
|
|
1083
1617
|
* the new signed channel. The TUI is rendered once, after the first connect.
|
|
1084
1618
|
*/
|
|
1085
|
-
async function _runUiReconnecting(endpoint, principalSpec) {
|
|
1619
|
+
async function _runUiReconnecting(endpoint, principalSpec, schema) {
|
|
1086
1620
|
const swappable = new SwappableSender();
|
|
1087
1621
|
let model;
|
|
1088
1622
|
const hubEndpoint = endpoint.kind === "ws" ? endpoint.url : endpoint.path;
|
|
@@ -1094,16 +1628,29 @@ async function _runUiReconnecting(endpoint, principalSpec) {
|
|
|
1094
1628
|
const signed = SigningSender.wrapChannel(channel, signing).sender;
|
|
1095
1629
|
const session = await setupSigning(signed, signing, principalSpec, { negotiateHubCaps: true });
|
|
1096
1630
|
swappable.setTarget(signed);
|
|
1631
|
+
channel.onClose(() => {
|
|
1632
|
+
swappable.setTarget(void 0);
|
|
1633
|
+
model?.views.disconnect();
|
|
1634
|
+
model?.identity.set("disconnected; reconnecting…", void 0);
|
|
1635
|
+
});
|
|
1097
1636
|
if (!model) {
|
|
1098
|
-
model = new UiModel(swappable);
|
|
1637
|
+
model = new UiModel(schema ? withStaticHubReflection(swappable, schema) : swappable);
|
|
1099
1638
|
model.identity.set(_formatIdentity(session), void 0);
|
|
1100
|
-
render(/* @__PURE__ */ jsx(App, { model })).waitUntilExit().
|
|
1101
|
-
|
|
1639
|
+
render(/* @__PURE__ */ jsx(App, { model }), { exitOnCtrlC: false }).waitUntilExit().then(async () => {
|
|
1640
|
+
model?.dispose();
|
|
1641
|
+
await model?.views.waitForIdle();
|
|
1642
|
+
handle.stop();
|
|
1643
|
+
}, () => handle.stop());
|
|
1644
|
+
} else {
|
|
1645
|
+
model.identity.set(_formatIdentity(session), void 0);
|
|
1646
|
+
model.views.reconnect();
|
|
1647
|
+
}
|
|
1102
1648
|
});
|
|
1103
1649
|
try {
|
|
1104
1650
|
await handle.done;
|
|
1105
1651
|
} finally {
|
|
1106
1652
|
model?.dispose();
|
|
1653
|
+
await model?.views.waitForIdle();
|
|
1107
1654
|
}
|
|
1108
1655
|
}
|
|
1109
1656
|
/** One-line identity label for the TUI header (source + truncated nodeId). */
|
|
@@ -1140,4 +1687,4 @@ var SwappableSender = class {
|
|
|
1140
1687
|
//#endregion
|
|
1141
1688
|
export { runUi };
|
|
1142
1689
|
|
|
1143
|
-
//# sourceMappingURL=runUi-
|
|
1690
|
+
//# sourceMappingURL=runUi-Dp2P3aNN.js.map
|