{"id":15693577,"url":"https://github.com/fuma-nama/discord-ui","last_synced_at":"2025-05-08T04:40:43.740Z","repository":{"id":58054395,"uuid":"529182440","full_name":"fuma-nama/discord-ui","owner":"fuma-nama","description":"Powerful Discord Message Builder UI Framework for Creating Interactive Message","archived":false,"fork":false,"pushed_at":"2022-09-16T14:37:11.000Z","size":212,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-04T21:10:52.425Z","etag":null,"topics":["discord-bot","discord-bot-framework","discord-framework","discord-ui","framework","kotlin","kotlin-library","ui-framework"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fuma-nama.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-08-26T08:46:03.000Z","updated_at":"2024-03-20T13:50:32.000Z","dependencies_parsed_at":"2023-01-18T10:37:28.020Z","dependency_job_id":null,"html_url":"https://github.com/fuma-nama/discord-ui","commit_stats":null,"previous_names":["fuma-nama/discord-ui"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuma-nama%2Fdiscord-ui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuma-nama%2Fdiscord-ui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuma-nama%2Fdiscord-ui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuma-nama%2Fdiscord-ui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fuma-nama","download_url":"https://codeload.github.com/fuma-nama/discord-ui/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253002778,"owners_count":21838634,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["discord-bot","discord-bot-framework","discord-framework","discord-ui","framework","kotlin","kotlin-library","ui-framework"],"created_at":"2024-10-03T18:46:07.593Z","updated_at":"2025-05-08T04:40:43.714Z","avatar_url":"https://github.com/fuma-nama.png","language":"Kotlin","funding_links":[],"categories":["Kotlin"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n    \u003cimg src=\"./document/Icon.jpg\" alt=\"banner\" width=\"600px\"/\u003e\n\u003c/div\u003e\n\n# DUI - Discord UI ![GitHub](https://img.shields.io/github/license/SonMooSans/discord-ui?style=for-the-badge) ![Sonatype Nexus (Releases)](https://img.shields.io/nexus/r/io.github.sonmoosans/dui?server=https%3A%2F%2Fs01.oss.sonatype.org) ![GitHub Repo stars](https://img.shields.io/github/stars/SonMooSans/discord-ui?style=social)\nHigh-performance Discord Message Component Based Kotlin UI Framework\n\u003cbr\u003e\nRender Reactive Message and Manage States and Listeners\n\n## Installation\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.sonmoosans\u003c/groupId\u003e\n    \u003cartifactId\u003edui\u003c/artifactId\u003e\n    \u003cversion\u003e1.4.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Features\nDUI provides high code quality, high performance, memory safe UI System\n* Manage Message Component Listeners\n* Rendering message like a reactive UI\n* Functional Programming style usage\n* Highly flexible: Rendering with everything such as `Graphics2d`\n* Memory-Safe: We use dynamic listener to reduce memory usage\n\n## Functional Programming Style\nEverything is Clean and Beautiful.\n\n\u003e ### Example\n\u003e Create a message containing an Embed that displays a number\n\u003e \u003cbr\u003e\n\u003e With a button that increases the number by clicking it\n```kotlin\nval counter = component\u003cUnit\u003e {\n    val count by useState(\"count\", 0)\n\n    embed(title = \"Counter\", description = count.toString())\n\n    row {\n        button(\"Increase\") {\n            count++\n            event.edit()\n        }\n    }\n}\n```\n\n## Useful Hooks\nDUI provided some built-in hooks for rendering messages\n\nAll Hooks contain a unique ID field so it can be called in any orders\n\u003cbr\u003e\nID of Built-in Hooks can be anonymous, which is generated from lambda\n\n```kotlin\nval theme = useContext(ThemeContext)\nval state by useState(\"id\", \"initial value\")\nval (count, setCount) = useState { \"initial value\" }\nval sync = useSync()\nval memo = useMemo(dependencies) { processString(state) }\nval confirmModal = useModal {\n    title = \"Do you sure?\"\n\n    row {\n        input(id = \"confirm\", label = \"Type 'I am gay' to confirm\")\n    }\n\n    submit {\n        //do something\n    }\n}\n\nuseChange(dependencies) {\n    println(\"Updated!\")\n}\nuseEffect(dependencies) {\n    println(\"Updated!\")\n}\nuseExport(data = \"Export Something\")\n```\n\n## Built-in Components\nDUI also provides some built-in Components\n\n```kotlin\nrowLayout { //Split into multi Action Rows if overflow\n    button(label = \"Test\") {\n        event.ignore()\n    }\n\n    menu {\n        option(\"Label\", \"Value\")\n\n        submit {\n            event.ignore()\n        }\n    }\n}\npager { //a simple Pager implementation\n    page {\n        embed(title = \"Page 1\")\n    }\n}\ntabLayout { //Adds a SelectMenu to switch between Tabs\n    tab(\"User\") {\n        text(\"Your Profile\")\n        proflie()\n    }\n\n    tab(\"Settings\") {\n        embed(title = \"Settings Tab\")\n    }\n}\n```\n\n## Memory Safe\nComponent only needs a `Data` instance for rendering.\n\n### ID Component\n```kotlin\nval Test = component\u003cProps\u003e {\n    embed(title = props.value)\n}\n```\nAll Data entries will be stored in a Map\n\u003cbr\u003e\nYou can implement your own management system above it\n\nRemember that You **must destroy** unused Data manually\n\n### Dynamic Components\n```kotlin\nval Test = dynamicComponent(YourGenerator) {\n    embed(title = props.value)\n}\n```\nIt reads props from event info using a `Generator` and invoke dynamic listeners with parsed data\n\u003cbr\u003e\nTherefore, it doesn't require to store any data\n\nHowever, you cannot store too many things to a listener id since length of listener Ids has a limit\n\n## Component Listeners\n### Data Based Listeners\n`IDComponent`, `OnceComponent` use it as the default listener type\n\n```kotlin\nval result = something()\nbutton(\"Do something\", dynamic = false) {\n    println(result)\n    event.edit()\n}\n```\nData Based Listeners are stored in each `Data` object\n\u003cbr\u003e\nTherefore, It will use some memory when there are a lot of data objects and listeners\n\n### Dynamic Listener\n`SingleDataComponent`, `DynamicComponent` use it as the default listener type\n```kotlin\nval ref = useRef { something() }\nbutton(\"Do Something\", dynamic = true) {\n    println(ref.current)\n    event.edit()\n}\n```\nYou can use **Dynamic Listener** instead to reduce memory usage\n\nSince they are bundled with Component itself\n\u003cbr\u003e\nDynamic Listeners only needs to be created once, and can be used for unlimited times.\n\nSince data is not synchronized, You should not access any data outside the Listener\n\u003cbr\u003e\n**You must wrap those variables inside a `useRef` hook to access them**\n\n### Change Default type of listener\n```kotlin\n//set default value of 'dynamic'\ndynamic = value\nbutton(\"...\") {\n    //do something\n}\n\n//set default value of 'dynamic' only in scope\ndynamic(value) {\n    button(\"...\") {\n        //do something\n    }\n}\n```\n### Note\nData based Listeners can override dynamic listeners by using the same ID\n### Listener ID Structure\nListener ID Structure: `[Component ID]-[Data ID]-[Listener ID]`\n\n```kotlin\nrow {\n    button(\"Do Something\") { //ID: 4343243243-3-432423432\n        println(\"Component Interaction Event\")\n    }\n\n    button(\"Remove\", id = \"onRemove\") { //ID: 4343243243-3-onRemove\n        println(\"Component Interaction Event\")\n    }\n    \n    menu(placeholder = \"Select Item\") {\n        option(\"...\", \"...\")\n        \n        submit(\"onSelect\") { //ID: 4343243243-3-onSelect\n        }\n    }\n}\n```\nTo use external Listener ID, don't pass the event handler\n\u003cbr\u003e\nTherefore, you can create your own Event handler\n\u003cbr\u003e\n```kotlin\nrow {\n    //For Select Menu, just pass the ID to root function instead of 'submit' function\n    menu(id = \"onRemove\", placeholder = \"Select Something\") {\n        option(\"...\", \"...\")\n    }\n    button(\"Remove\", id = \"onRemove\") //ID: onRemove\n}\n```\n\n### Highly Flexible\n\u003cimg src=\"./document/todo-example.jpg\" alt=\"banner\" width=\"200px\"/\u003e\n\nNot only embed or text, DUI supports render everything. Including rendering UI with Graphics2D\n\u003cbr\u003e\nDUI also has a small Utility for Rendering with Graphics2D\n\n```kotlin\n//You may wrap this in useMemo Hook\nval image = BufferedImage(500, 600, BufferedImage.TYPE_INT_RGB)\n\nwith (image.createGraphics()) {\n    val (w, h) = 450 to 100\n\n    font = font.deriveFont(25f)\n    translate((500 - w) / 2, 50)\n\n    for (i in 0..3) {\n        paint(Color.DARK_GRAY) {\n            fillRoundRect(0, 0, w, h, 20, 20)\n        }\n\n        translate(0, h + 10)\n    }\n}\n\nfiles {\n    file(\"ui.png\", image.toInputStream())\n}\n```\n\n## Getting Started\nCreate a Component\n```kotlin\nval example = component {\n    val count by useState(\"count\", 0)\n    \n    text(count.toString())\n\n    row {\n        button(\"Increase\") {\n            count++\n            event.edit()\n        }\n    }\n}\n```\nIn above example, we create a `count` state\n\u003cbr\u003e\nWhen \"Increase\" Button is clicked, Increase count state and Reply to the event\n\nThen, Register a Slash command (We use [BJDA](https://github.com/SonMooSans/B-JDA) for this)\n\u003cbr\u003e\nSee their tutorial to learn how to use BJDA\n```kotlin\nfun TestCommand() = command(\"test\", \"Testing Command\") {\n\n    execute {\n        val ui = example.create(event.user.idLong, Unit) {\n            //sync(event.hook)\n            //use with useSync hook to sync multi messages\n        }\n\n        event.reply(ui).queue()\n    }\n}\n```\n\n## Known issues\n* Dynamic Listeners unmounted after restarting bot\n\n## Support My Job\nGive this repo a star!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuma-nama%2Fdiscord-ui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffuma-nama%2Fdiscord-ui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuma-nama%2Fdiscord-ui/lists"}