@chrrxs/robloxstudio-mcp 2.14.0 → 2.15.1

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.
@@ -102,9 +102,11 @@ local RunService = _services.RunService
102
102
  local ServerStorage = _services.ServerStorage
103
103
  local RuntimeLogBuffer = TS.import(script, script.Parent, "RuntimeLogBuffer")
104
104
  local MemoryHandlers = TS.import(script, script.Parent, "handlers", "MemoryHandlers")
105
+ local SceneAnalysisHandlers = TS.import(script, script.Parent, "handlers", "SceneAnalysisHandlers")
105
106
  local CaptureHandlers = TS.import(script, script.Parent, "handlers", "CaptureHandlers")
106
107
  local InputHandlers = TS.import(script, script.Parent, "handlers", "InputHandlers")
107
108
  local LuauExec = TS.import(script, script.Parent, "LuauExec")
109
+ local State = TS.import(script, script.Parent, "State")
108
110
  local StudioTestService = game:GetService("StudioTestService")
109
111
  -- Mirror of Communication.computeInstanceId() — duplicated here because the
110
112
  -- client broker runs in the play-server DM where it can't easily import from
@@ -167,6 +169,7 @@ local CLIENT_BROKER_ALLOWED_ENDPOINTS = {
167
169
  ["/api/execute-luau"] = true,
168
170
  ["/api/get-runtime-logs"] = true,
169
171
  ["/api/get-memory-breakdown"] = true,
172
+ ["/api/get-scene-analysis"] = true,
170
173
  ["/api/multiplayer-test-state"] = true,
171
174
  ["/api/multiplayer-test-leave-client"] = true,
172
175
  ["/api/capture-begin"] = true,
@@ -199,6 +202,8 @@ local function reRegisterProxy(proxyId, role)
199
202
  placeName = resolvePlaceName(),
200
203
  dataModelName = game.Name,
201
204
  isRunning = RunService:IsRunning(),
205
+ pluginVersion = State.CURRENT_VERSION,
206
+ pluginVariant = State.PLUGIN_VARIANT,
202
207
  })
203
208
  end)
204
209
  end
@@ -324,7 +329,7 @@ end
324
329
  local function setupClientBroker()
325
330
  local rf = ReplicatedStorage:WaitForChild(BROKER_NAME, 10)
326
331
  if not rf or not rf:IsA("RemoteFunction") then
327
- warn(`[MCPFork] client: {BROKER_NAME} not found`)
332
+ warn(`[robloxstudio-mcp] client: {BROKER_NAME} not found`)
328
333
  return nil
329
334
  end
330
335
  rf.OnClientInvoke = function(payload)
@@ -340,6 +345,9 @@ local function setupClientBroker()
340
345
  if payload and payload.endpoint == "/api/get-memory-breakdown" then
341
346
  return MemoryHandlers.getMemoryBreakdown(payload.data or {})
342
347
  end
348
+ if payload and payload.endpoint == "/api/get-scene-analysis" then
349
+ return SceneAnalysisHandlers.getSceneAnalysis(payload.data or {})
350
+ end
343
351
  if payload and payload.endpoint == "/api/multiplayer-test-state" then
344
352
  return handleMultiplayerTestState()
345
353
  end
@@ -451,9 +459,11 @@ local function registerProxy(player, rf)
451
459
  placeName = resolvePlaceName(),
452
460
  dataModelName = game.Name,
453
461
  isRunning = RunService:IsRunning(),
462
+ pluginVersion = State.CURRENT_VERSION,
463
+ pluginVariant = State.PLUGIN_VARIANT,
454
464
  })
455
465
  if not ok or not res or not res.Success then
456
- warn(`[MCPFork] proxy register failed for {player.Name}`)
466
+ warn(`[robloxstudio-mcp] proxy register failed for {player.Name}`)
457
467
  return nil
458
468
  end
459
469
  local body = HttpService:JSONDecode(res.Body)
