azul-sync 1.3.1 → 1.3.2

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 (34) hide show
  1. package/dist/push.d.ts +2 -0
  2. package/dist/push.d.ts.map +1 -1
  3. package/dist/push.js +46 -1
  4. package/dist/push.js.map +1 -1
  5. package/package.json +1 -1
  6. package/plugin/README.md +0 -54
  7. package/plugin/sourcemap.json +0 -272
  8. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/Actor/AzulSync.server.luau +0 -906
  9. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/AzulService.luau +0 -573
  10. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/Config.luau +0 -31
  11. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/Enums.luau +0 -11
  12. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/Serializer.luau +0 -929
  13. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/CollapsibleTitledSection.luau +0 -214
  14. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/ColorPicker.luau +0 -360
  15. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/CustomTextButton.luau +0 -170
  16. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/DropdownMenu.luau +0 -363
  17. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/HorizontalLine.luau +0 -43
  18. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/ImageButtonWithText.luau +0 -181
  19. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledCheckbox.luau +0 -295
  20. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledColorInputPicker.luau +0 -294
  21. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledMultiChoice.luau +0 -163
  22. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledNumberInput.luau +0 -312
  23. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledRadioButton.luau +0 -55
  24. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledSlider.luau +0 -151
  25. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledTextInput.luau +0 -222
  26. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/LabeledToggleButton.luau +0 -73
  27. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/StatefulImageButton.luau +0 -125
  28. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/VerticalScrollingFrame.luau +0 -100
  29. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/VerticalSpacer.luau +0 -35
  30. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/Components/VerticallyScalingListFrame.luau +0 -107
  31. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/GuiUtilities.luau +0 -429
  32. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/StudioWidgets/RbxGui.luau +0 -4363
  33. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/UI.luau +0 -477
  34. package/plugin/sync/ReplicatedFirst/AzulCompanionPlugin/WebSocketClient.luau +0 -161
