roblox-opencode 1.0.0

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.
Files changed (248) hide show
  1. package/README.md +122 -0
  2. package/commands/setup-game.md +108 -0
  3. package/commands/sync-check.md +53 -0
  4. package/core/roblox-core.md +93 -0
  5. package/dist/server.js +167 -0
  6. package/package.json +35 -0
  7. package/skills/roblox-analytics/SKILL.md +277 -0
  8. package/skills/roblox-analytics/references/event-batcher.luau +75 -0
  9. package/skills/roblox-animation-vfx/SKILL.md +1325 -0
  10. package/skills/roblox-architecture/SKILL.md +863 -0
  11. package/skills/roblox-architecture/references/combat-systems.md +1381 -0
  12. package/skills/roblox-code-review/SKILL.md +687 -0
  13. package/skills/roblox-data/SKILL.md +889 -0
  14. package/skills/roblox-data/references/inventory-systems.md +1729 -0
  15. package/skills/roblox-debug/SKILL.md +99 -0
  16. package/skills/roblox-gui/SKILL.md +1103 -0
  17. package/skills/roblox-gui-fusion/SKILL.md +150 -0
  18. package/skills/roblox-gui-fusion/references/inventory.luau +427 -0
  19. package/skills/roblox-gui-fusion/references/settings-menu.luau +579 -0
  20. package/skills/roblox-gui-fusion/references/shop.luau +411 -0
  21. package/skills/roblox-luau-mastery/SKILL.md +1519 -0
  22. package/skills/roblox-monetization/SKILL.md +1084 -0
  23. package/skills/roblox-monetization/references/process-receipt.luau +131 -0
  24. package/skills/roblox-networking/SKILL.md +669 -0
  25. package/skills/roblox-networking/references/remote-validator.luau +193 -0
  26. package/skills/roblox-publish-checklist/SKILL.md +128 -0
  27. package/skills/roblox-runtime/SKILL.md +753 -0
  28. package/skills/roblox-sharp-edges/SKILL.md +295 -0
  29. package/skills/roblox-sync/SKILL.md +126 -0
  30. package/skills/roblox-testing/SKILL.md +943 -0
  31. package/skills/roblox-tooling/SKILL.md +150 -0
  32. package/vendor/LICENSES/ProfileStore-LICENSE +201 -0
  33. package/vendor/LICENSES/RbxUtil-LICENSE +7 -0
  34. package/vendor/LICENSES/promise-LICENSE +21 -0
  35. package/vendor/LICENSES/t-LICENSE +21 -0
  36. package/vendor/LICENSES/testez-LICENSE +201 -0
  37. package/vendor/README.md +84 -0
  38. package/vendor/fusion/Animation/ExternalTime.luau +84 -0
  39. package/vendor/fusion/Animation/Spring.luau +322 -0
  40. package/vendor/fusion/Animation/Stopwatch.luau +128 -0
  41. package/vendor/fusion/Animation/Tween.luau +187 -0
  42. package/vendor/fusion/Animation/getTweenDuration.luau +27 -0
  43. package/vendor/fusion/Animation/getTweenRatio.luau +47 -0
  44. package/vendor/fusion/Animation/lerpType.luau +164 -0
  45. package/vendor/fusion/Animation/packType.luau +100 -0
  46. package/vendor/fusion/Animation/springCoefficients.luau +80 -0
  47. package/vendor/fusion/Animation/unpackType.luau +103 -0
  48. package/vendor/fusion/Colour/Oklab.luau +70 -0
  49. package/vendor/fusion/Colour/sRGB.luau +55 -0
  50. package/vendor/fusion/External.luau +168 -0
  51. package/vendor/fusion/ExternalDebug.luau +70 -0
  52. package/vendor/fusion/Graph/Observer.luau +114 -0
  53. package/vendor/fusion/Graph/castToGraph.luau +29 -0
  54. package/vendor/fusion/Graph/change.luau +81 -0
  55. package/vendor/fusion/Graph/depend.luau +33 -0
  56. package/vendor/fusion/Graph/evaluate.luau +56 -0
  57. package/vendor/fusion/Instances/Attribute.luau +58 -0
  58. package/vendor/fusion/Instances/AttributeChange.luau +47 -0
  59. package/vendor/fusion/Instances/AttributeOut.luau +63 -0
  60. package/vendor/fusion/Instances/Child.luau +21 -0
  61. package/vendor/fusion/Instances/Children.luau +148 -0
  62. package/vendor/fusion/Instances/Hydrate.luau +33 -0
  63. package/vendor/fusion/Instances/New.luau +53 -0
  64. package/vendor/fusion/Instances/OnChange.luau +50 -0
  65. package/vendor/fusion/Instances/OnEvent.luau +54 -0
  66. package/vendor/fusion/Instances/Out.luau +69 -0
  67. package/vendor/fusion/Instances/applyInstanceProps.luau +149 -0
  68. package/vendor/fusion/Instances/defaultProps.luau +194 -0
  69. package/vendor/fusion/LICENSE +21 -0
  70. package/vendor/fusion/Logging/formatError.luau +49 -0
  71. package/vendor/fusion/Logging/messages.luau +52 -0
  72. package/vendor/fusion/Logging/parseError.luau +25 -0
  73. package/vendor/fusion/Memory/checkLifetime.luau +134 -0
  74. package/vendor/fusion/Memory/deriveScope.luau +24 -0
  75. package/vendor/fusion/Memory/deriveScopeImpl.luau +45 -0
  76. package/vendor/fusion/Memory/doCleanup.luau +79 -0
  77. package/vendor/fusion/Memory/innerScope.luau +34 -0
  78. package/vendor/fusion/Memory/legacyCleanup.luau +18 -0
  79. package/vendor/fusion/Memory/needsDestruction.luau +17 -0
  80. package/vendor/fusion/Memory/poisonScope.luau +34 -0
  81. package/vendor/fusion/Memory/scopePool.luau +55 -0
  82. package/vendor/fusion/Memory/scoped.luau +27 -0
  83. package/vendor/fusion/Memory/whichLivesLonger.luau +75 -0
  84. package/vendor/fusion/RobloxExternal.luau +98 -0
  85. package/vendor/fusion/State/Computed.luau +139 -0
  86. package/vendor/fusion/State/For/Disassembly.luau +211 -0
  87. package/vendor/fusion/State/For/ForTypes.luau +30 -0
  88. package/vendor/fusion/State/For/init.luau +110 -0
  89. package/vendor/fusion/State/ForKeys.luau +94 -0
  90. package/vendor/fusion/State/ForPairs.luau +97 -0
  91. package/vendor/fusion/State/ForValues.luau +94 -0
  92. package/vendor/fusion/State/Value.luau +88 -0
  93. package/vendor/fusion/State/castToState.luau +26 -0
  94. package/vendor/fusion/State/peek.luau +31 -0
  95. package/vendor/fusion/State/updateAll.luau +1 -0
  96. package/vendor/fusion/Types.luau +314 -0
  97. package/vendor/fusion/Utility/Contextual.luau +91 -0
  98. package/vendor/fusion/Utility/Safe.luau +23 -0
  99. package/vendor/fusion/Utility/isSimilar.luau +29 -0
  100. package/vendor/fusion/Utility/merge.luau +35 -0
  101. package/vendor/fusion/Utility/nameOf.luau +35 -0
  102. package/vendor/fusion/Utility/never.luau +14 -0
  103. package/vendor/fusion/Utility/nicknames.luau +11 -0
  104. package/vendor/fusion/Utility/xtypeof.luau +27 -0
  105. package/vendor/fusion/init.luau +82 -0
  106. package/vendor/profilestore/init.luau +2243 -0
  107. package/vendor/promise/init.luau +1982 -0
  108. package/vendor/rbxutil/buffer-util/Buffer.test.luau +25 -0
  109. package/vendor/rbxutil/buffer-util/BufferReader.luau +228 -0
  110. package/vendor/rbxutil/buffer-util/BufferWriter.luau +269 -0
  111. package/vendor/rbxutil/buffer-util/DataTypeBuffer.luau +223 -0
  112. package/vendor/rbxutil/buffer-util/Types.luau +60 -0
  113. package/vendor/rbxutil/buffer-util/index.d.ts +153 -0
  114. package/vendor/rbxutil/buffer-util/init.luau +41 -0
  115. package/vendor/rbxutil/buffer-util/package.json +16 -0
  116. package/vendor/rbxutil/buffer-util/wally.toml +9 -0
  117. package/vendor/rbxutil/comm/Client/ClientComm.luau +232 -0
  118. package/vendor/rbxutil/comm/Client/ClientRemoteProperty.luau +156 -0
  119. package/vendor/rbxutil/comm/Client/ClientRemoteSignal.luau +109 -0
  120. package/vendor/rbxutil/comm/Client/init.luau +135 -0
  121. package/vendor/rbxutil/comm/Server/RemoteProperty.luau +295 -0
  122. package/vendor/rbxutil/comm/Server/RemoteSignal.luau +211 -0
  123. package/vendor/rbxutil/comm/Server/ServerComm.luau +211 -0
  124. package/vendor/rbxutil/comm/Server/init.luau +140 -0
  125. package/vendor/rbxutil/comm/Types.luau +18 -0
  126. package/vendor/rbxutil/comm/Util.luau +27 -0
  127. package/vendor/rbxutil/comm/init.luau +35 -0
  128. package/vendor/rbxutil/comm/wally.toml +13 -0
  129. package/vendor/rbxutil/component/init.luau +759 -0
  130. package/vendor/rbxutil/component/init.test.luau +311 -0
  131. package/vendor/rbxutil/component/wally.toml +14 -0
  132. package/vendor/rbxutil/concur/init.luau +542 -0
  133. package/vendor/rbxutil/concur/init.test.luau +364 -0
  134. package/vendor/rbxutil/concur/wally.toml +8 -0
  135. package/vendor/rbxutil/enum-list/init.luau +101 -0
  136. package/vendor/rbxutil/enum-list/init.test.luau +91 -0
  137. package/vendor/rbxutil/enum-list/wally.toml +8 -0
  138. package/vendor/rbxutil/find/index.d.ts +20 -0
  139. package/vendor/rbxutil/find/init.luau +44 -0
  140. package/vendor/rbxutil/find/package.json +17 -0
  141. package/vendor/rbxutil/find/wally.toml +8 -0
  142. package/vendor/rbxutil/input/Gamepad.luau +559 -0
  143. package/vendor/rbxutil/input/Keyboard.luau +124 -0
  144. package/vendor/rbxutil/input/Mouse.luau +278 -0
  145. package/vendor/rbxutil/input/PreferredInput.luau +91 -0
  146. package/vendor/rbxutil/input/Touch.luau +120 -0
  147. package/vendor/rbxutil/input/init.luau +33 -0
  148. package/vendor/rbxutil/input/wally.toml +12 -0
  149. package/vendor/rbxutil/loader/index.d.ts +15 -0
  150. package/vendor/rbxutil/loader/init.luau +137 -0
  151. package/vendor/rbxutil/loader/wally.toml +8 -0
  152. package/vendor/rbxutil/log/index.d.ts +38 -0
  153. package/vendor/rbxutil/log/init.luau +746 -0
  154. package/vendor/rbxutil/log/wally.toml +8 -0
  155. package/vendor/rbxutil/net/init.luau +190 -0
  156. package/vendor/rbxutil/net/wally.toml +8 -0
  157. package/vendor/rbxutil/option/index.d.ts +44 -0
  158. package/vendor/rbxutil/option/init.luau +489 -0
  159. package/vendor/rbxutil/option/init.test.luau +342 -0
  160. package/vendor/rbxutil/option/wally.toml +8 -0
  161. package/vendor/rbxutil/pid/index.d.ts +53 -0
  162. package/vendor/rbxutil/pid/init.luau +195 -0
  163. package/vendor/rbxutil/pid/package.json +16 -0
  164. package/vendor/rbxutil/pid/wally.toml +9 -0
  165. package/vendor/rbxutil/quaternion/index.d.ts +117 -0
  166. package/vendor/rbxutil/quaternion/init.luau +570 -0
  167. package/vendor/rbxutil/quaternion/package.json +16 -0
  168. package/vendor/rbxutil/quaternion/wally.toml +9 -0
  169. package/vendor/rbxutil/query/index.d.ts +43 -0
  170. package/vendor/rbxutil/query/init.luau +117 -0
  171. package/vendor/rbxutil/query/package.json +18 -0
  172. package/vendor/rbxutil/query/wally.toml +9 -0
  173. package/vendor/rbxutil/sequent/index.d.ts +28 -0
  174. package/vendor/rbxutil/sequent/init.luau +340 -0
  175. package/vendor/rbxutil/sequent/package.json +16 -0
  176. package/vendor/rbxutil/sequent/wally.toml +9 -0
  177. package/vendor/rbxutil/ser/init.luau +175 -0
  178. package/vendor/rbxutil/ser/init.test.luau +50 -0
  179. package/vendor/rbxutil/ser/wally.toml +11 -0
  180. package/vendor/rbxutil/shake/index.d.ts +36 -0
  181. package/vendor/rbxutil/shake/init.luau +532 -0
  182. package/vendor/rbxutil/shake/init.test.luau +267 -0
  183. package/vendor/rbxutil/shake/package.json +16 -0
  184. package/vendor/rbxutil/shake/wally.toml +9 -0
  185. package/vendor/rbxutil/signal/index.d.ts +100 -0
  186. package/vendor/rbxutil/signal/init.luau +432 -0
  187. package/vendor/rbxutil/signal/init.test.luau +190 -0
  188. package/vendor/rbxutil/signal/package.json +17 -0
  189. package/vendor/rbxutil/signal/wally.toml +9 -0
  190. package/vendor/rbxutil/silo/TableWatcher.luau +65 -0
  191. package/vendor/rbxutil/silo/Util.luau +55 -0
  192. package/vendor/rbxutil/silo/init.luau +338 -0
  193. package/vendor/rbxutil/silo/init.test.luau +215 -0
  194. package/vendor/rbxutil/silo/wally.toml +8 -0
  195. package/vendor/rbxutil/spring/index.d.ts +40 -0
  196. package/vendor/rbxutil/spring/init.luau +97 -0
  197. package/vendor/rbxutil/spring/package.json +17 -0
  198. package/vendor/rbxutil/spring/wally.toml +8 -0
  199. package/vendor/rbxutil/stream/index.d.ts +88 -0
  200. package/vendor/rbxutil/stream/init.luau +597 -0
  201. package/vendor/rbxutil/stream/package.json +18 -0
  202. package/vendor/rbxutil/stream/wally.toml +9 -0
  203. package/vendor/rbxutil/streamable/Streamable.luau +202 -0
  204. package/vendor/rbxutil/streamable/StreamableUtil.luau +80 -0
  205. package/vendor/rbxutil/streamable/init.luau +8 -0
  206. package/vendor/rbxutil/streamable/wally.toml +12 -0
  207. package/vendor/rbxutil/symbol/init.luau +56 -0
  208. package/vendor/rbxutil/symbol/init.test.luau +37 -0
  209. package/vendor/rbxutil/symbol/wally.toml +8 -0
  210. package/vendor/rbxutil/table-util/init.luau +938 -0
  211. package/vendor/rbxutil/table-util/init.test.luau +439 -0
  212. package/vendor/rbxutil/table-util/wally.toml +8 -0
  213. package/vendor/rbxutil/task-queue/index.d.ts +27 -0
  214. package/vendor/rbxutil/task-queue/init.luau +97 -0
  215. package/vendor/rbxutil/task-queue/wally.toml +8 -0
  216. package/vendor/rbxutil/timer/index.d.ts +81 -0
  217. package/vendor/rbxutil/timer/init.luau +249 -0
  218. package/vendor/rbxutil/timer/init.test.luau +73 -0
  219. package/vendor/rbxutil/timer/wally.toml +11 -0
  220. package/vendor/rbxutil/tree/index.d.ts +15 -0
  221. package/vendor/rbxutil/tree/init.luau +137 -0
  222. package/vendor/rbxutil/tree/wally.toml +8 -0
  223. package/vendor/rbxutil/trove/index.d.ts +46 -0
  224. package/vendor/rbxutil/trove/init.luau +787 -0
  225. package/vendor/rbxutil/trove/init.test.luau +203 -0
  226. package/vendor/rbxutil/trove/wally.toml +8 -0
  227. package/vendor/rbxutil/typed-remote/init.luau +196 -0
  228. package/vendor/rbxutil/typed-remote/wally.toml +8 -0
  229. package/vendor/rbxutil/wait-for/index.d.ts +17 -0
  230. package/vendor/rbxutil/wait-for/init.luau +257 -0
  231. package/vendor/rbxutil/wait-for/init.test.luau +182 -0
  232. package/vendor/rbxutil/wait-for/wally.toml +11 -0
  233. package/vendor/t/t.lua +1350 -0
  234. package/vendor/testez/Context.lua +26 -0
  235. package/vendor/testez/Expectation.lua +311 -0
  236. package/vendor/testez/ExpectationContext.lua +38 -0
  237. package/vendor/testez/LifecycleHooks.lua +89 -0
  238. package/vendor/testez/Reporters/TeamCityReporter.lua +102 -0
  239. package/vendor/testez/Reporters/TextReporter.lua +106 -0
  240. package/vendor/testez/Reporters/TextReporterQuiet.lua +97 -0
  241. package/vendor/testez/TestBootstrap.lua +147 -0
  242. package/vendor/testez/TestEnum.lua +28 -0
  243. package/vendor/testez/TestPlan.lua +304 -0
  244. package/vendor/testez/TestPlanner.lua +40 -0
  245. package/vendor/testez/TestResults.lua +112 -0
  246. package/vendor/testez/TestRunner.lua +188 -0
  247. package/vendor/testez/TestSession.lua +243 -0
  248. package/vendor/testez/init.lua +40 -0