@@ -551,6 +561,7 @@ local InputHandlers = TS.import(script, script.Parent, "handlers", "InputHandler
551
561
  local LogHandlers = TS.import(script, script.Parent, "handlers", "LogHandlers")
552
562
  local SerializationHandlers = TS.import(script, script.Parent, "handlers", "SerializationHandlers")
553
563
  local MemoryHandlers = TS.import(script, script.Parent, "handlers", "MemoryHandlers")
564
+ local SceneAnalysisHandlers = TS.import(script, script.Parent, "handlers", "SceneAnalysisHandlers")
554
565
  -- Per-plugin-load random GUID. Used as the /poll URL param so the server
555
566
  -- can tell our polls apart from any other plugin's polls. Not user-facing —
556
567
  -- MCP tools and the LLM operate on instanceId (the place identifier).
@@ -578,6 +589,8 @@ end
578
589
  local instanceId = computeInstanceId()
579
590
  local assignedRole
580
591
  local duplicateInstanceRole = false
592
+ local hasVersionMismatch = false
593
+ local lastVersionMismatchWarningKey
581
594
  -- Cache the published place name from MarketplaceService:GetProductInfo so
582
595
  -- /ready can carry a friendly identifier (e.g. "Natural Disasters") distinct
583
596
  -- from game.Name (the DataModel name, often "Place1" in edit). We only fetch
@@ -683,6 +696,7 @@ local routeMap = {
683
696
  ["/api/export-rbxm"] = SerializationHandlers.exportRbxm,
684
697
  ["/api/import-rbxm"] = SerializationHandlers.importRbxm,
685
698
  ["/api/get-memory-breakdown"] = MemoryHandlers.getMemoryBreakdown,
699
+ ["/api/get-scene-analysis"] = SceneAnalysisHandlers.getSceneAnalysis,
686
700
  }
687
701
  local function processRequest(request)
688
702
  local endpoint = request.endpoint
@@ -776,6 +790,8 @@ function sendReady(conn)
776
790
  placeName = resolvePlaceName(),
777
791
  dataModelName = game.Name,
778
792
  isRunning = RunService:IsRunning(),
793
+ pluginVersion = State.CURRENT_VERSION,
794
+ pluginVariant = State.PLUGIN_VARIANT,
779
795
  pluginReady = true,
780
796
  timestamp = tick(),
781
797
  }),
@@ -841,6 +857,23 @@ local function pollForRequests(connIndex)
841
857
  local mcpConnected = data.mcpConnected == true
842
858
  conn.lastHttpOk = true
843
859
  conn.lastMcpOk = mcpConnected
860
+ local _condition = data.serverVersion
861
+ if _condition == nil then
862
+ _condition = "unknown"
863
+ end
864
+ local serverVersion = _condition
865
+ if data.versionMismatch == true then
866
+ hasVersionMismatch = true
867
+ local warningKey = `{State.CURRENT_VERSION}:{serverVersion}`
868
+ if lastVersionMismatchWarningKey ~= warningKey then
869
+ lastVersionMismatchWarningKey = warningKey
870
+ warn(`[MCPPlugin] Version mismatch: Studio plugin v{State.CURRENT_VERSION} / MCP v{serverVersion}. Run npx -y @chrrxs/robloxstudio-mcp@latest --auto-install-plugin and restart Studio.`)
871
+ end
872
+ UI.showBanner("version-mismatch", `Plugin v{State.CURRENT_VERSION} / MCP v{serverVersion} mismatch`)
873
+ elseif hasVersionMismatch then
874
+ hasVersionMismatch = false
875
+ UI.hideBanner("version-mismatch")
876
+ end
844
877
  -- Server tells us when its in-memory instances map doesn't have us
845
878
  -- (e.g. after an MCP process restart). Re-issue /ready immediately so
846
879
  -- target=server/client-N start routing again. The throttle inside
@@ -853,12 +886,12 @@ local function pollForRequests(connIndex)
853
886
  local el = ui
854
887
  el.step1Dot.BackgroundColor3 = Color3.fromRGB(34, 197, 94)
855
888
  el.step1Label.Text = "HTTP server (OK)"
856
- local _condition = mcpConnected
857
- if _condition then
889
+ local _condition_1 = mcpConnected
890
+ if _condition_1 then
858
891
  local _value = (string.find(el.statusLabel.Text, "Connected"))
