> For the complete documentation index, see [llms.txt](https://fluentv2.hungquan99.site/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://fluentv2.hungquan99.site/fluent-v2.md).

# Fluent v2

## Fluent v2

Fluent v2 is a Roblox UI library for building **Window → Tab/SubTab → Section → Element** interfaces. It uses the Windows 11 Fluent Design language, including acrylic backgrounds, rounded corners, and spring-based motion.

Use the guides to build your interface. Use the reference for API details.

### Explore the documentation

* [Getting started](/getting-started/create-a-window.md) — load Fluent and create your first window.
* [Build the interface](/build-the-interface/tabs-subtabs-and-sections.md) — add tabs, sections, and controls.
* [Guides](/guides/build-balanced-tabs.md) — design clear, balanced interfaces.
* [Configure and customize](/configure-and-customize/save-configurations.md) — persist settings and adjust appearance.
* [Reference](/reference/notifications-and-icons.md) — look up notifications and icons.
* [Examples](/examples/full-example.md) — build a complete interface.

***

### 1. Loading the library

Load the three files independently:

```lua
local Fluent = loadstring(game:HttpGet("https://raw.githubusercontent.com/hungquan99/Interface/refs/heads/main/Enhanced/betafluentv2.lua"))()
local SaveManager = loadstring(game:HttpGet("https://raw.githubusercontent.com/hungquan99/FluentUI/master/Addons/SaveManager.lua"))()
local InterfaceManager = loadstring(game:HttpGet("https://raw.githubusercontent.com/hungquan99/FluentUI/master/Addons/InterfaceManager.lua"))()
```

* `Fluent` is the core library. It creates windows, tabs, elements, notifications, and themes.
* `SaveManager` saves and loads element state to JSON configuration files.
* `InterfaceManager` persists interface preferences separately from script configuration values.

{% hint style="info" %}
`betafluentv2.lua` also defines `SaveManager` and `InterfaceManager` internally. It returns `Library, SaveManager, InterfaceManager, Mobile`.

The loading snippet captures only the first return value. Your script therefore uses the two separately loaded addon files.
{% endhint %}

***

### 2. Creating a window

```lua
local Window = Fluent:CreateWindow({
    Title = "My Script",
    SubTitle = "by You",
    Size = UDim2.fromOffset(580, 460),
    Icon = "settings",                 -- Lucide icon name
    Acrylic = true,                    -- has no effect in Studio
    Theme = "Dark",                    -- see Themes
    MinimizeKey = Enum.KeyCode.LeftControl,
    TabWidth = 160,
    Search = true,
    DropdownsOutsideWindow = false,

    -- Optional user-info block in the sidebar:
    UserInfoTitle = "Player",
    UserInfo = true,                   -- uses LocalPlayer.DisplayName
    UserInfoTop = false,
    UserInfoSubtitle = "linked",
    UserInfoSubtitleColor = Color3.fromRGB(0, 255, 0),
})
```

Call `CreateWindow` once. A later call prints a warning and returns `nil`.

The library uses scale and spring animations. It does not tween the window's actual `Size`.

#### Window methods

| Method                                                 | Description                          |
| ------------------------------------------------------ | ------------------------------------ |
| `Window:AddTab({ Title = "...", Icon = "..." })`       | Creates and returns a tab.           |
| `Window:SelectTab(TabObject)`                          | Switches to a tab.                   |
| `Window:Minimize()`                                    | Toggles the minimized state.         |
| `Window:ToggleSearch()`                                | Toggles the search bar when enabled. |
| `Window:Dialog({ Title, Content, Buttons = { ... } })` | Shows a centered modal dialog.       |
| `Window:Destroy()`                                     | Removes the whole interface.         |

```lua
Window:Dialog({
    Title = "Confirm",
    Content = "Are you sure you want to reset your configuration?",
    Buttons = {
        { Title = "Cancel", Callback = function() end },
        { Title = "Confirm", Callback = function() print("Reset") end },
    },
})
```

***

### 3. Tabs, SubTabs, and sections

```lua
local Tab = Window:AddTab({ Title = "Main", Icon = "home" })

-- A Section directly inside a Tab:
local Section = Tab:AddSection("Combat", "sword", false)
-- Tab:AddSection(Title, Icon, DefaultCollapsed, Idx)

-- A SubTab nested inside a Tab:
local SubTab = Tab:AddSubTab("Visuals", "eye")
local SubSection = SubTab:AddSection("ESP", "eye")
```

`Tab:AddSection` and `SubTab:AddSection` use this signature:

```lua
Tab:AddSection(Title: string, Icon: string?, DefaultCollapsed: boolean?, Idx: string?)
```

* `DefaultCollapsed` controls the initial state. Saved state overrides it.
* `Idx` enables `SaveManager` persistence for collapsed state.

Sections expose the following methods:

| Method                                | Description                                                                          |
| ------------------------------------- | ------------------------------------------------------------------------------------ |
| `Section:Toggle()`                    | Flips collapsed and expanded.                                                        |
| `Section:SetCollapsed(bool, noAnim?)` | Sets the state. `noAnim = true` skips animation.                                     |
| `Section:IsCollapsed()`               | Returns `true` or `false`.                                                           |
| `Section:SetValue(bool)`              | Available only when an `Idx` was provided. Equivalent to `SetCollapsed(bool, true)`. |

Call every element method on a `Tab`, `SubTab`, or `Section`. The syntax stays identical.

***

### 4. Elements

Stateful elements use a string `Idx` as their first argument. They register under `Fluent.Options[Idx]` and expose `:SetValue(...)`.

Every element supports `:SetTitle(text)`, `:SetDesc(text)`, and `:Visible(bool)`.

Group related controls into focused sections. This keeps long tabs easy to scan.

#### 4.1 Actions and information

**Button**

```lua
local MyButton = Section:AddButton({
    Title = "Say Hello",
    Description = "Prints to the console",
    Callback = function()
        print("Hello!")
    end,
})
```

Attach an optional text input to the same card:

```lua
Section:AddButton({
    Title = "Execute",
    Callback = function() end,
    Input = { Placeholder = "Type a command...", Size = "Small" },
})
```

Set `Input = true` to use the default input. `Input.Size` accepts `"Small"` or `"Large"`.

A button with an input exposes `.InputValue` and `:SetInputValue(text)`. `SaveManager` persists the text when the button has an `Idx`.

**Paragraph**

```lua
Section:AddParagraph({
    Title = "Note",
    Content = "This script only works once your character has spawned.",
})
```

Paragraphs are informational. They have no `Idx`, `Callback`, or `Value`.

**Divider**

```lua
Section:AddDivider({ Thickness = 1 })
```

Dividers are horizontal separators. They have no persistent state.

#### 4.2 Choices and numeric values

**Toggle**

```lua
local MyToggle = Section:AddToggle("MyToggleId", {
    Title = "Auto Farm",
    Description = "Automatically farms",
    Default = false,
    Callback = function(Value)
        print("Toggle:", Value)
    end,
})

MyToggle:SetValue(true)
MyToggle:OnChanged(function(Value) print("Changed:", Value) end)
```

Toggles support the same connected `Input` option as buttons.

**Slider**

```lua
local MySlider = Section:AddSlider("SpeedSlider", {
    Title = "Speed",
    Description = "Movement speed",
    Default = 16,
    Min = 16,
    Max = 100,
    Rounding = 0,
    Callback = function(Value) print(Value) end,
})

MySlider:SetValue(50)
```

`Default`, `Min`, `Max`, and `Rounding` are required. Omitting one raises an assertion error.

**Dropdown**

```lua
local MyDropdown = Section:AddDropdown("TargetDropdown", {
    Title = "Target",
    Description = "Choose a target",
    Values = { "Player", "NPC", "All" },
    Default = "Player",
    Multi = false,
    AllowNull = true,
    Search = true,
    KeepSearch = false,
    Callback = function(Value) print(Value) end,
})

MyDropdown:SetValue("NPC")
MyDropdown:SetValues({ "A", "B", "C" })
```

With `Multi = true`, the value becomes a `{ [value] = true }` table. `Default` can be a numeric index when `Multi` is disabled.

#### 4.3 Appearance and shortcuts

**Colorpicker**

```lua
local MyColor = Section:AddColorpicker("ESPColor", {
    Title = "ESP Color",
    Default = Color3.fromRGB(255, 0, 0),
    Transparency = 0,
    Callback = function(Color) print(Color) end,
})

MyColor:SetValueRGB(Color3.fromRGB(0, 255, 0), 0.2)
```

`Transparency` accepts values from `0` through `1`.

**Keybind**

```lua
local MyKeybind = Section:AddKeybind("ToggleKey", {
    Title = "Toggle UI",
    Default = "K",
    Mode = "Toggle",
    Callback = function(Value) print("Pressed:", Value) end,
})

MyKeybind:OnChanged(function() print("New key:", MyKeybind.Value) end)
```

Set `Mode` to `"Toggle"` or `"Hold"`.

#### 4.4 Text input

**Input**

```lua
local MyInput = Section:AddInput("WebhookInput", {
    Title = "Webhook URL",
    Default = "",
    Placeholder = "https://discord.com/api/webhooks/...",
    Numeric = false,
    Finished = true,
    MaxLength = 200,
    Callback = function(Value) print(Value) end,
})
```

Set `Numeric = true` for digits only. Set `Finished = false` to call the callback on each keystroke.

***

### 5. Building balanced tabs

Use several short sections instead of one large section. Keep each section focused on one task.

```lua
local MainTab = Window:AddTab({ Title = "Main", Icon = "home" })

local Automation = MainTab:AddSection("Automation", "zap", false, "AutomationSection")
local Movement = MainTab:AddSection("Movement", "move", false, "MovementSection")
local Display = MainTab:AddSection("Display", "eye", false, "DisplaySection")
```

Use `Idx` values when collapsed state should persist. Sections without an `Idx` work normally.

***

### 6. Notifications

```lua
Fluent:Notify({
    Title = "Success",
    Content = "Configuration saved.",
    SubContent = "",
    Duration = 5,
})
```

Omit `Duration` for a persistent notification.

***

### 7. SaveManager addon

`SaveManager` writes JSON files to `<Folder>/settings/<name>.json`.

It serializes and restores Toggle, Slider, Dropdown, Colorpicker, Keybind, Input, connected Button input, and identified Section state.

#### 7.1 Basic setup

```lua
SaveManager:SetLibrary(Fluent)
SaveManager:SetFolder("MyScript/Configs")
SaveManager:IgnoreThemeSettings()
SaveManager:SetIgnoreIndexes({ "SomeOptionId" })

local SettingsTab = Window:AddTab({ Title = "Settings", Icon = "settings" })
SaveManager:BuildConfigSection(SettingsTab)

-- Or manage configurations directly:
SaveManager:Save("profile1")
SaveManager:Load("profile1")
SaveManager:RefreshConfigList()
SaveManager:LoadAutoloadConfig()
```

`Save` and `Load` return `true` on success. On failure, they return `false, "reason"`.

#### 7.2 Saving section state

Only sections with an explicit `Idx` are persisted:

```lua
local CombatSection = Tab:AddSection("Combat", "sword", false, "CombatSection")
```

`SaveManager` saves `Section:IsCollapsed()`. It restores state through `Section:SetValue(bool)`.

Restores apply instantly without collapse or expand animation.

#### 7.3 Auto Save

`BuildConfigSection` adds an **Auto Save** toggle. Its `Idx` is `"SaveManager_AutoSave"` and it defaults to `true`.

The background loop runs every `SaveManager.AutoSaveDelay` seconds. The default delay is `1`. It writes only when serialized JSON changed.

The autosave target follows this order:

1. The config named by `autoload.txt`.
2. `SaveManager.AutoSaveConfigName`, which defaults to `"autosave"`.

Auto Save keeps `autoload.txt` synchronized with the active configuration.

#### 7.4 Other methods

| Method                               | Description                                                                        |
| ------------------------------------ | ---------------------------------------------------------------------------------- |
| `SaveManager:SetIgnoreIndexes(list)` | Adds `Idx` values that must not be saved.                                          |
| `SaveManager:IgnoreThemeSettings()`  | Ignores `InterfaceTheme`, `AcrylicToggle`, `TransparentToggle`, and `MenuKeybind`. |
| `SaveManager:BuildFolderTree()`      | Creates `<Folder>` and `<Folder>/settings`.                                        |
| `SaveManager:RemoveAutoloadConfig()` | Clears `autoload.txt`.                                                             |
| `SaveManager:Encode()`               | Returns current state as `true, jsonString`, without writing it.                   |

***

### 8. InterfaceManager addon

`InterfaceManager` stores theme, acrylic, window transparency, and minimize keybind preferences in `<Folder>/options.json`.

```lua
InterfaceManager:SetLibrary(Fluent)
InterfaceManager:SetFolder("MyScript/Configs")

local SettingsTab = Window:AddTab({ Title = "Settings", Icon = "settings" })
InterfaceManager:BuildInterfaceSection(SettingsTab)

InterfaceManager:SaveSettings()
InterfaceManager:LoadSettings()
```

`BuildInterfaceSection` adds a theme dropdown, desktop acrylic toggle, window-transparency slider, and minimize-bind keybind.

Each setting applies immediately and writes to `options.json` on change.

***

### 9. Themes

```lua
Fluent:SetTheme("Rose")
```

Built-in themes are `Dark`, `Darker`, `Amoled`, `Light`, `Rose`, and `Arctic`.

Other library calls:

```lua
Fluent:ToggleAcrylic(true)
Fluent:ToggleTransparency(true)
Fluent:SetWindowTransparency(0.1)
Fluent:Destroy()
```

***

### 10. Icons

Fluent uses the [Lucide](https://lucide.dev) icon set. Pass the icon name without a `lucide-` prefix.

```lua
Fluent:GetIcon("home")
```

This returns the matching `rbxassetid` string. It returns `nil` when no matching icon exists.

The library also tries to load an updated remote icon set. If that fails, it uses the built-in icon table.

***

### 11. Minimizer

The minimizer is a draggable floating button. It is useful on mobile and restores a minimized window.

```lua
Fluent:CreateMinimizer({
    Icon = "settings",
    Size = UDim2.new(0, 50, 0, 50),
    Position = UDim2.new(1, -70, 1, -85),
    Transparency = 0.3,
    Visible = true,
})
```

Calling this again returns the existing minimizer. Dragging is always enabled.

***

### 12. Full example

```lua
local Fluent = loadstring(game:HttpGet("https://raw.githubusercontent.com/hungquan99/Interface/refs/heads/main/Enhanced/betafluentv2.lua"))()
local SaveManager = loadstring(game:HttpGet("https://raw.githubusercontent.com/hungquan99/FluentUI/master/Addons/SaveManager.lua"))()
local InterfaceManager = loadstring(game:HttpGet("https://raw.githubusercontent.com/hungquan99/FluentUI/master/Addons/InterfaceManager.lua"))()

local Window = Fluent:CreateWindow({
    Title = "Demo Hub",
    SubTitle = "v1.0",
    Size = UDim2.fromOffset(580, 460),
    Acrylic = true,
    Theme = "Dark",
})

local MainTab = Window:AddTab({ Title = "Main", Icon = "home" })
local Automation = MainTab:AddSection("Automation", "zap", false, "AutomationSection")
local Movement = MainTab:AddSection("Movement", "move", false, "MovementSection")
local Display = MainTab:AddSection("Display", "eye", false, "DisplaySection")

Automation:AddToggle("AutoFarm", {
    Title = "Auto Farm",
    Default = false,
    Callback = function(v) print("AutoFarm:", v) end,
})

Automation:AddDropdown("FarmTarget", {
    Title = "Target",
    Values = { "Nearest", "All" },
    Default = "Nearest",
    Callback = function(v) print("Target:", v) end,
})

Movement:AddSlider("WalkSpeed", {
    Title = "Walk Speed",
    Default = 16, Min = 16, Max = 200, Rounding = 0,
    Callback = function(v)
        game.Players.LocalPlayer.Character.Humanoid.WalkSpeed = v
    end,
})

Display:AddToggle("ShowStatus", {
    Title = "Show Status",
    Default = true,
    Callback = function(v) print("ShowStatus:", v) end,
})

local SettingsTab = Window:AddTab({ Title = "Settings", Icon = "settings" })

SaveManager:SetLibrary(Fluent)
InterfaceManager:SetLibrary(Fluent)

SaveManager:SetFolder("DemoHub")
InterfaceManager:SetFolder("DemoHub")

InterfaceManager:BuildInterfaceSection(SettingsTab)
SaveManager:IgnoreThemeSettings()
SaveManager:BuildConfigSection(SettingsTab)
SaveManager:LoadAutoloadConfig()

Fluent:Notify({ Title = "Demo Hub", Content = "Loaded successfully!", Duration = 5 })
```

***

### Notes

* Sections 1–7 and 9–12 were verified against `betafluentv2.lua` and the `SaveManager.lua` addon.
* The InterfaceManager section reflects the equivalent implementation bundled in `betafluentv2.lua`.
* If your separate InterfaceManager addon differs, use its source as the authoritative reference.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://fluentv2.hungquan99.site/fluent-v2.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
