@modelnex/sdk 0.1.1

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/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # @modelnex/sdk
2
+
3
+ React SDK for natural language control of web apps. Register actions and context; an AI agent executes actions in response to user commands.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @modelnex/sdk react zod
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```tsx
14
+ import { ModelNexProvider, ModelNexChatBubble } from "@modelnex/sdk";
15
+
16
+ function App() {
17
+ const [items, setItems] = React.useState([]);
18
+
19
+ return (
20
+ <div>
21
+ <ModelNexChatBubble placeholder="Add item, list all..." />
22
+ <ul>{items.map(i => <li key={i.id}>{i.title}</li>)}</ul>
23
+ </div>
24
+ );
25
+ }
26
+
27
+ // Wrap your app (serverUrl defaults to http://localhost:3002)
28
+ export default () => (
29
+ <ModelNexProvider>
30
+ <App />
31
+ </ModelNexProvider>
32
+ );
33
+ ```
34
+
35
+ ## Exports
36
+
37
+ | Export | Purpose |
38
+ |--------|---------|
39
+ | `ModelNexProvider` | Wraps app, connects to agent server |
40
+ | `useRunCommand` | Run commands programmatically (for custom UI) |
41
+ | `UIStateProvider` | Holds UI state synced to agent |
42
+ | `useUIState` | Read/update UI state |
43
+ | `useViewportTrack` | Track element visibility |
44
+ | `useVisibleIds` | Get visible element IDs |
45
+ | `useAgentViewport` | Register visible IDs with agent |
46
+ | `ModelNexChatBubble` | Optional chat bubble UI |
47
+
48
+ ## Custom UI
49
+
50
+ Use `useRunCommand` instead of `ModelNexChatBubble` for your own command UI:
51
+
52
+ ```tsx
53
+ const runCommand = useRunCommand();
54
+
55
+ <button onClick={() => runCommand("Add a new card")}>Run</button>
56
+ ```
57
+
58
+
59
+ ## Peer Dependencies
60
+
61
+ - `react` >= 17
62
+ - `zod` >= 3
63
+
64
+ ## License
65
+
66
+ MIT
@@ -0,0 +1,69 @@
1
+ // src/utils/aom.ts
2
+ var uidMap = /* @__PURE__ */ new Map();
3
+ var nextUid = 1;
4
+ function generateMinifiedAOM() {
5
+ uidMap.clear();
6
+ nextUid = 1;
7
+ const interactives = document.querySelectorAll(
8
+ 'button, a, input, select, textarea, [role="button"], [role="link"], [role="tab"], [role="menuitem"], [role="option"]'
9
+ );
10
+ const nodes = [];
11
+ interactives.forEach((el) => {
12
+ if (!el.offsetParent && (el.offsetWidth === 0 || el.offsetHeight === 0)) {
13
+ return;
14
+ }
15
+ if (el.closest("#modelnex-studio-root") || el.closest("#modelnex-active-agent-root")) {
16
+ return;
17
+ }
18
+ const uid = `node:${nextUid++}`;
19
+ uidMap.set(uid, el);
20
+ let text = (el.textContent || "").replace(/\s+/g, " ").trim();
21
+ const ariaLabel = el.getAttribute("aria-label");
22
+ const placeholder = el.getAttribute("placeholder");
23
+ let value = void 0;
24
+ if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {
25
+ value = el.value;
26
+ }
27
+ let role = el.tagName.toLowerCase();
28
+ if (el.hasAttribute("role")) {
29
+ role = el.getAttribute("role");
30
+ } else if (role === "a") {
31
+ role = "link";
32
+ } else if (el instanceof HTMLInputElement) {
33
+ role = el.type ? `input[${el.type}]` : "input";
34
+ }
35
+ const node = { uid, role };
36
+ const displayLabel = ariaLabel || text || placeholder;
37
+ if (displayLabel) {
38
+ node.text = displayLabel.substring(0, 100);
39
+ }
40
+ if (el.getAttribute("name")) {
41
+ node.name = el.getAttribute("name");
42
+ }
43
+ if (value) {
44
+ node.value = value.substring(0, 100);
45
+ }
46
+ if (el instanceof HTMLAnchorElement && el.href) {
47
+ try {
48
+ const url = new URL(el.href);
49
+ node.href = url.pathname + url.search + url.hash;
50
+ } catch {
51
+ node.href = el.getAttribute("href");
52
+ }
53
+ }
54
+ nodes.push(node);
55
+ });
56
+ return { nodes };
57
+ }
58
+ function getElementByUid(uid) {
59
+ return uidMap.get(uid) || null;
60
+ }
61
+ function clearAOMMap() {
62
+ uidMap.clear();
63
+ nextUid = 1;
64
+ }
65
+ export {
66
+ clearAOMMap,
67
+ generateMinifiedAOM,
68
+ getElementByUid
69
+ };
@@ -0,0 +1,55 @@
1
+ // src/utils/dom-sync.ts
2
+ function waitForDomSettle(options = {}) {
3
+ const { timeoutMs = 5e3, debounceMs = 400, minWaitMs = 100 } = options;
4
+ return new Promise((resolve) => {
5
+ let debounceTimer = null;
6
+ let resolved = false;
7
+ const maxTimer = setTimeout(() => {
8
+ if (!resolved) {
9
+ resolved = true;
10
+ cleanup();
11
+ console.log("[DOM Sync] Forced resolution by max timeout");
12
+ resolve();
13
+ }
14
+ }, Math.max(timeoutMs, minWaitMs));
15
+ const finish = () => {
16
+ if (!resolved) {
17
+ resolved = true;
18
+ cleanup();
19
+ resolve();
20
+ }
21
+ };
22
+ const observer = new MutationObserver((mutations) => {
23
+ const hasSignificantMutations = mutations.some((m) => {
24
+ if (m.target instanceof HTMLElement) {
25
+ if (m.target.hasAttribute("data-modelnex-tour-highlight")) return false;
26
+ if (m.target.hasAttribute("data-modelnex-caption")) return false;
27
+ if (m.target.closest("#modelnex-studio-root")) return false;
28
+ if (m.target.closest("#modelnex-active-agent-root")) return false;
29
+ }
30
+ return true;
31
+ });
32
+ if (!hasSignificantMutations) return;
33
+ if (debounceTimer) clearTimeout(debounceTimer);
34
+ debounceTimer = setTimeout(finish, debounceMs);
35
+ });
36
+ const cleanup = () => {
37
+ observer.disconnect();
38
+ if (debounceTimer) clearTimeout(debounceTimer);
39
+ clearTimeout(maxTimer);
40
+ };
41
+ setTimeout(() => {
42
+ if (resolved) return;
43
+ observer.observe(document.body, {
44
+ childList: true,
45
+ subtree: true,
46
+ attributes: true,
47
+ characterData: true
48
+ });
49
+ debounceTimer = setTimeout(finish, debounceMs);
50
+ }, minWaitMs);
51
+ });
52
+ }
53
+ export {
54
+ waitForDomSettle
55
+ };