859
- _condition = not (_value ~= 0 and _value == _value and _value)
892
+ _condition_1 = not (_value ~= 0 and _value == _value and _value)
860
893
  end
861
- if _condition then
894
+ if _condition_1 then
862
895
  el.statusLabel.Text = "Connected"
863
896
  el.statusLabel.TextColor3 = Color3.fromRGB(34, 197, 94)
864
897
  el.statusIndicator.BackgroundColor3 = Color3.fromRGB(34, 197, 94)
@@ -889,11 +922,11 @@ local function pollForRequests(connIndex)
889
922
  conn.mcpWaitStartTime = tick()
890
923
  end
891
924
  local _exp = tick()
892
- local _condition_1 = conn.mcpWaitStartTime
893
- if _condition_1 == nil then
894
- _condition_1 = tick()
925
+ local _condition_2 = conn.mcpWaitStartTime
926
+ if _condition_2 == nil then
927
+ _condition_2 = tick()
895
928
  end
896
- local elapsed = _exp - _condition_1
929
+ local elapsed = _exp - _condition_2
897
930
  el.troubleshootLabel.Visible = elapsed > 8
898
931
  UI.startPulseAnimation()
899
932
  end
@@ -1102,11 +1135,9 @@ local function checkForUpdates()
1102
1135
  if _condition ~= "" and _condition then
1103
1136
  local latestVersion = data.version
1104
1137
  if Utils.compareVersions(State.CURRENT_VERSION, latestVersion) < 0 then
1105
- local ui = UI.getElements()
1106
- ui.updateBannerText.Text = `v{latestVersion} available - github.com/chrrxs/robloxstudio-mcp`
1107
- ui.updateBanner.Visible = true
1108
- ui.contentFrame.Position = UDim2.new(0, 8, 0, 92)
1109
- ui.contentFrame.Size = UDim2.new(1, -16, 1, -100)
1138
+ if not hasVersionMismatch then
1139
+ UI.showBanner("update", `v{latestVersion} available - github.com/chrrxs/robloxstudio-mcp`)
1140
+ end
1110
1141
  end
1111
1142
  end
1112
1143
  end
@@ -1256,9 +1287,9 @@ local function computeBridgeStamp()
1256
1287
  for i = 1, #combined do
1257
1288
  h = (h * 33 + (string.byte(combined, i))) % 2147483647
1258
1289
  end
1259
- -- "2.14.0" is replaced with the package version at package time
1290
+ -- "2.15.1" is replaced with the package version at package time
1260
1291
  -- (scripts/build-plugin.mjs injectVersion), so a release bump also restamps.
1261
- return `{tostring(h)}-2.14.0`
1292
+ return `{tostring(h)}-2.15.1`
1262
1293
  end
1263
1294
  local BRIDGE_STAMP = computeBridgeStamp()
1264
1295
  local function setSource(scriptInst, source)
