{"id":"kyrorblx/konsole","name":"konsole","scope":"kyrorblx","platform":"roblox","description":"Konsole is a little Roblox command console","version":"0.1.11","latest":"0.1.11","versions":["0.1.0","0.1.2","0.1.3","0.1.4","0.1.5","0.1.6","0.1.7","0.1.8","0.1.9","0.1.10","0.1.11"],"license":"MIT","licenseRating":"safe","licenseCaveats":[],"licenseVerified":true,"dependencies":{},"integrity":"b4e562498ebaa74a512e3298fa033bb2a5fd09d8a0ca416e7ae971f53421b07b","likes":0,"downloads":0,"install":"forest install kyrorblx/konsole","url":"https://forest.dev/p/roblox/kyrorblx/konsole","files":"https://api.forest.dev/ai/package/roblox/kyrorblx/konsole/files","readme":"<p align=\"center\">\r\n  <img src=\"assets/konsolelogo.png\" alt=\"Konsole\" width=\"420\">\r\n</p>\r\n\r\n<p align=\"center\">\r\n  Little Roblox typed command console for in-game admin commands, debug commands, and typed utilities.\r\n</p>\r\n\r\n<p align=\"center\">\r\n  <a href=\"#install\">Install</a>\r\n  ·\r\n  <a href=\"#how-it-works\">How It Works</a>\r\n  ·\r\n  <a href=\"#using-it\">Using It</a>\r\n  ·\r\n  <a href=\"#commands\">Commands</a>\r\n  ·\r\n  <a href=\"#api\">API</a>\r\n</p>\r\n\r\n## What Is This?\r\n\r\nKonsole is a typed compact command bar for Roblox games.\r\n\r\nKonsole, gives you an in-game terminal for commands like `kick`, `bring`, `tp`, `ranks`, and your own custom commands. It handles suggestions, typed arguments, command history, result tables, client/server dispatch, ranks, and small UI details like two command panes and argument chips.\r\n\r\n<p align=\"center\">\r\n  <img src=\"assets/konsole-demo.gif\" alt=\"Konsole demo\" width=\"720\">\r\n</p>\r\n\r\n## Install\r\n\r\nPut `Konsole.rbxm` in `ReplicatedStorage/Packages/Konsole`, then require it.\r\n\r\n```luau\r\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\r\n\r\nlocal Konsole = require(ReplicatedStorage.Packages.Konsole)\r\n```\r\n\r\nFor roblox-ts/npm:\r\n\r\n```sh\r\nnpm install @kyrorblx/konsole\r\n```\r\n\r\n```ts\r\nimport Konsole = require(\"@kyrorblx/konsole\");\r\n```\r\n\r\nFor Wally:\r\n\r\n```toml\r\n[dependencies]\r\nKonsole = \"kyrorblx/konsole@0.1.11\"\r\n```\r\n\r\nKonsole is shared, but the UI is client-side. The server hosts command execution. The client shows the command bar and forwards server commands through Konsole's remote bridge.\r\n\r\n## How It Works\r\n\r\nKonsole has three main pieces:\r\n\r\n- `Kommand`: stores command definitions, schemas, aliases, suggestions, and argument metadata.\r\n- `Dispatch`: parses text, checks rank/cooldown, converts arguments, and runs the right client or server implementation.\r\n- `Render`: creates the client UI, suggestions, history, result output, and the command input.\r\n\r\nThe usual setup is:\r\n\r\n1. Server calls `Konsole.host()`.\r\n2. Client calls `Konsole.show()` or `Konsole.toggle()`.\r\n3. Built-in commands register automatically.\r\n4. Server command definitions replicate to clients so suggestions and argument hints work.\r\n5. When a command has `server = \"someServerName\"`, the client forwards the text to the server.\r\n\r\nInline `run` commands can run where they are registered. Server commands should usually use `server = \"name\"` with `Konsole.implement(\"name\", callback)` or pass implementations into `Konsole.host(...)`.\r\n\r\n## Using It\r\n\r\nServer setup:\r\n\r\n```luau\r\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\r\n\r\nlocal Konsole = require(ReplicatedStorage.Packages.Konsole)\r\n\r\nKonsole.host()\r\n```\r\n\r\nClient setup:\r\n\r\n```luau\r\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\r\n\r\nlocal Konsole = require(ReplicatedStorage.Packages.Konsole)\r\n\r\nKonsole.show()\r\n```\r\n\r\nBy default, Konsole opens with `T`. You can change that:\r\n\r\n```luau\r\nKonsole.setActivationKeys({ Enum.KeyCode.Semicolon })\r\n```\r\n\r\nYou can also toggle manually:\r\n\r\n```luau\r\nKonsole.toggle()\r\nKonsole.show()\r\nKonsole.hide()\r\nKonsole.focus()\r\n```\r\n\r\n## Built-In Commands\r\n\r\nKonsole ships with a few small commands:\r\n\r\n- `cmds`: lists registered commands, rank, usage, and description\r\n- `clear` / `cls` / `clr`: clears the active Konsole chat pane\r\n- `ranks`: lists known ranks\r\n- `bring`: brings target players to you\r\n- `tp`: teleports you to a target player\r\n- `kick`: kicks a player\r\n- `ban`: bans players from the current server\r\n- `unban`: unbans a user ID from the current server\r\n- `kill`: kills a player\r\n\r\nThe ban command is server-local. It blocks players for the lifetime of that server, not permanently across all servers. If you want persistent bans, store ban state in a DataStore and implement your own `banServer` / `unbanServer`.\r\n\r\n## Commands\r\n\r\nA command definition looks like this:\r\n\r\n```luau\r\nKonsole.define({\r\n\tname = \"setscore\",\r\n\trank = 50,\r\n\taliases = { \"score\" },\r\n\tdescription = \"Sets the score.\",\r\n\tserver = \"setScoreServer\",\r\n\targs = {\r\n\t\t{\r\n\t\t\tname = \"home\",\r\n\t\t\ttype = \"number\",\r\n\t\t\trequired = true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname = \"away\",\r\n\t\t\ttype = \"number\",\r\n\t\t\trequired = true,\r\n\t\t},\r\n\t},\r\n})\r\n```\r\n\r\nThen bind the server implementation:\r\n\r\n```luau\r\nKonsole.implement(\"setScoreServer\", function(context, homeScore, awayScore)\r\n\tprint(context.entity, homeScore, awayScore)\r\n\treturn context.reply(`score set to {homeScore}-{awayScore}`)\r\nend)\r\n```\r\n\r\nOr pass implementations straight into `host`:\r\n\r\n```luau\r\nKonsole.host({\r\n\tsetScoreServer = function(context, homeScore, awayScore)\r\n\t\treturn context.reply(`score set to {homeScore}-{awayScore}`)\r\n\tend,\r\n})\r\n```\r\n\r\nCommands are normalized to lowercase internally. Aliases are normalized too.\r\n\r\n## Client Commands\r\n\r\nIf a command has `run` and no `server`, it runs locally.\r\n\r\n```luau\r\nKonsole.define({\r\n\tname = \"pinglocal\",\r\n\trank = 0,\r\n\tdescription = \"Runs on this client.\",\r\n\trun = function(context)\r\n\t\treturn context.reply(\"pong\")\r\n\tend,\r\n})\r\n```\r\n\r\nClient commands are useful for UI toggles, local debug views, camera tools, graphics settings, or anything that only affects one player.\r\n\r\n## Server Commands\r\n\r\nIf a command has `server`, Konsole forwards it to the server when typed by a client.\r\n\r\n```luau\r\nKonsole.define({\r\n\tname = \"announce\",\r\n\trank = 50,\r\n\tdescription = \"Sends a server announcement.\",\r\n\tserver = \"announceServer\",\r\n\targs = {\r\n\t\t{\r\n\t\t\tname = \"message\",\r\n\t\t\ttype = \"string\",\r\n\t\t\trequired = true,\r\n\t\t},\r\n\t},\r\n})\r\n\r\nKonsole.implement(\"announceServer\", function(context, message)\r\n\tprint(message)\r\n\treturn context.reply(\"announced\")\r\nend)\r\n```\r\n\r\nThe server is authoritative. Rank checks happen on the server before server commands run.\r\n\r\n## Arguments\r\n\r\nArguments tell Konsole how to parse and display command inputs.\r\n\r\n```luau\r\nargs = {\r\n\t{\r\n\t\tname = \"target\",\r\n\t\ttype = \"player\",\r\n\t\trequired = true,\r\n\t\tsuggestions = { \"me\" },\r\n\t},\r\n\t{\r\n\t\tname = \"reason\",\r\n\t\ttype = \"string\",\r\n\t\trequired = false,\r\n\t\tdefault = \"No reason provided.\",\r\n\t},\r\n}\r\n```\r\n\r\nBuilt-in types:\r\n\r\n- `string`: plain text\r\n- `number`: converted with `tonumber`\r\n- `boolean`: accepts `true`, `false`, `yes`, `no`, `on`, `off`, `1`, `0`\r\n- `player`: one player\r\n- `players`: one or more players\r\n\r\nPlayer shortcuts:\r\n\r\n- `me`: the command caller\r\n- `all` or `*`: every player\r\n- `others`: every player except the caller\r\n\r\nFor `player`, the token must resolve to exactly one player. For `players`, it can resolve to many.\r\n\r\nKonsole also uses argument metadata for the UI. When you type a command with args, it shows argument chips. Fixed-token args like `number` and `boolean` jump to the next chip when you press space. Bad argument types turn red while typing.\r\n\r\n## Suggestions\r\n\r\nSuggestions come from command names, aliases, and argument providers.\r\n\r\nFor a static list:\r\n\r\n```luau\r\n{\r\n\tname = \"mode\",\r\n\ttype = \"string\",\r\n\tsuggestions = { \"easy\", \"normal\", \"hard\" },\r\n}\r\n```\r\n\r\nFor player arguments, Konsole automatically suggests player names plus `me`, `all`, and `others`.\r\n\r\nUse Tab to accept a suggestion. Use Up and Down to move through suggestions. Use Left and Right to move between structured argument chips when your cursor is at the edge of a chip.\r\n\r\n## Results\r\n\r\nCommands can return:\r\n\r\n- `nil`: success\r\n- a string/number/etc: success message\r\n- a result table\r\n- `context.reply(...)`\r\n- `context.err(...)`\r\n\r\nBasic success:\r\n\r\n```luau\r\nreturn context.reply(\"done\")\r\n```\r\n\r\nBasic error:\r\n\r\n```luau\r\nreturn context.err(\"no-target\", \"No target player.\")\r\n```\r\n\r\nTable result:\r\n\r\n```luau\r\nreturn {\r\n\tok = true,\r\n\tkind = \"table\",\r\n\ttitle = \"Players\",\r\n\tmessage = \"current server\",\r\n\twidth = \"wide\",\r\n\tcolumns = { \"Name\", \"UserId\" },\r\n\trows = {\r\n\t\t{ Name = \"kio\", UserId = \"123\" },\r\n\t},\r\n}\r\n```\r\n\r\nThere are also helpers under `Konsole.Result`:\r\n\r\n```luau\r\nreturn Konsole.Result.ok(\"saved\")\r\nreturn Konsole.Result.err(\"bad-input\", \"That value is invalid.\")\r\nreturn Konsole.Result.table(\"Scores\", { \"Team\", \"Score\" }, rows)\r\nreturn Konsole.Result.status(\"State\", {\r\n\t{ Field = \"Round\", Value = \"2\" },\r\n\t{ Field = \"Alive\", Value = \"7\" },\r\n})\r\n```\r\n\r\nSuccessful results show with a checkmark. Errors show with an `x`.\r\n\r\n## Running Commands Inside Commands\r\n\r\nEvery command gets a `context.run(...)` helper.\r\n\r\n```luau\r\nKonsole.define({\r\n\tname = \"resetmatch\",\r\n\trank = 100,\r\n\tserver = \"resetMatchServer\",\r\n})\r\n\r\nKonsole.implement(\"resetMatchServer\", function(context)\r\n\tcontext.run(\"setscore 0 0\")\r\n\tcontext.run(\"bring all\")\r\n\treturn context.reply(\"match reset\")\r\nend)\r\n```\r\n\r\n`context.run` runs through the normal dispatch path, so ranks, arguments, cooldowns, and server implementations still apply.\r\n\r\n## Cooldowns\r\n\r\nCommands can have a built-in cooldown:\r\n\r\n```luau\r\nKonsole.define({\r\n\tname = \"daily\",\r\n\trank = 0,\r\n\tdescription = \"Claims a daily reward.\",\r\n\tserver = \"dailyServer\",\r\n\tcooldown = 60,\r\n})\r\n```\r\n\r\nThe cooldown is in seconds. If a player tries to run the command early, Konsole returns a cooldown error with the remaining time.\r\n\r\nCooldowns are tracked per command and per caller in memory. They reset when the server restarts.\r\n\r\n## Ranks\r\n\r\nEvery command has a rank. If no rank is provided, it is rank `0`.\r\n\r\nBuilt-in rank names include:\r\n\r\n- `player`: `0`\r\n- higher built-in names can be inspected with `ranks`\r\n\r\nSet a rank manually:\r\n\r\n```luau\r\nKonsole.setRank(player.UserId, 100)\r\n```\r\n\r\nRead a rank:\r\n\r\n```luau\r\nlocal rank = Konsole.getRank(player)\r\n```\r\n\r\nBind your own rank resolver:\r\n\r\n```luau\r\nKonsole.bindRanks(function(entity)\r\n\tif entity and entity.UserId == game.CreatorId then\r\n\t\treturn 100\r\n\tend\r\n\r\n\treturn nil\r\nend)\r\n```\r\n\r\nReturn `nil` to fall back to the built-in rank store.\r\n\r\n## Two Command Panes\r\n\r\nKonsole supports a second chat pane.\r\n\r\nOnce opened, each pane can be dragged independently. This is useful when you want to compare outputs, keep one command result visible, or run commands without losing context in the first pane.\r\n\r\n`clear` only clears the pane you typed it in. The public `Konsole.clear()` method clears the whole active client.\r\n\r\nKonsole saves history, scroll positions, pane positions, and restored width when the UI is closed and reopened.\r\n\r\n## UI Behavior\r\n\r\nKonsole is intentionally small.\r\n\r\nIt opens as a compact pill near the bottom of the screen. When output appears, it expands into history. When command output is wider than the current panel, the panel grows to fit visible content. Command output slides upward when appended. Suggestions and argument hints animate in and out.\r\n\r\nThe command input uses structured chips for arguments so you can see what each value means while typing.\r\n\r\nUseful keys:\r\n\r\n- `T`: default toggle\r\n- `Tab`: accept suggestion\r\n- `Up` / `Down`: move through suggestions\r\n- `Left` / `Right`: move between command/argument fields at the edges\r\n- `Backspace`: return to the previous field at the start of an argument\r\n- `Enter`: submit\r\n- `Escape`: close\r\n\r\n## Config\r\n\r\nPass config overrides into `Konsole.create(...)`.\r\n\r\n```luau\r\nlocal client = Konsole.create({\r\n\tinput = {\r\n\t\tactivationKeys = { Enum.KeyCode.Semicolon },\r\n\t\tforceclose = true, -- outside click fully closes instead of only releasing input focus\r\n\t},\r\n\tpanel = {\r\n\t\tposition = \"bc\", -- accepts a udim2 or a named position\r\n\t\twidth = 280,\r\n\t\toutputWidth = 380,\r\n\t\thistoryMaxHeight = 420,\r\n\t},\r\n\tcolor = {\r\n\t\tpanel = Color3.fromRGB(0, 0, 0),\r\n\t\tinputText = Color3.fromRGB(255, 255, 255),\r\n\t},\r\n})\r\n```\r\n\r\nConfig groups:\r\n\r\n- `font`\r\n- `panel`\r\n- `layout`\r\n- `motion`\r\n- `input`\r\n- `color`\r\n- `transparency`\r\n- `commands`\r\n\r\nCommon panel options:\r\n\r\n- `position`: a `UDim2`, or `br`, `tr`, `tl`, `bl`, `bc`, `tc` (full names such as `bottom right` also work)\r\n- `width`: base input width\r\n- `outputWidth`: base width once history exists\r\n- `maxWidth`: maximum width\r\n- `height`: collapsed input height\r\n- `historyMaxHeight`: max history height for each pane\r\n- `suggestionHeight`\r\n- `maxSuggestions`: maximum visible suggestions\r\n- `suggestionGap`\r\n- `displayOrder`\r\n\r\nCommon motion options:\r\n\r\n- `expandSmoothTime`\r\n- `openSmoothTime`\r\n- `outputSmoothTime`\r\n- `textFadeTime`\r\n- `textSlideOffset`\r\n- `itemSlideSmoothTime`\r\n- `collapseSmoothTime`\r\n- `hintFadeTime`\r\n- `hintSlideTime`\r\n\r\nCommon color options:\r\n\r\n- `panel`\r\n- `inputText`\r\n- `promptText`\r\n- `suggPanel`\r\n- `suggText`\r\n- `successRich`\r\n- `errorRich`\r\n- `warnRich`\r\n- `mutedText`\r\n\r\n## Custom Clients\r\n\r\nThe direct `Konsole.show()` style uses one shared default client.\r\n\r\nFor more control:\r\n\r\n```luau\r\nlocal client = Konsole.create({\r\n\tinput = {\r\n\t\tactivationKeys = { Enum.KeyCode.F2 },\r\n\t},\r\n})\r\n\r\nclient:bindRun(function(text)\r\n\treturn Konsole.Dispatch.execute(text)\r\nend)\r\n\r\nclient:setSuggestions(Konsole.Kommand.suggestions())\r\nclient:setSchemas(Konsole.Kommand.schemas())\r\nclient:show()\r\n```\r\n\r\nMost games can use the default client. Create a client when you want a different config, custom runner, or isolated UI behavior.\r\n\r\n## API\r\n\r\n```luau\r\nKonsole.create(options?)\r\nKonsole.host(serverImplementations?)\r\n\r\nKonsole.define(definition)\r\nKonsole.implement(name, callback)\r\nKonsole.run(text)\r\n\r\nKonsole.setRank(userId, rank)\r\nKonsole.getRank(entity)\r\nKonsole.bindRanks(resolver?)\r\n\r\nKonsole.show()\r\nKonsole.hide()\r\nKonsole.toggle()\r\nKonsole.focus()\r\nKonsole.clear()\r\nKonsole.destroy()\r\n\r\nKonsole.setActivationKeys(keys)\r\nKonsole.setEnabled(enabled)\r\nKonsole.setActivationUnlocksMouse(enabled)\r\nKonsole.setMouseUnlockDriver(getFn, setFn)\r\nKonsole.getCursorTarget()\r\n```\r\n\r\nClient methods returned by `Konsole.create(...)`:\r\n\r\n```luau\r\nclient:show()\r\nclient:hide()\r\nclient:toggle()\r\nclient:focus()\r\nclient:clear()\r\nclient:destroy()\r\nclient:bindRun(callback)\r\nclient:setSuggestions(list)\r\nclient:setSchemas(map)\r\nclient:setActivationKeys(keys)\r\nclient:setEnabled(enabled)\r\nclient:setActivationUnlocksMouse(enabled)\r\nclient:setMouseUnlockDriver(getFn, setFn)\r\nclient:getCursorTarget()\r\n```\r\n\r\nCommand definition shape:\r\n\r\n```luau\r\ntype Definition = {\r\n\tname: string,\r\n\trank: number | string?,\r\n\taliases: { string }?,\r\n\targs: { Argument }?,\r\n\tdescription: string?,\r\n\tcooldown: number?,\r\n\tserver: string?,\r\n\trun: ((context, ...any) -> any)?,\r\n}\r\n```\r\n\r\nArgument shape:\r\n\r\n```luau\r\ntype Argument = {\r\n\tname: string?,\r\n\ttype: string?,\r\n\tdefault: any,\r\n\trequired: boolean?,\r\n\tsuggestions: ({ string } | string)?,\r\n}\r\n```\r\n\r\nContext shape:\r\n\r\n```luau\r\ntype Context = {\r\n\tentity: Player?,\r\n\tkommand: Command,\r\n\ttext: string,\r\n\tdispatch: Dispatch,\r\n\tranks: Ranks,\r\n\trun: (text: any) -> any,\r\n\treply: (message: any?) -> Outcome,\r\n\terr: (code: any?, message: any?) -> Outcome,\r\n}\r\n```\r\n","readmeTruncated":false}