@rozenite/controls-plugin 2.2.0 → 2.3.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.
- package/CHANGELOG.md +16 -0
- package/dist/devtools/assets/panel-EMOeI6i6.css +1 -0
- package/dist/devtools/assets/panel-mjW0t-tQ.js +18 -0
- package/dist/devtools/panel.html +2 -2
- package/dist/react-native/cjs/package.json +3 -0
- package/dist/react-native/cjs/react-native.js +14 -0
- package/dist/react-native/cjs/src/react-native/controlsRegistry.js +70 -0
- package/dist/react-native/cjs/src/react-native/useControlsAgentTools.js +175 -0
- package/dist/react-native/cjs/src/react-native/useRozeniteControlsPlugin.js +177 -0
- package/dist/react-native/cjs/src/shared/agent-tools.js +51 -0
- package/dist/react-native/cjs/src/shared/messaging.js +2 -0
- package/dist/react-native/cjs/src/shared/serialization.js +102 -0
- package/dist/react-native/cjs/src/shared/types.js +5 -0
- package/dist/react-native/package.json +3 -0
- package/dist/react-native/react-native.d.ts +2 -0
- package/dist/react-native/react-native.js +11 -0
- package/dist/react-native/src/react-native/controlsRegistry.d.ts +23 -0
- package/dist/react-native/src/react-native/controlsRegistry.js +66 -0
- package/dist/react-native/src/react-native/useControlsAgentTools.d.ts +2 -0
- package/dist/react-native/src/react-native/useControlsAgentTools.js +171 -0
- package/dist/react-native/src/react-native/useRozeniteControlsPlugin.d.ts +3 -0
- package/dist/react-native/src/react-native/useRozeniteControlsPlugin.js +173 -0
- package/dist/react-native/src/shared/agent-tools.d.ts +48 -0
- package/dist/react-native/src/shared/agent-tools.js +48 -0
- package/dist/react-native/src/shared/messaging.d.ts +35 -0
- package/dist/react-native/src/shared/messaging.js +1 -0
- package/dist/react-native/src/shared/serialization.d.ts +22 -0
- package/dist/react-native/src/shared/serialization.js +96 -0
- package/dist/react-native/src/shared/types.d.ts +69 -0
- package/dist/react-native/src/shared/types.js +1 -0
- package/dist/rozenite.json +1 -1
- package/dist/sdk/package.json +3 -0
- package/dist/sdk/sdk.d.ts +10 -0
- package/dist/sdk/sdk.js +8 -0
- package/dist/sdk/src/shared/agent-tools.d.ts +48 -0
- package/dist/sdk/src/shared/agent-tools.js +51 -0
- package/dist/sdk/src/shared/messaging.d.ts +35 -0
- package/dist/sdk/src/shared/messaging.js +2 -0
- package/dist/sdk/src/shared/types.d.ts +69 -0
- package/dist/sdk/src/shared/types.js +5 -0
- package/package.json +17 -18
- package/react-native.ts +8 -1
- package/rozenite.config.ts +1 -0
- package/src/__tests__/release-bundle.test.ts +32 -0
- package/dist/devtools/assets/panel-BGnH7fJp.js +0 -18
- package/dist/devtools/assets/panel-DFvY5duB.css +0 -1
- package/dist/react-native/chunks/useRozeniteControlsPlugin.require.cjs +0 -1
- package/dist/react-native/chunks/useRozeniteControlsPlugin.require.js +0 -466
- package/dist/react-native/index.cjs +0 -1
- package/dist/react-native/index.d.ts +0 -138
- package/dist/react-native/index.js +0 -8
- package/dist/sdk/index.cjs +0 -1
- package/dist/sdk/index.d.ts +0 -150
- package/dist/sdk/index.js +0 -55
package/dist/devtools/panel.html
CHANGED
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
<script>
|
|
23
23
|
var __ROZENITE_PANEL__ = true;
|
|
24
24
|
</script>
|
|
25
|
-
<script type="module" crossorigin src="../devtools/assets/panel-
|
|
26
|
-
<link rel="stylesheet" crossorigin href="../devtools/assets/panel-
|
|
25
|
+
<script type="module" crossorigin src="../devtools/assets/panel-mjW0t-tQ.js"></script>
|
|
26
|
+
<link rel="stylesheet" crossorigin href="../devtools/assets/panel-EMOeI6i6.css">
|
|
27
27
|
</head>
|
|
28
28
|
<body>
|
|
29
29
|
<div id="root"></div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useRozeniteControlsPlugin = exports.createSection = void 0;
|
|
4
|
+
var types_1 = require("./src/shared/types");
|
|
5
|
+
Object.defineProperty(exports, "createSection", { enumerable: true, get: function () { return types_1.createSection; } });
|
|
6
|
+
const isDev = process.env.NODE_ENV !== 'production';
|
|
7
|
+
const isServer = typeof window === 'undefined' && typeof lynx === 'undefined';
|
|
8
|
+
if (isDev && !isServer) {
|
|
9
|
+
exports.useRozeniteControlsPlugin =
|
|
10
|
+
require('./src/react-native/useRozeniteControlsPlugin').useRozeniteControlsPlugin;
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
exports.useRozeniteControlsPlugin = () => null;
|
|
14
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.controlsRegistry = exports.createControlsRegistry = void 0;
|
|
4
|
+
const EMPTY_OPTIONS = {
|
|
5
|
+
sections: [],
|
|
6
|
+
};
|
|
7
|
+
const resolveOptionsInput = (previousOptions, input) => {
|
|
8
|
+
if (typeof input === 'function') {
|
|
9
|
+
return input(previousOptions);
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
...previousOptions,
|
|
13
|
+
...input,
|
|
14
|
+
sections: [...previousOptions.sections, ...input.sections],
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
const createControlsRegistry = () => {
|
|
18
|
+
const registrations = new Map();
|
|
19
|
+
const listeners = new Set();
|
|
20
|
+
let snapshot = 0;
|
|
21
|
+
const getOwnerId = () => registrations.keys().next().value;
|
|
22
|
+
const notify = () => {
|
|
23
|
+
snapshot += 1;
|
|
24
|
+
listeners.forEach((listener) => listener());
|
|
25
|
+
};
|
|
26
|
+
const getRegistrationEntries = (override) => {
|
|
27
|
+
const entries = Array.from(registrations.entries());
|
|
28
|
+
if (!override) {
|
|
29
|
+
return entries;
|
|
30
|
+
}
|
|
31
|
+
const overrideIndex = entries.findIndex(([id]) => id === override.id);
|
|
32
|
+
if (overrideIndex === -1) {
|
|
33
|
+
return [...entries, [override.id, override.input]];
|
|
34
|
+
}
|
|
35
|
+
return entries.map(([id, input]) => id === override.id ? [id, override.input] : [id, input]);
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
set(id, input) {
|
|
39
|
+
const previousOwnerId = getOwnerId();
|
|
40
|
+
registrations.set(id, input);
|
|
41
|
+
if (previousOwnerId !== getOwnerId()) {
|
|
42
|
+
notify();
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
delete(id) {
|
|
46
|
+
registrations.delete(id);
|
|
47
|
+
notify();
|
|
48
|
+
},
|
|
49
|
+
getOptions(override) {
|
|
50
|
+
return getRegistrationEntries(override)
|
|
51
|
+
.map(([, input]) => input)
|
|
52
|
+
.reduce(resolveOptionsInput, EMPTY_OPTIONS);
|
|
53
|
+
},
|
|
54
|
+
isOwner(id, override) {
|
|
55
|
+
const ownerEntry = getRegistrationEntries(override)[0];
|
|
56
|
+
return ownerEntry?.[0] === id;
|
|
57
|
+
},
|
|
58
|
+
getSnapshot() {
|
|
59
|
+
return snapshot;
|
|
60
|
+
},
|
|
61
|
+
subscribe(listener) {
|
|
62
|
+
listeners.add(listener);
|
|
63
|
+
return () => {
|
|
64
|
+
listeners.delete(listener);
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
exports.createControlsRegistry = createControlsRegistry;
|
|
70
|
+
exports.controlsRegistry = (0, exports.createControlsRegistry)();
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useControlsAgentTools = void 0;
|
|
4
|
+
const agent_bridge_1 = require("@rozenite/agent-bridge");
|
|
5
|
+
const agent_tools_1 = require("../shared/agent-tools");
|
|
6
|
+
const resolveItem = (sections, sectionId, itemId) => {
|
|
7
|
+
const section = sections.find((s) => s.id === sectionId);
|
|
8
|
+
if (!section) {
|
|
9
|
+
const available = sections.map((s) => s.id).join(', ');
|
|
10
|
+
throw new Error(`Section "${sectionId}" not found. Available: ${available || '(none)'}`);
|
|
11
|
+
}
|
|
12
|
+
const item = section.items.find((i) => i.id === itemId);
|
|
13
|
+
if (!item) {
|
|
14
|
+
const available = section.items.map((i) => i.id).join(', ');
|
|
15
|
+
throw new Error(`Item "${itemId}" not found in section "${sectionId}". Available: ${available || '(none)'}`);
|
|
16
|
+
}
|
|
17
|
+
return { section, item };
|
|
18
|
+
};
|
|
19
|
+
const useControlsAgentTools = (getSections, enabled = true) => {
|
|
20
|
+
(0, agent_bridge_1.useRozenitePluginAgentTool)({
|
|
21
|
+
pluginId: agent_tools_1.CONTROLS_AGENT_PLUGIN_ID,
|
|
22
|
+
tool: agent_tools_1.controlsToolDefinitions.listSections,
|
|
23
|
+
enabled,
|
|
24
|
+
handler: () => ({
|
|
25
|
+
sections: getSections().map((section) => ({
|
|
26
|
+
id: section.id,
|
|
27
|
+
title: section.title,
|
|
28
|
+
description: section.description,
|
|
29
|
+
items: section.items.map((item) => ({
|
|
30
|
+
id: item.id,
|
|
31
|
+
type: item.type,
|
|
32
|
+
title: item.title,
|
|
33
|
+
disabled: 'disabled' in item ? item.disabled : undefined,
|
|
34
|
+
})),
|
|
35
|
+
})),
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
(0, agent_bridge_1.useRozenitePluginAgentTool)({
|
|
39
|
+
pluginId: agent_tools_1.CONTROLS_AGENT_PLUGIN_ID,
|
|
40
|
+
tool: agent_tools_1.controlsToolDefinitions.getItem,
|
|
41
|
+
enabled,
|
|
42
|
+
handler: ({ sectionId, itemId }) => {
|
|
43
|
+
const { item } = resolveItem(getSections(), sectionId, itemId);
|
|
44
|
+
if (item.type === 'text') {
|
|
45
|
+
return {
|
|
46
|
+
sectionId,
|
|
47
|
+
item: {
|
|
48
|
+
id: item.id,
|
|
49
|
+
type: item.type,
|
|
50
|
+
title: item.title,
|
|
51
|
+
value: item.value,
|
|
52
|
+
description: item.description,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (item.type === 'toggle') {
|
|
57
|
+
return {
|
|
58
|
+
sectionId,
|
|
59
|
+
item: {
|
|
60
|
+
id: item.id,
|
|
61
|
+
type: item.type,
|
|
62
|
+
title: item.title,
|
|
63
|
+
value: item.value,
|
|
64
|
+
description: item.description,
|
|
65
|
+
disabled: item.disabled,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (item.type === 'button') {
|
|
70
|
+
return {
|
|
71
|
+
sectionId,
|
|
72
|
+
item: {
|
|
73
|
+
id: item.id,
|
|
74
|
+
type: item.type,
|
|
75
|
+
title: item.title,
|
|
76
|
+
actionLabel: item.actionLabel,
|
|
77
|
+
description: item.description,
|
|
78
|
+
disabled: item.disabled,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (item.type === 'select') {
|
|
83
|
+
return {
|
|
84
|
+
sectionId,
|
|
85
|
+
item: {
|
|
86
|
+
id: item.id,
|
|
87
|
+
type: item.type,
|
|
88
|
+
title: item.title,
|
|
89
|
+
value: item.value,
|
|
90
|
+
options: item.options,
|
|
91
|
+
description: item.description,
|
|
92
|
+
disabled: item.disabled,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
sectionId,
|
|
98
|
+
item: {
|
|
99
|
+
id: item.id,
|
|
100
|
+
type: item.type,
|
|
101
|
+
title: item.title,
|
|
102
|
+
value: item.value,
|
|
103
|
+
placeholder: item.placeholder,
|
|
104
|
+
applyLabel: item.applyLabel,
|
|
105
|
+
description: item.description,
|
|
106
|
+
disabled: item.disabled,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
(0, agent_bridge_1.useRozenitePluginAgentTool)({
|
|
112
|
+
pluginId: agent_tools_1.CONTROLS_AGENT_PLUGIN_ID,
|
|
113
|
+
tool: agent_tools_1.controlsToolDefinitions.setValue,
|
|
114
|
+
enabled,
|
|
115
|
+
handler: async ({ sectionId, itemId, value }) => {
|
|
116
|
+
const { item } = resolveItem(getSections(), sectionId, itemId);
|
|
117
|
+
if (item.type === 'text') {
|
|
118
|
+
throw new Error(`Item "${itemId}" is a read-only text item and cannot be updated.`);
|
|
119
|
+
}
|
|
120
|
+
if (item.type === 'button') {
|
|
121
|
+
throw new Error(`Item "${itemId}" is a button. Use press-button to trigger its action.`);
|
|
122
|
+
}
|
|
123
|
+
if (item.disabled) {
|
|
124
|
+
throw new Error(`Item "${itemId}" is disabled.`);
|
|
125
|
+
}
|
|
126
|
+
if (item.type === 'toggle') {
|
|
127
|
+
if (typeof value !== 'boolean') {
|
|
128
|
+
throw new Error(`Expected boolean value for toggle item "${itemId}".`);
|
|
129
|
+
}
|
|
130
|
+
if (item.validate) {
|
|
131
|
+
const result = item.validate(value);
|
|
132
|
+
if (!result.valid) {
|
|
133
|
+
throw new Error(result.message);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
await item.onUpdate(value);
|
|
137
|
+
return { applied: true, sectionId, itemId };
|
|
138
|
+
}
|
|
139
|
+
if (typeof value !== 'string') {
|
|
140
|
+
throw new Error(`Expected string value for ${item.type} item "${itemId}".`);
|
|
141
|
+
}
|
|
142
|
+
if (item.type === 'select') {
|
|
143
|
+
const validOptions = item.options.map((o) => o.value);
|
|
144
|
+
if (!validOptions.includes(value)) {
|
|
145
|
+
throw new Error(`Invalid option "${value}" for item "${itemId}". Valid options: ${validOptions.join(', ')}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (item.validate) {
|
|
149
|
+
const result = item.validate(value);
|
|
150
|
+
if (!result.valid) {
|
|
151
|
+
throw new Error(result.message);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
await item.onUpdate(value);
|
|
155
|
+
return { applied: true, sectionId, itemId };
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
(0, agent_bridge_1.useRozenitePluginAgentTool)({
|
|
159
|
+
pluginId: agent_tools_1.CONTROLS_AGENT_PLUGIN_ID,
|
|
160
|
+
tool: agent_tools_1.controlsToolDefinitions.pressButton,
|
|
161
|
+
enabled,
|
|
162
|
+
handler: async ({ sectionId, itemId }) => {
|
|
163
|
+
const { item } = resolveItem(getSections(), sectionId, itemId);
|
|
164
|
+
if (item.type !== 'button') {
|
|
165
|
+
throw new Error(`Item "${itemId}" is not a button (type: ${item.type}). Use set-value to update its value.`);
|
|
166
|
+
}
|
|
167
|
+
if (item.disabled) {
|
|
168
|
+
throw new Error(`Button "${itemId}" is disabled.`);
|
|
169
|
+
}
|
|
170
|
+
await item.onPress();
|
|
171
|
+
return { pressed: true, sectionId, itemId };
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
};
|
|
175
|
+
exports.useControlsAgentTools = useControlsAgentTools;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useRozeniteControlsPlugin = void 0;
|
|
4
|
+
const plugin_bridge_1 = require("@rozenite/plugin-bridge");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const serialization_1 = require("../shared/serialization");
|
|
7
|
+
const useControlsAgentTools_1 = require("./useControlsAgentTools");
|
|
8
|
+
const controlsRegistry_1 = require("./controlsRegistry");
|
|
9
|
+
const useRozeniteControlsPlugin = (optionsInput) => {
|
|
10
|
+
const client = (0, plugin_bridge_1.useRozeniteDevToolsClient)({
|
|
11
|
+
pluginId: '@rozenite/controls-plugin',
|
|
12
|
+
});
|
|
13
|
+
const registrationIdRef = (0, react_1.useRef)(Symbol('rozenite-controls'));
|
|
14
|
+
const registrationId = registrationIdRef.current;
|
|
15
|
+
const registrySnapshot = (0, react_1.useSyncExternalStore)(controlsRegistry_1.controlsRegistry.subscribe, controlsRegistry_1.controlsRegistry.getSnapshot, controlsRegistry_1.controlsRegistry.getSnapshot);
|
|
16
|
+
(0, react_1.useEffect)(() => {
|
|
17
|
+
return () => {
|
|
18
|
+
controlsRegistry_1.controlsRegistry.delete(registrationId);
|
|
19
|
+
};
|
|
20
|
+
}, [registrationId]);
|
|
21
|
+
(0, react_1.useEffect)(() => {
|
|
22
|
+
controlsRegistry_1.controlsRegistry.set(registrationId, optionsInput);
|
|
23
|
+
}, [optionsInput, registrationId]);
|
|
24
|
+
const isRegistryOwner = (0, react_1.useMemo)(() => controlsRegistry_1.controlsRegistry.isOwner(registrationId, {
|
|
25
|
+
id: registrationId,
|
|
26
|
+
input: optionsInput,
|
|
27
|
+
}), [optionsInput, registrySnapshot, registrationId]);
|
|
28
|
+
(0, useControlsAgentTools_1.useControlsAgentTools)(() => controlsRegistry_1.controlsRegistry.getOptions().sections, isRegistryOwner);
|
|
29
|
+
(0, react_1.useEffect)(() => {
|
|
30
|
+
if (!client) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
client.send('snapshot', {
|
|
34
|
+
type: 'snapshot',
|
|
35
|
+
sections: (0, serialization_1.serializeSections)(controlsRegistry_1.controlsRegistry.getOptions().sections),
|
|
36
|
+
});
|
|
37
|
+
}, [client, optionsInput, registrySnapshot]);
|
|
38
|
+
(0, react_1.useEffect)(() => {
|
|
39
|
+
if (!client || !isRegistryOwner) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const handleUpdateRequest = async ({ requestId, sectionId, itemId, value, }) => {
|
|
43
|
+
const key = (0, serialization_1.getActionRegistryKey)(sectionId, itemId);
|
|
44
|
+
const entry = (0, serialization_1.buildActionRegistry)(controlsRegistry_1.controlsRegistry.getOptions().sections).get(key);
|
|
45
|
+
if (!entry || entry.type === 'button') {
|
|
46
|
+
client.send('update-result', {
|
|
47
|
+
type: 'update-result',
|
|
48
|
+
requestId,
|
|
49
|
+
sectionId,
|
|
50
|
+
itemId,
|
|
51
|
+
status: 'error',
|
|
52
|
+
message: 'Update target not found.',
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
if (entry.type === 'toggle') {
|
|
58
|
+
if (typeof value !== 'boolean') {
|
|
59
|
+
client.send('update-result', {
|
|
60
|
+
type: 'update-result',
|
|
61
|
+
requestId,
|
|
62
|
+
sectionId,
|
|
63
|
+
itemId,
|
|
64
|
+
status: 'error',
|
|
65
|
+
message: 'Invalid toggle value.',
|
|
66
|
+
});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const result = (0, serialization_1.validateValue)(entry.validate, value);
|
|
70
|
+
if (!result.valid) {
|
|
71
|
+
client.send('update-result', {
|
|
72
|
+
type: 'update-result',
|
|
73
|
+
requestId,
|
|
74
|
+
sectionId,
|
|
75
|
+
itemId,
|
|
76
|
+
status: 'error',
|
|
77
|
+
message: result.message,
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
await entry.onUpdate(value);
|
|
82
|
+
client.send('update-result', {
|
|
83
|
+
type: 'update-result',
|
|
84
|
+
requestId,
|
|
85
|
+
sectionId,
|
|
86
|
+
itemId,
|
|
87
|
+
status: 'ok',
|
|
88
|
+
});
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (typeof value !== 'string') {
|
|
92
|
+
client.send('update-result', {
|
|
93
|
+
type: 'update-result',
|
|
94
|
+
requestId,
|
|
95
|
+
sectionId,
|
|
96
|
+
itemId,
|
|
97
|
+
status: 'error',
|
|
98
|
+
message: `Invalid ${entry.type} value.`,
|
|
99
|
+
});
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const result = (0, serialization_1.validateValue)(entry.validate, value);
|
|
103
|
+
if (!result.valid) {
|
|
104
|
+
client.send('update-result', {
|
|
105
|
+
type: 'update-result',
|
|
106
|
+
requestId,
|
|
107
|
+
sectionId,
|
|
108
|
+
itemId,
|
|
109
|
+
status: 'error',
|
|
110
|
+
message: result.message,
|
|
111
|
+
});
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
await entry.onUpdate(value);
|
|
115
|
+
client.send('update-result', {
|
|
116
|
+
type: 'update-result',
|
|
117
|
+
requestId,
|
|
118
|
+
sectionId,
|
|
119
|
+
itemId,
|
|
120
|
+
status: 'ok',
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
console.warn(`[Rozenite] Controls Plugin: Update failed for ${sectionId}/${itemId}.`, error);
|
|
125
|
+
client.send('update-result', {
|
|
126
|
+
type: 'update-result',
|
|
127
|
+
requestId,
|
|
128
|
+
sectionId,
|
|
129
|
+
itemId,
|
|
130
|
+
status: 'error',
|
|
131
|
+
message: 'Update failed on the device.',
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
const handleInvokeAction = async ({ sectionId, itemId, action }) => {
|
|
136
|
+
if (action !== 'press') {
|
|
137
|
+
console.warn(`[Rozenite] Controls Plugin: Unsupported action "${action}" for ${sectionId}/${itemId}.`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const key = (0, serialization_1.getActionRegistryKey)(sectionId, itemId);
|
|
141
|
+
const entry = (0, serialization_1.buildActionRegistry)(controlsRegistry_1.controlsRegistry.getOptions().sections).get(key);
|
|
142
|
+
if (!entry) {
|
|
143
|
+
console.warn(`[Rozenite] Controls Plugin: Action target not found for ${sectionId}/${itemId}.`);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
if (entry.type !== 'button') {
|
|
148
|
+
console.warn(`[Rozenite] Controls Plugin: Invalid press action payload for ${sectionId}/${itemId}.`);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
await entry.onPress();
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
console.warn(`[Rozenite] Controls Plugin: Action failed for ${sectionId}/${itemId}.`, error);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
const subscriptions = [
|
|
158
|
+
client.onMessage('get-snapshot', () => {
|
|
159
|
+
client.send('snapshot', {
|
|
160
|
+
type: 'snapshot',
|
|
161
|
+
sections: (0, serialization_1.serializeSections)(controlsRegistry_1.controlsRegistry.getOptions().sections),
|
|
162
|
+
});
|
|
163
|
+
}),
|
|
164
|
+
client.onMessage('update-request', (event) => {
|
|
165
|
+
void handleUpdateRequest(event);
|
|
166
|
+
}),
|
|
167
|
+
client.onMessage('invoke-action', (event) => {
|
|
168
|
+
void handleInvokeAction(event);
|
|
169
|
+
}),
|
|
170
|
+
];
|
|
171
|
+
return () => {
|
|
172
|
+
subscriptions.forEach((subscription) => subscription.remove());
|
|
173
|
+
};
|
|
174
|
+
}, [client, isRegistryOwner]);
|
|
175
|
+
return client;
|
|
176
|
+
};
|
|
177
|
+
exports.useRozeniteControlsPlugin = useRozeniteControlsPlugin;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.controlsToolDefinitions = exports.CONTROLS_AGENT_PLUGIN_ID = void 0;
|
|
4
|
+
const agent_shared_1 = require("@rozenite/agent-shared");
|
|
5
|
+
exports.CONTROLS_AGENT_PLUGIN_ID = '@rozenite/controls-plugin';
|
|
6
|
+
exports.controlsToolDefinitions = {
|
|
7
|
+
listSections: (0, agent_shared_1.defineAgentToolContract)({
|
|
8
|
+
name: 'list-sections',
|
|
9
|
+
description: 'List all controls sections with their item IDs, types, and titles. Does not include values — call get-item for that.',
|
|
10
|
+
inputSchema: { type: 'object', properties: {} },
|
|
11
|
+
}),
|
|
12
|
+
getItem: (0, agent_shared_1.defineAgentToolContract)({
|
|
13
|
+
name: 'get-item',
|
|
14
|
+
description: 'Get full details of a single controls item including its current value. For select items this includes available options.',
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
sectionId: { type: 'string', description: 'Section ID.' },
|
|
19
|
+
itemId: { type: 'string', description: 'Item ID.' },
|
|
20
|
+
},
|
|
21
|
+
required: ['sectionId', 'itemId'],
|
|
22
|
+
},
|
|
23
|
+
}),
|
|
24
|
+
setValue: (0, agent_shared_1.defineAgentToolContract)({
|
|
25
|
+
name: 'set-value',
|
|
26
|
+
description: 'Update the value of a toggle, select, or input item. Runs the validate callback when present. Fails for text (read-only) and button items.',
|
|
27
|
+
inputSchema: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
sectionId: { type: 'string', description: 'Section ID.' },
|
|
31
|
+
itemId: { type: 'string', description: 'Item ID.' },
|
|
32
|
+
value: {
|
|
33
|
+
description: 'New value. Boolean for toggle items, string for select/input items.',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
required: ['sectionId', 'itemId', 'value'],
|
|
37
|
+
},
|
|
38
|
+
}),
|
|
39
|
+
pressButton: (0, agent_shared_1.defineAgentToolContract)({
|
|
40
|
+
name: 'press-button',
|
|
41
|
+
description: "Trigger a button item's action. Fails if the item is not a button or is disabled.",
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: 'object',
|
|
44
|
+
properties: {
|
|
45
|
+
sectionId: { type: 'string', description: 'Section ID.' },
|
|
46
|
+
itemId: { type: 'string', description: 'Item ID.' },
|
|
47
|
+
},
|
|
48
|
+
required: ['sectionId', 'itemId'],
|
|
49
|
+
},
|
|
50
|
+
}),
|
|
51
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateValue = exports.getActionRegistryKey = exports.buildActionRegistry = exports.serializeSections = void 0;
|
|
4
|
+
const validateValue = (validate, value) => {
|
|
5
|
+
if (!validate) {
|
|
6
|
+
return { valid: true };
|
|
7
|
+
}
|
|
8
|
+
return validate(value);
|
|
9
|
+
};
|
|
10
|
+
exports.validateValue = validateValue;
|
|
11
|
+
const toSnapshotItem = (item) => {
|
|
12
|
+
if (item.type === 'text') {
|
|
13
|
+
return item;
|
|
14
|
+
}
|
|
15
|
+
if (item.type === 'toggle') {
|
|
16
|
+
return {
|
|
17
|
+
id: item.id,
|
|
18
|
+
type: item.type,
|
|
19
|
+
title: item.title,
|
|
20
|
+
value: item.value,
|
|
21
|
+
description: item.description,
|
|
22
|
+
disabled: item.disabled,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (item.type === 'button') {
|
|
26
|
+
return {
|
|
27
|
+
id: item.id,
|
|
28
|
+
type: item.type,
|
|
29
|
+
title: item.title,
|
|
30
|
+
actionLabel: item.actionLabel,
|
|
31
|
+
description: item.description,
|
|
32
|
+
disabled: item.disabled,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (item.type === 'select') {
|
|
36
|
+
return {
|
|
37
|
+
id: item.id,
|
|
38
|
+
type: item.type,
|
|
39
|
+
title: item.title,
|
|
40
|
+
value: item.value,
|
|
41
|
+
options: item.options,
|
|
42
|
+
description: item.description,
|
|
43
|
+
disabled: item.disabled,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
id: item.id,
|
|
48
|
+
type: item.type,
|
|
49
|
+
title: item.title,
|
|
50
|
+
value: item.value,
|
|
51
|
+
placeholder: item.placeholder,
|
|
52
|
+
applyLabel: item.applyLabel,
|
|
53
|
+
description: item.description,
|
|
54
|
+
disabled: item.disabled,
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
const serializeSections = (sections) => sections.map((section) => ({
|
|
58
|
+
id: section.id,
|
|
59
|
+
title: section.title,
|
|
60
|
+
description: section.description,
|
|
61
|
+
items: section.items.map(toSnapshotItem),
|
|
62
|
+
}));
|
|
63
|
+
exports.serializeSections = serializeSections;
|
|
64
|
+
const buildActionRegistry = (sections) => {
|
|
65
|
+
const registry = new Map();
|
|
66
|
+
sections.forEach((section) => {
|
|
67
|
+
section.items.forEach((item) => {
|
|
68
|
+
const key = `${section.id}:${item.id}`;
|
|
69
|
+
if (item.type === 'toggle') {
|
|
70
|
+
registry.set(key, {
|
|
71
|
+
type: 'toggle',
|
|
72
|
+
validate: item.validate,
|
|
73
|
+
onUpdate: item.onUpdate,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (item.type === 'button') {
|
|
77
|
+
registry.set(key, {
|
|
78
|
+
type: 'button',
|
|
79
|
+
onPress: item.onPress,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (item.type === 'select') {
|
|
83
|
+
registry.set(key, {
|
|
84
|
+
type: 'select',
|
|
85
|
+
validate: item.validate,
|
|
86
|
+
onUpdate: item.onUpdate,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (item.type === 'input') {
|
|
90
|
+
registry.set(key, {
|
|
91
|
+
type: 'input',
|
|
92
|
+
validate: item.validate,
|
|
93
|
+
onUpdate: item.onUpdate,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
return registry;
|
|
99
|
+
};
|
|
100
|
+
exports.buildActionRegistry = buildActionRegistry;
|
|
101
|
+
const getActionRegistryKey = (sectionId, itemId) => `${sectionId}:${itemId}`;
|
|
102
|
+
exports.getActionRegistryKey = getActionRegistryKey;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { createSection, type ControlsButtonItem, type ControlsInputItem, type ControlsItem, type ControlsSelectItem, type ControlsSelectOption, type ControlsSection, type ControlsTextItem, type ControlsToggleItem, type RozeniteControlsPluginOptionsInput, type ControlsValidationResult, type RozeniteControlsPluginOptions, type RozeniteControlsPluginOptionsUpdater, } from './src/shared/types';
|
|
2
|
+
export declare let useRozeniteControlsPlugin: typeof import('./src/react-native/useRozeniteControlsPlugin').useRozeniteControlsPlugin;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { createSection, } from './src/shared/types';
|
|
2
|
+
export let useRozeniteControlsPlugin;
|
|
3
|
+
const isDev = process.env.NODE_ENV !== 'production';
|
|
4
|
+
const isServer = typeof window === 'undefined' && typeof lynx === 'undefined';
|
|
5
|
+
if (isDev && !isServer) {
|
|
6
|
+
useRozeniteControlsPlugin =
|
|
7
|
+
require('./src/react-native/useRozeniteControlsPlugin').useRozeniteControlsPlugin;
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
useRozeniteControlsPlugin = () => null;
|
|
11
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { RozeniteControlsPluginOptions, RozeniteControlsPluginOptionsInput } from '../shared/types';
|
|
2
|
+
type ControlsRegistryListener = () => void;
|
|
3
|
+
type ControlsRegistryOverride = {
|
|
4
|
+
id: symbol;
|
|
5
|
+
input: RozeniteControlsPluginOptionsInput;
|
|
6
|
+
};
|
|
7
|
+
export declare const createControlsRegistry: () => {
|
|
8
|
+
set(id: symbol, input: RozeniteControlsPluginOptionsInput): void;
|
|
9
|
+
delete(id: symbol): void;
|
|
10
|
+
getOptions(override?: ControlsRegistryOverride): RozeniteControlsPluginOptions;
|
|
11
|
+
isOwner(id: symbol, override?: ControlsRegistryOverride): boolean;
|
|
12
|
+
getSnapshot(): number;
|
|
13
|
+
subscribe(listener: ControlsRegistryListener): () => void;
|
|
14
|
+
};
|
|
15
|
+
export declare const controlsRegistry: {
|
|
16
|
+
set(id: symbol, input: RozeniteControlsPluginOptionsInput): void;
|
|
17
|
+
delete(id: symbol): void;
|
|
18
|
+
getOptions(override?: ControlsRegistryOverride): RozeniteControlsPluginOptions;
|
|
19
|
+
isOwner(id: symbol, override?: ControlsRegistryOverride): boolean;
|
|
20
|
+
getSnapshot(): number;
|
|
21
|
+
subscribe(listener: ControlsRegistryListener): () => void;
|
|
22
|
+
};
|
|
23
|
+
export {};
|