@@ -5070,6 +5101,255 @@ return {
5070
5101
  </Properties>
5071
5102
  </Item>
5072
5103
  <Item class="ModuleScript" referent="16">
5104
+ <Properties>
5105
+ <string name="Name">SceneAnalysisHandlers</string>
5106
+ <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
5107
+ local MODE_CONFIGS = {
5108
+ instance_composition = {
5109
+ method = "GetInstanceCompositionAsync",
5110
+ query = function(service)
5111
+ return service:GetInstanceCompositionAsync()
5112
+ end,
5113
+ },
5114
+ script_memory = {
5115
+ method = "GetScriptMemoryAsync",
5116
+ query = function(service)
5117
+ return service:GetScriptMemoryAsync()
5118
+ end,
5119
+ },
5120
+ unparented_instances = {
5121
+ method = "GetUnparentedInstancesAsync",
5122
+ query = function(service)
5123
+ return service:GetUnparentedInstancesAsync()
5124
+ end,
5125
+ },
5126
+ triangle_composition = {
5127
+ method = "GetTriangleCompositionAsync",
5128
+ query = function(service)
5129
+ return service:GetTriangleCompositionAsync()
5130
+ end,
5131
+ sortByTriangles = true,
5132
+ },
5133
+ animation_memory = {
5134
+ method = "GetAnimationMemoryAsync",
5135
+ query = function(service)
5136
+ return service:GetAnimationMemoryAsync()
5137
+ end,
5138
+ },
5139
+ audio_memory = {
5140
+ method = "GetAudioMemoryAsync",
5141
+ query = function(service)
5142
+ return service:GetAudioMemoryAsync()
5143
+ end,
5144
+ },
5145
+ }
5146
+ local ALL_MODES = { "instance_composition", "script_memory", "unparented_instances", "triangle_composition", "animation_memory", "audio_memory" }
5147
+ local function betaDisabledError()
5148
+ return {
5149
+ error = "scene_analysis_not_enabled",
5150
+ message = "SceneAnalysisService is not enabled. Enable Scene Analysis in Studio Beta Features and restart Studio.",
5151
+ betaFeatureRequired = true,
5152
+ }
5153
+ end
5154
+ local function isBetaDisabledError(value)
5155
+ local _value = value
5156
+ local _condition = type(_value) == "string"
5157
+ if _condition then
5158
+ _condition = (string.find(value, "SceneAnalysisService is not enabled", 1, true)) ~= nil
5159
+ end
5160
+ return _condition
5161
+ end
5162
+ local function getSceneAnalysisService()
5163
+ local provider = game
5164
+ local ok, service = pcall(function()
5165
+ return provider:GetService("SceneAnalysisService")
5166
+ end)
5167
+ if not ok or not service then
5168
+ return {
5169
+ error = "scene_analysis_unavailable",
5170
+ message = `SceneAnalysisService is unavailable: {tostring(service)}`,
5171
+ }
5172
+ end
5173
+ return service
5174
+ end
5175
+ local function normalizeMode(mode)
5176
+ if mode == nil or mode == "all" then
5177
+ return "all"
5178
+ end
5179
+ local _mode = mode
5180
+ local _condition = not (type(_mode) == "string")
5181
+ if not _condition then
5182
+ _condition = MODE_CONFIGS[mode] == nil
5183
+ end
5184
+ if _condition then
5185
+ return {
5186
+ error = "invalid_mode",
5187
+ message = `mode must be one of: all, {table.concat(ALL_MODES, ", ")}`,
5188
+ }
5189
+ end
5190
+ return mode
5191
+ end
5192
+ local function normalizeTopN(topN)
5193
+ local _topN = topN
5194
+ if not (type(_topN) == "number") then
5195
+ return 10
5196
+ end
5197
+ return math.clamp(math.floor(topN), 1, 100)
5198
+ end
5199
+ local function countLeaves(node)
5200
+ local children = node.Children
5201
+ if children and #children > 0 then
5202
+ local total = 0
5203
+ for _, child in children do
5204
+ total += countLeaves(child)
5205
+ end
5206
+ return total
5207
+ end
5208
+ return 1
5209
+ end
5210
+ local function flattenLeaves(node, out)
5211
+ local children = node.Children
5212
+ if children and #children > 0 then
5213
+ for _, child in children do
5214
+ flattenLeaves(child, out)
5215
+ end
5216
+ return nil
5217
+ end
5218
+ local _out = out
5219
+ local _node = node
5220
+ table.insert(_out, _node)
5221
+ end
5222
+ local function compactEntry(node)
5223
+ local entry = {
5224
+ name = node.Name,
5225
+ }
5226
+ if node.Size ~= nil then
5227
+ entry.size = node.Size
5228
+ end
5229
+ if node.Sizes ~= nil then
5230
+ entry.sizes = node.Sizes
5231
+ end
5232
+ if node.AssetId ~= nil then
5233
+ entry.asset_id = node.AssetId
5234
+ end
5235
+ return entry
5236
+ end
5237
+ local function compactRoot(node, leafCount)
5238
+ local children = node.Children
5239
+ local root = {
5240
+ name = node.Name,
5241
+ child_count = if children then #children else 0,
5242
+ leaf_count = leafCount,
5243
+ }
5244
+ if node.Size ~= nil then
5245
+ root.size = node.Size
5246
+ end
5247
+ if node.Sizes ~= nil then
5248
+ root.sizes = node.Sizes
5249
+ end
5250
+ return root
5251
+ end
5252
+ local function metric(node, sortByTriangles)
5253
+ if sortByTriangles then
5254
+ local sizes = node.Sizes
5255
+ local triangles = if sizes then sizes.Triangles else nil
5256
+ local _condition = triangles
5257
+ if _condition == nil then
5258
+ _condition = 0
5259
+ end
5260
+ return _condition
5261
+ end
5262
+ local _condition = node.Size
5263
+ if _condition == nil then
5264
+ _condition = 0
5265
+ end
5266
+ return _condition
5267
+ end
5268
+ local function summarizeMode(mode, config, service, topN, raw)
5269
+ local started = os.clock()
5270
+ local ok, result = pcall(function()
5271
+ return config.query(service)
5272
+ end)
5273
+ local elapsedMs = math.floor((os.clock() - started) * 1000)
5274
+ if not ok then
5275
+ if isBetaDisabledError(result) then
5276
+ return betaDisabledError()
5277
+ end
5278
+ return {
5279
+ error = "scene_analysis_query_failed",
5280
+ mode = mode,
5281
+ method = config.method,
5282
+ message = tostring(result),
5283
+ }
5284
+ end
5285
+ local tree = result
5286
+ local leaves = {}
5287
+ flattenLeaves(tree, leaves)
5288
+ table.sort(leaves, function(a, b)
5289
+ return metric(a, config.sortByTriangles == true) > metric(b, config.sortByTriangles == true)
5290
+ end)
5291
+ local top = {}
5292
+ do
5293
+ local i = 0
5294
+ local _shouldIncrement = false
5295
+ while true do
5296
+ if _shouldIncrement then
5297
+ i += 1
5298
+ else
5299
+ _shouldIncrement = true
5300
+ end
5301
+ if not (i < math.min(topN, #leaves)) then
5302
+ break
5303
+ end
5304
+ local _arg0 = compactEntry(leaves[i + 1])
5305
+ table.insert(top, _arg0)
5306
+ end
5307
+ end
5308
+ local body = {
5309
+ mode = mode,
5310
+ method = config.method,
5311
+ elapsed_ms = elapsedMs,
5312
+ root = compactRoot(tree, #leaves),
5313
+ top = top,
5314
+ }
5315
+ if raw then
5316
+ body.tree = tree
5317
+ end
5318
+ return body
5319
+ end
5320
+ local function getSceneAnalysis(requestData)
5321
+ local mode = normalizeMode(requestData.mode)
5322
+ if not (type(mode) == "string") then
5323
+ return mode
5324
+ end
5325
+ local serviceOrError = getSceneAnalysisService()
5326
+ local _value = serviceOrError.IsA
5327
+ if not (_value ~= 0 and _value == _value and _value ~= "" and _value) then
5328
+ return serviceOrError
5329
+ end
5330
+ local service = serviceOrError
5331
+ local topN = normalizeTopN(requestData.topN)
5332
+ local raw = requestData.raw == true
5333
+ if mode ~= "all" then
5334
+ return summarizeMode(mode, MODE_CONFIGS[mode], service, topN, raw)
5335
+ end
5336
+ local body = {}
5337
+ for _, m in ALL_MODES do
5338
+ local result = summarizeMode(m, MODE_CONFIGS[m], service, topN, raw)
5339
+ if result.error == "scene_analysis_not_enabled" then
5340
+ return result
5341
+ end
5342
+ body[m] = result
5343
+ end
5344
+ return body
5345
+ end
5346
+ return {
5347
+ getSceneAnalysis = getSceneAnalysis,
5348
+ }
5349
+ ]]></string>
5350
+ </Properties>
5351
+ </Item>
5352
+ <Item class="ModuleScript" referent="17">
5073
5353
  <Properties>
5074
5354
  <string name="Name">ScriptHandlers</string>
5075
5355
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -5765,7 +6045,7 @@ return {
5765
6045
  ]]></string>
5766
6046
  </Properties>
5767
6047
  </Item>
5768
- <Item class="ModuleScript" referent="17">
6048
+ <Item class="ModuleScript" referent="18">
5769
6049
  <Properties>
5770
6050
  <string name="Name">SerializationHandlers</string>
5771
6051
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -5951,7 +6231,7 @@ return {
5951
6231
  ]]></string>
