breakpoint-mcp 1.0.0 → 1.2.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.
@@ -0,0 +1,175 @@
1
+ import { z } from "zod";
2
+ import { gate } from "../../confirm.js";
3
+ /** UI / Control / theming. control_* + container_add_child mutate the EDITED scene and are undoable via EditorUndoRedoManager and ungated (the node_* model). theme_* author a Theme on disk via ResourceSaver, so — like resource_* — they are file-writers gated by confirmation. */
4
+ export function registerUiTools(server, call) {
5
+ server.registerTool("control_create", {
6
+ title: "Create control",
7
+ description: "Instance a Control-derived UI node (Button, Label, Panel, VBoxContainer, TextureRect, …) under a parent (undoable). Refuses non-Control classes. Optional 'text' is applied only to controls that expose a 'text' property. Returns the new node's path.",
8
+ inputSchema: {
9
+ parent_path: z.string().describe("Parent node path relative to the scene root; \".\" for the root"),
10
+ type: z.string().describe("Control subclass to instance, e.g. Button, Label, Panel, VBoxContainer, TextureRect"),
11
+ name: z.string().optional().describe("Node name (defaults to the class name)"),
12
+ text: z.string().optional().describe("Initial text; applied only if the control has a 'text' property"),
13
+ },
14
+ }, async ({ parent_path, type, name, text }) => {
15
+ const params = { parent_path, type };
16
+ if (name !== undefined)
17
+ params.name = name;
18
+ if (text !== undefined)
19
+ params.text = text;
20
+ return call("control.create", params);
21
+ });
22
+ server.registerTool("container_add_child", {
23
+ title: "Add control to container",
24
+ description: "Instance a Control-derived child under a Container node (VBoxContainer, GridContainer, MarginContainer, …) so it participates in the container's layout (undoable). Refuses a non-Container parent or a non-Control child. Returns the new node's path.",
25
+ inputSchema: {
26
+ container_path: z.string().describe("Container node path relative to the scene root"),
27
+ type: z.string().describe("Control subclass to instance as the container's child, e.g. Button, Label"),
28
+ name: z.string().optional().describe("Node name (defaults to the class name)"),
29
+ },
30
+ }, async ({ container_path, type, name }) => call("container.add_child", name !== undefined ? { container_path, type, name } : { container_path, type }));
31
+ server.registerTool("control_set_anchors", {
32
+ title: "Set control anchors",
33
+ description: "Set one or more of a Control's anchors (left/top/right/bottom, each 0..1) directly (undoable). Only the sides you pass are changed; offsets are left as-is. Provide at least one side.",
34
+ inputSchema: {
35
+ path: z.string().describe("Control node path relative to the scene root"),
36
+ left: z.number().optional().describe("anchor_left (0..1)"),
37
+ top: z.number().optional().describe("anchor_top (0..1)"),
38
+ right: z.number().optional().describe("anchor_right (0..1)"),
39
+ bottom: z.number().optional().describe("anchor_bottom (0..1)"),
40
+ },
41
+ }, async ({ path, left, top, right, bottom }) => {
42
+ const params = { path };
43
+ if (left !== undefined)
44
+ params.left = left;
45
+ if (top !== undefined)
46
+ params.top = top;
47
+ if (right !== undefined)
48
+ params.right = right;
49
+ if (bottom !== undefined)
50
+ params.bottom = bottom;
51
+ return call("control.set_anchors", params);
52
+ });
53
+ server.registerTool("control_set_layout_preset", {
54
+ title: "Apply control layout preset",
55
+ description: "Apply a LayoutPreset to a Control via set_anchors_and_offsets_preset (undoable). 'preset' is a name (full_rect, center, top_left, center_top, left_wide, hcenter_wide, …) or the integer enum value. Optional resize_mode (0=min_size,1=keep_width,2=keep_height,3=keep_size) and margin.",
56
+ inputSchema: {
57
+ path: z.string().describe("Control node path relative to the scene root"),
58
+ preset: z.union([z.string(), z.number()]).describe("LayoutPreset name or integer (0..15)"),
59
+ resize_mode: z.number().int().optional().describe("LayoutPresetMode 0..3 (default 0 = min size)"),
60
+ margin: z.number().int().optional().describe("Margin in pixels applied by the preset (default 0)"),
61
+ },
62
+ }, async ({ path, preset, resize_mode, margin }) => {
63
+ const params = { path, preset };
64
+ if (resize_mode !== undefined)
65
+ params.resize_mode = resize_mode;
66
+ if (margin !== undefined)
67
+ params.margin = margin;
68
+ return call("control.set_layout_preset", params);
69
+ });
70
+ server.registerTool("control_set_size_flags", {
71
+ title: "Set control size flags",
72
+ description: "Set a Control's container size flags and/or stretch ratio (undoable). 'horizontal'/'vertical' are SizeFlags bitmasks (1=fill, 2=expand, 3=expand_fill, 4=shrink_center, 8=shrink_end). Provide at least one of horizontal/vertical/stretch_ratio.",
73
+ inputSchema: {
74
+ path: z.string().describe("Control node path relative to the scene root"),
75
+ horizontal: z.number().int().optional().describe("size_flags_horizontal bitmask"),
76
+ vertical: z.number().int().optional().describe("size_flags_vertical bitmask"),
77
+ stretch_ratio: z.number().optional().describe("size_flags_stretch_ratio (default 1.0)"),
78
+ },
79
+ }, async ({ path, horizontal, vertical, stretch_ratio }) => {
80
+ const params = { path };
81
+ if (horizontal !== undefined)
82
+ params.horizontal = horizontal;
83
+ if (vertical !== undefined)
84
+ params.vertical = vertical;
85
+ if (stretch_ratio !== undefined)
86
+ params.stretch_ratio = stretch_ratio;
87
+ return call("control.set_size_flags", params);
88
+ });
89
+ server.registerTool("control_set_theme", {
90
+ title: "Set control theme",
91
+ description: "Assign a Theme resource (res:// path) to a Control's 'theme' property so it and its children inherit it (undoable). Pass an empty theme_path to clear the override.",
92
+ inputSchema: {
93
+ path: z.string().describe("Control node path relative to the scene root"),
94
+ theme_path: z.string().describe("Theme res:// path, or \"\" to clear the theme override"),
95
+ },
96
+ }, async ({ path, theme_path }) => call("control.set_theme", { path, theme_path }));
97
+ server.registerTool("theme_create", {
98
+ title: "Create theme",
99
+ description: "Create a new empty Theme resource and save it to a res:// path. DESTRUCTIVE (writes a file) — gated by confirmation.",
100
+ inputSchema: {
101
+ to_path: z.string().describe("Destination res:// path, e.g. res://ui/main.theme or res://ui/main.tres"),
102
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
103
+ },
104
+ }, async ({ to_path, confirm }) => {
105
+ const blocked = await gate(server, confirm, `Create Theme resource at ${to_path}`);
106
+ if (blocked)
107
+ return blocked;
108
+ return call("theme.create", { to_path });
109
+ });
110
+ server.registerTool("theme_set_color", {
111
+ title: "Set theme color",
112
+ description: "Set a color entry on a Theme resource file and save it (e.g. font_color for the Button type). DESTRUCTIVE (writes a file) — gated by confirmation. 'color' is [r,g,b] or [r,g,b,a] with components 0..1.",
113
+ inputSchema: {
114
+ path: z.string().describe("Theme res:// path"),
115
+ name: z.string().describe("Theme item name, e.g. font_color"),
116
+ theme_type: z.string().describe("Theme type the item belongs to, e.g. Button, Label"),
117
+ color: z.array(z.number()).describe("[r,g,b] or [r,g,b,a], components 0..1"),
118
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
119
+ },
120
+ }, async ({ path, name, theme_type, color, confirm }) => {
121
+ const blocked = await gate(server, confirm, `Set theme color ${theme_type}/${name} in ${path}`);
122
+ if (blocked)
123
+ return blocked;
124
+ return call("theme.set_color", { path, name, theme_type, color });
125
+ });
126
+ server.registerTool("theme_set_font", {
127
+ title: "Set theme font",
128
+ description: "Set a font entry on a Theme resource file (loading a Font from a res:// path) and save it. DESTRUCTIVE (writes a file) — gated by confirmation.",
129
+ inputSchema: {
130
+ path: z.string().describe("Theme res:// path"),
131
+ name: z.string().describe("Theme item name, e.g. font"),
132
+ theme_type: z.string().describe("Theme type the item belongs to, e.g. Button, Label"),
133
+ font_path: z.string().describe("Font resource res:// path (FontFile / SystemFont / …)"),
134
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
135
+ },
136
+ }, async ({ path, name, theme_type, font_path, confirm }) => {
137
+ const blocked = await gate(server, confirm, `Set theme font ${theme_type}/${name} in ${path}`);
138
+ if (blocked)
139
+ return blocked;
140
+ return call("theme.set_font", { path, name, theme_type, font_path });
141
+ });
142
+ server.registerTool("theme_set_stylebox", {
143
+ title: "Set theme stylebox",
144
+ description: "Set a StyleBox entry on a Theme resource file (loading a StyleBox from a res:// path) and save it. DESTRUCTIVE (writes a file) — gated by confirmation.",
145
+ inputSchema: {
146
+ path: z.string().describe("Theme res:// path"),
147
+ name: z.string().describe("Theme item name, e.g. normal, pressed, panel"),
148
+ theme_type: z.string().describe("Theme type the item belongs to, e.g. Button, PanelContainer"),
149
+ stylebox_path: z.string().describe("StyleBox resource res:// path (StyleBoxFlat / StyleBoxTexture / …)"),
150
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
151
+ },
152
+ }, async ({ path, name, theme_type, stylebox_path, confirm }) => {
153
+ const blocked = await gate(server, confirm, `Set theme stylebox ${theme_type}/${name} in ${path}`);
154
+ if (blocked)
155
+ return blocked;
156
+ return call("theme.set_stylebox", { path, name, theme_type, stylebox_path });
157
+ });
158
+ server.registerTool("theme_set_constant", {
159
+ title: "Set theme constant",
160
+ description: "Set an integer constant entry on a Theme resource file and save it (e.g. h_separation for a BoxContainer). DESTRUCTIVE (writes a file) — gated by confirmation.",
161
+ inputSchema: {
162
+ path: z.string().describe("Theme res:// path"),
163
+ name: z.string().describe("Theme item name, e.g. h_separation, margin_left"),
164
+ theme_type: z.string().describe("Theme type the item belongs to, e.g. HBoxContainer, MarginContainer"),
165
+ value: z.number().int().describe("Integer constant value"),
166
+ confirm: z.boolean().optional().describe("Auto-approve this destructive action (skip the confirmation prompt)"),
167
+ },
168
+ }, async ({ path, name, theme_type, value, confirm }) => {
169
+ const blocked = await gate(server, confirm, `Set theme constant ${theme_type}/${name} in ${path}`);
170
+ if (blocked)
171
+ return blocked;
172
+ return call("theme.set_constant", { path, name, theme_type, value });
173
+ });
174
+ }
175
+ //# sourceMappingURL=ui.js.map