Use built-in controls first
If a menu, dialog, sprite, or node property can express the behavior, prefer that.
Extensions are for advanced projects that need code without modifying the core runtime.
Most projects should start with Properties fields and built-in nodes. Extensions are for the cases where your story needs behavior that would be too specific to build into the plugin for everyone.
VNC exposes a small, documented shape for user-authored modules. Instead of editing the runtime directly, you point the story to extension modules that match that shape.
The current supported seam is runtime button behavior. The conventional module path is:
ReplicatedStorage.VisualNovelCreatorExtensions.Client.ButtonsUse Runtime Menu > Button Extensions > Create Module in Properties to scaffold it, then enable extensions from plugin settings when the project intentionally uses trusted code.
Use built-in controls first
If a menu, dialog, sprite, or node property can express the behavior, prefer that.
Use extensions for project logic
If the behavior belongs to your game or story style, keep it in a small extension module.
Avoid runtime edits
Changing core runtime scripts directly makes updates harder and creates merge work later.
Extensions are disabled by default. Only enable them when the project intentionally uses trusted code.
VNC can check the conventional Buttons module shape without running your extension code from the editor. Runtime execution still happens in-game, so test extensions in a play session.
The documented shape makes customization safer:
The Buttons module must return one function. It receives a ButtonAdapter and may return a cleanup function.
--!strict
local ReplicatedStorage = game:GetService("ReplicatedStorage")local Runtime = ReplicatedStorage:WaitForChild("VisualNovelCreatorRuntime")local Contracts = require(Runtime:WaitForChild("ExtensionContracts"))
return function(button: Contracts.ButtonAdapter): (() -> ())? if button.Role == Contracts.ButtonRoles.Primary then return button:OnHoverChanged(function(isHovered) button:SetScale(if isHovered then 1.04 else 1) end) endendThe adapter exposes:
| Member | Use |
|---|---|
Id | Stable button ID from Contracts.ButtonIds. |
Role | Broad role from Contracts.ButtonRoles. |
Instance | The underlying GuiButton. |
Metadata | Button-specific metadata table. |
SetText(text) | Change visible button text. |
SetEnabled(enabled) | Enable or disable interaction. |
SetScale(scale) | Apply presentation scale. |
SetTransparency(transparency) | Apply presentation transparency. |
OnHoverChanged(callback) | Subscribe to hover state. Returns cleanup. |
OnPressedChanged(callback) | Subscribe to pressed state. Returns cleanup. |
OnActivated(callback) | Subscribe to activation. Returns cleanup. |
Contracts.ButtonIds.RuntimeMenu.NewGameContracts.ButtonIds.RuntimeMenu.ContinueContracts.ButtonIds.RuntimeMenu.SettingsContracts.ButtonIds.RuntimeMenu.ChapterSelectContracts.ButtonIds.RuntimeMenu.SaveGameContracts.ButtonIds.RuntimeMenu.LoadGameContracts.ButtonIds.RuntimeMenu.LogContracts.ButtonIds.RuntimeMenu.ReturnContracts.ButtonIds.RuntimeMenu.CloseContracts.ButtonIds.RuntimeMenu.OpenMenuContracts.ButtonIds.SaveLoad.SaveSlotContracts.ButtonIds.SaveLoad.LoadSlotContracts.ButtonIds.SaveLoad.PreviousPageContracts.ButtonIds.SaveLoad.NextPageContracts.ButtonIds.Settings.AutoAdvanceContracts.ButtonIds.Settings.DeleteStoryDataContracts.ButtonIds.ChapterSelect.ChapterContracts.ButtonIds.Confirmation.ConfirmContracts.ButtonIds.Confirmation.CancelContracts.ButtonRoles.PrimaryContracts.ButtonRoles.SecondaryContracts.ButtonRoles.DangerContracts.ButtonRoles.NavigationContracts.ButtonRoles.SlotUse these constants instead of hardcoded strings. That keeps your extension aligned when button text or layout changes.
Keep extension code cosmetic or presentation-oriented unless a future extension point explicitly allows game-state changes. That boundary keeps the core runtime updateable.
Extensions are part of the beta surface. Check Beta Limitations before depending on them for a release-critical workflow.