5952
6232
  </Properties>
5953
6233
  </Item>
5954
- <Item class="ModuleScript" referent="18">
6234
+ <Item class="ModuleScript" referent="19">
5955
6235
  <Properties>
5956
6236
  <string name="Name">TestHandlers</string>
5957
6237
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -6584,7 +6864,7 @@ return {
6584
6864
  </Properties>
6585
6865
  </Item>
6586
6866
  </Item>
6587
- <Item class="ModuleScript" referent="19">
6867
+ <Item class="ModuleScript" referent="20">
6588
6868
  <Properties>
6589
6869
  <string name="Name">LuauExec</string>
6590
6870
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -6902,7 +7182,7 @@ return {
6902
7182
  ]]></string>
6903
7183
  </Properties>
6904
7184
  </Item>
6905
- <Item class="ModuleScript" referent="20">
7185
+ <Item class="ModuleScript" referent="21">
6906
7186
  <Properties>
6907
7187
  <string name="Name">Recording</string>
6908
7188
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -6932,7 +7212,7 @@ return {
6932
7212
  ]]></string>
6933
7213
  </Properties>
6934
7214
  </Item>
6935
- <Item class="ModuleScript" referent="21">
7215
+ <Item class="ModuleScript" referent="22">
6936
7216
  <Properties>