@@ -1,477 +0,0 @@
1
- --!strict
2
- --[[
3
- Azul UI Module
4
- Manages the companion plugin's dock widget and settings interface
5
- ]]
6
-
7
- local UI = {}
8
-
9
- local Config = require("./Config")
10
- local Enums = require("./Enums")
11
-
12
- UI.__index = UI
13
-
14
- export type CallbackFunctions = {
15
- onStartSync: () -> (),
16
- onStopSync: () -> (),
17
- onDebugModeChanged: (boolean) -> ()?,
18
- onSilentModeChanged: (boolean) -> ()?,
19
- onConfigChanged: (string, any) -> (),
20
- onSettingsScopeChanged: (number) -> (),
21
- onClearGuids: () -> (),
22
- onSourcemapReload: () -> (),
23
- }
24
-
25
- type SelfType = {
26
- plugin: Plugin,
27
- config: { any },
28
- callbacks: CallbackFunctions,
29
- LOGO: string,
30
- LOGO_SYNCED: string,
31
- settingsScope: string,
32
- debugPrint: (...string) -> (),
33
- infoPrint: (...string) -> (),
34
- saveSettings: () -> (),
35
- loadSettings: () -> (),
36
- rebuildServiceSet: () -> (),
37
-
38
- isSyncEnabled: boolean,
39
-
40
- azulWidget: DockWidgetPluginGui,
41
- connectButton: PluginToolbarButton,
42
- titleImageLabel: ImageLabel,
43
- syncButton: any,
44
- debugModeCheckbox: any,
45
- silentModeCheckbox: any,
46
- settingsScopeDropdown: any,
47
- websocketUrlLabel: any,
48
- heartbeatIntervalLabel: any,
49
- batchIdleWindowLabel: any,
50
- scriptChangeDebounceLabel: any,
51
- listTypeLabel: any,
52
- serviceListLabel: any,
53
- excludedParentsLabel: any,
54
- clearGuidsButton: any,
55
- reloadSourcemapButton: any,
56
-
57
- GetSyncState: (SelfType) -> boolean,
58
- UpdateSyncState: (SelfType, boolean) -> (),
59
- UpdateConfig: (SelfType) -> (),
60
- SetSettingsScope: (SelfType, any) -> (),
61
- }
62
-
63
- UI.logo = "rbxassetid://134336592598474"
64
- UI.syncedLogo = "rbxassetid://103599828888609"
65
-
66
- local function createInfoLabel(text: string)
67
- local label = Instance.new("TextLabel")
68
- label.Name = "InfoLabel"
69
- label.RichText = true
70
- label.AutomaticSize = Enum.AutomaticSize.Y
71
- label.TextWrapped = true
72
- label.Size = UDim2.new(1, 0, 0, 15)
73
- label.BackgroundTransparency = 1
74
- label.TextColor3 = Color3.new(0.5, 0.5, 0.5)
75
- label.Font = Enum.Font.SourceSans
76
- label.TextSize = 15
77
- label.Text = `{text}<br/>`
78
- label.TextXAlignment = Enum.TextXAlignment.Left
79
-
80
- local padding = Instance.new("UIPadding")
81
- padding.PaddingLeft = UDim.new(0, 30)
82
- padding.PaddingRight = UDim.new(0, 30)
83
- padding.Parent = label
84
-
85
- return label
86
- end
87
-
88
- local function validateNumberInput(newValue: string, callback: (number) -> ())
89
- local numberValue = tonumber(newValue)
90
- if not numberValue then
91
- warn(`[Azul]: (UI): Invalid number input: {newValue}`)
92
- return
93
- end
94
- if numberValue <= 0 then
95
- warn(`[Azul]: (UI): Number input must be greater than 0: {newValue}`)
96
- return
97
- end
98
- callback(numberValue)
99
- end
100
-
101
- --[=[
102
- Initialize the UI widget
103
-
104
- Parameters:
105
- - plugin: The plugin object from the Roblox plugin API
106
- - config: Reference to the CONFIG table
107
- - settingsScope: Reference to the settingsScope variable`
108
- - callbacks: Table with callbacks:
109
- - onStartSync: Called when user clicks Connect
110
- - onStopSync: Called when user clicks Disconnect
111
- - onDebugModeChanged: Called when debug mode checkbox changes
112
- - onSilentModeChanged: Called when silent mode checkbox changes
113
- - onConfigChanged: Called when any config value changes (receives {key, value})
114
- - onSettingsScopeChanged: Called when scope changes
115
- - onClearGuids: Called when clear GUIDs button is clicked
116
- - helpers: Table with helper functions:
117
- - debugPrint: Debug print function
118
- - infoPrint: Info print function
119
- - saveSettings: Save settings function
120
- - loadSettings: Load settings function
121
- - rebuildServiceSet: Rebuild service set function
122
-
123
- Returns: Table with methods:
124
- - updateSyncState(enabled: boolean): Update UI to reflect sync state
125
- - updateConfig(): Refresh UI with current CONFIG values
126
- ]=]
127
- function UI.new(plugin, callbacks, settingsScope, helpers): SelfType
128
- local self = {} :: SelfType
129
- setmetatable(self, UI)
130
-
131
- -- Store references
132
- self.plugin = plugin
133
- self.callbacks = callbacks
134
- self.LOGO = UI.logo
135
- self.LOGO_SYNCED = UI.syncedLogo
136
- self.settingsScope = settingsScope
137
- self.debugPrint = helpers.debugPrint
138
- self.infoPrint = helpers.infoPrint
139
- self.saveSettings = helpers.saveSettings
140
- self.loadSettings = helpers.loadSettings
141
- self.rebuildServiceSet = helpers.rebuildServiceSet
142
-
143
- -- Track sync enabled state locally (PluginToolbarButton has SetActive but no GetActive)
144
- self.isSyncEnabled = false
145
-
146
- -- Import UI components
147
- local VerticalScrollingFrame = require("./StudioWidgets/Components/VerticalScrollingFrame")
148
- local CollapsibleTitledSection = require("./StudioWidgets/Components/CollapsibleTitledSection")
149
- local CustomTextButton = require("./StudioWidgets/Components/CustomTextButton")
150
- local LabeledCheckbox = require("./StudioWidgets/Components/LabeledCheckbox")
151
- local LabeledMultiChoice = require("./StudioWidgets/Components/LabeledMultiChoice")
152
- local LabeledTextInput = require("./StudioWidgets/Components/LabeledTextInput")
153
- local DropdownMenu = require("./StudioWidgets/Components/DropdownMenu")
154
-
155
- -- Create toolbar button
156
- local toolbar = plugin:CreateToolbar("Azul")
157
- self.connectButton = toolbar:CreateButton("Azul", "Connect/disconnect from sync daemon", self.LOGO)
158
-
159
- -- Create widget
160
- local widgetInfo = DockWidgetPluginGuiInfo.new(Enum.InitialDockState.Float, true, true, 345, 640, 300, 300)
161
-
162
- local azulWidget = plugin:CreateDockWidgetPluginGuiAsync("azulWidget", widgetInfo)
163
- azulWidget.Name = "AzulCompanionPlugin"
164
- azulWidget.Title = "Azul"
165
- self.azulWidget = azulWidget
166
-
167
- -- Create main scroll frame
168
- local mainScrollFrame = VerticalScrollingFrame.new("main")
169
- mainScrollFrame:GetContentsFrame().Parent = azulWidget
170
-
171
- local mainSectionListLayout = Instance.new("UIListLayout")
172
- mainSectionListLayout.Parent = mainScrollFrame:GetSectionFrame()
173
- mainSectionListLayout.Padding = UDim.new(0, 0)
174
- mainSectionListLayout.SortOrder = Enum.SortOrder.LayoutOrder
175
- mainSectionListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
176
-
177
- -- Main section
178
- local mainSection = CollapsibleTitledSection.new("mainSection", "Azul Companion Plugin", true, false, false, false)
179
- mainSection:GetSectionFrame().LayoutOrder = 0
180
- mainSection:GetSectionFrame().Parent = mainScrollFrame:GetContentsFrame()
181
-
182
- -- Title image
183
- local imageContainer = Instance.new("Frame")
184
- imageContainer.Size = UDim2.new(1, 0, 0, 70)
185
- imageContainer.BackgroundTransparency = 1
186
-
187
- local imageContainerListLayout = Instance.new("UIListLayout")
188
- imageContainerListLayout.FillDirection = Enum.FillDirection.Horizontal
189
- imageContainerListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
190
- imageContainerListLayout.VerticalAlignment = Enum.VerticalAlignment.Center
191
- imageContainerListLayout.Parent = imageContainer
192
-
193
- local titleImageLabel = Instance.new("ImageLabel")
194
- titleImageLabel.Size = UDim2.fromScale(0.2, 1)
195
- titleImageLabel.AutomaticSize = Enum.AutomaticSize.X
196
- titleImageLabel.BackgroundTransparency = 1
197
- titleImageLabel.Image = self.LOGO
198
- titleImageLabel.ScaleType = Enum.ScaleType.Fit
199
- titleImageLabel.LayoutOrder = 0
200
- titleImageLabel.Parent = imageContainer
201
-
202
- local azulText = Instance.new("TextLabel")
203
- azulText.Text = "<b>Azul</b>"
204
- azulText.RichText = true
205
- azulText.Font = Enum.Font.RobotoMono
206
- azulText.TextSize = 36
207
- azulText.TextScaled = true
208
- azulText.Size = UDim2.fromScale(0.1, 1)
209
- azulText.AutomaticSize = Enum.AutomaticSize.X
210
- azulText.TextColor3 = Color3.fromRGB(255, 255, 255)
211
- azulText.BackgroundTransparency = 1
212
- azulText.LayoutOrder = 1
213
- azulText.Parent = imageContainer
214
-
215
- local padding = Instance.new("UIPadding")
216
- padding.PaddingTop = UDim.new(0, 10)
217
- padding.PaddingBottom = UDim.new(0, 10)
218
- padding.Parent = imageContainer
219
-
220
- mainSection:AddChild(imageContainer, 0)
221
- self.titleImageLabel = titleImageLabel
222
-
223
- -- Sync button
224
- local syncButton = CustomTextButton.new("toggleSync", "Connect", false)
225
- syncButton.Parent = mainScrollFrame:GetContentsFrame()
226
- syncButton:SetSize(UDim2.fromScale(1, 0.2))
227
-
228
- local syncButtonPadding = Instance.new("UIPadding")
229
- syncButtonPadding.PaddingLeft = UDim.new(0.1, 0)
230
- syncButtonPadding.PaddingRight = UDim.new(0.1, 0)
231
- syncButtonPadding.Parent = syncButton:GetFrame()
232
-
233
- syncButton:SetClickedFunction(function()
234
- if self.isSyncEnabled then
235
- syncButton:GetButton().Text = "Stopping..."
236
- self.callbacks.onStopSync()
237
- else
238
- syncButton:GetButton().Text = "Connecting..."
239
- self.callbacks.onStartSync()
240
- end
241
- end)
242
- mainSection:AddChild(syncButton:GetFrame())
243
- self.syncButton = syncButton
244
-
245
- -- Silent Mode checkbox
246
- local silentModeCheckbox = LabeledCheckbox.new("silentMode", "Silent Mode", Config.SILENT_MODE, false)
247
- silentModeCheckbox:SetValue(Config.SILENT_MODE)
248
- silentModeCheckbox:SetValueChangedFunction(function(newValue)
249
- Config.SILENT_MODE = newValue
250
- helpers.infoPrint(`(UI): Silent mode set to: {newValue}`)
251
- end)
252
- mainSection:AddChild(silentModeCheckbox:GetFrame())
253
- mainSection:AddChild(createInfoLabel("Silent mode suppresses all console output except errors."))
254
- self.silentModeCheckbox = silentModeCheckbox
255
-
256
- -- Debug Mode checkbox
257
- local debugModeCheckbox = LabeledCheckbox.new("debugMode", "Debug Mode", Config.DEBUG_MODE, false)
258
- debugModeCheckbox:SetValue(Config.DEBUG_MODE)
259
- debugModeCheckbox:SetValueChangedFunction(function(newValue)
260
- Config.DEBUG_MODE = newValue
261
- helpers.debugPrint(`(UI): Debug mode set to: {newValue}`)
262
- end)
263
- mainSection:AddChild(debugModeCheckbox:GetFrame())
264
- mainSection:AddChild(createInfoLabel("Debug mode enables verbose logging to the output console."))
265
- self.debugModeCheckbox = debugModeCheckbox
266
-
267
- -- Settings section
268
- local settingsSection = CollapsibleTitledSection.new("settings", "Plugin Settings", true, true, true)
269
- settingsSection:GetSectionFrame().LayoutOrder = 1
270
- settingsSection:GetSectionFrame().Parent = mainScrollFrame:GetContentsFrame()
271
-
272
- -- Settings scope dropdown
273
- local scopeChoices = {
274
- { Id = 1, Text = "Global" },
275
- { Id = 2, Text = "Project" },
276
- }
277
- local settingsScopeDropdown = DropdownMenu.new("settingsScope", "Scope", scopeChoices, "Select ...")
278
- settingsSection:AddChild(settingsScopeDropdown:GetSectionFrame())
279
- self.settingsScopeDropdown = settingsScopeDropdown
280
-
281
- -- WebSocket URL input
282
- local websocketUrlLabel = LabeledTextInput.new("websocketUrl", "WebSocket URL:", Config.WS_URL)
283
- websocketUrlLabel:SetValue(Config.WS_URL)
284
- websocketUrlLabel:SetValueChangedFunction(function(newValue)
285
- Config.WS_URL = newValue
286
- self.callbacks.onConfigChanged("WS_URL", newValue)
287
- end)
288
- settingsSection:AddChild(websocketUrlLabel:GetFrame())
289
- self.websocketUrlLabel = websocketUrlLabel
290
-
291
- -- Heartbeat Interval input
292
- local heartbeatIntervalLabel =
293
- LabeledTextInput.new("heartbeatInterval", "Heartbeat Interval (s):", tostring(Config.HEARTBEAT_INTERVAL))
294
- heartbeatIntervalLabel:SetValue(tostring(Config.HEARTBEAT_INTERVAL))
295
- heartbeatIntervalLabel:SetValueChangedFunction(function(newValue)
296
- validateNumberInput(newValue, function(interval)
297
- Config.HEARTBEAT_INTERVAL = interval
298
- self.callbacks.onConfigChanged("HEARTBEAT_INTERVAL", interval)
299
- end)
300
- end)
301
- settingsSection:AddChild(heartbeatIntervalLabel:GetFrame())
302
- settingsSection:AddChild(createInfoLabel("Interval in seconds between heartbeat pings to the daemon."))
303
- self.heartbeatIntervalLabel = heartbeatIntervalLabel
304
-
305
- -- Batch Idle Window input
306
- local batchIdleWindowLabel =
307
- LabeledTextInput.new("batchIdleWindow", "Batch Idle Window (s):", tostring(Config.BATCH_IDLE_WINDOW))
308
- batchIdleWindowLabel:SetValue(tostring(Config.BATCH_IDLE_WINDOW))
309
- batchIdleWindowLabel:SetValueChangedFunction(function(newValue)
310
- validateNumberInput(newValue, function(idleWindow)
311
- Config.BATCH_IDLE_WINDOW = idleWindow
312
- self.callbacks.onConfigChanged("BATCH_IDLE_WINDOW", idleWindow)
313
- end)
314
- end)
315
- settingsSection:AddChild(batchIdleWindowLabel:GetFrame())
316
- settingsSection:AddChild(
317
- createInfoLabel(
318
- "Seconds of inactivity before sending batched updates to the daemon. A lower number means faster updates, a higher number can improve performance."
319
- )
320
- )
321
- self.batchIdleWindowLabel = batchIdleWindowLabel
322
-
323
- -- Script Change Debounce input
324
- local scriptChangeDebounceLabel =
325
- LabeledTextInput.new("scriptChangeDebounce", "Script Change Deb. (s):", tostring(Config.SCRIPT_CHANGE_DEBOUNCE))
326
- scriptChangeDebounceLabel:SetValue(tostring(Config.SCRIPT_CHANGE_DEBOUNCE))
327
- scriptChangeDebounceLabel:SetValueChangedFunction(function(newValue)
328
- validateNumberInput(newValue, function(debounce)
329
- Config.SCRIPT_CHANGE_DEBOUNCE = debounce
330
- self.callbacks.onConfigChanged("SCRIPT_CHANGE_DEBOUNCE", debounce)
331
- end)
332
- end)
333
- settingsSection:AddChild(scriptChangeDebounceLabel:GetFrame())
334
- settingsSection:AddChild(
335
- createInfoLabel(
336
- "Debounce time to batch script changes for before sending updates to the daemon. A lower number means faster updates, a higher number can improve performance."
337
- )
338
- )
339
- self.scriptChangeDebounceLabel = scriptChangeDebounceLabel
340
-
341
- -- List type dropdown
342
- local listTypeLabel = LabeledMultiChoice.new("listType", "Service List Type:", {
343
- { Id = "WHITELIST", Text = "Whitelist" },
344
- { Id = "BLACKLIST", Text = "Blacklist" },
345
- }, if Config.LIST_TYPE == Enums.listType.WHITELIST then 1 else 2)
346
- listTypeLabel:SetValueChangedFunction(function(newIndex)
347
- if newIndex == 1 then
348
- Config.LIST_TYPE = Enums.listType.WHITELIST
349
- else
350
- Config.LIST_TYPE = Enums.listType.BLACKLIST
351
- end
352
- self.callbacks.onConfigChanged("LIST_TYPE", Config.LIST_TYPE)
353
- end)
354
- settingsSection:AddChild(listTypeLabel:GetFrame())
355
- settingsSection:AddChild(
356
- createInfoLabel(
357
- "Whitelist: only services in the list are synced.<br />Blacklist: services in the list are excluded."
358
- )
359
- )
360
- self.listTypeLabel = listTypeLabel
361
-
362
- -- Service List input
363
- local serviceListLabel =
364
- LabeledTextInput.new("serviceList", "Service List:", table.concat(Config.SERVICE_LIST, ", "))
365
- serviceListLabel:SetMaxGraphemes(9999)
366
- serviceListLabel:SetValue(table.concat(Config.SERVICE_LIST, ", "))
367
- serviceListLabel:SetValueChangedFunction(function(newValue)
368
- local services = {}
369
- for serviceName in string.gmatch(newValue, "([^,%s]+)") do
370
- table.insert(services, serviceName)
371
- end
372
- Config.SERVICE_LIST = services
373
- self.rebuildServiceSet()
374
- self.callbacks.onConfigChanged("SERVICE_LIST", services)
375
- end)
376
- settingsSection:AddChild(serviceListLabel:GetFrame())
377
- settingsSection:AddChild(createInfoLabel("List of services to include/exclude based on the selected List Type."))
378
- self.serviceListLabel = serviceListLabel
379
-
380
- -- Excluded Parents input
381
- local excludedParentsLabel =
382
- LabeledTextInput.new("excludedParents", "Excluded Parents:", table.concat(Config.EXCLUDED_PARENTS, ", "))
383
- excludedParentsLabel:SetValue(table.concat(Config.EXCLUDED_PARENTS, ", "))
384
- excludedParentsLabel:SetValueChangedFunction(function(newValue)
385
- local excluded = {}
386
- for parentName in string.gmatch(newValue, "([^,%s]+)") do
387
- table.insert(excluded, parentName)
388
- end
389
- Config.EXCLUDED_PARENTS = excluded
390
- self.callbacks.onConfigChanged("EXCLUDED_PARENTS", excluded)
391
- end)
392
- settingsSection:AddChild(excludedParentsLabel:GetFrame())
393
- settingsSection:AddChild(createInfoLabel("List of parent paths to exclude from syncing, separated by commas."))
394
- self.excludedParentsLabel = excludedParentsLabel
395
-
396
- settingsScopeDropdown:SetValue(if self.settingsScope == Enums.scope.GLOBAL then 1 else 2)
397
-
398
- -- Settings scope callback
399
- settingsScopeDropdown:SetValueChangedFunction(function(newValue: number | string, newText: string)
400
- helpers.debugPrint("(UI): Settings scope changed to:", newText)
401
-
402
- local newScope
403
- if newValue == 1 then
404
- newScope = Enums.scope.GLOBAL
405
- elseif newValue == 2 then
406
- newScope = Enums.scope.PROJECT
407
- else
408
- return
409
- end
410
-
411
- self.callbacks.onSettingsScopeChanged(newScope)
412
- end)
413
-
414
- -- Reload sourcemap button
415
- local reloadSourcemapButton = CustomTextButton.new("reloadSourcemap", "Reload Sourcemap", false)
416
- reloadSourcemapButton:SetClickedFunction(function()
417
- self.callbacks.onSourcemapReload()
418
- end)
419
- reloadSourcemapButton:SetSize(UDim2.fromScale(1, 0.07))
420
-
421
- local clearButtonPadding = Instance.new("UIPadding")
422
- clearButtonPadding.PaddingLeft = UDim.new(0.1, 0)
423
- clearButtonPadding.PaddingRight = UDim.new(0.1, 0)
424
- clearButtonPadding.Parent = reloadSourcemapButton:GetFrame()
425
-
426
- settingsSection:AddChild(reloadSourcemapButton:GetFrame())
427
- settingsSection:AddChild(
428
- createInfoLabel("Something's not right? Force reload the sourcemap to reset the sync state.")
429
- )
430
- self.reloadSourcemapButton = reloadSourcemapButton
431
-
432
- -- Connect button click handler
433
- self.connectButton.Click:Connect(function()
434
- azulWidget.Enabled = not azulWidget.Enabled
435
- end)
436
-
437
- return (self :: any) :: SelfType
438
- end
439
-
440
- function UI:GetSyncState()
441
- return self.isSyncEnabled
442
- end
443
-
444
- function UI:UpdateSyncState(enabled: boolean)
445
- self.isSyncEnabled = enabled
446
- self.connectButton:SetActive(enabled)
447
- if enabled then
448
- self.connectButton.Icon = self.LOGO_SYNCED
449
- self.syncButton:GetButton().Text = "Disconnect"
450
- self.titleImageLabel.Image = self.LOGO_SYNCED
451
- else
452
- self.connectButton.Icon = self.LOGO
453
- self.syncButton:GetButton().Text = "Connect"
454
- self.titleImageLabel.Image = self.LOGO
455
- end
456
- end
457
-
458
- function UI:SetSettingsScope(scope)
459
- self.settingsScope = scope
460
- end
461
-
462
- function UI:UpdateConfig()
463
- self = self :: SelfType
464
-
465
- self.debugModeCheckbox:SetValue(Config.DEBUG_MODE)
466
- self.silentModeCheckbox:SetValue(Config.SILENT_MODE)
467
- self.settingsScopeDropdown:SetValue(if self.settingsScope == Enums.scope.GLOBAL then 1 else 2)
468
- self.websocketUrlLabel:SetValue(Config.WS_URL)
469
- self.heartbeatIntervalLabel:SetValue(tostring(Config.HEARTBEAT_INTERVAL))
470
- self.batchIdleWindowLabel:SetValue(tostring(Config.BATCH_IDLE_WINDOW))
471
- self.scriptChangeDebounceLabel:SetValue(tostring(Config.SCRIPT_CHANGE_DEBOUNCE))
472
- self.listTypeLabel:SetSelectedIndex(if Config.LIST_TYPE == Enums.listType.WHITELIST then 1 else 2)
473
- self.serviceListLabel:SetValue(table.concat(Config.SERVICE_LIST, ", "))
474
- self.excludedParentsLabel:SetValue(table.concat(Config.EXCLUDED_PARENTS, ", "))
475
- end
476
-
477
- return UI
@@ -1,161 +0,0 @@
1
- --!strict
2
- --[[
3
- WebSocket Client for Roblox Studio
4
-
5
- Uses Roblox Studio's native WebSocket support (WebStreamClient) for real-time
6
- bidirectional communication with the sync daemon.
7
-
8
- Ransomwave 2025
9
- ]]
10
-
11
- local HttpService = game:GetService("HttpService")
12
-
13
- local WebSocketClient = {}
14
- WebSocketClient.__index = WebSocketClient
15
-
16
- type configType = {
17
- debugMode: boolean?,
18
- silentMode: boolean?,
19
- }
20
-
21
- -- self type
22
- type WebSocketClient = {
23
- url: string,
24
- client: WebStreamClient,
25
- connected: boolean,
26
- messageHandlers: { [string]: (any) -> () },
27
- config: configType,
28
-
29
- new: (url: string?) -> WebSocketClient,
30
- on: (self: WebSocketClient, event: string, handler: (any) -> ()) -> (),
31
- connect: (self: WebSocketClient) -> boolean,
32
- handleMessage: (self: WebSocketClient, message: string) -> (),
33
- send: (self: WebSocketClient, message: string) -> boolean,
34
- disconnect: (self: WebSocketClient) -> (),
35
- debugPrint: (self: WebSocketClient, ...any) -> (),
36
- infoPrint: (self: WebSocketClient, ...any) -> (),
37
- }
38
-
39
- function WebSocketClient.new(url, config: configType?)
40
- local self = setmetatable({}, WebSocketClient)
41
- self.url = url or "ws://localhost:8080"
42
- self.client = nil
43
- self.connected = false
44
- self.messageHandlers = {}
45
- self.onClosed = Instance.new("BindableEvent")
46
- self.config = config or {
47
- debugMode = false,
48
- silentMode = true,
49
- }
50
-
51
- if self.config.debugMode then print("[🐛 WebSocket]: Debug mode is enabled!") end
52
-
53
- return (self :: any) :: WebSocketClient
54
- end
55
-
56
- function WebSocketClient:debugPrint(...)
57
- self = self :: WebSocketClient
58
- if self.config.silentMode or not self.config.debugMode then return end
59
- print(...)
60
- end
61
-
62
- function WebSocketClient:infoPrint(...)
63
- self = self :: WebSocketClient
64
- if self.config.silentMode then return end
65
- print(...)
66
- end
67
-
68
- function WebSocketClient:on(event, handler)
69
- self = self :: WebSocketClient
70
- self.messageHandlers[event] = handler
71
- end
72
-
73
- function WebSocketClient:connect()
74
- self = self :: WebSocketClient
75
- if self.connected then return true end
76
-
77
- -- Create WebSocket client using CreateWebStreamClient
78
- local success, result = pcall(function()
79
- return HttpService:CreateWebStreamClient(Enum.WebStreamClientType.WebSocket, {
80
- Url = self.url,
81
- })
82
- end)
83
-
84
- if not success then
85
- warn("[WebSocket]: Connection failed:", result)
86
- if self.messageHandlers.error then self.messageHandlers.error(result) end
87
- return false
88
- end
89
-
90
- self.client = result
91
- self.connected = true
92
-
93
- -- Set up message handler (only MessageReceived is documented)
94
- self.client.MessageReceived:Connect(function(message)
95
- local parseSuccess, parseError = pcall(function()
96
- self:handleMessage(message)
97
- end)
98
- if not parseSuccess then warn("[WebSocket]: Error handling message:", parseError) end
99
- end)
100
-
101
- -- Notify connection established
102
- self:debugPrint("[🐛 WebSocket]: Connected to", self.url)
103
- if self.messageHandlers.connect then task.defer(function()
104
- self.messageHandlers.connect()
105
- end) end
106
-
107
- return true
108
- end
109
-
110
- function WebSocketClient:handleMessage(message)
111
- self = self :: WebSocketClient
112
- if not message or message == "" then return end
113
-
114
- self:debugPrint("[🐛 WebSocket]: Received message:", string.sub(message, 1, 100))
115
-
116
- local success, data = pcall(function()
117
- return HttpService:JSONDecode(message)
118
- end)
119
-
120
- if success and self.messageHandlers.message then
121
- self.messageHandlers.message(data)
122
- elseif not success then
123
- warn("[WebSocket]: Failed to parse message:", message)
124
- end
125
- end
126
-
127
- function WebSocketClient:send(message)
128
- self = self :: WebSocketClient
129
- if not self.connected or not self.client then
130
- warn("[WebSocket]: Cannot send: not connected")
131
- return false
132
- end
133
-
134
- self:debugPrint("[🐛 WebSocket]: Sending:", string.sub(message, 1, 200))
135
-
136
- local success, err = pcall(function()
137
- self.client:Send(message)
138
- end)
139
-
140
- if not success then
141
- warn("[WebSocket]: Send failed:", err)
142
- return false
143
- end
144
-
145
- return true
146
- end
147
-
148
- function WebSocketClient:disconnect()
149
- self = self :: WebSocketClient
150
-
151
- if not self.connected or not self.client then return end
152
-
153
- self.connected = false
154
-
155
- self.client:Close()
156
- -- self.client = nil
157
-
158
- if self.messageHandlers.disconnect then self.messageHandlers.disconnect() end
159
- end
160
-
161
- return WebSocketClient