@chrrxs/robloxstudio-mcp 2.8.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.
- package/dist/index.js +4614 -0
- package/package.json +48 -0
- package/studio-plugin/INSTALLATION.md +150 -0
- package/studio-plugin/MCPInspectorPlugin.rbxmx +9074 -0
- package/studio-plugin/MCPPlugin.rbxmx +9074 -0
- package/studio-plugin/default.project.json +19 -0
- package/studio-plugin/dev.project.json +23 -0
- package/studio-plugin/inspector-icon.png +0 -0
- package/studio-plugin/package-lock.json +706 -0
- package/studio-plugin/package.json +19 -0
- package/studio-plugin/plugin.json +10 -0
- package/studio-plugin/src/modules/ClientBroker.ts +221 -0
- package/studio-plugin/src/modules/Communication.ts +399 -0
- package/studio-plugin/src/modules/Recording.ts +28 -0
- package/studio-plugin/src/modules/State.ts +94 -0
- package/studio-plugin/src/modules/UI.ts +725 -0
- package/studio-plugin/src/modules/Utils.ts +318 -0
- package/studio-plugin/src/modules/handlers/AssetHandlers.ts +241 -0
- package/studio-plugin/src/modules/handlers/BuildHandlers.ts +481 -0
- package/studio-plugin/src/modules/handlers/CaptureHandlers.ts +128 -0
- package/studio-plugin/src/modules/handlers/InputHandlers.ts +102 -0
- package/studio-plugin/src/modules/handlers/InstanceHandlers.ts +380 -0
- package/studio-plugin/src/modules/handlers/MetadataHandlers.ts +391 -0
- package/studio-plugin/src/modules/handlers/PropertyHandlers.ts +191 -0
- package/studio-plugin/src/modules/handlers/QueryHandlers.ts +827 -0
- package/studio-plugin/src/modules/handlers/ScriptHandlers.ts +530 -0
- package/studio-plugin/src/modules/handlers/TestHandlers.ts +277 -0
- package/studio-plugin/src/server/index.server.ts +63 -0
- package/studio-plugin/src/types/index.d.ts +44 -0
- package/studio-plugin/tsconfig.json +20 -0
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
import { TweenService } from "@rbxts/services";
|
|
2
|
+
import State from "./State";
|
|
3
|
+
import { Connection } from "../types";
|
|
4
|
+
|
|
5
|
+
interface UIElements {
|
|
6
|
+
screenGui: DockWidgetPluginGui;
|
|
7
|
+
mainFrame: Frame;
|
|
8
|
+
contentFrame: ScrollingFrame;
|
|
9
|
+
statusLabel: TextLabel;
|
|
10
|
+
detailStatusLabel: TextLabel;
|
|
11
|
+
statusIndicator: Frame;
|
|
12
|
+
statusPulse: Frame;
|
|
13
|
+
statusText: TextLabel;
|
|
14
|
+
connectButton: TextButton;
|
|
15
|
+
connectStroke: UIStroke;
|
|
16
|
+
urlInput: TextBox;
|
|
17
|
+
step1Dot: Frame;
|
|
18
|
+
step1Label: TextLabel;
|
|
19
|
+
step2Dot: Frame;
|
|
20
|
+
step2Label: TextLabel;
|
|
21
|
+
step3Dot: Frame;
|
|
22
|
+
step3Label: TextLabel;
|
|
23
|
+
troubleshootLabel: TextLabel;
|
|
24
|
+
updateBanner: Frame;
|
|
25
|
+
updateBannerText: TextLabel;
|
|
26
|
+
tabBar: Frame;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let elements: UIElements = undefined!;
|
|
30
|
+
let pulseAnimation: Tween | undefined;
|
|
31
|
+
let buttonHover = false;
|
|
32
|
+
|
|
33
|
+
interface TabButton {
|
|
34
|
+
frame: Frame;
|
|
35
|
+
label: TextLabel;
|
|
36
|
+
dot: Frame;
|
|
37
|
+
closeBtn: TextButton;
|
|
38
|
+
}
|
|
39
|
+
let tabButtons: Map<number, TabButton> = new Map();
|
|
40
|
+
|
|
41
|
+
const TWEEN_QUICK = new TweenInfo(0.15, Enum.EasingStyle.Quad, Enum.EasingDirection.Out);
|
|
42
|
+
|
|
43
|
+
function tweenProp(instance: Instance, props: Record<string, unknown>) {
|
|
44
|
+
TweenService.Create(instance, TWEEN_QUICK, props as unknown as { [key: string]: unknown }).Play();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const C = {
|
|
48
|
+
bg: Color3.fromRGB(14, 14, 14),
|
|
49
|
+
card: Color3.fromRGB(22, 22, 22),
|
|
50
|
+
surface: Color3.fromRGB(30, 30, 30),
|
|
51
|
+
border: Color3.fromRGB(38, 38, 38),
|
|
52
|
+
subtle: Color3.fromRGB(48, 48, 48),
|
|
53
|
+
muted: Color3.fromRGB(100, 100, 100),
|
|
54
|
+
dim: Color3.fromRGB(140, 140, 140),
|
|
55
|
+
label: Color3.fromRGB(180, 180, 180),
|
|
56
|
+
white: Color3.fromRGB(240, 240, 240),
|
|
57
|
+
green: Color3.fromRGB(52, 211, 153),
|
|
58
|
+
yellow: Color3.fromRGB(251, 191, 36),
|
|
59
|
+
red: Color3.fromRGB(248, 113, 113),
|
|
60
|
+
gray: Color3.fromRGB(120, 120, 120),
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const CORNER = new UDim(0, 4);
|
|
64
|
+
|
|
65
|
+
function getStatusDotColor(connIndex: number): Color3 {
|
|
66
|
+
const conn = State.getConnection(connIndex);
|
|
67
|
+
if (!conn || !conn.isActive) return C.red;
|
|
68
|
+
if (conn.consecutiveFailures >= conn.maxFailuresBeforeError) return C.red;
|
|
69
|
+
if (conn.lastHttpOk) return C.green;
|
|
70
|
+
return C.yellow;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function setButtonConnect(btn: TextButton, stroke: UIStroke) {
|
|
74
|
+
btn.Text = "Connect";
|
|
75
|
+
btn.TextColor3 = C.white;
|
|
76
|
+
btn.BackgroundColor3 = C.surface;
|
|
77
|
+
stroke.Color = C.subtle;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function setButtonDisconnect(btn: TextButton, stroke: UIStroke) {
|
|
81
|
+
btn.Text = "Disconnect";
|
|
82
|
+
btn.TextColor3 = C.red;
|
|
83
|
+
btn.BackgroundColor3 = C.bg;
|
|
84
|
+
stroke.Color = Color3.fromRGB(80, 30, 30);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function stopPulseAnimation() {
|
|
88
|
+
elements.statusPulse.Size = new UDim2(0, 10, 0, 10);
|
|
89
|
+
elements.statusPulse.Position = new UDim2(0, 0, 0, 0);
|
|
90
|
+
elements.statusPulse.BackgroundTransparency = 0.7;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function startPulseAnimation() {
|
|
94
|
+
elements.statusPulse.Size = new UDim2(0, 10, 0, 10);
|
|
95
|
+
elements.statusPulse.Position = new UDim2(0, 0, 0, 0);
|
|
96
|
+
elements.statusPulse.BackgroundTransparency = 0.7;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let refreshTabBar: () => void;
|
|
100
|
+
let switchToTab: (index: number) => void;
|
|
101
|
+
|
|
102
|
+
function createTabButton(connIndex: number) {
|
|
103
|
+
const conn = State.getConnection(connIndex);
|
|
104
|
+
if (!conn) return;
|
|
105
|
+
|
|
106
|
+
const isActive = connIndex === State.getActiveTabIndex();
|
|
107
|
+
|
|
108
|
+
const tabFrame = new Instance("Frame");
|
|
109
|
+
tabFrame.Size = new UDim2(0, 58, 1, -6);
|
|
110
|
+
tabFrame.Position = new UDim2(0, 0, 0, 3);
|
|
111
|
+
tabFrame.BackgroundColor3 = isActive ? C.surface : C.bg;
|
|
112
|
+
tabFrame.BackgroundTransparency = isActive ? 0 : 0.5;
|
|
113
|
+
tabFrame.BorderSizePixel = 0;
|
|
114
|
+
tabFrame.LayoutOrder = connIndex;
|
|
115
|
+
|
|
116
|
+
const tabCorner = new Instance("UICorner");
|
|
117
|
+
tabCorner.CornerRadius = new UDim(0, 3);
|
|
118
|
+
tabCorner.Parent = tabFrame;
|
|
119
|
+
|
|
120
|
+
const dot = new Instance("Frame");
|
|
121
|
+
dot.Size = new UDim2(0, 5, 0, 5);
|
|
122
|
+
dot.Position = new UDim2(0, 6, 0.5, -2);
|
|
123
|
+
dot.BackgroundColor3 = getStatusDotColor(connIndex);
|
|
124
|
+
dot.BorderSizePixel = 0;
|
|
125
|
+
dot.Parent = tabFrame;
|
|
126
|
+
|
|
127
|
+
const dotCorner = new Instance("UICorner");
|
|
128
|
+
dotCorner.CornerRadius = new UDim(1, 0);
|
|
129
|
+
dotCorner.Parent = dot;
|
|
130
|
+
|
|
131
|
+
const label = new Instance("TextLabel");
|
|
132
|
+
label.Size = new UDim2(1, -26, 1, 0);
|
|
133
|
+
label.Position = new UDim2(0, 14, 0, 0);
|
|
134
|
+
label.BackgroundTransparency = 1;
|
|
135
|
+
label.Text = tostring(conn.port);
|
|
136
|
+
label.TextColor3 = isActive ? C.label : C.muted;
|
|
137
|
+
label.TextSize = 10;
|
|
138
|
+
label.Font = Enum.Font.GothamMedium;
|
|
139
|
+
label.TextXAlignment = Enum.TextXAlignment.Left;
|
|
140
|
+
label.TextTruncate = Enum.TextTruncate.AtEnd;
|
|
141
|
+
label.Parent = tabFrame;
|
|
142
|
+
|
|
143
|
+
const closeBtn = new Instance("TextButton");
|
|
144
|
+
closeBtn.Size = new UDim2(0, 12, 0, 12);
|
|
145
|
+
closeBtn.Position = new UDim2(1, -15, 0.5, -6);
|
|
146
|
+
closeBtn.BackgroundTransparency = 1;
|
|
147
|
+
closeBtn.Text = "x";
|
|
148
|
+
closeBtn.TextColor3 = C.muted;
|
|
149
|
+
closeBtn.TextSize = 8;
|
|
150
|
+
closeBtn.Font = Enum.Font.GothamBold;
|
|
151
|
+
closeBtn.Parent = tabFrame;
|
|
152
|
+
|
|
153
|
+
const clickBtn = new Instance("TextButton");
|
|
154
|
+
clickBtn.Size = new UDim2(1, -14, 1, 0);
|
|
155
|
+
clickBtn.Position = new UDim2(0, 0, 0, 0);
|
|
156
|
+
clickBtn.BackgroundTransparency = 1;
|
|
157
|
+
clickBtn.Text = "";
|
|
158
|
+
clickBtn.Parent = tabFrame;
|
|
159
|
+
|
|
160
|
+
clickBtn.Activated.Connect(() => switchToTab(connIndex));
|
|
161
|
+
closeBtn.Activated.Connect(() => {
|
|
162
|
+
const c = State.getConnection(connIndex);
|
|
163
|
+
if (c && c.isActive) return;
|
|
164
|
+
if (State.getConnections().size() <= 1) return;
|
|
165
|
+
State.removeConnection(connIndex);
|
|
166
|
+
refreshTabBar();
|
|
167
|
+
switchToTab(State.getActiveTabIndex());
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
tabFrame.Parent = elements.tabBar;
|
|
171
|
+
tabButtons.set(connIndex, { frame: tabFrame, label, dot, closeBtn });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
refreshTabBar = () => {
|
|
175
|
+
tabButtons.forEach((tb) => {
|
|
176
|
+
if (tb.frame) tb.frame.Destroy();
|
|
177
|
+
});
|
|
178
|
+
tabButtons = new Map();
|
|
179
|
+
for (let i = 0; i < State.getConnections().size(); i++) {
|
|
180
|
+
createTabButton(i);
|
|
181
|
+
}
|
|
182
|
+
tabButtons.forEach((tb, i) => {
|
|
183
|
+
const active = i === State.getActiveTabIndex();
|
|
184
|
+
if (tb.frame) {
|
|
185
|
+
tb.frame.BackgroundColor3 = active ? C.surface : C.bg;
|
|
186
|
+
tb.frame.BackgroundTransparency = active ? 0 : 0.5;
|
|
187
|
+
}
|
|
188
|
+
if (tb.label) tb.label.TextColor3 = active ? C.label : C.muted;
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
switchToTab = (index: number) => {
|
|
193
|
+
if (index < 0 || index >= State.getConnections().size()) return;
|
|
194
|
+
State.setActiveTabIndex(index);
|
|
195
|
+
const conn = State.getActiveConnection();
|
|
196
|
+
|
|
197
|
+
tabButtons.forEach((tb, i) => {
|
|
198
|
+
const active = i === index;
|
|
199
|
+
if (tb.frame) {
|
|
200
|
+
tweenProp(tb.frame, { BackgroundColor3: active ? C.surface : C.bg, BackgroundTransparency: active ? 0 : 0.5 });
|
|
201
|
+
}
|
|
202
|
+
if (tb.label) tb.label.TextColor3 = active ? C.label : C.muted;
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
elements.urlInput.Text = conn.serverUrl;
|
|
206
|
+
updateUIState();
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
function updateTabDot(connIndex: number) {
|
|
210
|
+
const tb = tabButtons.get(connIndex);
|
|
211
|
+
if (tb && tb.dot) {
|
|
212
|
+
tb.dot.BackgroundColor3 = getStatusDotColor(connIndex);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function updateTabLabel(connIndex: number) {
|
|
217
|
+
const conn = State.getConnection(connIndex);
|
|
218
|
+
const tb = tabButtons.get(connIndex);
|
|
219
|
+
if (conn && tb && tb.label) {
|
|
220
|
+
tb.label.Text = tostring(conn.port);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function init(pluginRef: Plugin) {
|
|
225
|
+
const CURRENT_VERSION = State.CURRENT_VERSION;
|
|
226
|
+
|
|
227
|
+
const screenGui = pluginRef.CreateDockWidgetPluginGuiAsync(
|
|
228
|
+
"MCPServerInterface",
|
|
229
|
+
new DockWidgetPluginGuiInfo(Enum.InitialDockState.Float, false, false, 300, 260, 260, 200),
|
|
230
|
+
);
|
|
231
|
+
(screenGui as unknown as { Title: string }).Title = `MCP Server v${CURRENT_VERSION}`;
|
|
232
|
+
|
|
233
|
+
const mainFrame = new Instance("Frame");
|
|
234
|
+
mainFrame.Size = new UDim2(1, 0, 1, 0);
|
|
235
|
+
mainFrame.BackgroundColor3 = C.bg;
|
|
236
|
+
mainFrame.BorderSizePixel = 0;
|
|
237
|
+
mainFrame.Parent = screenGui;
|
|
238
|
+
|
|
239
|
+
const header = new Instance("Frame");
|
|
240
|
+
header.Size = new UDim2(1, 0, 0, 40);
|
|
241
|
+
header.BackgroundColor3 = C.bg;
|
|
242
|
+
header.BorderSizePixel = 0;
|
|
243
|
+
header.Parent = mainFrame;
|
|
244
|
+
|
|
245
|
+
const headerLine = new Instance("Frame");
|
|
246
|
+
headerLine.Size = new UDim2(1, -16, 0, 1);
|
|
247
|
+
headerLine.Position = new UDim2(0, 8, 1, -1);
|
|
248
|
+
headerLine.BackgroundColor3 = C.border;
|
|
249
|
+
headerLine.BorderSizePixel = 0;
|
|
250
|
+
headerLine.Parent = header;
|
|
251
|
+
|
|
252
|
+
const titleLabel = new Instance("TextLabel");
|
|
253
|
+
titleLabel.Size = new UDim2(1, -50, 0, 22);
|
|
254
|
+
titleLabel.Position = new UDim2(0, 10, 0, 2);
|
|
255
|
+
titleLabel.BackgroundTransparency = 1;
|
|
256
|
+
titleLabel.RichText = true;
|
|
257
|
+
titleLabel.Text = `<font color="#F0F0F0">MCP</font> <font color="#646464">v${CURRENT_VERSION}</font>`;
|
|
258
|
+
titleLabel.TextColor3 = C.white;
|
|
259
|
+
titleLabel.TextSize = 12;
|
|
260
|
+
titleLabel.Font = Enum.Font.GothamBold;
|
|
261
|
+
titleLabel.TextXAlignment = Enum.TextXAlignment.Left;
|
|
262
|
+
titleLabel.Parent = header;
|
|
263
|
+
|
|
264
|
+
const creditsLabel = new Instance("TextLabel");
|
|
265
|
+
creditsLabel.Size = new UDim2(1, -20, 0, 12);
|
|
266
|
+
creditsLabel.Position = new UDim2(0, 10, 0, 23);
|
|
267
|
+
creditsLabel.BackgroundTransparency = 1;
|
|
268
|
+
creditsLabel.RichText = true;
|
|
269
|
+
creditsLabel.Text = '<font color="#999999">by</font> <font color="#CCCCCC">@BoshyDx</font> <font color="#666666">|</font> <font color="#999999">discord</font> <font color="#CCCCCC">boshyz</font>';
|
|
270
|
+
creditsLabel.TextColor3 = C.muted;
|
|
271
|
+
creditsLabel.TextSize = 8;
|
|
272
|
+
creditsLabel.Font = Enum.Font.GothamMedium;
|
|
273
|
+
creditsLabel.TextXAlignment = Enum.TextXAlignment.Left;
|
|
274
|
+
creditsLabel.Parent = header;
|
|
275
|
+
|
|
276
|
+
const statusContainer = new Instance("Frame");
|
|
277
|
+
statusContainer.Size = new UDim2(0, 20, 0, 22);
|
|
278
|
+
statusContainer.Position = new UDim2(1, -26, 0, 2);
|
|
279
|
+
statusContainer.BackgroundTransparency = 1;
|
|
280
|
+
statusContainer.Parent = header;
|
|
281
|
+
|
|
282
|
+
const statusIndicator = new Instance("Frame");
|
|
283
|
+
statusIndicator.Size = new UDim2(0, 8, 0, 8);
|
|
284
|
+
statusIndicator.Position = new UDim2(0.5, -4, 0.5, -4);
|
|
285
|
+
statusIndicator.BackgroundColor3 = C.red;
|
|
286
|
+
statusIndicator.BorderSizePixel = 0;
|
|
287
|
+
statusIndicator.Parent = statusContainer;
|
|
288
|
+
|
|
289
|
+
const statusCorner = new Instance("UICorner");
|
|
290
|
+
statusCorner.CornerRadius = new UDim(1, 0);
|
|
291
|
+
statusCorner.Parent = statusIndicator;
|
|
292
|
+
|
|
293
|
+
const statusPulse = new Instance("Frame");
|
|
294
|
+
statusPulse.Size = new UDim2(0, 10, 0, 10);
|
|
295
|
+
statusPulse.Position = new UDim2(0, 0, 0, 0);
|
|
296
|
+
statusPulse.BackgroundColor3 = C.red;
|
|
297
|
+
statusPulse.BackgroundTransparency = 0.7;
|
|
298
|
+
statusPulse.BorderSizePixel = 0;
|
|
299
|
+
statusPulse.Parent = statusIndicator;
|
|
300
|
+
|
|
301
|
+
const pulseCorner = new Instance("UICorner");
|
|
302
|
+
pulseCorner.CornerRadius = new UDim(1, 0);
|
|
303
|
+
pulseCorner.Parent = statusPulse;
|
|
304
|
+
|
|
305
|
+
const statusText = new Instance("TextLabel");
|
|
306
|
+
statusText.Size = new UDim2(0, 0, 0, 0);
|
|
307
|
+
statusText.BackgroundTransparency = 1;
|
|
308
|
+
statusText.Text = "OFFLINE";
|
|
309
|
+
statusText.TextTransparency = 1;
|
|
310
|
+
statusText.TextSize = 1;
|
|
311
|
+
statusText.Font = Enum.Font.GothamMedium;
|
|
312
|
+
statusText.TextColor3 = C.white;
|
|
313
|
+
statusText.Parent = statusContainer;
|
|
314
|
+
|
|
315
|
+
const tabBar = new Instance("Frame");
|
|
316
|
+
tabBar.Size = new UDim2(1, 0, 0, 22);
|
|
317
|
+
tabBar.Position = new UDim2(0, 0, 0, 40);
|
|
318
|
+
tabBar.BackgroundColor3 = C.bg;
|
|
319
|
+
tabBar.BorderSizePixel = 0;
|
|
320
|
+
tabBar.Parent = mainFrame;
|
|
321
|
+
|
|
322
|
+
const tabBarLayout = new Instance("UIListLayout");
|
|
323
|
+
tabBarLayout.FillDirection = Enum.FillDirection.Horizontal;
|
|
324
|
+
tabBarLayout.Padding = new UDim(0, 2);
|
|
325
|
+
tabBarLayout.SortOrder = Enum.SortOrder.LayoutOrder;
|
|
326
|
+
tabBarLayout.VerticalAlignment = Enum.VerticalAlignment.Center;
|
|
327
|
+
tabBarLayout.Parent = tabBar;
|
|
328
|
+
|
|
329
|
+
const tabBarPadding = new Instance("UIPadding");
|
|
330
|
+
tabBarPadding.PaddingLeft = new UDim(0, 8);
|
|
331
|
+
tabBarPadding.PaddingRight = new UDim(0, 8);
|
|
332
|
+
tabBarPadding.Parent = tabBar;
|
|
333
|
+
|
|
334
|
+
const addTabBtn = new Instance("TextButton");
|
|
335
|
+
addTabBtn.Size = new UDim2(0, 18, 0, 18);
|
|
336
|
+
addTabBtn.BackgroundColor3 = C.surface;
|
|
337
|
+
addTabBtn.BackgroundTransparency = 0.5;
|
|
338
|
+
addTabBtn.BorderSizePixel = 0;
|
|
339
|
+
addTabBtn.Text = "+";
|
|
340
|
+
addTabBtn.TextColor3 = C.muted;
|
|
341
|
+
addTabBtn.TextSize = 12;
|
|
342
|
+
addTabBtn.Font = Enum.Font.GothamMedium;
|
|
343
|
+
addTabBtn.LayoutOrder = 999;
|
|
344
|
+
addTabBtn.Parent = tabBar;
|
|
345
|
+
|
|
346
|
+
const addTabCorner = new Instance("UICorner");
|
|
347
|
+
addTabCorner.CornerRadius = new UDim(0, 3);
|
|
348
|
+
addTabCorner.Parent = addTabBtn;
|
|
349
|
+
|
|
350
|
+
addTabBtn.MouseEnter.Connect(() => tweenProp(addTabBtn, { BackgroundTransparency: 0, BackgroundColor3: C.subtle }));
|
|
351
|
+
addTabBtn.MouseLeave.Connect(() => tweenProp(addTabBtn, { BackgroundTransparency: 0.5, BackgroundColor3: C.surface }));
|
|
352
|
+
addTabBtn.Activated.Connect(() => {
|
|
353
|
+
const newIndex = State.addConnection();
|
|
354
|
+
if (newIndex !== undefined) {
|
|
355
|
+
refreshTabBar();
|
|
356
|
+
switchToTab(newIndex);
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
const updateBanner = new Instance("Frame");
|
|
361
|
+
updateBanner.Size = new UDim2(1, -16, 0, 24);
|
|
362
|
+
updateBanner.Position = new UDim2(0, 8, 0, 64);
|
|
363
|
+
updateBanner.BackgroundColor3 = Color3.fromRGB(40, 32, 10);
|
|
364
|
+
updateBanner.BorderSizePixel = 0;
|
|
365
|
+
updateBanner.Visible = false;
|
|
366
|
+
updateBanner.Parent = mainFrame;
|
|
367
|
+
|
|
368
|
+
const updateBannerCorner = new Instance("UICorner");
|
|
369
|
+
updateBannerCorner.CornerRadius = new UDim(0, 3);
|
|
370
|
+
updateBannerCorner.Parent = updateBanner;
|
|
371
|
+
|
|
372
|
+
const updateBannerText = new Instance("TextLabel");
|
|
373
|
+
updateBannerText.Size = new UDim2(1, -16, 1, 0);
|
|
374
|
+
updateBannerText.Position = new UDim2(0, 8, 0, 0);
|
|
375
|
+
updateBannerText.BackgroundTransparency = 1;
|
|
376
|
+
updateBannerText.Text = "";
|
|
377
|
+
updateBannerText.TextColor3 = C.yellow;
|
|
378
|
+
updateBannerText.TextSize = 9;
|
|
379
|
+
updateBannerText.Font = Enum.Font.GothamMedium;
|
|
380
|
+
updateBannerText.TextXAlignment = Enum.TextXAlignment.Left;
|
|
381
|
+
updateBannerText.Parent = updateBanner;
|
|
382
|
+
|
|
383
|
+
const contentY = 66;
|
|
384
|
+
const contentFrame = new Instance("ScrollingFrame");
|
|
385
|
+
contentFrame.Size = new UDim2(1, -16, 1, -(contentY + 8));
|
|
386
|
+
contentFrame.Position = new UDim2(0, 8, 0, contentY);
|
|
387
|
+
contentFrame.BackgroundTransparency = 1;
|
|
388
|
+
contentFrame.BorderSizePixel = 0;
|
|
389
|
+
contentFrame.ScrollBarThickness = 2;
|
|
390
|
+
contentFrame.ScrollBarImageColor3 = C.subtle;
|
|
391
|
+
contentFrame.CanvasSize = new UDim2(0, 0, 0, 0);
|
|
392
|
+
contentFrame.AutomaticCanvasSize = Enum.AutomaticSize.Y;
|
|
393
|
+
contentFrame.Parent = mainFrame;
|
|
394
|
+
|
|
395
|
+
const card = new Instance("Frame");
|
|
396
|
+
card.Size = new UDim2(1, 0, 0, 0);
|
|
397
|
+
card.AutomaticSize = Enum.AutomaticSize.Y;
|
|
398
|
+
card.BackgroundColor3 = C.card;
|
|
399
|
+
card.BorderSizePixel = 0;
|
|
400
|
+
card.LayoutOrder = 1;
|
|
401
|
+
card.Parent = contentFrame;
|
|
402
|
+
|
|
403
|
+
const cardCorner = new Instance("UICorner");
|
|
404
|
+
cardCorner.CornerRadius = CORNER;
|
|
405
|
+
cardCorner.Parent = card;
|
|
406
|
+
|
|
407
|
+
const cardPadding = new Instance("UIPadding");
|
|
408
|
+
cardPadding.PaddingLeft = new UDim(0, 10);
|
|
409
|
+
cardPadding.PaddingRight = new UDim(0, 10);
|
|
410
|
+
cardPadding.PaddingTop = new UDim(0, 8);
|
|
411
|
+
cardPadding.PaddingBottom = new UDim(0, 10);
|
|
412
|
+
cardPadding.Parent = card;
|
|
413
|
+
|
|
414
|
+
const cardLayout = new Instance("UIListLayout");
|
|
415
|
+
cardLayout.Padding = new UDim(0, 6);
|
|
416
|
+
cardLayout.SortOrder = Enum.SortOrder.LayoutOrder;
|
|
417
|
+
cardLayout.Parent = card;
|
|
418
|
+
|
|
419
|
+
const urlInput = new Instance("TextBox");
|
|
420
|
+
urlInput.Size = new UDim2(1, 0, 0, 26);
|
|
421
|
+
urlInput.BackgroundColor3 = C.bg;
|
|
422
|
+
urlInput.BorderSizePixel = 0;
|
|
423
|
+
urlInput.Text = "http://localhost:58741";
|
|
424
|
+
urlInput.TextColor3 = C.label;
|
|
425
|
+
urlInput.TextSize = 11;
|
|
426
|
+
urlInput.Font = Enum.Font.GothamMedium;
|
|
427
|
+
urlInput.ClearTextOnFocus = false;
|
|
428
|
+
urlInput.PlaceholderText = "Server URL...";
|
|
429
|
+
urlInput.PlaceholderColor3 = C.muted;
|
|
430
|
+
urlInput.LayoutOrder = 1;
|
|
431
|
+
urlInput.Parent = card;
|
|
432
|
+
|
|
433
|
+
const urlCorner = new Instance("UICorner");
|
|
434
|
+
urlCorner.CornerRadius = CORNER;
|
|
435
|
+
urlCorner.Parent = urlInput;
|
|
436
|
+
|
|
437
|
+
const urlPadding = new Instance("UIPadding");
|
|
438
|
+
urlPadding.PaddingLeft = new UDim(0, 8);
|
|
439
|
+
urlPadding.PaddingRight = new UDim(0, 8);
|
|
440
|
+
urlPadding.Parent = urlInput;
|
|
441
|
+
|
|
442
|
+
urlInput.FocusLost.Connect(() => {
|
|
443
|
+
const conn = State.getActiveConnection();
|
|
444
|
+
if (!conn || conn.isActive) return;
|
|
445
|
+
conn.serverUrl = urlInput.Text;
|
|
446
|
+
const [portStr] = conn.serverUrl.match(":(%d+)$");
|
|
447
|
+
if (portStr) conn.port = tonumber(portStr) ?? conn.port;
|
|
448
|
+
updateTabLabel(State.getActiveTabIndex());
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
const statusRow = new Instance("Frame");
|
|
452
|
+
statusRow.Size = new UDim2(1, 0, 0, 14);
|
|
453
|
+
statusRow.BackgroundTransparency = 1;
|
|
454
|
+
statusRow.LayoutOrder = 2;
|
|
455
|
+
statusRow.Parent = card;
|
|
456
|
+
|
|
457
|
+
const statusLabel = new Instance("TextLabel");
|
|
458
|
+
statusLabel.Size = new UDim2(1, 0, 1, 0);
|
|
459
|
+
statusLabel.BackgroundTransparency = 1;
|
|
460
|
+
statusLabel.Text = "Disconnected";
|
|
461
|
+
statusLabel.TextColor3 = C.red;
|
|
462
|
+
statusLabel.TextSize = 10;
|
|
463
|
+
statusLabel.Font = Enum.Font.GothamBold;
|
|
464
|
+
statusLabel.TextXAlignment = Enum.TextXAlignment.Left;
|
|
465
|
+
statusLabel.TextWrapped = true;
|
|
466
|
+
statusLabel.Parent = statusRow;
|
|
467
|
+
|
|
468
|
+
const detailStatusLabel = new Instance("TextLabel");
|
|
469
|
+
detailStatusLabel.Size = new UDim2(0.5, 0, 1, 0);
|
|
470
|
+
detailStatusLabel.Position = new UDim2(0.5, 0, 0, 0);
|
|
471
|
+
detailStatusLabel.BackgroundTransparency = 1;
|
|
472
|
+
detailStatusLabel.Text = "HTTP: X MCP: X";
|
|
473
|
+
detailStatusLabel.TextColor3 = C.muted;
|
|
474
|
+
detailStatusLabel.TextSize = 9;
|
|
475
|
+
detailStatusLabel.Font = Enum.Font.GothamMedium;
|
|
476
|
+
detailStatusLabel.TextXAlignment = Enum.TextXAlignment.Right;
|
|
477
|
+
detailStatusLabel.TextWrapped = true;
|
|
478
|
+
detailStatusLabel.Parent = statusRow;
|
|
479
|
+
|
|
480
|
+
const stepsFrame = new Instance("Frame");
|
|
481
|
+
stepsFrame.Size = new UDim2(1, 0, 0, 0);
|
|
482
|
+
stepsFrame.AutomaticSize = Enum.AutomaticSize.Y;
|
|
483
|
+
stepsFrame.BackgroundTransparency = 1;
|
|
484
|
+
stepsFrame.LayoutOrder = 3;
|
|
485
|
+
stepsFrame.Parent = card;
|
|
486
|
+
|
|
487
|
+
const stepsLayout = new Instance("UIListLayout");
|
|
488
|
+
stepsLayout.Padding = new UDim(0, 1);
|
|
489
|
+
stepsLayout.FillDirection = Enum.FillDirection.Vertical;
|
|
490
|
+
stepsLayout.SortOrder = Enum.SortOrder.LayoutOrder;
|
|
491
|
+
stepsLayout.Parent = stepsFrame;
|
|
492
|
+
|
|
493
|
+
function createStepRow(text: string, order: number): [Frame, Frame, TextLabel] {
|
|
494
|
+
const row = new Instance("Frame");
|
|
495
|
+
row.Size = new UDim2(1, 0, 0, 13);
|
|
496
|
+
row.BackgroundTransparency = 1;
|
|
497
|
+
row.LayoutOrder = order;
|
|
498
|
+
|
|
499
|
+
const d = new Instance("Frame");
|
|
500
|
+
d.Size = new UDim2(0, 4, 0, 4);
|
|
501
|
+
d.Position = new UDim2(0, 1, 0, 5);
|
|
502
|
+
d.BackgroundColor3 = C.gray;
|
|
503
|
+
d.BorderSizePixel = 0;
|
|
504
|
+
d.Parent = row;
|
|
505
|
+
|
|
506
|
+
const dCorner = new Instance("UICorner");
|
|
507
|
+
dCorner.CornerRadius = new UDim(1, 0);
|
|
508
|
+
dCorner.Parent = d;
|
|
509
|
+
|
|
510
|
+
const lbl = new Instance("TextLabel");
|
|
511
|
+
lbl.Size = new UDim2(1, -12, 1, 0);
|
|
512
|
+
lbl.Position = new UDim2(0, 12, 0, 0);
|
|
513
|
+
lbl.BackgroundTransparency = 1;
|
|
514
|
+
lbl.Text = text;
|
|
515
|
+
lbl.TextColor3 = C.dim;
|
|
516
|
+
lbl.TextSize = 9;
|
|
517
|
+
lbl.Font = Enum.Font.GothamMedium;
|
|
518
|
+
lbl.TextXAlignment = Enum.TextXAlignment.Left;
|
|
519
|
+
lbl.Parent = row;
|
|
520
|
+
|
|
521
|
+
row.Parent = stepsFrame;
|
|
522
|
+
return [row, d, lbl];
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const [, step1Dot, step1Label] = createStepRow("HTTP server", 1);
|
|
526
|
+
const [, step2Dot, step2Label] = createStepRow("MCP bridge", 2);
|
|
527
|
+
const [, step3Dot, step3Label] = createStepRow("Commands", 3);
|
|
528
|
+
|
|
529
|
+
const troubleshootLabel = new Instance("TextLabel");
|
|
530
|
+
troubleshootLabel.Size = new UDim2(1, 0, 0, 24);
|
|
531
|
+
troubleshootLabel.BackgroundTransparency = 1;
|
|
532
|
+
troubleshootLabel.TextWrapped = true;
|
|
533
|
+
troubleshootLabel.Visible = false;
|
|
534
|
+
troubleshootLabel.Text = "MCP not responding. Close node.exe and restart server.";
|
|
535
|
+
troubleshootLabel.TextColor3 = C.yellow;
|
|
536
|
+
troubleshootLabel.TextSize = 9;
|
|
537
|
+
troubleshootLabel.Font = Enum.Font.GothamMedium;
|
|
538
|
+
troubleshootLabel.TextXAlignment = Enum.TextXAlignment.Left;
|
|
539
|
+
troubleshootLabel.LayoutOrder = 4;
|
|
540
|
+
troubleshootLabel.Parent = card;
|
|
541
|
+
|
|
542
|
+
const connectButton = new Instance("TextButton");
|
|
543
|
+
connectButton.Size = new UDim2(1, 0, 0, 28);
|
|
544
|
+
connectButton.BackgroundColor3 = C.surface;
|
|
545
|
+
connectButton.BackgroundTransparency = 0;
|
|
546
|
+
connectButton.BorderSizePixel = 0;
|
|
547
|
+
connectButton.Text = "Connect";
|
|
548
|
+
connectButton.TextColor3 = C.white;
|
|
549
|
+
connectButton.TextSize = 11;
|
|
550
|
+
connectButton.Font = Enum.Font.GothamBold;
|
|
551
|
+
connectButton.LayoutOrder = 5;
|
|
552
|
+
connectButton.Parent = card;
|
|
553
|
+
|
|
554
|
+
const connectCorner = new Instance("UICorner");
|
|
555
|
+
connectCorner.CornerRadius = CORNER;
|
|
556
|
+
connectCorner.Parent = connectButton;
|
|
557
|
+
|
|
558
|
+
const connectStroke = new Instance("UIStroke");
|
|
559
|
+
connectStroke.Color = C.subtle;
|
|
560
|
+
connectStroke.Thickness = 1;
|
|
561
|
+
connectStroke.Parent = connectButton;
|
|
562
|
+
|
|
563
|
+
connectButton.MouseEnter.Connect(() => {
|
|
564
|
+
buttonHover = true;
|
|
565
|
+
const conn = State.getActiveConnection();
|
|
566
|
+
if (conn && conn.isActive) {
|
|
567
|
+
tweenProp(connectButton, { BackgroundColor3: C.surface });
|
|
568
|
+
tweenProp(connectStroke, { Color: Color3.fromRGB(100, 35, 35) });
|
|
569
|
+
} else {
|
|
570
|
+
tweenProp(connectButton, { BackgroundColor3: C.subtle });
|
|
571
|
+
tweenProp(connectStroke, { Color: C.muted });
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
connectButton.MouseLeave.Connect(() => {
|
|
576
|
+
buttonHover = false;
|
|
577
|
+
const conn = State.getActiveConnection();
|
|
578
|
+
if (conn && conn.isActive) {
|
|
579
|
+
setButtonDisconnect(connectButton, connectStroke);
|
|
580
|
+
} else {
|
|
581
|
+
setButtonConnect(connectButton, connectStroke);
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
elements = {
|
|
587
|
+
screenGui, mainFrame, contentFrame, statusLabel, detailStatusLabel,
|
|
588
|
+
statusIndicator, statusPulse, statusText, connectButton, connectStroke,
|
|
589
|
+
urlInput, step1Dot, step1Label, step2Dot, step2Label, step3Dot, step3Label,
|
|
590
|
+
troubleshootLabel, updateBanner, updateBannerText, tabBar,
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
refreshTabBar();
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
function updateUIState() {
|
|
597
|
+
const conn = State.getActiveConnection();
|
|
598
|
+
if (!conn) return;
|
|
599
|
+
const el = elements;
|
|
600
|
+
|
|
601
|
+
if (!conn.isActive) {
|
|
602
|
+
el.statusLabel.Text = "Disconnected";
|
|
603
|
+
el.statusLabel.TextColor3 = C.muted;
|
|
604
|
+
el.statusIndicator.BackgroundColor3 = C.red;
|
|
605
|
+
el.statusPulse.BackgroundColor3 = C.red;
|
|
606
|
+
el.statusText.Text = "OFFLINE";
|
|
607
|
+
el.detailStatusLabel.Text = "";
|
|
608
|
+
el.detailStatusLabel.TextColor3 = C.muted;
|
|
609
|
+
stopPulseAnimation();
|
|
610
|
+
|
|
611
|
+
el.step1Dot.BackgroundColor3 = C.gray;
|
|
612
|
+
el.step1Label.Text = "HTTP server";
|
|
613
|
+
el.step2Dot.BackgroundColor3 = C.gray;
|
|
614
|
+
el.step2Label.Text = "MCP bridge";
|
|
615
|
+
el.step3Dot.BackgroundColor3 = C.gray;
|
|
616
|
+
el.step3Label.Text = "Commands";
|
|
617
|
+
el.troubleshootLabel.Visible = false;
|
|
618
|
+
|
|
619
|
+
if (!buttonHover) setButtonConnect(el.connectButton, el.connectStroke);
|
|
620
|
+
el.urlInput.TextEditable = true;
|
|
621
|
+
el.urlInput.BackgroundColor3 = C.bg;
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if (!buttonHover) setButtonDisconnect(el.connectButton, el.connectStroke);
|
|
626
|
+
el.urlInput.TextEditable = false;
|
|
627
|
+
el.urlInput.BackgroundColor3 = C.card;
|
|
628
|
+
|
|
629
|
+
if (conn.lastHttpOk && conn.lastMcpOk) {
|
|
630
|
+
el.statusLabel.Text = "Connected";
|
|
631
|
+
el.statusLabel.TextColor3 = Color3.fromRGB(34, 197, 94);
|
|
632
|
+
el.statusIndicator.BackgroundColor3 = Color3.fromRGB(34, 197, 94);
|
|
633
|
+
el.statusPulse.BackgroundColor3 = Color3.fromRGB(34, 197, 94);
|
|
634
|
+
el.statusText.Text = "ONLINE";
|
|
635
|
+
el.detailStatusLabel.Text = "HTTP: OK MCP: OK";
|
|
636
|
+
el.detailStatusLabel.TextColor3 = Color3.fromRGB(34, 197, 94);
|
|
637
|
+
el.step1Dot.BackgroundColor3 = Color3.fromRGB(34, 197, 94);
|
|
638
|
+
el.step1Label.Text = "HTTP server (OK)";
|
|
639
|
+
el.step2Dot.BackgroundColor3 = Color3.fromRGB(34, 197, 94);
|
|
640
|
+
el.step2Label.Text = "MCP bridge (OK)";
|
|
641
|
+
el.step3Dot.BackgroundColor3 = Color3.fromRGB(34, 197, 94);
|
|
642
|
+
el.step3Label.Text = "Commands (OK)";
|
|
643
|
+
el.troubleshootLabel.Visible = false;
|
|
644
|
+
stopPulseAnimation();
|
|
645
|
+
} else if (conn.lastHttpOk && !conn.lastMcpOk) {
|
|
646
|
+
el.statusLabel.Text = "Waiting for MCP server";
|
|
647
|
+
el.statusLabel.TextColor3 = Color3.fromRGB(245, 158, 11);
|
|
648
|
+
el.statusIndicator.BackgroundColor3 = Color3.fromRGB(245, 158, 11);
|
|
649
|
+
el.statusPulse.BackgroundColor3 = Color3.fromRGB(245, 158, 11);
|
|
650
|
+
el.statusText.Text = "WAITING";
|
|
651
|
+
el.detailStatusLabel.Text = "HTTP: OK MCP: ...";
|
|
652
|
+
el.detailStatusLabel.TextColor3 = Color3.fromRGB(245, 158, 11);
|
|
653
|
+
el.step1Dot.BackgroundColor3 = Color3.fromRGB(34, 197, 94);
|
|
654
|
+
el.step1Label.Text = "HTTP server (OK)";
|
|
655
|
+
el.step2Dot.BackgroundColor3 = Color3.fromRGB(245, 158, 11);
|
|
656
|
+
el.step2Label.Text = "MCP bridge (waiting...)";
|
|
657
|
+
el.step3Dot.BackgroundColor3 = Color3.fromRGB(245, 158, 11);
|
|
658
|
+
el.step3Label.Text = "Commands (waiting...)";
|
|
659
|
+
const elapsed = conn.mcpWaitStartTime !== undefined ? tick() - conn.mcpWaitStartTime : 0;
|
|
660
|
+
el.troubleshootLabel.Visible = elapsed > 8;
|
|
661
|
+
startPulseAnimation();
|
|
662
|
+
} else if (conn.consecutiveFailures >= conn.maxFailuresBeforeError) {
|
|
663
|
+
el.statusLabel.Text = "Server unavailable";
|
|
664
|
+
el.statusLabel.TextColor3 = Color3.fromRGB(239, 68, 68);
|
|
665
|
+
el.statusIndicator.BackgroundColor3 = Color3.fromRGB(239, 68, 68);
|
|
666
|
+
el.statusPulse.BackgroundColor3 = Color3.fromRGB(239, 68, 68);
|
|
667
|
+
el.statusText.Text = "ERROR";
|
|
668
|
+
el.detailStatusLabel.Text = "HTTP: X MCP: X";
|
|
669
|
+
el.detailStatusLabel.TextColor3 = Color3.fromRGB(239, 68, 68);
|
|
670
|
+
el.step1Dot.BackgroundColor3 = Color3.fromRGB(239, 68, 68);
|
|
671
|
+
el.step1Label.Text = "HTTP server (error)";
|
|
672
|
+
el.step2Dot.BackgroundColor3 = Color3.fromRGB(239, 68, 68);
|
|
673
|
+
el.step2Label.Text = "MCP bridge (error)";
|
|
674
|
+
el.step3Dot.BackgroundColor3 = Color3.fromRGB(239, 68, 68);
|
|
675
|
+
el.step3Label.Text = "Commands (error)";
|
|
676
|
+
el.troubleshootLabel.Visible = false;
|
|
677
|
+
stopPulseAnimation();
|
|
678
|
+
} else if (conn.consecutiveFailures > 5) {
|
|
679
|
+
const waitTime = math.ceil(conn.currentRetryDelay);
|
|
680
|
+
el.statusLabel.Text = `Retrying (${waitTime}s)`;
|
|
681
|
+
el.statusLabel.TextColor3 = Color3.fromRGB(245, 158, 11);
|
|
682
|
+
el.statusIndicator.BackgroundColor3 = Color3.fromRGB(245, 158, 11);
|
|
683
|
+
el.statusPulse.BackgroundColor3 = Color3.fromRGB(245, 158, 11);
|
|
684
|
+
el.statusText.Text = "RETRY";
|
|
685
|
+
el.detailStatusLabel.Text = "HTTP: ... MCP: ...";
|
|
686
|
+
el.detailStatusLabel.TextColor3 = Color3.fromRGB(245, 158, 11);
|
|
687
|
+
el.step1Dot.BackgroundColor3 = Color3.fromRGB(245, 158, 11);
|
|
688
|
+
el.step1Label.Text = "HTTP server (retrying...)";
|
|
689
|
+
el.step2Dot.BackgroundColor3 = Color3.fromRGB(245, 158, 11);
|
|
690
|
+
el.step2Label.Text = "MCP bridge (retrying...)";
|
|
691
|
+
el.step3Dot.BackgroundColor3 = Color3.fromRGB(245, 158, 11);
|
|
692
|
+
el.step3Label.Text = "Commands (retrying...)";
|
|
693
|
+
el.troubleshootLabel.Visible = false;
|
|
694
|
+
startPulseAnimation();
|
|
695
|
+
} else {
|
|
696
|
+
el.statusLabel.Text = conn.consecutiveFailures > 1
|
|
697
|
+
? `Connecting (attempt ${conn.consecutiveFailures})`
|
|
698
|
+
: "Connecting...";
|
|
699
|
+
el.statusLabel.TextColor3 = C.yellow;
|
|
700
|
+
el.statusIndicator.BackgroundColor3 = C.yellow;
|
|
701
|
+
el.statusPulse.BackgroundColor3 = C.yellow;
|
|
702
|
+
el.statusText.Text = "CONNECTING";
|
|
703
|
+
el.detailStatusLabel.Text = conn.consecutiveFailures === 0 ? "..." : "HTTP: ... MCP: ...";
|
|
704
|
+
el.detailStatusLabel.TextColor3 = C.muted;
|
|
705
|
+
el.step1Dot.BackgroundColor3 = C.yellow;
|
|
706
|
+
el.step1Label.Text = "HTTP server (connecting...)";
|
|
707
|
+
el.step2Dot.BackgroundColor3 = C.yellow;
|
|
708
|
+
el.step2Label.Text = "MCP bridge (connecting...)";
|
|
709
|
+
el.step3Dot.BackgroundColor3 = C.yellow;
|
|
710
|
+
el.step3Label.Text = "Commands (connecting...)";
|
|
711
|
+
el.troubleshootLabel.Visible = false;
|
|
712
|
+
startPulseAnimation();
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
export = {
|
|
717
|
+
elements: undefined as unknown as UIElements,
|
|
718
|
+
init,
|
|
719
|
+
updateUIState,
|
|
720
|
+
updateTabDot,
|
|
721
|
+
updateTabLabel,
|
|
722
|
+
stopPulseAnimation,
|
|
723
|
+
startPulseAnimation,
|
|
724
|
+
getElements: () => elements,
|
|
725
|
+
};
|