6937
7217
  <string name="Name">RenderMonitor</string>
6938
7218
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -7000,7 +7280,7 @@ return {
7000
7280
  ]]></string>
7001
7281
  </Properties>
7002
7282
  </Item>
7003
- <Item class="ModuleScript" referent="22">
7283
+ <Item class="ModuleScript" referent="23">
7004
7284
  <Properties>
7005
7285
  <string name="Name">RuntimeLogBuffer</string>
7006
7286
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -7181,11 +7461,12 @@ return {
7181
7461
  ]]></string>
7182
7462
  </Properties>
7183
7463
  </Item>
7184
- <Item class="ModuleScript" referent="23">
7464
+ <Item class="ModuleScript" referent="24">
7185
7465
  <Properties>
7186
7466
  <string name="Name">State</string>
7187
7467
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
7188
- local CURRENT_VERSION = "2.14.0"
7468
+ local CURRENT_VERSION = "2.15.1"
7469
+ local PLUGIN_VARIANT = "inspector"
7189
7470
  local MAX_CONNECTIONS = 5
7190
7471
  local BASE_PORT = 58741
7191
7472
  local activeTabIndex = 0
@@ -7263,6 +7544,7 @@ local function getConnections()
7263
7544
  end
7264
7545
  return {
7265
7546
  CURRENT_VERSION = CURRENT_VERSION,
7547
+ PLUGIN_VARIANT = PLUGIN_VARIANT,
7266
7548
  MAX_CONNECTIONS = MAX_CONNECTIONS,
7267
7549
  BASE_PORT = BASE_PORT,
7268
7550
  connections = connections,
@@ -7277,7 +7559,7 @@ return {
7277
7559
  ]]></string>
7278
7560
  </Properties>
7279
7561
  </Item>
7280
- <Item class="ModuleScript" referent="24">
7562
+ <Item class="ModuleScript" referent="25">
7281
7563
  <Properties>
7282
7564
  <string name="Name">StopPlayMonitor</string>
7283
7565
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -7422,7 +7704,7 @@ return {
7422
7704
  ]]></string>
7423
7705
  </Properties>
7424
7706
  </Item>
7425
- <Item class="ModuleScript" referent="25">
7707
+ <Item class="ModuleScript" referent="26">
7426
7708
  <Properties>
7427
7709
  <string name="Name">UI</string>
7428
7710
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -7435,6 +7717,7 @@ local buttonHover = false
7435
7717
  local toolbarButton
7436
7718
  local toolbarIcons
7437
7719
  local lastToolbarIcon
7720
+ local activeBannerKind
7438
7721
  local updateToolbarIcon
7439
7722
  local function setToolbarButton(btn, icons)
7440
7723
  toolbarButton = btn