@@ -0,0 +1,579 @@
1
+ --[[
2
+ Settings Menu — Fusion 0.3
3
+ Source patterns: VirtualButFake/fusion-components (MIT), dphfox/Fusion docs
4
+ Ported to Fusion 0.3 scoped syntax.
5
+
6
+ A complete settings menu with:
7
+ - Tabbed sections (Audio, Graphics, Controls)
8
+ - Sliders (volume, render distance)
9
+ - Toggle switches (fullscreen, vsync, show FPS)
10
+ - Dropdown select (quality preset)
11
+ - Spring open/close animation
12
+ - Saves to a settings table (caller persists however they want)
13
+
14
+ ADAPT THIS:
15
+ - Replace settings entries with your game's actual settings
16
+ - Wire OnSettingChanged to your DataStore or local storage
17
+ - Add/remove tabs and controls as needed
18
+ - Adjust colors to match your game's palette
19
+
20
+ Requires: Fusion 0.3 (wally: elttob/fusion@0.3.0)
21
+ ]]
22
+
23
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
24
+ local Players = game:GetService("Players")
25
+
26
+ -- Adjust this path to match your project structure:
27
+ -- Wally: require(ReplicatedStorage.Packages.Fusion)
28
+ -- Custom: require(game.ReplicatedStorage.Fusion)
29
+ -- Vendored: (default, from roblox-opencode setup)
30
+ local Fusion = require(ReplicatedStorage[".opencode"].vendor.fusion)
31
+ local scoped = Fusion.scoped
32
+ local peek = Fusion.peek
33
+ local Children = Fusion.Children
34
+ local OnEvent = Fusion.OnEvent
35
+
36
+ --------------------------------------------------------------------------------
37
+ -- Types
38
+ --------------------------------------------------------------------------------
39
+
40
+ type SettingValue = number | boolean | string
41
+
42
+ type SliderSetting = {
43
+ Type: "slider",
44
+ Label: string,
45
+ Key: string,
46
+ Min: number,
47
+ Max: number,
48
+ Step: number?,
49
+ Default: number,
50
+ }
51
+
52
+ type ToggleSetting = {
53
+ Type: "toggle",
54
+ Label: string,
55
+ Key: string,
56
+ Default: boolean,
57
+ }
58
+
59
+ type DropdownSetting = {
60
+ Type: "dropdown",
61
+ Label: string,
62
+ Key: string,
63
+ Options: { string },
64
+ Default: string,
65
+ }
66
+
67
+ type SettingEntry = SliderSetting | ToggleSetting | DropdownSetting
68
+
69
+ type SettingsTab = {
70
+ Name: string,
71
+ Icon: string?,
72
+ Settings: { SettingEntry },
73
+ }
74
+
75
+ type SettingsProps = {
76
+ Visible: Fusion.Value<boolean>,
77
+ Tabs: { SettingsTab },
78
+ Values: { [string]: Fusion.Value<SettingValue> },
79
+ OnSettingChanged: (key: string, value: SettingValue) -> (),
80
+ OnClose: () -> (),
81
+ }
82
+
83
+ --------------------------------------------------------------------------------
84
+ -- Theme
85
+ --------------------------------------------------------------------------------
86
+
87
+ local COLORS = {
88
+ Background = Color3.fromRGB(18, 18, 26),
89
+ Surface = Color3.fromRGB(28, 28, 40),
90
+ SurfaceAlt = Color3.fromRGB(35, 35, 48),
91
+ Primary = Color3.fromRGB(80, 140, 255),
92
+ Text = Color3.fromRGB(235, 235, 240),
93
+ TextSecondary = Color3.fromRGB(155, 155, 170),
94
+ TextMuted = Color3.fromRGB(90, 90, 105),
95
+ ToggleOn = Color3.fromRGB(80, 200, 120),
96
+ ToggleOff = Color3.fromRGB(60, 60, 75),
97
+ SliderTrack = Color3.fromRGB(45, 45, 60),
98
+ SliderFill = Color3.fromRGB(80, 140, 255),
99
+ TabActive = Color3.fromRGB(80, 140, 255),
100
+ TabInactive = Color3.fromRGB(60, 60, 75),
101
+ }
102
+
103
+ local FONT = {
104
+ Title = Font.new("rbxasset://fonts/families/GothamSSm.json", Enum.FontWeight.Bold),
105
+ Body = Font.new("rbxasset://fonts/families/GothamSSm.json", Enum.FontWeight.Medium),
106
+ Small = Font.new("rbxasset://fonts/families/GothamSSm.json", Enum.FontWeight.Regular),
107
+ }
108
+
109
+ --------------------------------------------------------------------------------
110
+ -- Toggle Switch component
111
+ --------------------------------------------------------------------------------
112
+
113
+ local function ToggleSwitch(
114
+ scope: Fusion.Scope,
115
+ state: Fusion.Value<boolean>,
116
+ onToggle: (newState: boolean) -> ()
117
+ )
118
+ return scope:New "TextButton" {
119
+ Name = "Toggle",
120
+ BackgroundColor3 = scope:Spring(scope:Computed(function(use)
121
+ return if use(state) then COLORS.ToggleOn else COLORS.ToggleOff
122
+ end), 25),
123
+ Size = UDim2.fromOffset(40, 22),
124
+ AutoButtonColor = false,
125
+ Text = "",
126
+
127
+ [OnEvent "Activated"] = function()
128
+ local newVal = not peek(state)
129
+ state:set(newVal)
130
+ onToggle(newVal)
131
+ end,
132
+
133
+ [Children] = {
134
+ scope:New "UICorner" { CornerRadius = UDim.new(1, 0) },
135
+
136
+ -- Knob
137
+ scope:New "Frame" {
138
+ Name = "Knob",
139
+ AnchorPoint = Vector2.new(0, 0.5),
140
+ Position = scope:Spring(scope:Computed(function(use)
141
+ return if use(state)
142
+ then UDim2.new(1, -20, 0.5, 0)
143
+ else UDim2.new(0, 2, 0.5, 0)
144
+ end), 30),
145
+ Size = UDim2.fromOffset(18, 18),
146
+ BackgroundColor3 = COLORS.Text,
147
+ [Children] = { scope:New "UICorner" { CornerRadius = UDim.new(1, 0) } },
148
+ },
149
+ },
150
+ }
151
+ end
152
+
153
+ --------------------------------------------------------------------------------
154
+ -- Slider component
155
+ --------------------------------------------------------------------------------
156
+
157
+ local function SettingsSlider(
158
+ scope: Fusion.Scope,
159
+ value: Fusion.Value<number>,
160
+ min: number,
161
+ max: number,
162
+ step: number?,
163
+ onChange: (newValue: number) -> ()
164
+ )
165
+ local effectiveStep = step or 1
166
+ local range = max - min
167
+
168
+ local percentage = scope:Computed(function(use)
169
+ return (use(value) - min) / range
170
+ end)
171
+
172
+ return scope:New "Frame" {
173
+ Name = "SliderContainer",
174
+ BackgroundTransparency = 1,
175
+ Size = UDim2.new(1, 0, 0, 20),
176
+
177
+ [Children] = {
178
+ -- Track
179
+ scope:New "TextButton" {
180
+ Name = "Track",
181
+ BackgroundColor3 = COLORS.SliderTrack,
182
+ AnchorPoint = Vector2.new(0, 0.5),
183
+ Position = UDim2.new(0, 0, 0.5, 0),
184
+ Size = UDim2.new(1, -40, 0, 6),
185
+ AutoButtonColor = false,
186
+ Text = "",
187
+
188
+ [OnEvent "InputBegan"] = function(input)
189
+ if input.UserInputType == Enum.UserInputType.MouseButton1
190
+ or input.UserInputType == Enum.UserInputType.Touch then
191
+ -- Basic click-to-set (full drag requires InputChanged listener)
192
+ local track = input.Position
193
+ -- Note: for full drag support, connect UserInputService.InputChanged
194
+ -- and track isDragging state. Simplified here for reference clarity.
195
+ end
196
+ end,
197
+
198
+ [Children] = {
199
+ scope:New "UICorner" { CornerRadius = UDim.new(1, 0) },
200
+
201
+ -- Fill
202
+ scope:New "Frame" {
203
+ Name = "Fill",
204
+ BackgroundColor3 = COLORS.SliderFill,
205
+ Size = scope:Spring(scope:Computed(function(use)
206
+ return UDim2.new(use(percentage), 0, 1, 0)
207
+ end), 30),
208
+ [Children] = { scope:New "UICorner" { CornerRadius = UDim.new(1, 0) } },
209
+ },
210
+
211
+ -- Handle
212
+ scope:New "Frame" {
213
+ Name = "Handle",
214
+ AnchorPoint = Vector2.new(0.5, 0.5),
215
+ Position = scope:Spring(scope:Computed(function(use)
216
+ return UDim2.new(use(percentage), 0, 0.5, 0)
217
+ end), 30),
218
+ Size = UDim2.fromOffset(14, 14),
219
+ BackgroundColor3 = COLORS.Text,
220
+ ZIndex = 2,
221
+ [Children] = { scope:New "UICorner" { CornerRadius = UDim.new(1, 0) } },
222
+ },
223
+ },
224
+ },
225
+
226
+ -- Value label
227
+ scope:New "TextLabel" {
228
+ Name = "ValueLabel",
229
+ BackgroundTransparency = 1,
230
+ AnchorPoint = Vector2.new(1, 0.5),
231
+ Position = UDim2.new(1, 0, 0.5, 0),
232
+ Size = UDim2.fromOffset(36, 16),
233
+ FontFace = FONT.Small,
234
+ Text = scope:Computed(function(use)
235
+ local v = use(value)
236
+ return if v == math.floor(v) then tostring(math.floor(v)) else string.format("%.1f", v)
237
+ end),
238
+ TextColor3 = COLORS.TextSecondary,
239
+ TextSize = 12,
240
+ TextXAlignment = Enum.TextXAlignment.Right,
241
+ },
242
+ },
243
+ }
244
+ end
245
+
246
+ --------------------------------------------------------------------------------
247
+ -- Dropdown component
248
+ --------------------------------------------------------------------------------
249
+
250
+ local function SettingsDropdown(
251
+ scope: Fusion.Scope,
252
+ selected: Fusion.Value<string>,
253
+ options: { string },
254
+ onChange: (newValue: string) -> ()
255
+ )
256
+ local isOpen = scope:Value(false)
257
+
258
+ return scope:New "Frame" {
259
+ Name = "DropdownContainer",
260
+ BackgroundTransparency = 1,
261
+ Size = UDim2.new(0, 140, 0, 26),
262
+ ClipsDescendants = false,
263
+
264
+ [Children] = {
265
+ -- Trigger button
266
+ scope:New "TextButton" {
267
+ Name = "Trigger",
268
+ BackgroundColor3 = COLORS.SurfaceAlt,
269
+ Size = UDim2.new(1, 0, 0, 26),
270
+ AutoButtonColor = false,
271
+ Text = "",
272
+ [OnEvent "Activated"] = function()
273
+ isOpen:set(not peek(isOpen))
274
+ end,
275
+ [Children] = {
276
+ scope:New "UICorner" { CornerRadius = UDim.new(0, 4) },
277
+ scope:New "UIPadding" {
278
+ PaddingLeft = UDim.new(0, 8),
279
+ PaddingRight = UDim.new(0, 8),
280
+ },
281
+ scope:New "TextLabel" {
282
+ BackgroundTransparency = 1,
283
+ Size = UDim2.new(1, -16, 1, 0),
284
+ FontFace = FONT.Small,
285
+ Text = selected,
286
+ TextColor3 = COLORS.Text,
287
+ TextSize = 12,
288
+ TextXAlignment = Enum.TextXAlignment.Left,
289
+ },
290
+ scope:New "TextLabel" {
291
+ BackgroundTransparency = 1,
292
+ AnchorPoint = Vector2.new(1, 0.5),
293
+ Position = UDim2.new(1, 0, 0.5, 0),
294
+ Size = UDim2.fromOffset(12, 12),
295
+ FontFace = FONT.Small,
296
+ Text = "\u{25BC}",
297
+ TextColor3 = COLORS.TextMuted,
298
+ TextSize = 8,
299
+ },
300
+ },
301
+ },
302
+
303
+ -- Options list
304
+ scope:New "Frame" {
305
+ Name = "OptionsList",
306
+ BackgroundColor3 = COLORS.SurfaceAlt,
307
+ Position = UDim2.new(0, 0, 1, 4),
308
+ Size = UDim2.new(1, 0, 0, #options * 24 + 4),
309
+ Visible = isOpen,
310
+ ZIndex = 50,
311
+ ClipsDescendants = true,
312
+ [Children] = {
313
+ scope:New "UICorner" { CornerRadius = UDim.new(0, 4) },
314
+ scope:New "UIPadding" { PaddingTop = UDim.new(0, 2), PaddingBottom = UDim.new(0, 2) },
315
+ scope:New "UIListLayout" {
316
+ FillDirection = Enum.FillDirection.Vertical,
317
+ Padding = UDim.new(0, 0),
318
+ SortOrder = Enum.SortOrder.LayoutOrder,
319
+ },
320
+
321
+ scope:ForPairs(options, function(use, optScope, i, option)
322
+ return i, optScope:New "TextButton" {
323
+ Name = "Option_" .. option,
324
+ BackgroundColor3 = COLORS.SurfaceAlt,
325
+ BackgroundTransparency = 0,
326
+ Size = UDim2.new(1, 0, 0, 24),
327
+ AutoButtonColor = false,
328
+ FontFace = FONT.Small,
329
+ Text = option,
330
+ TextColor3 = optScope:Computed(function(use)
331
+ return if use(selected) == option then COLORS.Primary else COLORS.Text
332
+ end),
333
+ TextSize = 12,
334
+ LayoutOrder = i,
335
+ [OnEvent "Activated"] = function()
336
+ selected:set(option)
337
+ onChange(option)
338
+ isOpen:set(false)
339
+ end,
340
+ }
341
+ end),
342
+ },
343
+ },
344
+ },
345
+ }
346
+ end
347
+
348
+ --------------------------------------------------------------------------------
349
+ -- Setting Row (renders one setting entry)
350
+ --------------------------------------------------------------------------------
351
+
352
+ local function SettingRow(
353
+ scope: Fusion.Scope,
354
+ entry: SettingEntry,
355
+ values: { [string]: Fusion.Value<SettingValue> },
356
+ onChanged: (string, SettingValue) -> ()
357
+ )
358
+ local valueState = values[entry.Key]
359
+
360
+ local control
361
+ if entry.Type == "slider" then
362
+ control = SettingsSlider(scope, valueState :: Fusion.Value<number>, entry.Min, entry.Max, entry.Step, function(v)
363
+ onChanged(entry.Key, v)
364
+ end)
365
+ elseif entry.Type == "toggle" then
366
+ control = ToggleSwitch(scope, valueState :: Fusion.Value<boolean>, function(v)
367
+ onChanged(entry.Key, v)
368
+ end)
369
+ elseif entry.Type == "dropdown" then
370
+ control = SettingsDropdown(scope, valueState :: Fusion.Value<string>, entry.Options, function(v)
371
+ onChanged(entry.Key, v)
372
+ end)
373
+ end
374
+
375
+ return scope:New "Frame" {
376
+ Name = "Row_" .. entry.Key,
377
+ BackgroundTransparency = 1,
378
+ Size = UDim2.new(1, 0, 0, 32),
379
+
380
+ [Children] = {
381
+ scope:New "TextLabel" {
382
+ BackgroundTransparency = 1,
383
+ Size = UDim2.new(0.5, -8, 1, 0),
384
+ FontFace = FONT.Body,
385
+ Text = entry.Label,
386
+ TextColor3 = COLORS.Text,
387
+ TextSize = 13,
388
+ TextXAlignment = Enum.TextXAlignment.Left,
389
+ },
390
+
391
+ scope:New "Frame" {
392
+ BackgroundTransparency = 1,
393
+ AnchorPoint = Vector2.new(1, 0.5),
394
+ Position = UDim2.new(1, 0, 0.5, 0),
395
+ Size = UDim2.new(0.5, -8, 0, 26),
396
+ [Children] = {
397
+ scope:New "UIListLayout" {
398
+ FillDirection = Enum.FillDirection.Horizontal,
399
+ HorizontalAlignment = Enum.HorizontalAlignment.Right,
400
+ VerticalAlignment = Enum.VerticalAlignment.Center,
401
+ },
402
+ control,
403
+ },
404
+ },
405
+ },
406
+ }
407
+ end
408
+
409
+ --------------------------------------------------------------------------------
410
+ -- Settings Screen (main export)
411
+ --------------------------------------------------------------------------------
412
+
413
+ local function SettingsScreen(props: SettingsProps)
414
+ local scope = scoped(Fusion)
415
+
416
+ local activeTab = scope:Value(1)
417
+
418
+ local screenGui = scope:New "ScreenGui" {
419
+ Name = "SettingsScreen",
420
+ ResetOnSpawn = false,
421
+ IgnoreGuiInset = true,
422
+ Enabled = props.Visible,
423
+ DisplayOrder = 20,
424
+ Parent = Players.LocalPlayer:WaitForChild("PlayerGui"),
425
+
426
+ [Children] = {
427
+ -- Backdrop
428
+ scope:New "TextButton" {
429
+ Name = "Backdrop",
430
+ BackgroundColor3 = Color3.fromRGB(0, 0, 0),
431
+ BackgroundTransparency = scope:Spring(scope:Computed(function(use)
432
+ return if use(props.Visible) then 0.5 else 1
433
+ end), 20),
434
+ Size = UDim2.new(1, 0, 1, 0),
435
+ AutoButtonColor = false,
436
+ Text = "",
437
+ [OnEvent "Activated"] = props.OnClose,
438
+ },
439
+
440
+ -- Settings panel (centered card)
441
+ scope:New "Frame" {
442
+ Name = "SettingsPanel",
443
+ AnchorPoint = Vector2.new(0.5, 0.5),
444
+ Position = UDim2.fromScale(0.5, 0.5),
445
+ Size = UDim2.new(0.85, 0, 0.7, 0),
446
+ BackgroundColor3 = COLORS.Background,
447
+
448
+ [Children] = {
449
+ scope:New "UICorner" { CornerRadius = UDim.new(0, 12) },
450
+ scope:New "UISizeConstraint" {
451
+ MinSize = Vector2.new(300, 250),
452
+ MaxSize = Vector2.new(500, 450),
453
+ },
454
+ scope:New "UIPadding" {
455
+ PaddingTop = UDim.new(0, 16),
456
+ PaddingBottom = UDim.new(0, 16),
457
+ PaddingLeft = UDim.new(0, 16),
458
+ PaddingRight = UDim.new(0, 16),
459
+ },
460
+ scope:New "UIListLayout" {
461
+ FillDirection = Enum.FillDirection.Vertical,
462
+ Padding = UDim.new(0, 12),
463
+ SortOrder = Enum.SortOrder.LayoutOrder,
464
+ },
465
+
466
+ -- Header
467
+ scope:New "Frame" {
468
+ BackgroundTransparency = 1,
469
+ Size = UDim2.new(1, 0, 0, 24),
470
+ LayoutOrder = 1,
471
+ [Children] = {
472
+ scope:New "TextLabel" {
473
+ BackgroundTransparency = 1,
474
+ Size = UDim2.new(1, -30, 1, 0),
475
+ FontFace = FONT.Title,
476
+ Text = "Settings",
477
+ TextColor3 = COLORS.Text,
478
+ TextSize = 18,
479
+ TextXAlignment = Enum.TextXAlignment.Left,
480
+ },
481
+ scope:New "TextButton" {
482
+ AnchorPoint = Vector2.new(1, 0),
483
+ Position = UDim2.fromScale(1, 0),
484
+ Size = UDim2.fromOffset(24, 24),
485
+ BackgroundTransparency = 1,
486
+ FontFace = FONT.Title,
487
+ Text = "\u{2715}",
488
+ TextColor3 = COLORS.TextSecondary,
489
+ TextSize = 16,
490
+ AutoButtonColor = false,
491
+ [OnEvent "Activated"] = props.OnClose,
492
+ },
493
+ },
494
+ },
495
+
496
+ -- Tab bar
497
+ scope:New "Frame" {
498
+ Name = "TabBar",
499
+ BackgroundTransparency = 1,
500
+ Size = UDim2.new(1, 0, 0, 28),
501
+ LayoutOrder = 2,
502
+ [Children] = {
503
+ scope:New "UIListLayout" {
504
+ FillDirection = Enum.FillDirection.Horizontal,
505
+ Padding = UDim.new(0, 4),
506
+ VerticalAlignment = Enum.VerticalAlignment.Center,
507
+ },
508
+
509
+ scope:ForPairs(props.Tabs, function(use, tabScope, i, tab)
510
+ local isActive = tabScope:Computed(function(use)
511
+ return use(activeTab) == i
512
+ end)
513
+
514
+ return i, tabScope:New "TextButton" {
515
+ Name = "Tab_" .. tab.Name,
516
+ BackgroundColor3 = tabScope:Spring(tabScope:Computed(function(use)
517
+ return if use(isActive) then COLORS.TabActive else COLORS.TabInactive
518
+ end), 25),
519
+ Size = UDim2.new(0, 70, 0, 26),
520
+ AutoButtonColor = false,
521
+ FontFace = FONT.Body,
522
+ Text = tab.Name,
523
+ TextColor3 = tabScope:Computed(function(use)
524
+ return if use(isActive) then COLORS.Text else COLORS.TextSecondary
525
+ end),
526
+ TextSize = 11,
527
+ LayoutOrder = i,
528
+ [OnEvent "Activated"] = function()
529
+ activeTab:set(i)
530
+ end,
531
+ [Children] = { tabScope:New "UICorner" { CornerRadius = UDim.new(0, 4) } },
532
+ }
533
+ end),
534
+ },
535
+ },
536
+
537
+ -- Settings content (scrollable)
538
+ scope:New "ScrollingFrame" {
539
+ Name = "SettingsContent",
540
+ BackgroundTransparency = 1,
541
+ Size = UDim2.new(1, 0, 1, -76),
542
+ LayoutOrder = 3,
543
+ AutomaticCanvasSize = Enum.AutomaticSize.Y,
544
+ ScrollingDirection = Enum.ScrollingDirection.Y,
545
+ ScrollBarThickness = 3,
546
+ ScrollBarImageColor3 = COLORS.TextMuted,
547
+ CanvasSize = UDim2.new(0, 0, 0, 0),
548
+
549
+ [Children] = {
550
+ scope:New "UIListLayout" {
551
+ FillDirection = Enum.FillDirection.Vertical,
552
+ Padding = UDim.new(0, 8),
553
+ SortOrder = Enum.SortOrder.LayoutOrder,
554
+ },
555
+ scope:New "UIPadding" { PaddingTop = UDim.new(0, 4), PaddingBottom = UDim.new(0, 4) },
556
+
557
+ -- Render settings for active tab
558
+ scope:Computed(function(use)
559
+ local tabIndex = use(activeTab)
560
+ local tab = props.Tabs[tabIndex]
561
+ if not tab then return {} end
562
+
563
+ local rows = {}
564
+ for i, entry in tab.Settings do
565
+ rows[i] = SettingRow(scope, entry, props.Values, props.OnSettingChanged)
566
+ end
567
+ return rows
568
+ end),
569
+ },
570
+ },
571
+ },
572
+ },
573
+ },
574
+ }
575
+
576
+ return screenGui, scope
577
+ end
578
+
579
+ return SettingsScreen