Skip to content

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.

ts
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

ts
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 use linkButton() with a toggleable button.
  • createWidget() returns a View for a dock widget.
  • createOverlay() returns a View intended for overlay UI.
  • buildMenu() returns a Menu builder.
  • createButton() returns a Comet Button wrapper.

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.
ts
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.

ts
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 to commit when onSuccess is 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() and redo() are direct wrappers over ChangeHistoryService.

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

ts
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.plugin exposes the underlying Plugin instance.
  • saveSelection(true) uses Plugin.SaveSelectedToRoblox().
  • saveSelection(false, name) uses Plugin.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

ts
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 temporary Sound, plays it, and schedules cleanup through Comet's tracker.
  • preloadSounds() warms the cache before playback.

Choosing the right utility system

  • Use GUI for plugin UI.
  • Use History for undo and redo boundaries.
  • Use Studio for plugin-facing Studio APIs.
  • Use Audio for plugin sound playback.

For lifecycle behavior, dependency rules, and system registration, see Systems.