@@ -7465,6 +7748,23 @@ local TWEEN_QUICK = TweenInfo.new(0.15, Enum.EasingStyle.Quad, Enum.EasingDirect
7465
7748
  local function tweenProp(instance, props)
7466
7749
  TweenService:Create(instance, TWEEN_QUICK, props):Play()
7467
7750
  end
7751
+ local function showBanner(kind, text)
7752
+ activeBannerKind = kind
7753
+ elements.updateBannerText.Text = text
7754
+ elements.updateBanner.Visible = true
7755
+ elements.contentFrame.Position = UDim2.new(0, 8, 0, 92)
7756
+ elements.contentFrame.Size = UDim2.new(1, -16, 1, -100)
7757
+ end
7758
+ local function hideBanner(kind)
7759
+ if kind ~= nil and activeBannerKind ~= kind then
7760
+ return nil
7761
+ end
7762
+ activeBannerKind = nil
7763
+ elements.updateBanner.Visible = false
7764
+ elements.updateBannerText.Text = ""
7765
+ elements.contentFrame.Position = UDim2.new(0, 8, 0, 66)
7766
+ elements.contentFrame.Size = UDim2.new(1, -16, 1, -74)
7767
+ end
7468
7768
  local C = {
7469
7769
  bg = Color3.fromRGB(14, 14, 14),
7470
7770
  card = Color3.fromRGB(22, 22, 22),
@@ -8166,6 +8466,8 @@ return {
8166
8466
  startPulseAnimation = startPulseAnimation,
8167
8467
  setToolbarButton = setToolbarButton,
8168
8468
  updateToolbarIcon = updateToolbarIcon,
8469
+ showBanner = showBanner,
8470
+ hideBanner = hideBanner,
8169
8471
  getElements = function()
8170
8472
  return elements
8171
8473
  end,
@@ -8173,7 +8475,7 @@ return {
8173
8475
  ]]></string>
8174
8476
  </Properties>
8175
8477
  </Item>
8176
- <Item class="ModuleScript" referent="26">
8478
+ <Item class="ModuleScript" referent="27">
8177
8479
  <Properties>
8178
8480
  <string name="Name">Utils</string>
8179
8481
  <string name="Source"><![CDATA[-- Compiled with roblox-ts v3.0.0
@@ -8703,11 +9005,11 @@ return {
8703
9005
  </Properties>
8704
9006
  </Item>
8705
9007
  </Item>
8706
- <Item class="Folder" referent="30">
9008
+ <Item class="Folder" referent="31">
8707
9009
  <Properties>
8708
9010
  <string name="Name">include</string>
8709
9011
  </Properties>
8710
- <Item class="ModuleScript" referent="27">
9012
+ <Item class="ModuleScript" referent="28">
8711
9013
  <Properties>
8712
9014
  <string name="Name">Promise</string>
8713
9015
  <string name="Source"><![CDATA[--[[
@@ -10781,7 +11083,7 @@ return Promise
10781
11083
  ]]></string>
10782
11084
  </Properties>
10783
11085
  </Item>
10784
- <Item class="ModuleScript" referent="28">
11086
+ <Item class="ModuleScript" referent="29">
10785
11087
  <Properties>
10786
11088
  <string name="Name">RuntimeLib</string>
10787
11089
  <string name="Source"><![CDATA[local Promise = require(script.Parent.Promise)
@@ -11048,15 +11350,15 @@ return TS
11048
11350
  </Properties>
11049
11351
  </Item>
11050
11352
  </Item>
11051
- <Item class="Folder" referent="31">
11353
+ <Item class="Folder" referent="32">
11052
11354
  <Properties>
11053
11355
  <string name="Name">node_modules</string>
11054
11356
  </Properties>
11055
- <Item class="Folder" referent="32">
11357
+ <Item class="Folder" referent="33">
11056
11358
  <Properties>
11057
11359
  <string name="Name">@rbxts</string>
11058
11360
  </Properties>
11059
- <Item class="ModuleScript" referent="29">
11361
+ <Item class="ModuleScript" referent="30">
11060
11362
  <Properties>
11061
11363
  <string name="Name">services</string>
11062
11364
  <string name="Source"><![CDATA[return setmetatable({}, {