diff --git a/typings/index.d.ts b/typings/index.d.ts index a7a812f..6f6a279 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -3,7 +3,7 @@ declare namespace Konsole { type BooleanArgument = `${boolean}` | "yes" | "no" | "on" | "off" | "1" | "0"; - + export type Argument = | { name?: string; @@ -45,6 +45,49 @@ declare namespace Konsole { message: string; } + export type ResultKind = "text" | "table" | "error" | "warn"; + + export interface RenderResult { + ok: boolean; + kind: ResultKind; + code?: string; + title: string; + message: string; + width: string; + columns?: string[]; + rows: Array>; + hints?: string[]; + } + + export interface ResultOptions { + kind?: ResultKind; + title?: string; + message?: string; + width?: string; + columns?: string[]; + rows?: Array>; + hints?: string[]; + } + + export interface ResultApi { + ok: (message?: unknown, options?: ResultOptions) => RenderResult; + err: (code?: unknown, message?: unknown, options?: ResultOptions) => RenderResult; + table: ( + title?: unknown, + columns?: string[], + rows?: Array>, + options?: ResultOptions, + ) => RenderResult; + status: ( + title?: unknown, + rows?: Array<{ Field: unknown; Value: unknown }>, + options?: ResultOptions, + ) => RenderResult; + from: (value: unknown) => RenderResult; + } + + export type ExecuteResult = Outcome | RenderResult; + type ArgumentValue = T["type"] extends "number" ? number : T["type"] extends "boolean" ? boolean : @@ -52,48 +95,313 @@ declare namespace Konsole { T["type"] extends "players" ? Player[] : string; - export type Run = (context: Context, ...args: unknown[]) => unknown; - export type RankResolver = (entity: unknown) => number | undefined; - export type ConfigOverrides = Record>; + /** + * What actually reaches the callback: `parse` skips an argument entirely + * when it is `required: false`, has no `default`, and no token was typed — + * so only that combination can be undefined. Defaults are inserted raw + * (no conversion), and an absent `required` behaves like required. + */ + type ArgumentRuntimeValue = T extends { required: false } + ? T extends { default: {} } + ? ArgumentValue + : ArgumentValue | undefined + : ArgumentValue; + + export type Args = ReadonlyArray; + + /** + * A server implementation name whose argument tuple was captured from the + * `args` schema of the `define(...)` call that produced it. Passing one to + * `implement(...)` types the callback parameters automatically. + * At runtime this is a plain string. + */ + export type ServerName = string & { readonly __args: A }; + + export type Run = (context: Context, ...args: A) => unknown; - type RunArgs = { - [K in keyof T]: ArgumentValue; + /** Built-in rank names ship with Konsole; custom ones can be defined via `Ranks.define`. */ + export type RankName = + | "player" + | "helper" + | "trial" + | "lowmod" + | "moderator" + | "mod" + | "admin" + | "administrator" + | "creator" + | "owner" + | (string & {}); + + /** Anything rank lookups accept: a Player instance or a tonumber-able user id. */ + export type RankEntity = Player | number | string | undefined; + + export type RankResolver = (entity: RankEntity) => number | undefined; + + export interface Rank { + id: number; + name: string; + aliases: string[]; + builtin?: boolean; + } + + export interface RanksModule { + readonly define: (id: number, name: string, aliases?: string[]) => Rank; + readonly resolve: (value: number | RankName) => number | undefined; + readonly label: (id: number) => string; + readonly list: () => Rank[]; + readonly set: (userId: number | string, rank: number | RankName) => number; + readonly bind: (fn?: RankResolver) => void; + readonly get: (entity: RankEntity) => number; + readonly allows: (entity: RankEntity, required: number | RankName) => boolean; + } + + export interface DispatchModule { + readonly remoteName: string; + readonly definitionsRemoteName: string; + readonly timeout: number; + readonly host: (serverImplementations?: Record>>) => RemoteFunction | undefined; + readonly implement: (name: string, callback: Run>) => void; + readonly execute: (text: string, entity?: RankEntity) => ExecuteResult; + } + + export interface KommandModule { + readonly define: (definition: Definition) => Command; + readonly find: (name: string) => Command | undefined; + readonly list: () => Command[]; + readonly schemas: () => Record; + readonly suggestions: () => string[]; + readonly watch: (fn?: () => void) => void; + } + + /** + * Converts one typed token. Returns `[true, value]` on success or + * `[false, errorMessage]` on failure. + */ + export type Converter = (token: string, caller?: Player) => LuaTuple<[boolean, unknown]>; + + export interface ArgumentsModule { + /** Built-in converter registry (`string`, `number`, `boolean`, `player`, `players`). */ + readonly types: Record; + readonly tokenize: (text?: string) => string[]; + readonly split: (text?: string) => LuaTuple<[string | undefined, string[]]>; + readonly parse: ( + argDefinitions: Argument[] | undefined, + tokens: string[], + caller?: Player, + ) => LuaTuple<[boolean, unknown]>; + } + + export interface ChatModule { + readonly bindKonsoleOpen: (callback: () => void) => () => void; + readonly unbindKonsoleOpen: () => void; + } + + export interface ConfigGroups { + panel: { + width: number; + outputWidth: number; + wideWidth: number; + maxWidth: number; + collapsedWidth: number; + addChatCollapsedWidth: number; + addChatHoverWidth: number; + height: number; + inputHeight: number; + commandHeight: number; + historyLineHeight: number; + historyChunkLines: number; + historyMaxHeight: number; + viewportTopInset: number; + suggestionHeight: number; + suggestionGap: number; + suggestionRadius: number; + maxSuggestions: number; + outputRadius: number; + displayOrder: number; + shadowAssetId: string; + arrowAssetId: string; + addChatAssetId: string; + stackGap: number; + }; + color: { + panel: Color3; + inputText: Color3; + promptText: Color3; + suggPanel: Color3; + suggText: Color3; + suggMatch: string; + suggSub: string; + hintGhost: Color3; + successRich: string; + successText: Color3; + errorRich: string; + errorText: Color3; + warnRich: string; + warnText: Color3; + mutedRich: string; + mutedText: Color3; + shadowColor: Color3; + }; + motion: { + expandSmoothTime: number; + openSmoothTime: number; + openSlideOffset: number; + outputSmoothTime: number; + listSmoothTime: number; + textFadeTime: number; + textSlideOffset: number; + itemSlideSmoothTime: number; + collapseSmoothTime: number; + exitFadeTime: number; + hintFadeTime: number; + hintSlideTime: number; + }; + layout: { + paddingX: number; + paddingY: number; + promptSize: number; + textSize: number; + suggTextSize: number; + hintTextSize: number; + hintOffsetY: number; + itemGap: number; + }; + transparency: { + panel: number; + suggPanel: number; + shadow: number; + arrow: number; + text: number; + suggText: number; + dimText: number; + }; + input: { + activationKeys: Enum.KeyCode[]; + activationPriority: number; + forceclose: boolean; + command: string; + alias: string; + }; + font: { + body: Enum.Font; + title: Enum.Font; + mono: Enum.Font; + }; + commands: { + defaultLimit: number; + aliases: Record; + }; + } + + export type ConfigOverrides = { + [K in keyof ConfigGroups]?: Partial; }; - export interface Definition { + export interface ConfigModule { + readonly groups: ConfigGroups; + readonly merge: (options?: ConfigOverrides) => ConfigGroups; + } + + export interface RenderModule { + readonly Result: ResultApi; + readonly Formatter: unknown; + readonly Text: unknown; + readonly config: (options?: ConfigOverrides) => ConfigGroups; + readonly new: (options?: ConfigOverrides) => Client; + } + + type RunArgs = number extends (T & { length: number })["length"] + ? [] + : { [K in keyof T]: ArgumentRuntimeValue }; + + export interface Definition< + T extends readonly Argument[] = readonly Argument[], + S extends string | undefined = string | undefined, + > { name: string; - rank?: number | string; + rank?: number | RankName; aliases?: string[]; - args?: T; + args?: T & readonly Argument[]; description?: string; cooldown?: number; - server?: string; + server?: S; run?: (context: Context, ...args: RunArgs) => unknown; } - export interface Command { + export interface Command { name: string; rank: number; aliases: string[]; args: Argument[]; description: string; cooldown: number; - server?: string; - run?: Run; + server: S extends string ? ServerName : undefined; + run?: Run; } export interface Context { entity?: Player; kommand: Command; text: string; - dispatch: unknown; - ranks: unknown; - run: (text: unknown) => unknown; + dispatch: DispatchModule; + ranks: RanksModule; + run: (text: string) => ExecuteResult; reply: (message?: unknown) => Outcome; err: (code?: unknown, message?: unknown) => Outcome; } + /** + * A console client returned by `Konsole.create(...)`. Its members are + * colon-methods on the Luau side, so they are declared as methods here — + * roblox-ts emits `client:show()` style calls for them. + */ export interface Client { + bindRun(callback: (text: string) => unknown): void; + clear(): void; + destroy(): void; + focus(): void; + getCursorTarget(): Instance | undefined; + hide(): void; + setActivationKeys(keys: Enum.KeyCode[]): void; + setActivationUnlocksMouse(enabled: boolean): void; + setEnabled(enabled: boolean): void; + setMouseUnlockDriver(getFn?: () => boolean, setFn?: (enabled: boolean) => void): void; + setSchemas(map: Record): void; + setSuggestions(list: string[]): void; + show(): void; + toggle(): void; + } + + /** + * The top-level API. Unlike `Client`, these are dot-functions on the Luau + * side (they delegate to a lazily created default client), so they are + * declared as function properties — roblox-ts emits `Konsole.show()`. + */ + export interface Api { + Arguments: ArgumentsModule; + Chat: ChatModule; + Config: ConfigModule; + Dispatch: DispatchModule; + Kommand: KommandModule; + Ranks: RanksModule; + Render: RenderModule; + Result: ResultApi; + + bindRanks: (resolver?: RankResolver) => void; + create: (options?: ConfigOverrides) => Client; + define: ( + definition: Definition, + ) => Command, S>; + getRank: (entity: RankEntity) => number; + host: (serverImplementations?: Record>>) => RemoteFunction | undefined; + implement: { + (name: ServerName, callback: (context: Context, ...args: A) => unknown): void; + (name: string, callback: (context: Context, ...args: T) => unknown): void; + }; + run: (text: string) => ExecuteResult; + setRank: (userId: number | string, rank: number | RankName) => number; + bindRun: (callback: (text: string) => unknown) => void; clear: () => void; destroy: () => void; @@ -104,32 +412,11 @@ declare namespace Konsole { setActivationUnlocksMouse: (enabled: boolean) => void; setEnabled: (enabled: boolean) => void; setMouseUnlockDriver: (getFn?: () => boolean, setFn?: (enabled: boolean) => void) => void; - setSchemas: (map: Record) => void; + setSchemas: (map: Record) => void; setSuggestions: (list: string[]) => void; show: () => void; toggle: () => void; } - - export interface Api extends Client { - Arguments: unknown; - Chat: unknown; - Config: unknown; - Dispatch: unknown; - Kommand: unknown; - Ranks: unknown; - Render: unknown; - Result: unknown; - bindRanks: (resolver?: RankResolver) => void; - create: (options?: ConfigOverrides) => Client; - define: ( - definition: Definition, - ) => Command; - getRank: (entity: unknown) => number; - host: (serverImplementations?: Record) => RemoteFunction | undefined; - implement: (name: string, callback: Run) => void; - run: (text: string) => Outcome; - setRank: (userId: unknown, rank: unknown) => number; - } } declare const Konsole: Konsole.Api;