@el4cteo/rbx-studio-mcp 0.3.0 → 0.3.5
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/README.md +20 -26
- package/dist/index.js +1 -1
- package/dist/lib/format.js +18 -2
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/pluginbuild.js +23 -13
- package/dist/lib/pluginbuild.js.map +1 -1
- package/dist/tools/api.js +20 -1
- package/dist/tools/api.js.map +1 -1
- package/dist/tools/debug.js +18 -8
- package/dist/tools/debug.js.map +1 -1
- package/dist/tools/discover.js +5 -0
- package/dist/tools/discover.js.map +1 -1
- package/dist/tools/instances.js +49 -2
- package/dist/tools/instances.js.map +1 -1
- package/dist/tools/perf.js +11 -1
- package/dist/tools/perf.js.map +1 -1
- package/dist/tools/scripts.js +27 -3
- package/dist/tools/scripts.js.map +1 -1
- package/dist/tools/session.js +16 -0
- package/dist/tools/session.js.map +1 -1
- package/package.json +1 -1
- package/plugin/src/Config.luau +1 -1
- package/plugin/src/Console.luau +257 -116
- package/plugin/src/Paths.luau +138 -69
- package/plugin/src/Serialize.luau +43 -3
- package/plugin/src/ThemePicker.luau +458 -0
- package/plugin/src/Themes/Aurora.luau +147 -0
- package/plugin/src/Themes/Blueprint.luau +213 -0
- package/plugin/src/Themes/Draw.luau +177 -0
- package/plugin/src/Themes/Lattice.luau +304 -0
- package/plugin/src/Themes/Nebula.luau +182 -0
- package/plugin/src/Themes/Observatory.luau +200 -0
- package/plugin/src/Themes/Orbit.luau +268 -0
- package/plugin/src/Themes/Phosphor.luau +200 -0
- package/plugin/src/Themes/Theme.luau +121 -0
- package/plugin/src/Themes/Void.luau +230 -0
- package/plugin/src/Themes/init.luau +116 -0
- package/plugin/src/Visuals.luau +788 -907
- package/plugin/src/handlers/Discover.luau +75 -1
- package/plugin/src/handlers/Exec.luau +17 -5
- package/plugin/src/handlers/Instances.luau +35 -3
- package/plugin/src/handlers/Scripts.luau +58 -2
- package/plugin/src/init.server.luau +386 -354
package/plugin/src/Console.luau
CHANGED
|
@@ -18,39 +18,50 @@
|
|
|
18
18
|
Instances, and colour spans do the same job in a fraction of the code.
|
|
19
19
|
]]
|
|
20
20
|
|
|
21
|
+
local ThemePicker = require(script.Parent.ThemePicker)
|
|
22
|
+
local Themes = require(script.Parent.Themes)
|
|
21
23
|
local Visuals = require(script.Parent.Visuals)
|
|
22
24
|
|
|
23
25
|
local Console = {}
|
|
24
26
|
|
|
25
|
-
--
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
27
|
+
--[[
|
|
28
|
+
Ring buffer bound, counted in logged rows rather than in rendered lines.
|
|
29
|
+
|
|
30
|
+
It used to bound the rendered lines, which meant a burst of failures -- each
|
|
31
|
+
writing a message plus two wrapped lines of explanation -- evicted three
|
|
32
|
+
times as much history as a burst of successes. Bounding the records makes
|
|
33
|
+
"the last three hundred things that happened" mean what it says.
|
|
34
|
+
]]
|
|
35
|
+
local MAX_RECORDS = 300
|
|
36
|
+
|
|
37
|
+
--[[
|
|
38
|
+
The active preset's colours, rebound rather than re-read.
|
|
39
|
+
|
|
40
|
+
Every use site in this file is a plain `PALETTE.dim`, and there are about
|
|
41
|
+
forty of them. Turning each into a function call to satisfy theming would
|
|
42
|
+
have been forty edits to working code for no gain: the palette changes only
|
|
43
|
+
when the user picks a different preset, so it is simply reassigned there and
|
|
44
|
+
the reads stay as they were.
|
|
45
|
+
|
|
46
|
+
The catch is that anything which COPIES a colour out of here keeps the old
|
|
47
|
+
one. Instances built in `mount` do exactly that, which is why `applyTheme`
|
|
48
|
+
has to walk them by hand.
|
|
49
|
+
]]
|
|
50
|
+
local PALETTE: Themes.Palette = Themes.palette()
|
|
43
51
|
|
|
44
52
|
-- Sigil plus colour per level. The sigil is what makes the log scannable at a
|
|
45
53
|
-- glance; colour alone fails for anyone who cannot separate red from green.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
-- Named by palette key rather than by colour, because this table is built once
|
|
55
|
+
-- at load and a colour captured then belongs to whichever preset happened to
|
|
56
|
+
-- be active at the time.
|
|
57
|
+
local LEVELS: { [string]: { sigil: string, key: string } } = {
|
|
58
|
+
ok = { sigil = "\u{25C6}", key = "green" },
|
|
59
|
+
error = { sigil = "\u{2715}", key = "red" },
|
|
60
|
+
warn = { sigil = "\u{25B2}", key = "amber" },
|
|
61
|
+
info = { sigil = "\u{25C6}", key = "violet" },
|
|
62
|
+
dim = { sigil = "\u{00B7}", key = "dim" },
|
|
63
|
+
call = { sigil = "\u{25B8}", key = "cyan" },
|
|
64
|
+
reply = { sigil = "\u{25C2}", key = "violet" },
|
|
54
65
|
}
|
|
55
66
|
|
|
56
67
|
export type Level = "ok" | "error" | "warn" | "info" | "dim" | "call" | "reply"
|
|
@@ -105,8 +116,28 @@ local function wrapDetail(detail: string): { string }
|
|
|
105
116
|
return lines
|
|
106
117
|
end
|
|
107
118
|
|
|
119
|
+
--[[
|
|
120
|
+
One logged row, before it is coloured.
|
|
121
|
+
|
|
122
|
+
Holding the parts rather than the finished string is what lets a theme
|
|
123
|
+
switch repaint history: see `renderRecord`.
|
|
124
|
+
]]
|
|
125
|
+
type Record = {
|
|
126
|
+
level: Level,
|
|
127
|
+
message: string,
|
|
128
|
+
detail: string?,
|
|
129
|
+
stamp: string,
|
|
130
|
+
}
|
|
131
|
+
|
|
108
132
|
type State = {
|
|
109
|
-
|
|
133
|
+
records: { Record },
|
|
134
|
+
-- The last status reported, replayed after a theme switch. Without it the
|
|
135
|
+
-- header has no way to know what colour it should be wearing.
|
|
136
|
+
status: string,
|
|
137
|
+
statusMeta: string,
|
|
138
|
+
-- The accent rule's gradient. A ColorSequence rather than a Color3, so it
|
|
139
|
+
-- cannot ride the generic role list.
|
|
140
|
+
ruleFade: UIGradient?,
|
|
110
141
|
--[[
|
|
111
142
|
Rows written, as opposed to lines rendered.
|
|
112
143
|
|
|
@@ -144,7 +175,10 @@ type State = {
|
|
|
144
175
|
}
|
|
145
176
|
|
|
146
177
|
local state: State = {
|
|
147
|
-
|
|
178
|
+
records = {},
|
|
179
|
+
ruleFade = nil,
|
|
180
|
+
status = "disconnected",
|
|
181
|
+
statusMeta = "",
|
|
148
182
|
entries = 0,
|
|
149
183
|
label = nil,
|
|
150
184
|
scroller = nil,
|
|
@@ -189,13 +223,89 @@ local function span(color: Color3, value: string): string
|
|
|
189
223
|
return string.format('<font color="%s">%s</font>', hex(color), escape(value))
|
|
190
224
|
end
|
|
191
225
|
|
|
226
|
+
--[[
|
|
227
|
+
Turns one record into the lines it occupies.
|
|
228
|
+
|
|
229
|
+
Rendering is deferred to here rather than done when the row is logged, which
|
|
230
|
+
is the change that made theming possible at all: a line that has already had
|
|
231
|
+
`#A78BFA` baked into it cannot be recoloured, so switching preset used to
|
|
232
|
+
leave the whole session's history in the previous theme's palette while new
|
|
233
|
+
rows arrived in the new one. Now nothing is coloured until it is painted.
|
|
234
|
+
]]
|
|
235
|
+
local function renderRecord(record: Record): { string }
|
|
236
|
+
local spec = LEVELS[record.level] or LEVELS.info
|
|
237
|
+
local detail = record.detail
|
|
238
|
+
|
|
239
|
+
--[[
|
|
240
|
+
Measured in characters, not bytes.
|
|
241
|
+
|
|
242
|
+
`#body` counts bytes, and every sigil in this console is a 3-byte UTF-8
|
|
243
|
+
glyph, so it over-counted each line by two and pushed the latency column
|
|
244
|
+
two places left on exactly the lines that had a latency. The column was
|
|
245
|
+
never straight, and the cause was invisible until two different sigils
|
|
246
|
+
sat next to each other.
|
|
247
|
+
]]
|
|
248
|
+
--[[
|
|
249
|
+
Only a detail that will actually sit in the column costs the message any
|
|
250
|
+
of its width. Cutting the message to make room for a detail that then
|
|
251
|
+
goes on its own line below would shorten it for nothing.
|
|
252
|
+
]]
|
|
253
|
+
local detailWidth = if detail then (utf8.len(detail) or #detail) else 0
|
|
254
|
+
local inlineDetail = detail ~= nil
|
|
255
|
+
and (utf8.len(record.message) or #record.message) + detailWidth + 3
|
|
256
|
+
<= DETAIL_COLUMN + INLINE_DETAIL
|
|
257
|
+
|
|
258
|
+
local trimmed = record.message
|
|
259
|
+
local budget = DETAIL_COLUMN - 3
|
|
260
|
+
if inlineDetail and (utf8.len(trimmed) or #trimmed) > budget then
|
|
261
|
+
-- Cut rather than wrap. A wrapped line destroys the alignment and
|
|
262
|
+
-- carries the detail off the end of the visible width as well.
|
|
263
|
+
local offset = utf8.offset(trimmed, budget) or budget
|
|
264
|
+
trimmed = string.sub(trimmed, 1, offset - 1) .. utf8.char(0x2026)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
local body = string.format("%s %s", spec.sigil, trimmed)
|
|
268
|
+
local bodyWidth = utf8.len(body) or #body
|
|
269
|
+
local lines = { span(PALETTE.dim, record.stamp) .. " " .. span(PALETTE[spec.key], body) }
|
|
270
|
+
|
|
271
|
+
--[[
|
|
272
|
+
Short details ride the right-hand column; long ones get their own lines.
|
|
273
|
+
|
|
274
|
+
The column exists for latencies -- "12ms" stacking into something
|
|
275
|
+
readable -- and it was applied to every detail regardless of length. A
|
|
276
|
+
sentence of prose therefore started at column 52, ran off the widget, and
|
|
277
|
+
wrapped back to column 0, so the explanation of a standby session came
|
|
278
|
+
out as a ragged block that began in the middle of the screen and ended at
|
|
279
|
+
the left edge. Anything that will not fit beside the message is better
|
|
280
|
+
off beneath it.
|
|
281
|
+
]]
|
|
282
|
+
if detail then
|
|
283
|
+
if inlineDetail then
|
|
284
|
+
local padding = math.max(1, DETAIL_COLUMN - bodyWidth)
|
|
285
|
+
lines[1] ..= span(PALETTE.dim, string.rep(" ", padding) .. detail)
|
|
286
|
+
else
|
|
287
|
+
for _, wrapped in wrapDetail(detail) do
|
|
288
|
+
table.insert(lines, span(PALETTE.dim, DETAIL_INDENT .. wrapped))
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
return lines
|
|
293
|
+
end
|
|
294
|
+
|
|
192
295
|
local function paint()
|
|
193
296
|
state.dirty = false
|
|
194
297
|
local label = state.label
|
|
195
298
|
if not label then
|
|
196
299
|
return
|
|
197
300
|
end
|
|
198
|
-
|
|
301
|
+
|
|
302
|
+
local lines: { string } = {}
|
|
303
|
+
for _, record in state.records do
|
|
304
|
+
for _, line in renderRecord(record) do
|
|
305
|
+
table.insert(lines, line)
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
label.Text = table.concat(lines, "\n")
|
|
199
309
|
|
|
200
310
|
-- Only follow the tail when the user has not scrolled up to read history.
|
|
201
311
|
local scroller = state.scroller
|
|
@@ -261,64 +371,18 @@ end
|
|
|
261
371
|
ragged offset.
|
|
262
372
|
]]
|
|
263
373
|
function Console.log(level: Level, message: string, detail: string?)
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
sat next to each other.
|
|
274
|
-
]]
|
|
275
|
-
--[[
|
|
276
|
-
Only a detail that will actually sit in the column costs the message any
|
|
277
|
-
of its width. Cutting the message to make room for a detail that then
|
|
278
|
-
goes on its own line below would shorten it for nothing.
|
|
279
|
-
]]
|
|
280
|
-
local detailWidth = if detail then (utf8.len(detail) or #detail) else 0
|
|
281
|
-
local inlineDetail = detail ~= nil
|
|
282
|
-
and (utf8.len(message) or #message) + detailWidth + 3 <= DETAIL_COLUMN + INLINE_DETAIL
|
|
283
|
-
|
|
284
|
-
local trimmed = message
|
|
285
|
-
local budget = DETAIL_COLUMN - 3
|
|
286
|
-
if inlineDetail and (utf8.len(trimmed) or #trimmed) > budget then
|
|
287
|
-
-- Cut rather than wrap. A wrapped line destroys the alignment and
|
|
288
|
-
-- carries the detail off the end of the visible width as well.
|
|
289
|
-
local offset = utf8.offset(trimmed, budget) or budget
|
|
290
|
-
trimmed = string.sub(trimmed, 1, offset - 1) .. utf8.char(0x2026)
|
|
291
|
-
end
|
|
292
|
-
|
|
293
|
-
local body = string.format("%s %s", spec.sigil, trimmed)
|
|
294
|
-
local bodyWidth = utf8.len(body) or #body
|
|
295
|
-
local line = span(PALETTE.dim, os.date("%H:%M:%S") :: string) .. " " .. span(spec.color, body)
|
|
296
|
-
|
|
297
|
-
--[[
|
|
298
|
-
Short details ride the right-hand column; long ones get their own lines.
|
|
299
|
-
|
|
300
|
-
The column exists for latencies -- "12ms" stacking into something
|
|
301
|
-
readable -- and it was applied to every detail regardless of length. A
|
|
302
|
-
sentence of prose therefore started at column 52, ran off the widget, and
|
|
303
|
-
wrapped back to column 0, so the explanation of a standby session came
|
|
304
|
-
out as a ragged block that began in the middle of the screen and ended at
|
|
305
|
-
the left edge. Anything that will not fit beside the message is better
|
|
306
|
-
off beneath it.
|
|
307
|
-
]]
|
|
308
|
-
table.insert(state.lines, line)
|
|
374
|
+
table.insert(state.records, {
|
|
375
|
+
level = level,
|
|
376
|
+
message = message,
|
|
377
|
+
detail = detail,
|
|
378
|
+
-- Stamped when the row happened, not when it is painted. A repaint after
|
|
379
|
+
-- a theme switch re-renders every line, and re-reading the clock there
|
|
380
|
+
-- would restamp the whole session to the moment the user changed colour.
|
|
381
|
+
stamp = os.date("%H:%M:%S") :: string,
|
|
382
|
+
})
|
|
309
383
|
state.entries += 1
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
local padding = math.max(1, DETAIL_COLUMN - bodyWidth)
|
|
313
|
-
state.lines[#state.lines] ..= span(PALETTE.dim, string.rep(" ", padding) .. detail)
|
|
314
|
-
else
|
|
315
|
-
for _, wrapped in wrapDetail(detail) do
|
|
316
|
-
table.insert(state.lines, span(PALETTE.dim, DETAIL_INDENT .. wrapped))
|
|
317
|
-
end
|
|
318
|
-
end
|
|
319
|
-
end
|
|
320
|
-
while #state.lines > MAX_LINES do
|
|
321
|
-
table.remove(state.lines, 1)
|
|
384
|
+
while #state.records > MAX_RECORDS do
|
|
385
|
+
table.remove(state.records, 1)
|
|
322
386
|
end
|
|
323
387
|
redraw()
|
|
324
388
|
end
|
|
@@ -344,11 +408,11 @@ end
|
|
|
344
408
|
and heavier still. Urgency drives both the spin rate and how strongly the
|
|
345
409
|
colour takes over, so the two never disagree.
|
|
346
410
|
]]
|
|
347
|
-
local KIND_LOOK: { [string]: {
|
|
348
|
-
read = {
|
|
349
|
-
write = {
|
|
350
|
-
run = {
|
|
351
|
-
debug = {
|
|
411
|
+
local KIND_LOOK: { [string]: { key: string, urgency: number } } = {
|
|
412
|
+
read = { key = "cyan", urgency = 0.3 },
|
|
413
|
+
write = { key = "violet", urgency = 0.7 },
|
|
414
|
+
run = { key = "green", urgency = 0.9 },
|
|
415
|
+
debug = { key = "amber", urgency = 0.5 },
|
|
352
416
|
}
|
|
353
417
|
|
|
354
418
|
function Console.beginCall(title: string, kind: string)
|
|
@@ -356,7 +420,7 @@ function Console.beginCall(title: string, kind: string)
|
|
|
356
420
|
state.generation += 1
|
|
357
421
|
local look = KIND_LOOK[kind] or KIND_LOOK.read
|
|
358
422
|
Visuals.setCaption(title)
|
|
359
|
-
Visuals.setKind(look.
|
|
423
|
+
Visuals.setKind(PALETTE[look.key], look.urgency)
|
|
360
424
|
refreshCounters()
|
|
361
425
|
end
|
|
362
426
|
|
|
@@ -499,7 +563,7 @@ function Console.clear()
|
|
|
499
563
|
the one that went.
|
|
500
564
|
]]
|
|
501
565
|
local cleared = state.entries
|
|
502
|
-
table.clear(state.
|
|
566
|
+
table.clear(state.records)
|
|
503
567
|
state.entries = 0
|
|
504
568
|
state.calls = 0
|
|
505
569
|
state.errors = 0
|
|
@@ -524,6 +588,9 @@ end
|
|
|
524
588
|
visible without scrolling, however long the session has run.
|
|
525
589
|
]]
|
|
526
590
|
function Console.setStatus(status: string, meta: string)
|
|
591
|
+
state.status = status
|
|
592
|
+
state.statusMeta = meta
|
|
593
|
+
|
|
527
594
|
local dot = state.statusDot
|
|
528
595
|
local text = state.statusText
|
|
529
596
|
local metaLabel = state.metaText
|
|
@@ -550,14 +617,37 @@ function Console.setStatus(status: string, meta: string)
|
|
|
550
617
|
Visuals.setTint(color)
|
|
551
618
|
end
|
|
552
619
|
|
|
620
|
+
--[[
|
|
621
|
+
Records which palette colour an Instance is wearing, and puts it on.
|
|
622
|
+
|
|
623
|
+
The console builds about thirty Instances and copies a colour into each. A
|
|
624
|
+
copy does not follow the palette when the user picks a different preset, so
|
|
625
|
+
the panel would keep the old theme's header, rule, scrollbar and buttons
|
|
626
|
+
while the log underneath repainted -- which looks less like a theme than
|
|
627
|
+
like a half-finished render.
|
|
628
|
+
|
|
629
|
+
Rather than keep thirty named references and re-set thirty properties by
|
|
630
|
+
hand, each one declares the property and palette key it is wearing when it
|
|
631
|
+
is built, and `Console.applyTheme` walks the list. Adding a widget therefore
|
|
632
|
+
cannot forget to theme it: the same call that colours it registers it.
|
|
633
|
+
]]
|
|
634
|
+
type Role = { instance: Instance, property: string, key: string }
|
|
635
|
+
local roles: { Role } = {}
|
|
636
|
+
|
|
637
|
+
local function themed<T>(instance: T & Instance, property: string, key: string): T
|
|
638
|
+
table.insert(roles, { instance = instance, property = property, key = key })
|
|
639
|
+
;(instance :: any)[property] = PALETTE[key]
|
|
640
|
+
return instance
|
|
641
|
+
end
|
|
642
|
+
|
|
553
643
|
local function makeButton(parent: Instance, text: string, order: number): TextButton
|
|
554
644
|
local button = Instance.new("TextButton")
|
|
555
645
|
button.Name = text
|
|
556
646
|
button.Text = text
|
|
557
647
|
button.Font = Enum.Font.Code
|
|
558
648
|
button.TextSize = 11
|
|
559
|
-
button
|
|
560
|
-
button
|
|
649
|
+
themed(button, "TextColor3", "dim")
|
|
650
|
+
themed(button, "BackgroundColor3", "background")
|
|
561
651
|
button.AutoButtonColor = false
|
|
562
652
|
button.BorderSizePixel = 0
|
|
563
653
|
button.Size = UDim2.new(0, 76, 0, 20)
|
|
@@ -569,7 +659,7 @@ local function makeButton(parent: Instance, text: string, order: number): TextBu
|
|
|
569
659
|
corner.Parent = button
|
|
570
660
|
|
|
571
661
|
local stroke = Instance.new("UIStroke")
|
|
572
|
-
stroke
|
|
662
|
+
themed(stroke, "Color", "dim")
|
|
573
663
|
stroke.Transparency = 0.6
|
|
574
664
|
stroke.Parent = button
|
|
575
665
|
|
|
@@ -590,6 +680,10 @@ end
|
|
|
590
680
|
export type Handlers = {
|
|
591
681
|
onReconnect: () -> (),
|
|
592
682
|
onClear: () -> (),
|
|
683
|
+
-- Called after a preset switch has already been applied, so the caller's
|
|
684
|
+
-- only job is to remember it. Persistence lives there because
|
|
685
|
+
-- `plugin:SetSetting` is not reachable from a ModuleScript.
|
|
686
|
+
onTheme: (string) -> (),
|
|
593
687
|
}
|
|
594
688
|
|
|
595
689
|
--[[
|
|
@@ -599,14 +693,14 @@ export type Handlers = {
|
|
|
599
693
|
function Console.mount(parent: Instance, handlers: Handlers)
|
|
600
694
|
local root = Instance.new("Frame")
|
|
601
695
|
root.Size = UDim2.fromScale(1, 1)
|
|
602
|
-
root
|
|
696
|
+
themed(root, "BackgroundColor3", "background")
|
|
603
697
|
root.BorderSizePixel = 0
|
|
604
698
|
root.Parent = parent
|
|
605
699
|
|
|
606
700
|
-- Header ---------------------------------------------------------------
|
|
607
701
|
local header = Instance.new("Frame")
|
|
608
702
|
header.Size = UDim2.new(1, 0, 0, 32)
|
|
609
|
-
header
|
|
703
|
+
themed(header, "BackgroundColor3", "surface")
|
|
610
704
|
header.BorderSizePixel = 0
|
|
611
705
|
header.Parent = root
|
|
612
706
|
|
|
@@ -619,6 +713,10 @@ function Console.mount(parent: Instance, handlers: Handlers)
|
|
|
619
713
|
dot.Text = "\u{25CF}"
|
|
620
714
|
dot.Font = Enum.Font.Code
|
|
621
715
|
dot.TextSize = 13
|
|
716
|
+
-- Deliberately NOT registered with `themed`. Red here is the value it
|
|
717
|
+
-- STARTS at, not the colour it wears: what it should be is whatever
|
|
718
|
+
-- `setStatus` last reported. Registering it meant every theme switch
|
|
719
|
+
-- repainted a connected session red.
|
|
622
720
|
dot.TextColor3 = PALETTE.red
|
|
623
721
|
dot.BackgroundTransparency = 1
|
|
624
722
|
dot.Size = UDim2.new(0, 12, 1, 0)
|
|
@@ -641,7 +739,7 @@ function Console.mount(parent: Instance, handlers: Handlers)
|
|
|
641
739
|
meta.Text = ""
|
|
642
740
|
meta.Font = Enum.Font.Code
|
|
643
741
|
meta.TextSize = 11
|
|
644
|
-
meta
|
|
742
|
+
themed(meta, "TextColor3", "dim")
|
|
645
743
|
meta.TextXAlignment = Enum.TextXAlignment.Left
|
|
646
744
|
meta.TextTruncate = Enum.TextTruncate.AtEnd
|
|
647
745
|
meta.BackgroundTransparency = 1
|
|
@@ -663,11 +761,11 @@ function Console.mount(parent: Instance, handlers: Handlers)
|
|
|
663
761
|
clientsChip.AnchorPoint = Vector2.new(1, 0.5)
|
|
664
762
|
clientsChip.Position = UDim2.new(1, -252, 0.5, 0)
|
|
665
763
|
clientsChip.Size = UDim2.new(0, 84, 0, 18)
|
|
666
|
-
clientsChip
|
|
764
|
+
themed(clientsChip, "BackgroundColor3", "background")
|
|
667
765
|
clientsChip.BorderSizePixel = 0
|
|
668
766
|
clientsChip.Font = Enum.Font.Code
|
|
669
767
|
clientsChip.TextSize = 11
|
|
670
|
-
clientsChip
|
|
768
|
+
themed(clientsChip, "TextColor3", "cyan")
|
|
671
769
|
clientsChip.Text = ""
|
|
672
770
|
clientsChip.Visible = false
|
|
673
771
|
clientsChip.Parent = header
|
|
@@ -678,7 +776,7 @@ function Console.mount(parent: Instance, handlers: Handlers)
|
|
|
678
776
|
chipCorner.Parent = clientsChip
|
|
679
777
|
|
|
680
778
|
local chipStroke = Instance.new("UIStroke")
|
|
681
|
-
chipStroke
|
|
779
|
+
themed(chipStroke, "Color", "cyan")
|
|
682
780
|
chipStroke.Transparency = 0.6
|
|
683
781
|
chipStroke.Parent = clientsChip
|
|
684
782
|
|
|
@@ -706,12 +804,13 @@ function Console.mount(parent: Instance, handlers: Handlers)
|
|
|
706
804
|
local rule = Instance.new("Frame")
|
|
707
805
|
rule.Position = UDim2.new(0, 0, 0, 32)
|
|
708
806
|
rule.Size = UDim2.new(1, 0, 0, 1)
|
|
709
|
-
rule
|
|
807
|
+
themed(rule, "BackgroundColor3", "rule")
|
|
710
808
|
rule.BorderSizePixel = 0
|
|
711
809
|
rule.Parent = root
|
|
712
810
|
|
|
713
811
|
local ruleFade = Instance.new("UIGradient")
|
|
714
812
|
ruleFade.Color = ColorSequence.new(PALETTE.rule)
|
|
813
|
+
state.ruleFade = ruleFade
|
|
715
814
|
ruleFade.Transparency = NumberSequence.new({
|
|
716
815
|
NumberSequenceKeypoint.new(0, 0.15),
|
|
717
816
|
NumberSequenceKeypoint.new(1, 0.85),
|
|
@@ -725,7 +824,7 @@ function Console.mount(parent: Instance, handlers: Handlers)
|
|
|
725
824
|
scroller.BackgroundTransparency = 1
|
|
726
825
|
scroller.BorderSizePixel = 0
|
|
727
826
|
scroller.ScrollBarThickness = 5
|
|
728
|
-
scroller
|
|
827
|
+
themed(scroller, "ScrollBarImageColor3", "violet")
|
|
729
828
|
scroller.ScrollBarImageTransparency = 0.5
|
|
730
829
|
scroller.CanvasSize = UDim2.new()
|
|
731
830
|
scroller.AutomaticCanvasSize = Enum.AutomaticSize.Y
|
|
@@ -747,7 +846,7 @@ function Console.mount(parent: Instance, handlers: Handlers)
|
|
|
747
846
|
label.Font = Enum.Font.Code
|
|
748
847
|
label.TextSize = 12
|
|
749
848
|
label.LineHeight = 1.25
|
|
750
|
-
label
|
|
849
|
+
themed(label, "TextColor3", "text")
|
|
751
850
|
label.RichText = true
|
|
752
851
|
label.TextWrapped = true
|
|
753
852
|
label.TextXAlignment = Enum.TextXAlignment.Left
|
|
@@ -761,7 +860,7 @@ function Console.mount(parent: Instance, handlers: Handlers)
|
|
|
761
860
|
footer.AnchorPoint = Vector2.new(0, 1)
|
|
762
861
|
footer.Position = UDim2.fromScale(0, 1)
|
|
763
862
|
footer.Size = UDim2.new(1, 0, 0, 22)
|
|
764
|
-
footer
|
|
863
|
+
themed(footer, "BackgroundColor3", "surface")
|
|
765
864
|
footer.BorderSizePixel = 0
|
|
766
865
|
footer.Parent = root
|
|
767
866
|
|
|
@@ -774,7 +873,7 @@ function Console.mount(parent: Instance, handlers: Handlers)
|
|
|
774
873
|
prompt.Text = "rbx\u{00B7}studio"
|
|
775
874
|
prompt.Font = Enum.Font.Code
|
|
776
875
|
prompt.TextSize = 11
|
|
777
|
-
prompt
|
|
876
|
+
themed(prompt, "TextColor3", "violet")
|
|
778
877
|
prompt.TextXAlignment = Enum.TextXAlignment.Left
|
|
779
878
|
prompt.BackgroundTransparency = 1
|
|
780
879
|
prompt.Size = UDim2.new(0, 70, 1, 0)
|
|
@@ -793,7 +892,7 @@ function Console.mount(parent: Instance, handlers: Handlers)
|
|
|
793
892
|
counters.Text = "idle"
|
|
794
893
|
counters.Font = Enum.Font.Code
|
|
795
894
|
counters.TextSize = 11
|
|
796
|
-
counters
|
|
895
|
+
themed(counters, "TextColor3", "dim")
|
|
797
896
|
counters.TextXAlignment = Enum.TextXAlignment.Right
|
|
798
897
|
counters.BackgroundTransparency = 1
|
|
799
898
|
counters.AnchorPoint = Vector2.new(1, 0)
|
|
@@ -810,14 +909,7 @@ function Console.mount(parent: Instance, handlers: Handlers)
|
|
|
810
909
|
costs legibility is a bad trade however good it looks, and this is a
|
|
811
910
|
console before it is anything else.
|
|
812
911
|
]]
|
|
813
|
-
Visuals.mount(root
|
|
814
|
-
background = PALETTE.background,
|
|
815
|
-
surface = PALETTE.surface,
|
|
816
|
-
text = PALETTE.text,
|
|
817
|
-
dim = PALETTE.dim,
|
|
818
|
-
violet = PALETTE.violet,
|
|
819
|
-
red = PALETTE.red,
|
|
820
|
-
})
|
|
912
|
+
Visuals.mount(root)
|
|
821
913
|
local band = root:FindFirstChild("ActivityBand") :: Frame
|
|
822
914
|
band.Position = UDim2.new(0, 0, 0, 33)
|
|
823
915
|
band.Size = UDim2.new(1, 0, 0, Visuals.BAND_HEIGHT)
|
|
@@ -846,6 +938,20 @@ function Console.mount(parent: Instance, handlers: Handlers)
|
|
|
846
938
|
visualsButton.Text = if Visuals.isVisible() then "\u{25C6} visuals" else "visuals"
|
|
847
939
|
end)
|
|
848
940
|
|
|
941
|
+
--[[
|
|
942
|
+
The preset drawer, mounted last so its tab sits over everything.
|
|
943
|
+
|
|
944
|
+
It has to be the last child of `root` as well as the highest ZIndex: the
|
|
945
|
+
band, the log and the footer are all built before it and a drawer that
|
|
946
|
+
opens behind the log is a drawer nobody can click.
|
|
947
|
+
]]
|
|
948
|
+
ThemePicker.mount(root, function(id)
|
|
949
|
+
Console.applyTheme()
|
|
950
|
+
ThemePicker.applyTheme()
|
|
951
|
+
layoutLog()
|
|
952
|
+
handlers.onTheme(id)
|
|
953
|
+
end)
|
|
954
|
+
|
|
849
955
|
-- Treat "scrolled away from the bottom" as the user reading history, and
|
|
850
956
|
-- stop yanking the view down under them until they scroll back.
|
|
851
957
|
scroller:GetPropertyChangedSignal("CanvasPosition"):Connect(function()
|
|
@@ -854,4 +960,39 @@ function Console.mount(parent: Instance, handlers: Handlers)
|
|
|
854
960
|
end)
|
|
855
961
|
end
|
|
856
962
|
|
|
963
|
+
--[[
|
|
964
|
+
Switches the whole panel to the active preset.
|
|
965
|
+
|
|
966
|
+
Three things have to move together or the switch looks broken: the palette
|
|
967
|
+
this file reads, the Instances that copied a colour out of it, and the log,
|
|
968
|
+
whose rows are re-rendered from records rather than recoloured in place. The
|
|
969
|
+
band goes last because remounting a preset is the expensive part and there
|
|
970
|
+
is no reason to make the text wait for it.
|
|
971
|
+
]]
|
|
972
|
+
function Console.applyTheme()
|
|
973
|
+
PALETTE = Themes.palette()
|
|
974
|
+
|
|
975
|
+
for _, role in roles do
|
|
976
|
+
-- A widget can outlive its registration if the panel is rebuilt, and
|
|
977
|
+
-- writing to a destroyed Instance throws.
|
|
978
|
+
if role.instance.Parent ~= nil or role.instance:IsA("UIStroke") then
|
|
979
|
+
pcall(function()
|
|
980
|
+
(role.instance :: any)[role.property] = PALETTE[role.key]
|
|
981
|
+
end)
|
|
982
|
+
end
|
|
983
|
+
end
|
|
984
|
+
|
|
985
|
+
local ruleFade = state.ruleFade
|
|
986
|
+
if ruleFade then
|
|
987
|
+
ruleFade.Color = ColorSequence.new(PALETTE.rule)
|
|
988
|
+
end
|
|
989
|
+
|
|
990
|
+
refreshCounters()
|
|
991
|
+
-- Replayed rather than recoloured: the status light's colour is a fact about
|
|
992
|
+
-- the connection, and the only thing that knows it is `setStatus`.
|
|
993
|
+
Console.setStatus(state.status, state.statusMeta)
|
|
994
|
+
redraw()
|
|
995
|
+
Visuals.applyTheme()
|
|
996
|
+
end
|
|
997
|
+
|
|
857
998
|
return Console
|