Utility Systems
Comet includes internal systems that wrap common Roblox Studio plugin APIs. You pull them in with Dependency() from your own systems just like any other dependency.
import { Dependency, GUI, OnInit, System } from "@rbxts/comet";
@System()
export class UiSystem implements OnInit {
private gui = Dependency(GUI);
onInit() {
const widget = this.gui.createWidget("Inspector", new Vector2(320, 240), new Vector2(240, 180));
}
}These systems are internal and lazy by default, so they are only constructed when something depends on them.
GUI
GUI creates wrappers for plugin widgets, overlays, menus, and toolbar buttons.
Handles
- Creating dock widgets.
- Creating viewport overlays.
- Building plugin menus.
- Creating toolbar buttons.
Example
import { Dependency, GUI, OnInit, System } from "@rbxts/comet";
@System()
export class InspectorSystem implements OnInit {
private gui = Dependency(GUI);
onInit() {
const widget = this.gui.createWidget(
"Inspector",
new Vector2(320, 240),
new Vector2(240, 180),
Enum.InitialDockState.Right
);
const button = this.gui.createButton(
"Inspector",
"Toggle the inspector",
"rbxassetid://1234567890",
true
);
widget.linkButton(button);
}
}Notes
- Views start hidden by default. Call
setVisible(true)yourself or uselinkButton()with a toggleable button. createWidget()returns aViewfor a dock widget.createOverlay()returns aViewintended for overlay UI.buildMenu()returns aMenubuilder.createButton()returns a CometButtonwrapper.
History
History wraps ChangeHistoryService for better undo and redo boundary handling.
Handles
- Grouping changes into a single undo step.
- Canceling a failed operation.
- Appending a change to the previous history entry.
- Triggering explicit undo or redo actions.
record()
record() starts a recording and returns a control object with three possible outcomes:
commit()saves the recording.append()merges it with the previous recording when possible.cancel()aborts the recording and reverts the change.
import { Dependency, History, OnInit, System } from "@rbxts/comet";
@System()
export class EditSystem implements OnInit {
private history = Dependency(History);
onInit() {
const recording = this.history.record("Create Part");
const part = new Instance("Part");
part.Parent = workspace;
recording.commit();
}
}try()
try() wraps a change in a pcall-style flow. If the callback succeeds, Comet finishes the recording with the configured success mode. If it fails, Comet cancels the recording and rethrows the error.
import { Dependency, History, System } from "@rbxts/comet";
@System()
export class SafeEditSystem {
private history = Dependency(History);
public async duplicateSelection() {
await this.history.try(
{
name: "Duplicate Selection",
onSuccess: "append",
},
() => {
for (const instance of game.GetService("Selection").Get()) {
instance.Clone().Parent = instance.Parent;
}
}
);
}
}Notes
- If a recording is left open and a new one begins, Comet cancels the older recording and logs a warning.
try()defaults tocommitwhenonSuccessis omitted.try()only catches errors thrown during the callback itself. It does not await asynchronous work started inside the callback before finishing the recording.undo()andredo()are direct wrappers overChangeHistoryService.
Studio
Studio wraps common plugin APIs with a smaller, typed surface area.
Handles
- Creating plugin actions.
- Reading or changing Studio selection.
- Listening for selection changes.
- Prompting for existing assets.
- Saving the current selection.
- Opening scripts or selecting ribbon tools.
Example
import { Dependency, Studio, System } from "@rbxts/comet";
@System()
export class SelectionWatcherSystem {
private studio = Dependency(Studio);
constructor() {
this.studio.onSelectionChanged((selection) => {
print(`Selected ${selection.size()} instances`);
});
}
}Notes
studio.pluginexposes the underlyingPlugininstance.saveSelection(true)usesPlugin.SaveSelectedToRoblox().saveSelection(false, name)usesPlugin.PromptSaveSelection().onSelectionChanged()tracks its connection automatically, so Comet cleans it up on plugin unload.
Audio
Audio helps with plugin sound playback. It uses GUI internally to host Sound instances inside a view container.
Handles
- Preloading UI sounds.
- Reusing cached sounds.
- Playing one-off sounds without managing cleanup manually.
Example
import { Audio, Dependency, OnInit, System } from "@rbxts/comet";
@System()
export class SoundSystem implements OnInit {
private audio = Dependency(Audio);
onInit() {
this.audio.preloadSounds([
"rbxassetid://1234567890",
"rbxassetid://0987654321",
]);
this.audio.play("rbxassetid://1234567890");
}
}Notes
play()reuses cached sounds and restarts them if needed.playUnique()creates a temporarySound, plays it, and schedules cleanup through Comet's tracker.preloadSounds()warms the cache before playback.
Choosing the right utility system
- Use
GUIfor plugin UI. - Use
Historyfor undo and redo boundaries. - Use
Studiofor plugin-facing Studio APIs. - Use
Audiofor plugin sound playback.
For lifecycle behavior, dependency rules, and system registration, see Systems.