@hediet/linkrpc-cli 0.0.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runUi-D-8LMU4F.js","names":[],"sources":["../../src/ui/UiModel.ts","../../src/ui/schemaInspect.ts","../../src/ui/FieldRow.tsx","../../src/ui/App.tsx","../../src/ui/runUi.tsx"],"sourcesContent":["import {\n Disposable,\n ObservableLazyPromise,\n ObservablePromise,\n autorun,\n derived,\n observableValue,\n} from \"@vscode/observables\";\nimport type {\n MethodSchema,\n LinkRpcInterfaceSchema as SvcInterfaceSchema,\n LinkRpcJsonSchema as SvcJsonSchema,\n} from \"@hediet/linkrpc\";\nimport {\n type CliChannel,\n fetchDefaults,\n fetchSchema,\n walkHubDetailed,\n} from \"@hediet/linkrpc-client\";\nimport { validateValueAgainstSchema } from \"../validation\";\n\nexport interface MethodKey {\n /** \"\" means the root (no `serviceId::` prefix on the wire). */\n readonly serviceId: string;\n readonly interfaceId: string;\n /** The synthetic default entry invokes methods in bare form. */\n readonly isDefault?: boolean;\n /** `undefined` => no method picked yet; the view shows the method list. */\n readonly methodName: string | undefined;\n}\n\nexport interface UiServiceListing {\n readonly serviceId: string;\n readonly interfaceId: string;\n readonly hash?: string;\n readonly discoveredFrom: string;\n readonly path?: string;\n readonly isDefault?: boolean;\n}\n\nexport interface CallOutcome {\n readonly result: unknown;\n readonly latencyMs: number;\n}\n\nexport interface HistoryEntry {\n readonly method: string;\n readonly params: unknown;\n readonly result: unknown | undefined;\n readonly error: unknown | undefined;\n readonly latencyMs: number;\n readonly timestamp: number;\n}\n\nexport interface FieldDesc {\n readonly name: string;\n readonly required: boolean;\n readonly schema: SvcJsonSchema;\n}\n\nexport type MethodListItem = MethodSchema & { readonly name: string };\n\nexport type SchemaState =\n | { kind: \"none\" }\n | { kind: \"loading\" }\n | { kind: \"error\"; error: unknown }\n | { kind: \"loaded\"; schema: SvcInterfaceSchema; method: MethodListItem | undefined };\n\n/**\n * View model for the TUI. Owns the live channel and an in-memory schema cache\n * keyed by interface id. Loading state for every async lookup is expressed as\n * an `ObservablePromise` from `@vscode/observables` — no ad-hoc loading flags.\n */\nexport class UiModel extends Disposable {\n /** Initial directory fetch — kicked off in the constructor. */\n public readonly servicesPromise: ObservablePromise<UiServiceListing[]>;\n\n /** Non-fatal discovery failures for services that could not be inspected. */\n public readonly discoveryWarnings = observableValue<readonly string[]>(\n \"UiModel.discoveryWarnings\",\n [],\n );\n\n public readonly selection = observableValue<MethodKey | undefined>(\n \"UiModel.selection\",\n undefined,\n );\n\n public readonly formValues = observableValue<Record<string, unknown>>(\n \"UiModel.formValues\",\n {},\n );\n\n /** Index of the currently-focused form field (clamped at render time). */\n public readonly formCursor = observableValue<number>(\n \"UiModel.formCursor\",\n 0,\n );\n\n /** True when the focused field is in input mode (text being typed). */\n public readonly formEditing = observableValue<boolean>(\n \"UiModel.formEditing\",\n false,\n );\n\n /** Which Miller column currently owns keyboard focus. */\n public readonly focusedColumn = observableValue<0 | 1 | 2>(\n \"UiModel.focusedColumn\",\n 0,\n );\n\n public readonly rawJsonMode = observableValue<boolean>(\n \"UiModel.rawJsonMode\",\n false,\n );\n\n public readonly rawJsonText = observableValue<string>(\n \"UiModel.rawJsonText\",\n \"{}\",\n );\n\n public readonly history = observableValue<HistoryEntry[]>(\n \"UiModel.history\",\n [],\n );\n\n /**\n * Wraps the in-flight call (if any). `undefined` until the user first\n * submits, then re-assigned on each submit. The view reads\n * `promiseResult` to render loading / result / error.\n */\n public readonly lastCall = observableValue<ObservablePromise<CallOutcome> | undefined>(\n \"UiModel.lastCall\",\n undefined,\n );\n\n /**\n * Server→client stream messages received during the in-flight call.\n * Reset to `[]` on each `submit()`, then appended to as `$stream::send`\n * notifications arrive. The view renders these live below the result.\n */\n public readonly streamChunks = observableValue<readonly unknown[]>(\n \"UiModel.streamChunks\",\n [],\n );\n\n /**\n * One-line, human-readable description of the identity signing outbound\n * calls (e.g. `managed (node abcd012345…)`). Rendered in the header.\n * `undefined` until `runUi` resolves the principal.\n */\n public readonly identity = observableValue<string | undefined>(\n \"UiModel.identity\",\n undefined,\n );\n\n /**\n * Per-interface schema cache. `ObservableLazyPromise` defers the actual\n * fetch until something asks for it; the autorun below makes \"something\n * asks\" happen whenever a method on that interface is selected.\n */\n private readonly _schemas = new Map<string, ObservableLazyPromise<SvcInterfaceSchema>>();\n\n constructor(\n private readonly _channel: CliChannel,\n ) {\n super();\n this.servicesPromise = ObservablePromise.fromFn(async () => {\n const result = await loadUiServices(_channel);\n this.discoveryWarnings.set(result.warnings, undefined);\n return result.services;\n });\n // Drain the bare promise so an early close (test teardown, user quit\n // before the call settles) doesn't surface as an unhandled rejection.\n // The error is still observable via `servicesPromise.promiseResult`.\n this.servicesPromise.promise.catch(() => { });\n\n // When selection changes, kick the lazy schema promise so its\n // observable state starts ticking. The derived below only OBSERVES;\n // it doesn't trigger.\n this._register(autorun((reader) => {\n const sel = this.selection.read(reader);\n if (!sel) return;\n void this._getOrCreateSchema(\n sel.serviceId,\n sel.interfaceId,\n sel.isDefault === true,\n ).getPromise().catch(() => { });\n }));\n // Miller-column convenience: when the methods column gains focus and\n // no method is picked yet, auto-select the first one so the form\n // column shows a preview instead of an empty pane.\n this._register(autorun((reader) => {\n if (this.focusedColumn.read(reader) !== 1) return;\n const sel = this.selection.read(reader);\n if (!sel || sel.methodName !== undefined) return;\n const methods = this.currentMethods.read(reader);\n if (methods.length === 0) return;\n this.selection.set({ ...sel, methodName: methods[0].name }, undefined);\n }));\n }\n\n public readonly currentSchemaState = derived(this, (reader): SchemaState => {\n const sel = this.selection.read(reader);\n if (!sel) return { kind: \"none\" };\n const lazy = this._getOrCreateSchema(\n sel.serviceId,\n sel.interfaceId,\n sel.isDefault === true,\n );\n const result = lazy.cachedPromiseResult.read(reader);\n if (!result) return { kind: \"loading\" };\n if (result.error) return { kind: \"error\", error: result.error };\n const schema = result.data!;\n const methodSchema = sel.methodName !== undefined\n ? schema.methods[sel.methodName]\n : undefined;\n const method = methodSchema && sel.methodName !== undefined\n ? { ...methodSchema, name: sel.methodName }\n : undefined;\n return { kind: \"loaded\", schema, method };\n });\n\n /** Flat list of top-level fields for the currently-selected method. */\n public readonly currentFields = derived(this, (reader): readonly FieldDesc[] => {\n const state = this.currentSchemaState.read(reader);\n if (state.kind !== \"loaded\" || !state.method) return [];\n const paramsSchema = state.method.params;\n return collectTopLevelFields(\n paramsSchema,\n state.schema.components?.schemas ?? {},\n );\n });\n\n /** All methods on the currently-selected interface (column 2). */\n public readonly currentMethods = derived(this, (reader): readonly MethodListItem[] => {\n const state = this.currentSchemaState.read(reader);\n return state.kind === \"loaded\"\n ? Object.entries(state.schema.methods).map(([name, method]) => ({ name, ...method }))\n : [];\n });\n\n /**\n * Whether the currently-selected method declares stream payloads in\n * either direction. Drives the \"streaming\" affordance in the view.\n */\n public readonly currentMethodStreams = derived(this, (reader): { server: boolean; client: boolean } => {\n const state = this.currentSchemaState.read(reader);\n if (state.kind !== \"loaded\" || !state.method) return { server: false, client: false };\n return {\n server: state.method.serverStream !== undefined,\n client: state.method.clientStream !== undefined,\n };\n });\n\n /** Per-field validation errors against the live method schema. */\n public readonly formErrors = derived(this, (reader): ReadonlyMap<string, string> => {\n const state = this.currentSchemaState.read(reader);\n const errors = new Map<string, string>();\n if (state.kind !== \"loaded\" || !state.method) return errors;\n const paramsSchema = state.method.params;\n const components = state.schema.components?.schemas ?? {};\n const resolvedParamsSchema = resolveSchema(paramsSchema, components);\n if (!isObjectSchema(resolvedParamsSchema)) return errors;\n const values = this.formValues.read(reader);\n const required = new Set(resolvedParamsSchema.required ?? []);\n for (const [name, sub] of Object.entries(resolvedParamsSchema.properties)) {\n const value = values[name];\n if (value === undefined) {\n if (required.has(name)) errors.set(name, \"required\");\n continue;\n }\n const reason = validateValueAgainstSchema(value, sub, components);\n if (reason !== undefined) errors.set(name, reason);\n }\n return errors;\n });\n\n public readonly canSubmit = derived(this, (reader): boolean => {\n const sel = this.selection.read(reader);\n if (!sel || sel.methodName === undefined) return false;\n const state = this.currentSchemaState.read(reader);\n if (state.kind !== \"loaded\" || !state.method) return false;\n return this.formErrors.read(reader).size === 0;\n });\n\n public select(key: MethodKey): void {\n this.selection.set(key, undefined);\n this.formValues.set({}, undefined);\n this.rawJsonText.set(\"{}\", undefined);\n this.formCursor.set(0, undefined);\n this.formEditing.set(false, undefined);\n }\n\n /** Pick a concrete method on the currently-selected service/interface. */\n public selectMethod(methodName: string): void {\n const sel = this.selection.get();\n if (!sel) return;\n this.select({ ...sel, methodName });\n }\n\n public focusColumn(column: 0 | 1 | 2): void {\n this.focusedColumn.set(column, undefined);\n }\n\n /**\n * Move the column-0 cursor by `delta`. The cursor is implicit: it's the\n * row whose `(serviceId, interfaceId)` matches the current selection.\n * Stepping off the ends clamps; methodName is cleared so the methods\n * column re-previews the new service.\n */\n public moveServiceCursor(delta: -1 | 1): void {\n const services = this.servicesPromise.promiseResult.get()?.data ?? [];\n if (services.length === 0) return;\n const sel = this.selection.get();\n const currentIdx = sel\n ? services.findIndex((s) =>\n s.serviceId === sel.serviceId\n && s.interfaceId === sel.interfaceId\n && (s.isDefault === true) === (sel.isDefault === true))\n : -1;\n const nextIdx = Math.max(0, Math.min(services.length - 1, currentIdx + delta));\n const next = services[nextIdx];\n this.select({\n serviceId: next.serviceId,\n interfaceId: next.interfaceId,\n methodName: undefined,\n ...(next.isDefault === true ? { isDefault: true } : {}),\n });\n }\n\n /**\n * Move the column-1 cursor by `delta`. Triggers a full `select()` so the\n * form column starts fresh for the newly-previewed method (Miller-style\n * \"each cursor move is a new preview\").\n */\n public moveMethodCursor(delta: -1 | 1): void {\n const methods = this.currentMethods.get();\n if (methods.length === 0) return;\n const sel = this.selection.get();\n if (!sel) return;\n const currentIdx = sel.methodName !== undefined\n ? methods.findIndex((m) => m.name === sel.methodName)\n : -1;\n const nextIdx = Math.max(0, Math.min(methods.length - 1, currentIdx + delta));\n this.selectMethod(methods[nextIdx].name);\n }\n\n public setField(name: string, value: unknown): void {\n const current = this.formValues.get();\n if (value === undefined) {\n // Clearing a field — drop the key so required-checks fire and so\n // wire payloads don't carry explicit `undefined`s.\n const { [name]: _drop, ...rest } = current;\n void _drop;\n this.formValues.set(rest, undefined);\n return;\n }\n this.formValues.set({ ...current, [name]: value }, undefined);\n }\n\n /**\n * Returns the exact `{ method, params }` `submit` would send right\n * now, or `undefined` when no method is fully selected. Lets a host\n * (e.g. the explorer's access-request prompt) preview the concrete\n * call before issuing a one-shot capability bound to it.\n */\n public peekPendingCall(): { method: string; params: unknown } | undefined {\n const sel = this.selection.get();\n if (!sel || sel.methodName === undefined) return undefined;\n const params = this._collectParams();\n const method = sel.isDefault === true\n ? sel.methodName\n : sel.serviceId\n ? `${sel.serviceId}::${sel.interfaceId}::${sel.methodName}`\n : `${sel.interfaceId}::${sel.methodName}`;\n return { method, params };\n }\n\n public submit(): void {\n const sel = this.selection.get();\n if (!sel || sel.methodName === undefined) return;\n const params = this._collectParams();\n const wireMethod = sel.isDefault === true\n ? sel.methodName\n : sel.serviceId\n ? `${sel.serviceId}::${sel.interfaceId}::${sel.methodName}`\n : `${sel.interfaceId}::${sel.methodName}`;\n const start = performance.now();\n this.streamChunks.set([], undefined);\n const promise = ObservablePromise.fromFn(async () => {\n // Stream-capable send: appends any server→client `$stream::send`\n // messages to `streamChunks` as they arrive, then resolves with\n // the call's final response. Behaves like a plain request when the\n // method does not stream.\n const call = this._channel.sendRequestWithStream(wireMethod, params as never, {\n onStreamMessage: (payload) => {\n this.streamChunks.set([...this.streamChunks.get(), payload], undefined);\n },\n });\n const result = await call.result;\n return { result, latencyMs: performance.now() - start } satisfies CallOutcome;\n });\n this.lastCall.set(promise, undefined);\n // Record into history once it settles — keep one place that writes\n // the journal, so views don't have to worry about it.\n promise.promise.then(\n (outcome) => {\n this.history.set(\n [\n ...this.history.get(),\n { method: wireMethod, params, result: outcome.result, error: undefined, latencyMs: outcome.latencyMs, timestamp: Date.now() },\n ],\n undefined,\n );\n },\n (error) => {\n const latencyMs = performance.now() - start;\n this.history.set(\n [\n ...this.history.get(),\n { method: wireMethod, params, result: undefined, error, latencyMs, timestamp: Date.now() },\n ],\n undefined,\n );\n },\n );\n }\n\n private _collectParams(): unknown {\n if (this.rawJsonMode.get()) {\n const text = this.rawJsonText.get().trim();\n if (text.length === 0) return undefined;\n try {\n return JSON.parse(text);\n } catch {\n // The view shows the parse error; submit still sends the\n // unparsable text as a string so the wire layer can complain.\n return text;\n }\n }\n return this.formValues.get();\n }\n\n private _getOrCreateSchema(\n serviceId: string,\n interfaceId: string,\n isDefault: boolean,\n ): ObservableLazyPromise<SvcInterfaceSchema> {\n const key = `${isDefault ? \"default\" : serviceId}::${interfaceId}`;\n let p = this._schemas.get(key);\n if (!p) {\n // Fetch schemas from the directory that reported the interface.\n const listings = this.servicesPromise.promiseResult.get()?.data ?? [];\n const match = listings.find(\n (s) =>\n s.serviceId === serviceId\n && s.interfaceId === interfaceId\n && (s.isDefault === true) === isDefault,\n );\n const reporter = isDefault ? \"\" : (match?.discoveredFrom ?? serviceId);\n const target = reporter === \"\" ? undefined : reporter;\n p = new ObservableLazyPromise(() =>\n fetchSchema(this._channel, interfaceId, match?.hash, target));\n this._schemas.set(key, p);\n }\n return p;\n }\n}\n\ninterface UiServicesResult {\n readonly services: UiServiceListing[];\n readonly warnings: readonly string[];\n}\n\nasync function loadUiServices(channel: CliChannel): Promise<UiServicesResult> {\n const [walkResult, defaultsResult] = await Promise.all([\n walkHubDetailed(channel),\n fetchDefaults(channel).then(\n (defaults) => ({ ok: true as const, defaults }),\n (error: unknown) => ({ ok: false as const, error }),\n ),\n ]);\n\n const warnings = walkResult.inaccessible.map(({ serviceId, reason }) =>\n `Service \"${serviceId}\" does not offer a usable hubrpc.directory::list: ${reason}`\n );\n if (!defaultsResult.ok) {\n warnings.push(\n `The root service does not offer hubrpc.defaults::get: ${getErrorMessage(defaultsResult.error)}`,\n );\n return { services: walkResult.listings, warnings };\n }\n\n const { defaults } = defaultsResult;\n if (defaults.interfaceId === undefined) {\n return { services: walkResult.listings, warnings };\n }\n return {\n services: [{\n serviceId: \"\",\n interfaceId: defaults.interfaceId,\n ...(defaults.hash === undefined ? {} : { hash: defaults.hash }),\n discoveredFrom: \"\",\n isDefault: true,\n }, ...walkResult.listings],\n warnings,\n };\n}\n\nfunction getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction collectTopLevelFields(\n schema: SvcJsonSchema | undefined,\n components: Readonly<Record<string, SvcJsonSchema>>,\n): FieldDesc[] {\n const resolved = resolveSchema(schema, components);\n if (!isObjectSchema(resolved)) return [];\n const required = new Set(resolved.required ?? []);\n return Object.entries(resolved.properties).map(([name, sub]) => ({\n name,\n required: required.has(name),\n schema: resolveSchema(sub, components) ?? sub,\n }));\n}\n\nfunction resolveSchema(\n schema: SvcJsonSchema | undefined,\n components: Readonly<Record<string, SvcJsonSchema>>,\n seen: ReadonlySet<string> = new Set(),\n): SvcJsonSchema | undefined {\n if (\n schema === undefined\n || typeof schema !== \"object\"\n || !(\"$ref\" in schema)\n ) {\n return schema;\n }\n const prefix = \"#/components/schemas/\";\n if (!schema.$ref.startsWith(prefix) || seen.has(schema.$ref)) {\n return schema;\n }\n const target = components[schema.$ref.slice(prefix.length)];\n if (target === undefined) {\n return schema;\n }\n return resolveSchema(target, components, new Set([...seen, schema.$ref]));\n}\n\nfunction isObjectSchema(\n s: SvcJsonSchema | undefined,\n): s is Extract<SvcJsonSchema, { type: \"object\"; properties: Record<string, SvcJsonSchema> }> {\n return !!s\n && typeof s === \"object\"\n && \"type\" in s\n && s.type === \"object\"\n && \"properties\" in s;\n}\n","import type { LinkRpcJsonSchema as SvcJsonSchema } from \"@hediet/linkrpc\";\n\n/**\n * Coarse classification of a field for the TUI. The form uses this to pick\n * a widget — fall back to free-form JSON editing for anything we can't render\n * inline.\n */\nexport type FieldKind =\n | \"string\"\n | \"number\"\n | \"integer\"\n | \"boolean\"\n | \"enum\"\n | \"json\";\n\nexport interface FieldClassification {\n readonly kind: FieldKind;\n /** Enumerated values for `kind === \"enum\"`. */\n readonly enumValues?: ReadonlyArray<unknown>;\n}\n\nexport function classifyField(schema: SvcJsonSchema): FieldClassification {\n if (typeof schema === \"boolean\") return { kind: \"json\" };\n if (\"enum\" in schema) {\n return { kind: \"enum\", enumValues: schema.enum };\n }\n if (\"const\" in schema) {\n return { kind: \"enum\", enumValues: [schema.const] };\n }\n if (\"anyOf\" in schema) {\n // Treat unions of constants (or single-value branches) as enums so\n // they get the cycle widget instead of a JSON editor.\n const values: unknown[] = [];\n for (const branch of schema.anyOf) {\n if (typeof branch === \"object\" && \"const\" in branch) values.push(branch.const);\n else if (typeof branch === \"object\" && \"enum\" in branch) values.push(...branch.enum);\n else return { kind: \"json\" };\n }\n return { kind: \"enum\", enumValues: values };\n }\n if (\"type\" in schema && typeof schema.type === \"string\") {\n switch (schema.type) {\n case \"string\": return { kind: \"string\" };\n case \"number\": return { kind: \"number\" };\n case \"integer\": return { kind: \"integer\" };\n case \"boolean\": return { kind: \"boolean\" };\n default: return { kind: \"json\" }; // object, array, null\n }\n }\n return { kind: \"json\" };\n}\n\n/** A sensible default value for a freshly-focused field, by kind. */\nexport function defaultValueFor(c: FieldClassification): unknown {\n switch (c.kind) {\n case \"string\": return \"\";\n case \"number\":\n case \"integer\": return 0;\n case \"boolean\": return false;\n case \"enum\": return c.enumValues?.[0] ?? null;\n case \"json\": return null;\n }\n}\n\n/**\n * Move to the next/previous value in an enum field. Wraps at the ends. The\n * cycle is what powers `←`/`→` on enum fields without needing edit mode.\n */\nexport function cycleEnum(\n values: ReadonlyArray<unknown>,\n current: unknown,\n delta: 1 | -1,\n): unknown {\n if (values.length === 0) return current;\n const idx = values.findIndex((v) => deepEqual(v, current));\n const next = (idx < 0 ? 0 : idx + delta + values.length) % values.length;\n return values[next];\n}\n\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a !== typeof b) return false;\n if (a === null || b === null) return false;\n if (typeof a !== \"object\") return false;\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\n/** Parse a user-typed string into a value compatible with the given kind. */\nexport function parseTypedValue(\n raw: string,\n kind: FieldKind,\n): { ok: true; value: unknown } | { ok: false; error: string } {\n switch (kind) {\n case \"string\": return { ok: true, value: raw };\n case \"number\": {\n if (raw.trim() === \"\") return { ok: true, value: undefined };\n const n = Number(raw);\n if (!Number.isFinite(n)) return { ok: false, error: `not a number: ${raw}` };\n return { ok: true, value: n };\n }\n case \"integer\": {\n if (raw.trim() === \"\") return { ok: true, value: undefined };\n const n = Number(raw);\n if (!Number.isInteger(n)) return { ok: false, error: `not an integer: ${raw}` };\n return { ok: true, value: n };\n }\n case \"json\": {\n if (raw.trim() === \"\") return { ok: true, value: undefined };\n try { return { ok: true, value: JSON.parse(raw) }; }\n catch (e) { return { ok: false, error: (e as Error).message }; }\n }\n case \"boolean\":\n case \"enum\":\n // These don't go through edit mode at all.\n return { ok: false, error: \"not editable via text input\" };\n }\n}\n\n/** Render the current value for display next to the field label. */\nexport function displayValue(value: unknown, kind: FieldKind): string {\n if (value === undefined) return \"(unset)\";\n if (kind === \"string\") return typeof value === \"string\" ? value : JSON.stringify(value);\n if (kind === \"boolean\") return value ? \"[x]\" : \"[ ]\";\n if (kind === \"json\") {\n const text = JSON.stringify(value);\n return text.length > 60 ? text.slice(0, 57) + \"...\" : text;\n }\n return JSON.stringify(value);\n}\n\n/** A short human description of the schema, shown in dim text after the value. */\nexport function describeSchema(s: SvcJsonSchema): string {\n if (typeof s === \"boolean\") return s ? \"any\" : \"never\";\n if (\"type\" in s && typeof s.type === \"string\") return s.type;\n if (\"enum\" in s) return `enum(${s.enum.map((v) => JSON.stringify(v)).join(\" | \")})`;\n if (\"const\" in s) return `const ${JSON.stringify(s.const)}`;\n if (\"anyOf\" in s) return s.anyOf.map(describeSchema).join(\" | \");\n if (\"$ref\" in s) return s.$ref;\n return \"?\";\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport TextInput from \"ink-text-input\";\nimport type { FieldDesc, UiModel } from \"./UiModel\";\nimport {\n classifyField,\n describeSchema,\n parseTypedValue,\n type FieldClassification,\n} from \"./schemaInspect\";\n\nexport interface FieldRowProps {\n readonly model: UiModel;\n readonly field: FieldDesc;\n readonly value: unknown;\n readonly focused: boolean;\n readonly editing: boolean;\n readonly error: string | undefined;\n}\n\nconst NAME_W = 12;\nconst VALUE_W = 24;\n\n/**\n * One row of the method form. To keep ink/yoga happy across resizes, the\n * non-editing path renders the whole row as a single `<Text>` with nested\n * colored spans, instead of multiple Boxes that try to share the column's\n * width. Boxes-around-Text in row direction tend to collapse to the\n * narrowest measurement on the first paint — single-Text rows lay out\n * predictably.\n *\n * The editing path swaps the value column for a `TextInput`; the surrounding\n * row stays a Box because TextInput is a component, not a string.\n */\nexport const FieldRow: React.FC<FieldRowProps> = ({ model, field, value, focused, editing, error }) => {\n const cls = classifyField(field.schema);\n const cursor = focused ? \"› \" : \" \";\n const namePadded = pad(field.name, NAME_W);\n const optionalMark = field.required ? \"\" : \"?\";\n const typeLabel = describeSchema(field.schema);\n\n return (\n <Box flexDirection=\"column\" flexShrink={0}>\n {editing ? (\n <Box flexDirection=\"row\" flexShrink={0}>\n <Text color={focused ? \"cyan\" : undefined}>{cursor}</Text>\n <Text bold>{namePadded}</Text>\n <Text dimColor>{optionalMark === \"\" ? \"\" : optionalMark + \" \"}</Text>\n <Box flexGrow={1} flexShrink={1}>\n <TextEditor model={model} field={field} value={value} cls={cls} />\n </Box>\n <Text dimColor> {typeLabel}</Text>\n </Box>\n ) : (\n <Text>\n <Text color={focused ? \"cyan\" : undefined}>{cursor}</Text>\n <Text bold>{namePadded}</Text>\n <Text dimColor>{optionalMark + \" \"}</Text>\n <Text color=\"yellow\">{pad(formatValue(value, cls), VALUE_W)}</Text>\n <Text dimColor> {typeLabel}</Text>\n </Text>\n )}\n {error ? (\n <Box marginLeft={NAME_W + 2} flexShrink={0}>\n <Text color=\"red\">! {error}</Text>\n </Box>\n ) : null}\n </Box>\n );\n};\n\nconst TextEditor: React.FC<{\n model: UiModel;\n field: FieldDesc;\n value: unknown;\n cls: FieldClassification;\n}> = ({ model, field, value, cls }) => {\n const initial = textForEditing(value, cls.kind);\n const [draft, setDraft] = React.useState(initial);\n React.useEffect(() => { setDraft(initial); }, [initial]);\n\n return (\n <TextInput\n value={draft}\n onChange={setDraft}\n onSubmit={(submitted) => {\n const parsed = parseTypedValue(submitted, cls.kind);\n if (parsed.ok) {\n model.setField(field.name, parsed.value);\n model.formEditing.set(false, undefined);\n }\n // Invalid input: stay in edit mode. The error row already\n // shows the schema-derived complaint via `formErrors`.\n }}\n focus={true}\n />\n );\n};\n\nfunction textForEditing(value: unknown, kind: FieldClassification[\"kind\"]): string {\n if (value === undefined) return \"\";\n if (kind === \"string\") return typeof value === \"string\" ? value : JSON.stringify(value);\n if (kind === \"json\") return JSON.stringify(value);\n return String(value);\n}\n\nfunction formatValue(value: unknown, cls: FieldClassification): string {\n if (value === undefined) return \"(unset)\";\n switch (cls.kind) {\n case \"boolean\": return value ? \"[x]\" : \"[ ]\";\n case \"enum\": return `< ${stringifyEnum(value)} >`;\n case \"string\": return typeof value === \"string\" ? value : JSON.stringify(value);\n case \"json\": {\n const text = JSON.stringify(value);\n return text.length > 40 ? text.slice(0, 37) + \"...\" : text;\n }\n default: return JSON.stringify(value);\n }\n}\n\nfunction stringifyEnum(value: unknown): string {\n return typeof value === \"string\" ? value : JSON.stringify(value);\n}\n\n/** Pad to `width` with spaces, or truncate with `…` if too long. */\nfunction pad(text: string, width: number): string {\n if (text.length === width) return text;\n if (text.length < width) return text + \" \".repeat(width - text.length);\n return text.slice(0, width - 1) + \"…\";\n}\n","import React from \"react\";\nimport { Box, Text, useApp, useInput, useStdout } from \"ink\";\nimport { observableValue } from \"@vscode/observables\";\nimport { useObservable } from \"./useObservable\";\nimport { UiModel } from \"./UiModel\";\nimport { FieldRow } from \"./FieldRow\";\nimport { classifyField, cycleEnum, defaultValueFor } from \"./schemaInspect\";\nimport { useScroll } from \"./scroll\";\nimport type { MethodSchema } from \"@hediet/linkrpc\";\n\nexport interface AppProps {\n readonly model: UiModel;\n}\n\n// Fixed-height regions framing the scrollable columns. Heights are explicit\n// (not flex) so the windowing math below knows exactly how many rows each list\n// may render — Ink/Yoga gives no usable measurement before paint.\nconst IDENTITY_HEADER_H = 1;\nconst FOOTER_H = 1;\nconst RESULT_H = 8;\n/** Column chrome that is not list rows: top border + bottom border + title. */\nconst COLUMN_CHROME_H = 3;\n\n/** Re-render on terminal resize so the height math tracks the live row count. */\nfunction useTerminalRows(): number {\n const { stdout } = useStdout();\n const [rows, setRows] = React.useState(stdout.rows ?? 30);\n React.useEffect(() => {\n const onResize = () => setRows(stdout.rows ?? 30);\n stdout.on(\"resize\", onResize);\n return () => {\n stdout.off(\"resize\", onResize);\n };\n }, [stdout]);\n return rows;\n}\n\n/**\n * Miller-column TUI: three columns left → right (services, methods, form)\n * plus a result pane at the bottom. ←/→ moves focus between columns, ↑/↓\n * moves the cursor inside the focused column. Each cursor move in columns\n * 0 / 1 immediately previews into the column to its right.\n */\nexport const App: React.FC<AppProps> = ({ model }) => {\n const focusedColumn = useObservable(model.focusedColumn);\n const editing = useObservable(model.formEditing);\n const discoveryWarnings = useObservable(model.discoveryWarnings);\n const termRows = useTerminalRows();\n const { exit } = useApp();\n\n useInput((input, key) => {\n // While editing a field, the text input owns input — don't intercept\n // ← / → for column nav (those move the text cursor).\n if (editing) return;\n if (input === \"q\") {\n exit();\n return;\n }\n if (key.leftArrow && focusedColumn > 0) {\n model.focusColumn((focusedColumn - 1) as 0 | 1 | 2);\n return;\n }\n if (key.rightArrow && focusedColumn < 2) {\n model.focusColumn((focusedColumn + 1) as 0 | 1 | 2);\n return;\n }\n });\n\n // Body (the three columns) gets whatever rows are left after the fixed\n // header, result pane and footer. `bodyHeight` is the column height; the\n // list area inside a column is that minus the column chrome.\n const headerHeight = IDENTITY_HEADER_H + discoveryWarnings.length;\n const bodyHeight = Math.max(COLUMN_CHROME_H + 1, termRows - headerHeight - RESULT_H - FOOTER_H);\n const listHeight = Math.max(1, bodyHeight - COLUMN_CHROME_H);\n\n return (\n <Box flexDirection=\"column\" height={termRows}>\n <Header model={model} warnings={discoveryWarnings} />\n <Box height={bodyHeight}>\n <ServicesColumn model={model} focused={focusedColumn === 0} height={bodyHeight} listHeight={listHeight} />\n <MethodsColumn model={model} focused={focusedColumn === 1} height={bodyHeight} listHeight={listHeight} />\n <FormColumn model={model} focused={focusedColumn === 2} height={bodyHeight} listHeight={listHeight} />\n </Box>\n <ResultPane model={model} />\n <Box>\n <Text dimColor>\n {\"\\u2190/\\u2192: switch column | \\u2191/\\u2193: move | enter: edit / submit | esc: cancel edit | q: quit\"}\n </Text>\n </Box>\n </Box>\n );\n};\n\n/** \"▲ N more\" / \"▼ N more\" indicator row, shown only when items are hidden. */\nconst ScrollIndicator: React.FC<{ direction: \"up\" | \"down\"; count: number }> = ({ direction, count }) => {\n if (count <= 0) return null;\n const arrow = direction === \"up\" ? \"\\u25b2\" : \"\\u25bc\";\n return <Text dimColor>{` ${arrow} ${count} more`}</Text>;\n};\n\n// -- header: signing identity --\n\nconst Header: React.FC<{ model: UiModel; warnings: readonly string[] }> = ({ model, warnings }) => {\n const identity = useObservable(model.identity);\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text dimColor>identity: </Text>\n <Text>{identity ?? \"(resolving…)\"}</Text>\n </Box>\n {warnings.map((warning) => (\n <Text key={warning} color=\"yellow\" wrap=\"truncate-end\">! {warning}</Text>\n ))}\n </Box>\n );\n};\n\n// -- col 0: services --\n\nconst ServicesColumn: React.FC<{ model: UiModel; focused: boolean; height: number; listHeight: number }> = ({ model, focused, height, listHeight }) => {\n const result = useObservable(model.servicesPromise.promiseResult);\n const selection = useObservable(model.selection);\n\n useInput(\n (_input, key) => {\n if (key.upArrow) model.moveServiceCursor(-1);\n else if (key.downArrow) model.moveServiceCursor(1);\n else if (key.return) model.focusColumn(1);\n },\n { isActive: focused },\n );\n\n const services = !result || result.error ? [] : (result.data ?? []);\n const cursorIdx = selection\n ? services.findIndex((s) =>\n s.serviceId === selection.serviceId\n && s.interfaceId === selection.interfaceId\n && (s.isDefault === true) === (selection.isDefault === true))\n : -1;\n const win = useScroll(services.length, Math.max(0, cursorIdx), listHeight);\n\n return (\n <Column title=\"Services\" focused={focused} width=\"25%\" height={height}>\n {!result ? (\n <Text dimColor>Loading…</Text>\n ) : result.error ? (\n <Text color=\"red\">Error: {String(result.error)}</Text>\n ) : services.length === 0 ? (\n <Text dimColor>(no services)</Text>\n ) : (\n <>\n <ScrollIndicator direction=\"up\" count={win.above} />\n {services.slice(win.start, win.end).map((s) => {\n const isCursor = !!selection\n && selection.serviceId === s.serviceId\n && selection.interfaceId === s.interfaceId\n && (selection.isDefault === true) === (s.isDefault === true);\n return (\n <Text\n key={`${s.isDefault === true ? \"default\" : s.serviceId}/${s.interfaceId}`}\n color={isCursor ? (focused ? \"cyan\" : \"white\") : undefined}\n wrap=\"truncate-end\"\n >\n {(isCursor ? \"\\u203a \" : \" \")\n + (s.isDefault === true ? \"default \" : s.serviceId ? `${s.serviceId} ` : \"\")}\n <Text dimColor>{s.interfaceId}</Text>\n </Text>\n );\n })}\n <ScrollIndicator direction=\"down\" count={win.below} />\n </>\n )}\n </Column>\n );\n};\n\n// -- col 1: methods --\n\nconst MethodsColumn: React.FC<{ model: UiModel; focused: boolean; height: number; listHeight: number }> = ({ model, focused, height, listHeight }) => {\n const selection = useObservable(model.selection);\n const schemaState = useObservable(model.currentSchemaState);\n const methods = useObservable(model.currentMethods);\n\n useInput(\n (_input, key) => {\n if (key.upArrow) model.moveMethodCursor(-1);\n else if (key.downArrow) model.moveMethodCursor(1);\n else if (key.return) model.focusColumn(2);\n },\n { isActive: focused },\n );\n\n const cursorIdx = selection?.methodName !== undefined\n ? methods.findIndex((m) => m.name === selection.methodName)\n : -1;\n const win = useScroll(methods.length, Math.max(0, cursorIdx), listHeight);\n\n return (\n <Column title=\"Methods\" focused={focused} width=\"25%\" height={height}>\n {!selection ? (\n <Text dimColor>(select a service)</Text>\n ) : schemaState.kind === \"loading\" ? (\n <Text dimColor>Loading…</Text>\n ) : schemaState.kind === \"error\" ? (\n <Text color=\"red\">Error: {String(schemaState.error)}</Text>\n ) : methods.length === 0 ? (\n <Text dimColor>(no methods)</Text>\n ) : (\n <>\n <ScrollIndicator direction=\"up\" count={win.above} />\n {methods.slice(win.start, win.end).map((m) => {\n const isCursor = selection.methodName === m.name;\n return (\n <Text\n key={m.name}\n color={isCursor ? (focused ? \"cyan\" : \"white\") : undefined}\n wrap=\"truncate-end\"\n >\n {(isCursor ? \"\\u203a \" : \" \")}\n <Text dimColor>{m.result === undefined ? \"notify \" : \"req \"}</Text>\n {m.name}\n {m.serverStream !== undefined ? <Text color=\"magenta\">{\" \\u2193stream\"}</Text> : null}\n </Text>\n );\n })}\n <ScrollIndicator direction=\"down\" count={win.below} />\n </>\n )}\n </Column>\n );\n};\n\n// -- col 2: form --\n\nconst FormColumn: React.FC<{ model: UiModel; focused: boolean; height: number; listHeight: number }> = ({ model, focused, height, listHeight }) => {\n const selection = useObservable(model.selection);\n const schemaState = useObservable(model.currentSchemaState);\n\n return (\n <Column title=\"Form\" focused={focused} flexGrow={1} height={height}>\n {!selection ? (\n <Text dimColor>(select a service first)</Text>\n ) : selection.methodName === undefined ? (\n <Text dimColor>(select a method first)</Text>\n ) : schemaState.kind === \"loading\" ? (\n <Text dimColor>Loading…</Text>\n ) : schemaState.kind === \"loaded\" && schemaState.method ? (\n <MethodForm model={model} method={schemaState.method} focused={focused} listHeight={listHeight} />\n ) : schemaState.kind === \"error\" ? (\n <Text color=\"red\">Error: {String(schemaState.error)}</Text>\n ) : (\n <Text color=\"red\">Method \"{selection.methodName}\" not in schema</Text>\n )}\n </Column>\n );\n};\n\nconst MethodForm: React.FC<{ model: UiModel; method: MethodSchema; focused: boolean; listHeight: number }> = ({ model, method, focused, listHeight }) => {\n const fields = useObservable(model.currentFields);\n const formValues = useObservable(model.formValues);\n const errors = useObservable(model.formErrors);\n const canSubmit = useObservable(model.canSubmit);\n const cursor = useObservable(model.formCursor);\n const editing = useObservable(model.formEditing);\n\n const submitRowIdx = fields.length;\n const safeCursor = Math.max(0, Math.min(submitRowIdx, cursor));\n const focusedField = safeCursor < submitRowIdx ? fields[safeCursor] : undefined;\n\n // Rows the field list may occupy: the column's list area minus the method\n // header (name + optional summary + blank) and the submit affordance\n // (blank + submit line). Fields are windowed so a long parameter list never\n // overflows the column and corrupts the panes below.\n const headerLines = 1 + (method.summary ? 1 : 0) + 1;\n const submitLines = 2;\n const fieldsHeight = Math.max(1, listHeight - headerLines - submitLines);\n const win = useScroll(fields.length, Math.min(safeCursor, Math.max(0, fields.length - 1)), fieldsHeight);\n\n useInput(\n (input, key) => {\n if (key.upArrow) {\n model.formCursor.set(Math.max(0, safeCursor - 1), undefined);\n return;\n }\n if (key.downArrow) {\n model.formCursor.set(Math.min(submitRowIdx, safeCursor + 1), undefined);\n return;\n }\n if (focusedField) {\n const cls = classifyField(focusedField.schema);\n const current = formValues[focusedField.name];\n if (cls.kind === \"boolean\" && input === \" \") {\n model.setField(focusedField.name, !current);\n return;\n }\n if (cls.kind === \"enum\") {\n if (key.leftArrow || key.rightArrow) {\n const dir: 1 | -1 = key.rightArrow ? 1 : -1;\n const seed = current === undefined ? defaultValueFor(cls) : current;\n model.setField(focusedField.name, cycleEnum(cls.enumValues ?? [], seed, dir));\n return;\n }\n }\n if (key.return) {\n if (cls.kind === \"boolean\") {\n model.setField(focusedField.name, !current);\n return;\n }\n if (cls.kind === \"enum\") {\n const seed = current === undefined ? defaultValueFor(cls) : current;\n model.setField(focusedField.name, cycleEnum(cls.enumValues ?? [], seed, 1));\n return;\n }\n if (current === undefined) {\n model.setField(focusedField.name, defaultValueFor(cls));\n }\n model.formEditing.set(true, undefined);\n return;\n }\n if (input === \"x\" && focusedField.required === false && current !== undefined) {\n model.setField(focusedField.name, undefined);\n return;\n }\n } else if (safeCursor === submitRowIdx && key.return) {\n if (canSubmit) model.submit();\n return;\n }\n },\n { isActive: focused && !editing },\n );\n\n useInput(\n (_input, key) => {\n if (key.escape) model.formEditing.set(false, undefined);\n },\n { isActive: focused && editing },\n );\n\n return (\n <Box flexDirection=\"column\" flexShrink={0}>\n <Text bold>{(method.result === undefined ? \"notify \" : \"request \") + selection?.methodName}</Text>\n {method.summary ? <Text dimColor>{method.summary}</Text> : null}\n <Box marginTop={1} flexDirection=\"column\" flexShrink={0}>\n {fields.length === 0 ? (\n <Text dimColor>(no parameters)</Text>\n ) : (\n <>\n <ScrollIndicator direction=\"up\" count={win.above} />\n {fields.slice(win.start, win.end).map((f, i) => {\n const idx = win.start + i;\n return (\n <FieldRow\n key={f.name}\n model={model}\n field={f}\n value={formValues[f.name]}\n focused={focused && safeCursor === idx}\n editing={editing && safeCursor === idx}\n error={errors.get(f.name)}\n />\n );\n })}\n <ScrollIndicator direction=\"down\" count={win.below} />\n </>\n )}\n </Box>\n <Box marginTop={1} flexShrink={0}>\n <Text color={canSubmit ? \"green\" : \"gray\"}>{submitLabel(focused && safeCursor === submitRowIdx, canSubmit)}</Text>\n </Box>\n </Box>\n );\n};\n\nfunction submitLabel(focused: boolean, canSubmit: boolean): string {\n const prefix = focused ? \"\\u203a \" : \" \";\n const hint = canSubmit ? \"\" : \" \\u2014 fix errors first\";\n return `${prefix}[Submit${hint}]`;\n}\n\n// -- shared column shell --\n\nconst Column: React.FC<{\n title: string;\n focused: boolean;\n children: React.ReactNode;\n width?: string;\n flexGrow?: number;\n height?: number;\n}> = ({ title, focused, children, width, flexGrow, height }) => {\n return (\n <Box\n flexDirection=\"column\"\n width={width}\n flexGrow={flexGrow}\n height={height}\n overflow=\"hidden\"\n borderStyle=\"single\"\n borderColor={focused ? \"cyan\" : undefined}\n paddingX={1}\n >\n <Text bold>{title}</Text>\n {children}\n </Box>\n );\n};\n\n// -- result pane --\n\nconst ResultPane: React.FC<{ model: UiModel }> = ({ model }) => {\n const promise = useObservable(model.lastCall);\n const result = useObservable(promise ? promise.promiseResult : NO_RESULT);\n const chunks = useObservable(model.streamChunks);\n\n return (\n <Box flexDirection=\"column\" height={RESULT_H} overflow=\"hidden\" borderStyle=\"single\" paddingX={1}>\n <Text bold>Result</Text>\n {chunks.length > 0 ? (\n <Box flexDirection=\"column\">\n {chunks.map((c, i) => (\n <Text key={i} dimColor>{\"\\u2502 \" + formatChunk(c)}</Text>\n ))}\n </Box>\n ) : null}\n {!promise ? (\n <Text dimColor>(no calls yet)</Text>\n ) : !result ? (\n <Text dimColor>{chunks.length > 0 ? \"Streaming…\" : \"Calling…\"}</Text>\n ) : result.error ? (\n <Text color=\"red\">Error: {formatError(result.error)}</Text>\n ) : (\n <Text>\n {JSON.stringify(result.data?.result)}\n {\" \"}\n <Text dimColor>{result.data?.latencyMs.toFixed(1)}ms</Text>\n </Text>\n )}\n </Box>\n );\n};\n\nconst NO_RESULT = observableValue<undefined>(\"App.NO_RESULT\", undefined);\n\nfunction formatChunk(c: unknown): string {\n return typeof c === \"string\" ? c : JSON.stringify(c);\n}\n\nfunction formatError(e: unknown): string {\n if (e instanceof Error) return e.message;\n return String(e);\n}\n","import { render } from \"ink\";\nimport {\n ChannelConnector,\n type IRequestSender,\n type JsonValue,\n type RawStreamingCall,\n RpcError,\n type SendOpts,\n type SigningCallCtx,\n SigningSender,\n type StreamSendOpts,\n} from \"@hediet/linkrpc\";\nimport { isHubEndpoint, openHubChannel } from \"@hediet/linkrpc/node\";\nimport { type CliSigning, connect } from \"@hediet/linkrpc-client\";\nimport type { ResolvedEndpoint } from \"@hediet/linkrpc-client\";\nimport { setupSigning } from \"@hediet/linkrpc-client\";\nimport type { SigningSession } from \"@hediet/linkrpc-client\";\nimport { formatPrincipalSource, type PrincipalSpec } from \"@hediet/linkrpc-client\";\nimport { UiModel } from \"./UiModel\";\nimport { App } from \"./App\";\n\nexport interface RunUiOptions {\n readonly endpoint: ResolvedEndpoint;\n readonly principalSpec: PrincipalSpec;\n}\n\nexport async function runUi(opts: RunUiOptions): Promise<void> {\n if (isHubEndpoint(opts.endpoint)) {\n await _runUiReconnecting(opts.endpoint, opts.principalSpec);\n } else {\n await _runUiOnce(opts.endpoint, opts.principalSpec);\n }\n}\n\n/** Non-Hub endpoints: a single connection, no redial. */\nasync function _runUiOnce(endpoint: ResolvedEndpoint, principalSpec: PrincipalSpec): Promise<void> {\n const conn = await connect(endpoint);\n const identity = _isRawEndpoint(endpoint)\n ? \"unsigned (raw endpoint)\"\n : _formatIdentity(\n await setupSigning(conn.channel, conn.signing, principalSpec, {\n negotiateHubCaps: false,\n }),\n );\n const model = new UiModel(conn.channel);\n model.identity.set(identity, undefined);\n const instance = render(<App model={model} />);\n try {\n await instance.waitUntilExit();\n } finally {\n model.dispose();\n conn.close();\n }\n}\n\nfunction _isRawEndpoint(endpoint: ResolvedEndpoint): boolean {\n return endpoint.kind === \"ws-no-init\"\n || (endpoint.kind === \"socket\" && endpoint.brokerMode === \"raw\");\n}\n\n/**\n * Hub / env connection: keep the UI alive across socket drops. A stable\n * {@link SwappableSender} backs the {@link UiModel}; on every (re)connect we\n * open a fresh hub channel, install signing, and point the swappable sender at\n * the new signed channel. The TUI is rendered once, after the first connect.\n */\nasync function _runUiReconnecting(\n endpoint: Extract<ResolvedEndpoint, { kind: \"ws\"; } | { kind: \"socket\"; }>,\n principalSpec: PrincipalSpec,\n): Promise<void> {\n const swappable = new SwappableSender();\n let model: UiModel | undefined;\n\n const hubEndpoint = endpoint.kind === \"ws\" ? endpoint.url : endpoint.path;\n const connector = ChannelConnector.expBackoff(() =>\n openHubChannel({ endpoint: hubEndpoint, token: endpoint.token ?? \"\" })\n );\n\n const handle = connector.keepConnected(async ({ channel }) => {\n const signing: CliSigning = {};\n const signed = SigningSender.wrapChannel(channel, signing).sender;\n const session = await setupSigning(signed, signing, principalSpec, {\n negotiateHubCaps: true,\n });\n swappable.setTarget(signed);\n\n if (!model) {\n model = new UiModel(swappable);\n model.identity.set(_formatIdentity(session), undefined);\n const instance = render(<App model={model} />);\n // When the user quits the TUI, stop redialing and tear down.\n void instance.waitUntilExit().finally(() => handle.stop());\n } else {\n // Reconnected: update header identity label. Pending/next calls\n // use the swapped target; signing hooks live on that sender.\n model.identity.set(_formatIdentity(session), undefined);\n }\n });\n\n try {\n await handle.done;\n } finally {\n model?.dispose();\n }\n}\n\n/** One-line identity label for the TUI header (source + truncated nodeId). */\nfunction _formatIdentity(session: SigningSession): string {\n return formatPrincipalSource(session.principalSource, session.principal.identity.publicSigningIdentity.principal);\n}\n\n/**\n * An {@link IRequestSender} whose delegate can be swapped at runtime. Lets the\n * {@link UiModel} hold one stable channel reference while the underlying signed\n * channel is replaced on each reconnect. Calls issued while disconnected reject.\n */\nclass SwappableSender implements IRequestSender<SigningCallCtx> {\n private _target: IRequestSender<SigningCallCtx> | undefined;\n\n public setTarget(target: IRequestSender<SigningCallCtx> | undefined): void {\n this._target = target;\n }\n\n private _require(): IRequestSender<SigningCallCtx> {\n if (!this._target) {\n throw new RpcError(\"not connected\", -32000);\n }\n return this._target;\n }\n\n public sendRequest(\n method: string,\n params: JsonValue | undefined,\n opts?: SendOpts<SigningCallCtx>,\n ): Promise<JsonValue> {\n return this._require().sendRequest(method, params, opts);\n }\n\n public sendNotification(\n method: string,\n params: JsonValue | undefined,\n opts?: SendOpts<SigningCallCtx>,\n ): Promise<void> {\n return this._require().sendNotification(method, params, opts);\n }\n\n public sendRequestWithStream(\n method: string,\n params: JsonValue | undefined,\n opts?: StreamSendOpts<SigningCallCtx>,\n ): RawStreamingCall {\n return this._require().sendRequestWithStream(method, params, opts);\n }\n\n public close(): void {\n this._target?.close();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ import { o as autorun } from "./cli-D_-vlAa2.js";
2
+ import React, { useEffect, useReducer, useRef } from "react";
3
+ //#region src/ui/useObservable.ts
4
+ /**
5
+ * Minimal React hook that subscribes a component to an observable value.
6
+ *
7
+ * We don't pull in `@vscode/observables-react` here because it imports
8
+ * `react-dom` (for `unstable_batchedUpdates`), which doesn't exist on the
9
+ * terminal renderer (`ink`). The mechanics are the same — `autorun` plus a
10
+ * `useReducer`-driven forceUpdate.
11
+ */
12
+ function useObservable(obs) {
13
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
14
+ const valueRef = useRef(void 0);
15
+ useEffect(() => {
16
+ const disposable = autorun((reader) => {
17
+ const v = obs.read(reader);
18
+ const prev = valueRef.current;
19
+ valueRef.current = { value: v };
20
+ if (prev !== void 0) forceUpdate();
21
+ });
22
+ return () => disposable.dispose();
23
+ }, [obs]);
24
+ if (valueRef.current === void 0) valueRef.current = { value: obs.get() };
25
+ return valueRef.current.value;
26
+ }
27
+ //#endregion
28
+ //#region src/ui/scroll.ts
29
+ /**
30
+ * Pure scroll math for a vertical list rendered into a fixed-height viewport.
31
+ *
32
+ * The cursor stays stationary until it reaches a viewport edge, then the window
33
+ * scrolls (classic list behaviour) rather than re-centering on every move.
34
+ * `prevOffset` is the offset from the previous render; the returned `offset`
35
+ * should be fed back in next time.
36
+ *
37
+ * One row on each overflowing side is reserved for a "▲/▼ more" indicator, so an
38
+ * indicator never hides a real item; this makes the visible item count
39
+ * `height - 2` while scrolling. When the list fits, the full range is returned
40
+ * with no indicators.
41
+ */
42
+ function computeWindow(count, cursor, prevOffset, height) {
43
+ if (height <= 0 || count <= 0) return {
44
+ offset: 0,
45
+ window: {
46
+ start: 0,
47
+ end: 0,
48
+ above: 0,
49
+ below: 0
50
+ }
51
+ };
52
+ if (count <= height) return {
53
+ offset: 0,
54
+ window: {
55
+ start: 0,
56
+ end: count,
57
+ above: 0,
58
+ below: 0
59
+ }
60
+ };
61
+ const visible = Math.max(1, height - 2);
62
+ const c = Math.max(0, Math.min(count - 1, cursor));
63
+ let offset = prevOffset;
64
+ offset = Math.min(offset, c);
65
+ offset = Math.max(offset, c - visible + 1);
66
+ offset = Math.max(0, Math.min(offset, count - visible));
67
+ const end = offset + visible;
68
+ return {
69
+ offset,
70
+ window: {
71
+ start: offset,
72
+ end,
73
+ above: offset,
74
+ below: count - end
75
+ }
76
+ };
77
+ }
78
+ /**
79
+ * Stateful wrapper around {@link computeWindow}. Ink has no scroll container, so
80
+ * any list longer than the available rows must be sliced by hand; without this
81
+ * the overflow corrupts sibling panes and the terminal frame. The scroll offset
82
+ * is kept in a ref so it survives re-renders without involving the view model.
83
+ */
84
+ function useScroll(count, cursor, height) {
85
+ const offsetRef = React.useRef(0);
86
+ const { offset, window } = computeWindow(count, cursor, offsetRef.current, height);
87
+ offsetRef.current = offset;
88
+ return window;
89
+ }
90
+ //#endregion
91
+ export { useObservable as n, useScroll as t };
92
+
93
+ //# sourceMappingURL=scroll-BTzAYh4N.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-BTzAYh4N.js","names":[],"sources":["../../src/ui/useObservable.ts","../../src/ui/scroll.ts"],"sourcesContent":["import { useEffect, useReducer, useRef } from \"react\";\nimport { autorun, type IObservable } from \"@vscode/observables\";\n\n/**\n * Minimal React hook that subscribes a component to an observable value.\n *\n * We don't pull in `@vscode/observables-react` here because it imports\n * `react-dom` (for `unstable_batchedUpdates`), which doesn't exist on the\n * terminal renderer (`ink`). The mechanics are the same — `autorun` plus a\n * `useReducer`-driven forceUpdate.\n */\nexport function useObservable<T>(obs: IObservable<T>): T {\n const [, forceUpdate] = useReducer((x: number) => x + 1, 0);\n const valueRef = useRef<{ value: T } | undefined>(undefined);\n\n useEffect(() => {\n const disposable = autorun((reader) => {\n const v = obs.read(reader);\n const prev = valueRef.current;\n valueRef.current = { value: v };\n if (prev !== undefined) forceUpdate();\n });\n return () => disposable.dispose();\n }, [obs]);\n\n if (valueRef.current === undefined) {\n // First render: read the current value without subscribing.\n // `get()` skips the reader-based dependency tracking.\n valueRef.current = { value: obs.get() };\n }\n return valueRef.current.value;\n}\n","import React from \"react\";\n\nexport interface ScrollWindow {\n /** First visible item index (inclusive). */\n readonly start: number;\n /** One past the last visible item index (exclusive). */\n readonly end: number;\n /** How many items are hidden above the window. */\n readonly above: number;\n /** How many items are hidden below the window. */\n readonly below: number;\n}\n\n/**\n * Pure scroll math for a vertical list rendered into a fixed-height viewport.\n *\n * The cursor stays stationary until it reaches a viewport edge, then the window\n * scrolls (classic list behaviour) rather than re-centering on every move.\n * `prevOffset` is the offset from the previous render; the returned `offset`\n * should be fed back in next time.\n *\n * One row on each overflowing side is reserved for a \"▲/▼ more\" indicator, so an\n * indicator never hides a real item; this makes the visible item count\n * `height - 2` while scrolling. When the list fits, the full range is returned\n * with no indicators.\n */\nexport function computeWindow(\n count: number,\n cursor: number,\n prevOffset: number,\n height: number,\n): { offset: number; window: ScrollWindow } {\n if (height <= 0 || count <= 0) {\n return { offset: 0, window: { start: 0, end: 0, above: 0, below: 0 } };\n }\n if (count <= height) {\n return { offset: 0, window: { start: 0, end: count, above: 0, below: 0 } };\n }\n\n // Reserve a row top and bottom for indicators so layout stays stable while\n // scrolling. At the very edges one reserved row goes unused (rendered blank).\n const visible = Math.max(1, height - 2);\n const c = Math.max(0, Math.min(count - 1, cursor));\n\n let offset = prevOffset;\n offset = Math.min(offset, c); // cursor scrolled above the window\n offset = Math.max(offset, c - visible + 1); // cursor scrolled below the window\n offset = Math.max(0, Math.min(offset, count - visible));\n\n const end = offset + visible;\n return { offset, window: { start: offset, end, above: offset, below: count - end } };\n}\n\n/**\n * Stateful wrapper around {@link computeWindow}. Ink has no scroll container, so\n * any list longer than the available rows must be sliced by hand; without this\n * the overflow corrupts sibling panes and the terminal frame. The scroll offset\n * is kept in a ref so it survives re-renders without involving the view model.\n */\nexport function useScroll(count: number, cursor: number, height: number): ScrollWindow {\n const offsetRef = React.useRef(0);\n const { offset, window } = computeWindow(count, cursor, offsetRef.current, height);\n offsetRef.current = offset;\n return window;\n}\n"],"mappings":";;;;;;;;;;;AAWA,SAAgB,cAAiB,KAAwB;CACrD,MAAM,GAAG,eAAe,YAAY,MAAc,IAAI,GAAG,CAAC;CAC1D,MAAM,WAAW,OAAiC,KAAA,CAAS;CAE3D,gBAAgB;EACZ,MAAM,aAAa,SAAS,WAAW;GACnC,MAAM,IAAI,IAAI,KAAK,MAAM;GACzB,MAAM,OAAO,SAAS;GACtB,SAAS,UAAU,EAAE,OAAO,EAAE;GAC9B,IAAI,SAAS,KAAA,GAAW,YAAY;EACxC,CAAC;EACD,aAAa,WAAW,QAAQ;CACpC,GAAG,CAAC,GAAG,CAAC;CAER,IAAI,SAAS,YAAY,KAAA,GAGrB,SAAS,UAAU,EAAE,OAAO,IAAI,IAAI,EAAE;CAE1C,OAAO,SAAS,QAAQ;AAC5B;;;;;;;;;;;;;;;;ACLA,SAAgB,cACZ,OACA,QACA,YACA,QACwC;CACxC,IAAI,UAAU,KAAK,SAAS,GACxB,OAAO;EAAE,QAAQ;EAAG,QAAQ;GAAE,OAAO;GAAG,KAAK;GAAG,OAAO;GAAG,OAAO;EAAE;CAAE;CAEzE,IAAI,SAAS,QACT,OAAO;EAAE,QAAQ;EAAG,QAAQ;GAAE,OAAO;GAAG,KAAK;GAAO,OAAO;GAAG,OAAO;EAAE;CAAE;CAK7E,MAAM,UAAU,KAAK,IAAI,GAAG,SAAS,CAAC;CACtC,MAAM,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,QAAQ,GAAG,MAAM,CAAC;CAEjD,IAAI,SAAS;CACb,SAAS,KAAK,IAAI,QAAQ,CAAC;CAC3B,SAAS,KAAK,IAAI,QAAQ,IAAI,UAAU,CAAC;CACzC,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,QAAQ,QAAQ,OAAO,CAAC;CAEtD,MAAM,MAAM,SAAS;CACrB,OAAO;EAAE;EAAQ,QAAQ;GAAE,OAAO;GAAQ;GAAK,OAAO;GAAQ,OAAO,QAAQ;EAAI;CAAE;AACvF;;;;;;;AAQA,SAAgB,UAAU,OAAe,QAAgB,QAA8B;CACnF,MAAM,YAAY,MAAM,OAAO,CAAC;CAChC,MAAM,EAAE,QAAQ,WAAW,cAAc,OAAO,QAAQ,UAAU,SAAS,MAAM;CACjF,UAAU,UAAU;CACpB,OAAO;AACX"}
package/dist/hub.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {}
package/dist/hub.js ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { t as runCli } from "./chunks/cli-D_-vlAa2.js";
3
+ //#region src/hub.ts
4
+ runCli("hub");
5
+ //#endregion
6
+ export {};
7
+
8
+ //# sourceMappingURL=hub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hub.js","names":[],"sources":["../src/hub.ts"],"sourcesContent":["import { runCli } from './cli';\n\nrunCli('hub');\n"],"mappings":";;;AAEA,OAAO,KAAK"}