@ai11y/react 0.0.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/.turbo/turbo-build.log +29 -0
- package/CHANGELOG.md +39 -0
- package/README.md +52 -0
- package/dist/components/Ai11yProvider.d.mts +36 -0
- package/dist/components/Ai11yProvider.d.mts.map +1 -0
- package/dist/components/Ai11yProvider.mjs +52 -0
- package/dist/components/Ai11yProvider.mjs.map +1 -0
- package/dist/components/Marker.d.mts +22 -0
- package/dist/components/Marker.d.mts.map +1 -0
- package/dist/components/Marker.mjs +31 -0
- package/dist/components/Marker.mjs.map +1 -0
- package/dist/hooks/useAi11yContext.d.mts +18 -0
- package/dist/hooks/useAi11yContext.d.mts.map +1 -0
- package/dist/hooks/useAi11yContext.mjs +23 -0
- package/dist/hooks/useAi11yContext.mjs.map +1 -0
- package/dist/hooks/useChat.d.mts +31 -0
- package/dist/hooks/useChat.d.mts.map +1 -0
- package/dist/hooks/useChat.mjs +90 -0
- package/dist/hooks/useChat.mjs.map +1 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.mjs +6 -0
- package/package.json +41 -0
- package/src/components/Ai11yProvider.tsx +118 -0
- package/src/components/Marker.tsx +55 -0
- package/src/hooks/useAi11yContext.ts +20 -0
- package/src/hooks/useChat.ts +147 -0
- package/src/index.ts +4 -0
- package/tsconfig.json +22 -0
- package/tsdown.config.ts +17 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
|
|
2
|
+
> @ai11y/react@0.0.1 build /home/runner/work/ai11y/ai11y/packages/react
|
|
3
|
+
> tsdown
|
|
4
|
+
|
|
5
|
+
[34mℹ[39m tsdown [2mv0.18.1[22m powered by rolldown [2mv1.0.0-beta.55[22m
|
|
6
|
+
[34mℹ[39m config file: [4m/home/runner/work/ai11y/ai11y/packages/react/tsdown.config.ts[24m (unrun)
|
|
7
|
+
[34mℹ[39m entry: [34msrc/index.ts[39m
|
|
8
|
+
[34mℹ[39m tsconfig: [34mtsconfig.json[39m
|
|
9
|
+
[34mℹ[39m Build start
|
|
10
|
+
[34mℹ[39m [2mdist/[22m[1mindex.mjs[22m [2m0.28 kB[22m [2m│ gzip: 0.14 kB[22m
|
|
11
|
+
[34mℹ[39m [2mdist/[22mhooks/useChat.mjs.map [2m6.75 kB[22m [2m│ gzip: 2.10 kB[22m
|
|
12
|
+
[34mℹ[39m [2mdist/[22mcomponents/Ai11yProvider.mjs.map [2m4.31 kB[22m [2m│ gzip: 1.51 kB[22m
|
|
13
|
+
[34mℹ[39m [2mdist/[22mhooks/useChat.mjs [2m3.23 kB[22m [2m│ gzip: 1.05 kB[22m
|
|
14
|
+
[34mℹ[39m [2mdist/[22mcomponents/Marker.mjs.map [2m1.95 kB[22m [2m│ gzip: 0.95 kB[22m
|
|
15
|
+
[34mℹ[39m [2mdist/[22mcomponents/Ai11yProvider.mjs [2m1.79 kB[22m [2m│ gzip: 0.69 kB[22m
|
|
16
|
+
[34mℹ[39m [2mdist/[22mcomponents/Ai11yProvider.d.mts.map [2m0.86 kB[22m [2m│ gzip: 0.39 kB[22m
|
|
17
|
+
[34mℹ[39m [2mdist/[22mcomponents/Marker.mjs [2m0.86 kB[22m [2m│ gzip: 0.48 kB[22m
|
|
18
|
+
[34mℹ[39m [2mdist/[22mhooks/useAi11yContext.mjs.map [2m0.79 kB[22m [2m│ gzip: 0.46 kB[22m
|
|
19
|
+
[34mℹ[39m [2mdist/[22mhooks/useChat.d.mts.map [2m0.77 kB[22m [2m│ gzip: 0.36 kB[22m
|
|
20
|
+
[34mℹ[39m [2mdist/[22mhooks/useAi11yContext.mjs [2m0.61 kB[22m [2m│ gzip: 0.35 kB[22m
|
|
21
|
+
[34mℹ[39m [2mdist/[22mcomponents/Marker.d.mts.map [2m0.35 kB[22m [2m│ gzip: 0.23 kB[22m
|
|
22
|
+
[34mℹ[39m [2mdist/[22mhooks/useAi11yContext.d.mts.map [2m0.18 kB[22m [2m│ gzip: 0.14 kB[22m
|
|
23
|
+
[34mℹ[39m [2mdist/[22m[32m[1mindex.d.mts[22m[39m [2m0.28 kB[22m [2m│ gzip: 0.14 kB[22m
|
|
24
|
+
[34mℹ[39m [2mdist/[22m[32mcomponents/Ai11yProvider.d.mts[39m [2m1.14 kB[22m [2m│ gzip: 0.50 kB[22m
|
|
25
|
+
[34mℹ[39m [2mdist/[22m[32mhooks/useChat.d.mts[39m [2m0.80 kB[22m [2m│ gzip: 0.41 kB[22m
|
|
26
|
+
[34mℹ[39m [2mdist/[22m[32mcomponents/Marker.d.mts[39m [2m0.55 kB[22m [2m│ gzip: 0.32 kB[22m
|
|
27
|
+
[34mℹ[39m [2mdist/[22m[32mhooks/useAi11yContext.d.mts[39m [2m0.47 kB[22m [2m│ gzip: 0.29 kB[22m
|
|
28
|
+
[34mℹ[39m 18 files, total: 25.95 kB
|
|
29
|
+
[32m✔[39m Build complete in [32m3027ms[39m
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# @ai11y/react
|
|
2
|
+
|
|
3
|
+
## 0.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#10](https://github.com/maerzhase/ai11y/pull/10)
|
|
8
|
+
[`a578f29`](https://github.com/maerzhase/ai11y/commit/a578f296c21cd19c935ff8003442677f7a1cb72d)
|
|
9
|
+
Thanks [@maerzhase](https://github.com/maerzhase)! - Docs and demo
|
|
10
|
+
improvements: naming (ai11y ≠ a11y), Describe/Plan/Act boundaries, UI context
|
|
11
|
+
payload viewer, annotation guidance, Why not ARIA, Security & privacy,
|
|
12
|
+
multi-step demo
|
|
13
|
+
|
|
14
|
+
- [#10](https://github.com/maerzhase/ai11y/pull/10)
|
|
15
|
+
[`a578f29`](https://github.com/maerzhase/ai11y/commit/a578f296c21cd19c935ff8003442677f7a1cb72d)
|
|
16
|
+
Thanks [@maerzhase](https://github.com/maerzhase)! - Fix conversation history
|
|
17
|
+
not being passed to agent due to async state update timing issue
|
|
18
|
+
|
|
19
|
+
- [#10](https://github.com/maerzhase/ai11y/pull/10)
|
|
20
|
+
[`a578f29`](https://github.com/maerzhase/ai11y/commit/a578f296c21cd19c935ff8003442677f7a1cb72d)
|
|
21
|
+
Thanks [@maerzhase](https://github.com/maerzhase)! - Add custom state
|
|
22
|
+
management and privacy protection features
|
|
23
|
+
- **Custom State API**: `setState()` now merges with existing state (like
|
|
24
|
+
React's setState), allowing multiple components to independently manage
|
|
25
|
+
state keys. Added `clearState()` helper for resetting state.
|
|
26
|
+
- **Privacy Protection**: Automatically redact sensitive input values
|
|
27
|
+
(passwords, hidden fields, credit card fields) from UI context. Values are
|
|
28
|
+
replaced with `[REDACTED]` to prevent exposure to AI agents.
|
|
29
|
+
- **Sensitive Marker Support**: Added `data-ai-sensitive` attribute and
|
|
30
|
+
`sensitive` prop to Marker component for explicitly marking sensitive
|
|
31
|
+
fields.
|
|
32
|
+
- **Agent Improvements**: Enhanced agent prompts with security rules to
|
|
33
|
+
prevent filling password fields and improved pronoun resolution for better
|
|
34
|
+
context tracking in conversations.
|
|
35
|
+
|
|
36
|
+
- Updated dependencies
|
|
37
|
+
[[`a578f29`](https://github.com/maerzhase/ai11y/commit/a578f296c21cd19c935ff8003442677f7a1cb72d),
|
|
38
|
+
[`a578f29`](https://github.com/maerzhase/ai11y/commit/a578f296c21cd19c935ff8003442677f7a1cb72d)]:
|
|
39
|
+
- @ai11y/core@0.0.1
|
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# @ai11y/react
|
|
2
|
+
|
|
3
|
+
Thin React wrapper for ai11y. Uses the same **describe → plan → act** flow: wrap
|
|
4
|
+
your app in `Ai11yProvider`, mark elements with `Marker`, and use
|
|
5
|
+
`useAi11yContext()` for `describe` and `act`; call `plan()` from `@ai11y/core`.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @ai11y/react @ai11y/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
Wrap your app in `Ai11yProvider` and use `Marker` so elements are registered for
|
|
16
|
+
`describe()`. Get `describe` and `act` from `useAi11yContext()` and call
|
|
17
|
+
`plan()` from `@ai11y/core`:
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { Ai11yProvider, Marker, useAi11yContext } from "@ai11y/react";
|
|
21
|
+
import { plan } from "@ai11y/core";
|
|
22
|
+
|
|
23
|
+
function App() {
|
|
24
|
+
return (
|
|
25
|
+
<Ai11yProvider onNavigate={(route) => navigate(route)}>
|
|
26
|
+
<YourApp />
|
|
27
|
+
</Ai11yProvider>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function Chat() {
|
|
32
|
+
const { describe, act } = useAi11yContext();
|
|
33
|
+
const handleSubmit = async (input: string) => {
|
|
34
|
+
const ui = describe();
|
|
35
|
+
const { reply, instructions } = await plan(ui, input);
|
|
36
|
+
for (const instruction of instructions ?? []) act(instruction);
|
|
37
|
+
return reply;
|
|
38
|
+
};
|
|
39
|
+
// ... render input and messages
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Mark elements so your agent can see them:
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<Marker id="save_btn" label="Save" intent="Save the document">
|
|
47
|
+
<button onClick={onSave}>Save</button>
|
|
48
|
+
</Marker>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
For the plan step with an LLM, point the client at your server endpoint; see
|
|
52
|
+
[packages/agent/README.md](../agent/README.md).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as _ai11y_core0 from "@ai11y/core";
|
|
2
|
+
import { AgentConfig, Ai11yError, Ai11yEvent, Ai11yState } from "@ai11y/core";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
5
|
+
|
|
6
|
+
//#region src/components/Ai11yProvider.d.ts
|
|
7
|
+
interface Ai11yProviderContextValue {
|
|
8
|
+
state: Ai11yState;
|
|
9
|
+
currentRoute: string;
|
|
10
|
+
lastError: Ai11yError | null;
|
|
11
|
+
events: Ai11yEvent[];
|
|
12
|
+
onNavigate?: (route: string) => void;
|
|
13
|
+
track: (event: string, payload?: unknown) => void;
|
|
14
|
+
reportError: (error: Error, meta?: {
|
|
15
|
+
surface?: string;
|
|
16
|
+
markerId?: string;
|
|
17
|
+
}) => void;
|
|
18
|
+
describe: () => _ai11y_core0.Ai11yContext;
|
|
19
|
+
act: (instruction: _ai11y_core0.Instruction) => void;
|
|
20
|
+
agentConfig: AgentConfig | null;
|
|
21
|
+
}
|
|
22
|
+
interface Ai11yProviderProps {
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
initialState?: Ai11yState;
|
|
25
|
+
onNavigate?: (route: string) => void;
|
|
26
|
+
agentConfig?: AgentConfig;
|
|
27
|
+
}
|
|
28
|
+
declare function Ai11yProvider({
|
|
29
|
+
children,
|
|
30
|
+
initialState,
|
|
31
|
+
onNavigate,
|
|
32
|
+
agentConfig
|
|
33
|
+
}: Ai11yProviderProps): react_jsx_runtime0.JSX.Element;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { Ai11yProvider, Ai11yProviderContextValue };
|
|
36
|
+
//# sourceMappingURL=Ai11yProvider.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Ai11yProvider.d.mts","names":[],"sources":["../../src/components/Ai11yProvider.tsx"],"sourcesContent":[],"mappings":";;;;;;UAiBiB,yBAAA;SACT;;aAEI;EAHK,MAAA,EAIR,UAJQ,EAAA;EACT,UAAA,CAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAEI,KAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,EAAA,GAAA,IAAA;EACH,WAAA,EAAA,CAAA,KAAA,EAIA,KAJA,EAAA,IAO0C,CAP1C,EAAA;IAIA,OAAA,CAAA,EAAA,MAAA;IAAK,QAGyB,CAAA,EAAA,MAAA;EAAY,CAAA,EAAA,GAAA,IACT;EAC5B,QAAA,EAAA,GAAA,GALA,YAAA,CAGyB,YAEzB;EAAW,GAAA,EAAA,CAAA,WAAA,EAF0B,YAAA,CACT,WACjB,EAAA,GAAA,IAAA;EAOf,WAAA,EAPI,WAOc,GAAA,IAAA;;UAAlB,kBAAA,CAIK;EAAW,QAAA,EAHf,KAAA,CAAM,SAGS;EAGV,YAAA,CAAA,EALA,UAKa;EAC5B,UAAA,CAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EACA,WAAA,CAAA,EALc,WAKd;;AAEA,iBAJe,aAAA,CAIf;EAAA,QAAA;EAAA,YAAA;EAAA,UAAA;EAAA;AAAA,CAAA,EACE,kBADF,CAAA,EACoB,kBAAA,CAAA,GAAA,CAAA,OADpB"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createClient, getError, getEvents, getRoute, getState, setState, subscribe, subscribeToStore } from "@ai11y/core";
|
|
2
|
+
import { createContext, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
|
|
5
|
+
//#region src/components/Ai11yProvider.tsx
|
|
6
|
+
const Ai11yProviderContext = createContext(null);
|
|
7
|
+
function Ai11yProvider({ children, initialState = {}, onNavigate, agentConfig }) {
|
|
8
|
+
const clientRef = useRef(createClient({ onNavigate }));
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (Object.keys(initialState).length > 0) setState(initialState);
|
|
11
|
+
}, [initialState]);
|
|
12
|
+
const [currentRoute, setCurrentRoute] = useState(() => getRoute() || "/");
|
|
13
|
+
const [uiState, setUIState] = useState(() => {
|
|
14
|
+
return getState() || {};
|
|
15
|
+
});
|
|
16
|
+
const [lastError, setLastError] = useState(() => {
|
|
17
|
+
return getError() || null;
|
|
18
|
+
});
|
|
19
|
+
const [events, setEvents] = useState(() => getEvents());
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
return subscribeToStore((type, value$1) => {
|
|
22
|
+
if (type === "route") setCurrentRoute(value$1 || "/");
|
|
23
|
+
else if (type === "state") setUIState(value$1 || {});
|
|
24
|
+
else if (type === "error") setLastError(value$1 || null);
|
|
25
|
+
});
|
|
26
|
+
}, []);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
return subscribe(() => {
|
|
29
|
+
setEvents([...getEvents()]);
|
|
30
|
+
});
|
|
31
|
+
}, []);
|
|
32
|
+
const value = {
|
|
33
|
+
state: uiState,
|
|
34
|
+
currentRoute,
|
|
35
|
+
lastError,
|
|
36
|
+
events,
|
|
37
|
+
onNavigate,
|
|
38
|
+
track: clientRef.current.track.bind(clientRef.current),
|
|
39
|
+
reportError: clientRef.current.reportError.bind(clientRef.current),
|
|
40
|
+
describe: clientRef.current.describe.bind(clientRef.current),
|
|
41
|
+
act: clientRef.current.act.bind(clientRef.current),
|
|
42
|
+
agentConfig: agentConfig ?? null
|
|
43
|
+
};
|
|
44
|
+
return /* @__PURE__ */ jsx(Ai11yProviderContext.Provider, {
|
|
45
|
+
value,
|
|
46
|
+
children
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
export { Ai11yProvider, Ai11yProviderContext };
|
|
52
|
+
//# sourceMappingURL=Ai11yProvider.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Ai11yProvider.mjs","names":["value","value: Ai11yProviderContextValue"],"sources":["../../src/components/Ai11yProvider.tsx"],"sourcesContent":["import {\n\ttype AgentConfig,\n\ttype Ai11yError,\n\ttype Ai11yEvent,\n\ttype Ai11yState,\n\tcreateClient,\n\tgetError,\n\tgetEvents,\n\tgetRoute,\n\tgetState,\n\tsetState,\n\tsubscribe,\n\tsubscribeToStore,\n} from \"@ai11y/core\";\nimport type React from \"react\";\nimport { createContext, useEffect, useRef, useState } from \"react\";\n\nexport interface Ai11yProviderContextValue {\n\tstate: Ai11yState;\n\tcurrentRoute: string;\n\tlastError: Ai11yError | null;\n\tevents: Ai11yEvent[];\n\tonNavigate?: (route: string) => void;\n\ttrack: (event: string, payload?: unknown) => void;\n\treportError: (\n\t\terror: Error,\n\t\tmeta?: { surface?: string; markerId?: string },\n\t) => void;\n\tdescribe: () => import(\"@ai11y/core\").Ai11yContext;\n\tact: (instruction: import(\"@ai11y/core\").Instruction) => void;\n\tagentConfig: AgentConfig | null;\n}\n\nconst Ai11yProviderContext = createContext<Ai11yProviderContextValue | null>(\n\tnull,\n);\n\ninterface Ai11yProviderProps {\n\tchildren: React.ReactNode;\n\tinitialState?: Ai11yState;\n\tonNavigate?: (route: string) => void;\n\tagentConfig?: AgentConfig;\n}\n\nexport function Ai11yProvider({\n\tchildren,\n\tinitialState = {},\n\tonNavigate,\n\tagentConfig,\n}: Ai11yProviderProps) {\n\tconst clientRef = useRef(\n\t\tcreateClient({\n\t\t\tonNavigate,\n\t\t}),\n\t);\n\n\tuseEffect(() => {\n\t\tif (Object.keys(initialState).length > 0) {\n\t\t\tsetState(initialState);\n\t\t}\n\t}, [initialState]);\n\n\tconst [currentRoute, setCurrentRoute] = useState<string>(\n\t\t() => getRoute() || \"/\",\n\t);\n\tconst [uiState, setUIState] = useState<Ai11yState>(() => {\n\t\tconst coreState = getState();\n\t\treturn coreState || {};\n\t});\n\tconst [lastError, setLastError] = useState<Ai11yError | null>(() => {\n\t\tconst coreError = getError();\n\t\treturn coreError || null;\n\t});\n\tconst [events, setEvents] = useState<Ai11yEvent[]>(() => getEvents());\n\n\tuseEffect(() => {\n\t\tconst unsubscribe = subscribeToStore(\n\t\t\t(type: \"route\" | \"state\" | \"error\", value: unknown) => {\n\t\t\t\tif (type === \"route\") {\n\t\t\t\t\tsetCurrentRoute((value as string) || \"/\");\n\t\t\t\t} else if (type === \"state\") {\n\t\t\t\t\tsetUIState((value as Ai11yState) || {});\n\t\t\t\t} else if (type === \"error\") {\n\t\t\t\t\tsetLastError((value as Ai11yError | null) || null);\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\t\treturn unsubscribe;\n\t}, []);\n\n\tuseEffect(() => {\n\t\tconst unsubscribe = subscribe(() => {\n\t\t\tsetEvents([...getEvents()]);\n\t\t});\n\t\treturn unsubscribe;\n\t}, []);\n\n\tconst value: Ai11yProviderContextValue = {\n\t\tstate: uiState,\n\t\tcurrentRoute,\n\t\tlastError,\n\t\tevents,\n\t\tonNavigate,\n\t\ttrack: clientRef.current.track.bind(clientRef.current),\n\t\treportError: clientRef.current.reportError.bind(clientRef.current),\n\t\tdescribe: clientRef.current.describe.bind(clientRef.current),\n\t\tact: clientRef.current.act.bind(clientRef.current),\n\t\tagentConfig: agentConfig ?? null,\n\t};\n\n\treturn (\n\t\t<Ai11yProviderContext.Provider value={value}>\n\t\t\t{children}\n\t\t</Ai11yProviderContext.Provider>\n\t);\n}\n\nexport { Ai11yProviderContext };\n"],"mappings":";;;;;AAiCA,MAAM,uBAAuB,cAC5B,KACA;AASD,SAAgB,cAAc,EAC7B,UACA,eAAe,EAAE,EACjB,YACA,eACsB;CACtB,MAAM,YAAY,OACjB,aAAa,EACZ,YACA,CAAC,CACF;AAED,iBAAgB;AACf,MAAI,OAAO,KAAK,aAAa,CAAC,SAAS,EACtC,UAAS,aAAa;IAErB,CAAC,aAAa,CAAC;CAElB,MAAM,CAAC,cAAc,mBAAmB,eACjC,UAAU,IAAI,IACpB;CACD,MAAM,CAAC,SAAS,cAAc,eAA2B;AAExD,SADkB,UAAU,IACR,EAAE;GACrB;CACF,MAAM,CAAC,WAAW,gBAAgB,eAAkC;AAEnE,SADkB,UAAU,IACR;GACnB;CACF,MAAM,CAAC,QAAQ,aAAa,eAA6B,WAAW,CAAC;AAErE,iBAAgB;AAYf,SAXoB,kBAClB,MAAmC,YAAmB;AACtD,OAAI,SAAS,QACZ,iBAAiBA,WAAoB,IAAI;YAC/B,SAAS,QACnB,YAAYA,WAAwB,EAAE,CAAC;YAC7B,SAAS,QACnB,cAAcA,WAA+B,KAAK;IAGpD;IAEC,EAAE,CAAC;AAEN,iBAAgB;AAIf,SAHoB,gBAAgB;AACnC,aAAU,CAAC,GAAG,WAAW,CAAC,CAAC;IAC1B;IAEA,EAAE,CAAC;CAEN,MAAMC,QAAmC;EACxC,OAAO;EACP;EACA;EACA;EACA;EACA,OAAO,UAAU,QAAQ,MAAM,KAAK,UAAU,QAAQ;EACtD,aAAa,UAAU,QAAQ,YAAY,KAAK,UAAU,QAAQ;EAClE,UAAU,UAAU,QAAQ,SAAS,KAAK,UAAU,QAAQ;EAC5D,KAAK,UAAU,QAAQ,IAAI,KAAK,UAAU,QAAQ;EAClD,aAAa,eAAe;EAC5B;AAED,QACC,oBAAC,qBAAqB;EAAgB;EACpC;GAC8B"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
3
|
+
|
|
4
|
+
//#region src/components/Marker.d.ts
|
|
5
|
+
interface MarkerProps {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
intent: string;
|
|
9
|
+
children: React.ReactElement;
|
|
10
|
+
/** Mark this element as containing sensitive data that should be redacted from the UI context */
|
|
11
|
+
sensitive?: boolean;
|
|
12
|
+
}
|
|
13
|
+
declare function Marker({
|
|
14
|
+
id,
|
|
15
|
+
label,
|
|
16
|
+
intent,
|
|
17
|
+
children,
|
|
18
|
+
sensitive
|
|
19
|
+
}: MarkerProps): react_jsx_runtime0.JSX.Element;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { Marker };
|
|
22
|
+
//# sourceMappingURL=Marker.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Marker.d.mts","names":[],"sources":["../../src/components/Marker.tsx"],"sourcesContent":[],"mappings":";;;;UAQU,WAAA;;;EAAA,MAAA,EAAA,MAAW;EAsBL,QAAA,EAlBL,KAAA,CAAM,YAkBK;EACrB;EACA,SAAA,CAAA,EAAA,OAAA;;AAEA,iBAJe,MAAA,CAIf;EAAA,EAAA;EAAA,KAAA;EAAA,MAAA;EAAA,QAAA;EAAA;AAAA,CAAA,EAEE,WAFF,CAAA,EAEa,kBAAA,CAAA,GAAA,CAAA,OAFb"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ATTRIBUTE_ID, ATTRIBUTE_INTENT, ATTRIBUTE_LABEL, ATTRIBUTE_SENSITIVE } from "@ai11y/core";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
4
|
+
|
|
5
|
+
//#region src/components/Marker.tsx
|
|
6
|
+
function assignRef(ref, value) {
|
|
7
|
+
if (!ref) return;
|
|
8
|
+
if (typeof ref === "function") {
|
|
9
|
+
ref(value);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
ref.current = value;
|
|
14
|
+
} catch {}
|
|
15
|
+
}
|
|
16
|
+
function Marker({ id, label, intent, children, sensitive }) {
|
|
17
|
+
const existingRef = children.props.ref;
|
|
18
|
+
return /* @__PURE__ */ jsx(Fragment, { children: React.cloneElement(children, {
|
|
19
|
+
ref: (node) => {
|
|
20
|
+
assignRef(existingRef, node);
|
|
21
|
+
},
|
|
22
|
+
[ATTRIBUTE_ID]: id,
|
|
23
|
+
...label && { [ATTRIBUTE_LABEL]: label },
|
|
24
|
+
...intent && { [ATTRIBUTE_INTENT]: intent },
|
|
25
|
+
...sensitive && { [ATTRIBUTE_SENSITIVE]: "true" }
|
|
26
|
+
}) });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
export { Marker };
|
|
31
|
+
//# sourceMappingURL=Marker.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Marker.mjs","names":[],"sources":["../../src/components/Marker.tsx"],"sourcesContent":["import {\n\tATTRIBUTE_ID,\n\tATTRIBUTE_INTENT,\n\tATTRIBUTE_LABEL,\n\tATTRIBUTE_SENSITIVE,\n} from \"@ai11y/core\";\nimport React from \"react\";\n\ninterface MarkerProps {\n\tid: string;\n\tlabel: string;\n\tintent: string;\n\tchildren: React.ReactElement;\n\t/** Mark this element as containing sensitive data that should be redacted from the UI context */\n\tsensitive?: boolean;\n}\n\nfunction assignRef<T>(ref: React.Ref<T> | undefined, value: T | null) {\n\tif (!ref) return;\n\tif (typeof ref === \"function\") {\n\t\tref(value);\n\t\treturn;\n\t}\n\ttry {\n\t\t(ref as React.MutableRefObject<T | null>).current = value;\n\t} catch {\n\t\t// ignore read-only refs\n\t}\n}\n\nexport function Marker({\n\tid,\n\tlabel,\n\tintent,\n\tchildren,\n\tsensitive,\n}: MarkerProps) {\n\tconst existingRef = (children.props as { ref?: React.Ref<HTMLElement> }).ref;\n\tconst childWithRef = React.cloneElement(children, {\n\t\tref: (node: HTMLElement | null) => {\n\t\t\tassignRef(existingRef, node);\n\t\t},\n\t\t[ATTRIBUTE_ID]: id,\n\t\t...(label && { [ATTRIBUTE_LABEL]: label }),\n\t\t...(intent && { [ATTRIBUTE_INTENT]: intent }),\n\t\t...(sensitive && { [ATTRIBUTE_SENSITIVE]: \"true\" }),\n\t} as React.HTMLAttributes<HTMLElement> & {\n\t\t[ATTRIBUTE_ID]: string;\n\t\t[ATTRIBUTE_LABEL]?: string;\n\t\t[ATTRIBUTE_INTENT]?: string;\n\t\t[ATTRIBUTE_SENSITIVE]?: string;\n\t});\n\n\treturn <>{childWithRef}</>;\n}\n"],"mappings":";;;;;AAiBA,SAAS,UAAa,KAA+B,OAAiB;AACrE,KAAI,CAAC,IAAK;AACV,KAAI,OAAO,QAAQ,YAAY;AAC9B,MAAI,MAAM;AACV;;AAED,KAAI;AACH,EAAC,IAAyC,UAAU;SAC7C;;AAKT,SAAgB,OAAO,EACtB,IACA,OACA,QACA,UACA,aACe;CACf,MAAM,cAAe,SAAS,MAA2C;AAgBzE,QAAO,0CAfc,MAAM,aAAa,UAAU;EACjD,MAAM,SAA6B;AAClC,aAAU,aAAa,KAAK;;GAE5B,eAAe;EAChB,GAAI,SAAS,GAAG,kBAAkB,OAAO;EACzC,GAAI,UAAU,GAAG,mBAAmB,QAAQ;EAC5C,GAAI,aAAa,GAAG,sBAAsB,QAAQ;EAClD,CAKC,GAEwB"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Ai11yProviderContextValue } from "../components/Ai11yProvider.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/hooks/useAi11yContext.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hook to access the AI accessibility context
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const ctx = useAi11yContext()
|
|
11
|
+
* const ui = ctx.describe()
|
|
12
|
+
* ctx.act({ action: "click", id: "save_button" })
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
declare function useAi11yContext(): Ai11yProviderContextValue;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { useAi11yContext };
|
|
18
|
+
//# sourceMappingURL=useAi11yContext.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAi11yContext.d.mts","names":[],"sources":["../../src/hooks/useAi11yContext.ts"],"sourcesContent":[],"mappings":";;;;;;;AAaA;;;;;;;iBAAgB,eAAA,CAAA,GAAe"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Ai11yProviderContext } from "../components/Ai11yProvider.mjs";
|
|
2
|
+
import { useContext } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/hooks/useAi11yContext.ts
|
|
5
|
+
/**
|
|
6
|
+
* Hook to access the AI accessibility context
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const ctx = useAi11yContext()
|
|
11
|
+
* const ui = ctx.describe()
|
|
12
|
+
* ctx.act({ action: "click", id: "save_button" })
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
function useAi11yContext() {
|
|
16
|
+
const context = useContext(Ai11yProviderContext);
|
|
17
|
+
if (!context) throw new Error("useAi11yContext must be used within Ai11yProvider");
|
|
18
|
+
return context;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { useAi11yContext };
|
|
23
|
+
//# sourceMappingURL=useAi11yContext.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAi11yContext.mjs","names":[],"sources":["../../src/hooks/useAi11yContext.ts"],"sourcesContent":["import { useContext } from \"react\";\nimport { Ai11yProviderContext } from \"../components/Ai11yProvider.js\";\n\n/**\n * Hook to access the AI accessibility context\n *\n * @example\n * ```tsx\n * const ctx = useAi11yContext()\n * const ui = ctx.describe()\n * ctx.act({ action: \"click\", id: \"save_button\" })\n * ```\n */\nexport function useAi11yContext() {\n\tconst context = useContext(Ai11yProviderContext);\n\tif (!context) {\n\t\tthrow new Error(\"useAi11yContext must be used within Ai11yProvider\");\n\t}\n\treturn context;\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,SAAgB,kBAAkB;CACjC,MAAM,UAAU,WAAW,qBAAqB;AAChD,KAAI,CAAC,QACJ,OAAM,IAAI,MAAM,oDAAoD;AAErE,QAAO"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { AgentResponse, Instruction } from "@ai11y/core";
|
|
2
|
+
|
|
3
|
+
//#region src/hooks/useChat.d.ts
|
|
4
|
+
interface Message {
|
|
5
|
+
id: string;
|
|
6
|
+
type: "user" | "assistant" | "system";
|
|
7
|
+
content: string;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
}
|
|
10
|
+
interface UseChatOptions {
|
|
11
|
+
onSubmit: (message: string, messages: Message[]) => Promise<AgentResponse>;
|
|
12
|
+
onInstruction?: (instruction: Instruction) => void;
|
|
13
|
+
onMessage?: () => void;
|
|
14
|
+
initialMessage?: string;
|
|
15
|
+
}
|
|
16
|
+
interface UseChatReturn {
|
|
17
|
+
messages: Message[];
|
|
18
|
+
input: string;
|
|
19
|
+
setInput: (value: string) => void;
|
|
20
|
+
isProcessing: boolean;
|
|
21
|
+
handleSubmit: (e: React.FormEvent) => Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
declare function useChat({
|
|
24
|
+
onSubmit,
|
|
25
|
+
onInstruction,
|
|
26
|
+
onMessage,
|
|
27
|
+
initialMessage
|
|
28
|
+
}: UseChatOptions): UseChatReturn;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { useChat };
|
|
31
|
+
//# sourceMappingURL=useChat.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useChat.d.mts","names":[],"sources":["../../src/hooks/useChat.ts"],"sourcesContent":[],"mappings":";;;UAGiB,OAAA;;EAAA,IAAA,EAAA,MAAO,GAAA,WAAA,GAAA,QAAA;EAOP,OAAA,EAAA,MAAA;EACsB,SAAA,EAAA,MAAA;;AAAc,UADpC,cAAA,CACoC;EACtB,QAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,QAAA,EADQ,OACR,EAAA,EAAA,GADsB,OACtB,CAD8B,aAC9B,CAAA;EAAW,aAAA,CAAA,EAAA,CAAA,WAAA,EAAX,WAAW,EAAA,GAAA,IAAA;EAKzB,SAAA,CAAA,EAAA,GAAA,GAAa,IAAA;EACnB,cAAA,CAAA,EAAA,MAAA;;AAI4B,UALtB,aAAA,CAKsB;EAAO,QAAA,EAJnC,OAImC,EAAA;EAG9B,KAAA,EAAA,MAAO;EACtB,QAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EACA,YAAA,EAAA,OAAA;EACA,YAAA,EAAA,CAAA,CAAA,EANkB,KAAA,CAAM,SAMxB,EAAA,GANsC,OAMtC,CAAA,IAAA,CAAA;;AAEE,iBALa,OAAA,CAKb;EAAA,QAAA;EAAA,aAAA;EAAA,SAAA;EAAA;AAAA,CAAA,EAAA,cAAA,CAAA,EAAiB,aAAjB"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { useCallback, useRef, useState } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/hooks/useChat.ts
|
|
4
|
+
function useChat({ onSubmit, onInstruction, onMessage, initialMessage = "Hi! I'm your AI agent. I can help you navigate, click buttons, and highlight elements. Try saying 'go to billing' or 'click connect stripe'." }) {
|
|
5
|
+
const [messages, setMessages] = useState([{
|
|
6
|
+
id: "welcome",
|
|
7
|
+
type: "assistant",
|
|
8
|
+
content: initialMessage,
|
|
9
|
+
timestamp: Date.now()
|
|
10
|
+
}]);
|
|
11
|
+
const [input, setInput] = useState("");
|
|
12
|
+
const [isProcessing, setIsProcessing] = useState(false);
|
|
13
|
+
const processingRef = useRef(false);
|
|
14
|
+
const messageIdCounterRef = useRef(0);
|
|
15
|
+
const inputRef = useRef(input);
|
|
16
|
+
const isProcessingRef = useRef(isProcessing);
|
|
17
|
+
const messagesRef = useRef(messages);
|
|
18
|
+
inputRef.current = input;
|
|
19
|
+
isProcessingRef.current = isProcessing;
|
|
20
|
+
messagesRef.current = messages;
|
|
21
|
+
return {
|
|
22
|
+
messages,
|
|
23
|
+
input,
|
|
24
|
+
setInput,
|
|
25
|
+
isProcessing,
|
|
26
|
+
handleSubmit: useCallback(async (e) => {
|
|
27
|
+
e.preventDefault();
|
|
28
|
+
if (!inputRef.current.trim() || isProcessingRef.current || processingRef.current) return;
|
|
29
|
+
const userMessage = inputRef.current.trim();
|
|
30
|
+
setInput("");
|
|
31
|
+
setIsProcessing(true);
|
|
32
|
+
processingRef.current = true;
|
|
33
|
+
queueMicrotask(() => onMessage?.());
|
|
34
|
+
messageIdCounterRef.current += 1;
|
|
35
|
+
const userMsg = {
|
|
36
|
+
id: `user-${Date.now()}-${messageIdCounterRef.current}`,
|
|
37
|
+
type: "user",
|
|
38
|
+
content: userMessage,
|
|
39
|
+
timestamp: Date.now()
|
|
40
|
+
};
|
|
41
|
+
const updatedMessages = [...messagesRef.current, userMsg];
|
|
42
|
+
setMessages(updatedMessages);
|
|
43
|
+
queueMicrotask(() => onMessage?.());
|
|
44
|
+
try {
|
|
45
|
+
const response = await onSubmit(userMessage, updatedMessages);
|
|
46
|
+
if (processingRef.current) {
|
|
47
|
+
messageIdCounterRef.current += 1;
|
|
48
|
+
const assistantMsg = {
|
|
49
|
+
id: `assistant-${Date.now()}-${messageIdCounterRef.current}`,
|
|
50
|
+
type: "assistant",
|
|
51
|
+
content: response.reply,
|
|
52
|
+
timestamp: Date.now()
|
|
53
|
+
};
|
|
54
|
+
setMessages((prevMsgs) => {
|
|
55
|
+
if (prevMsgs.some((msg) => msg.id === assistantMsg.id || msg.type === "assistant" && msg.content === response.reply && msg.timestamp === assistantMsg.timestamp)) return prevMsgs;
|
|
56
|
+
return [...prevMsgs, assistantMsg];
|
|
57
|
+
});
|
|
58
|
+
queueMicrotask(() => onMessage?.());
|
|
59
|
+
if (response.instructions && onInstruction) for (const instruction of response.instructions) onInstruction(instruction);
|
|
60
|
+
}
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (processingRef.current) {
|
|
63
|
+
messageIdCounterRef.current += 1;
|
|
64
|
+
const errorMsg = {
|
|
65
|
+
id: `error-${Date.now()}-${messageIdCounterRef.current}`,
|
|
66
|
+
type: "assistant",
|
|
67
|
+
content: `Sorry, I encountered an error: ${error instanceof Error ? error.message : "Unknown error"}. Please try again.`,
|
|
68
|
+
timestamp: Date.now()
|
|
69
|
+
};
|
|
70
|
+
setMessages((prevMsgs) => {
|
|
71
|
+
if (prevMsgs.some((msg) => msg.id === errorMsg.id || msg.type === "assistant" && msg.content === errorMsg.content && msg.timestamp === errorMsg.timestamp)) return prevMsgs;
|
|
72
|
+
return [...prevMsgs, errorMsg];
|
|
73
|
+
});
|
|
74
|
+
queueMicrotask(() => onMessage?.());
|
|
75
|
+
}
|
|
76
|
+
} finally {
|
|
77
|
+
setIsProcessing(false);
|
|
78
|
+
processingRef.current = false;
|
|
79
|
+
}
|
|
80
|
+
}, [
|
|
81
|
+
onSubmit,
|
|
82
|
+
onInstruction,
|
|
83
|
+
onMessage
|
|
84
|
+
])
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
//#endregion
|
|
89
|
+
export { useChat };
|
|
90
|
+
//# sourceMappingURL=useChat.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useChat.mjs","names":["userMsg: Message","assistantMsg: Message","errorMsg: Message"],"sources":["../../src/hooks/useChat.ts"],"sourcesContent":["import type { AgentResponse, Instruction } from \"@ai11y/core\";\nimport { useCallback, useRef, useState } from \"react\";\n\nexport interface Message {\n\tid: string;\n\ttype: \"user\" | \"assistant\" | \"system\";\n\tcontent: string;\n\ttimestamp: number;\n}\n\nexport interface UseChatOptions {\n\tonSubmit: (message: string, messages: Message[]) => Promise<AgentResponse>;\n\tonInstruction?: (instruction: Instruction) => void;\n\tonMessage?: () => void;\n\tinitialMessage?: string;\n}\n\nexport interface UseChatReturn {\n\tmessages: Message[];\n\tinput: string;\n\tsetInput: (value: string) => void;\n\tisProcessing: boolean;\n\thandleSubmit: (e: React.FormEvent) => Promise<void>;\n}\n\nexport function useChat({\n\tonSubmit,\n\tonInstruction,\n\tonMessage,\n\tinitialMessage = \"Hi! I'm your AI agent. I can help you navigate, click buttons, and highlight elements. Try saying 'go to billing' or 'click connect stripe'.\",\n}: UseChatOptions): UseChatReturn {\n\tconst [messages, setMessages] = useState<Message[]>([\n\t\t{\n\t\t\tid: \"welcome\",\n\t\t\ttype: \"assistant\",\n\t\t\tcontent: initialMessage,\n\t\t\ttimestamp: Date.now(),\n\t\t},\n\t]);\n\tconst [input, setInput] = useState(\"\");\n\tconst [isProcessing, setIsProcessing] = useState(false);\n\tconst processingRef = useRef(false);\n\tconst messageIdCounterRef = useRef(0);\n\tconst inputRef = useRef(input);\n\tconst isProcessingRef = useRef(isProcessing);\n\tconst messagesRef = useRef(messages);\n\tinputRef.current = input;\n\tisProcessingRef.current = isProcessing;\n\tmessagesRef.current = messages;\n\n\tconst handleSubmit = useCallback(\n\t\tasync (e: React.FormEvent) => {\n\t\t\te.preventDefault();\n\t\t\tif (\n\t\t\t\t!inputRef.current.trim() ||\n\t\t\t\tisProcessingRef.current ||\n\t\t\t\tprocessingRef.current\n\t\t\t)\n\t\t\t\treturn;\n\n\t\t\tconst userMessage = inputRef.current.trim();\n\t\t\tsetInput(\"\");\n\t\t\tsetIsProcessing(true);\n\t\t\tprocessingRef.current = true;\n\t\t\tqueueMicrotask(() => onMessage?.());\n\n\t\t\tmessageIdCounterRef.current += 1;\n\t\t\tconst userMsg: Message = {\n\t\t\t\tid: `user-${Date.now()}-${messageIdCounterRef.current}`,\n\t\t\t\ttype: \"user\",\n\t\t\t\tcontent: userMessage,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t};\n\n\t\t\tconst updatedMessages = [...messagesRef.current, userMsg];\n\t\t\tsetMessages(updatedMessages);\n\t\t\tqueueMicrotask(() => onMessage?.());\n\n\t\t\ttry {\n\t\t\t\tconst response = await onSubmit(userMessage, updatedMessages);\n\n\t\t\t\t// Add agent reply only if we're still processing (prevent duplicates)\n\t\t\t\tif (processingRef.current) {\n\t\t\t\t\tmessageIdCounterRef.current += 1;\n\t\t\t\t\tconst assistantMsg: Message = {\n\t\t\t\t\t\tid: `assistant-${Date.now()}-${messageIdCounterRef.current}`,\n\t\t\t\t\t\ttype: \"assistant\",\n\t\t\t\t\t\tcontent: response.reply,\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t};\n\t\t\t\t\tsetMessages((prevMsgs) => {\n\t\t\t\t\t\tconst exists = prevMsgs.some(\n\t\t\t\t\t\t\t(msg) =>\n\t\t\t\t\t\t\t\tmsg.id === assistantMsg.id ||\n\t\t\t\t\t\t\t\t(msg.type === \"assistant\" &&\n\t\t\t\t\t\t\t\t\tmsg.content === response.reply &&\n\t\t\t\t\t\t\t\t\tmsg.timestamp === assistantMsg.timestamp),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (exists) return prevMsgs;\n\t\t\t\t\t\treturn [...prevMsgs, assistantMsg];\n\t\t\t\t\t});\n\t\t\t\t\tqueueMicrotask(() => onMessage?.());\n\n\t\t\t\t\tif (response.instructions && onInstruction) {\n\t\t\t\t\t\tfor (const instruction of response.instructions) {\n\t\t\t\t\t\t\tonInstruction(instruction);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (processingRef.current) {\n\t\t\t\t\tmessageIdCounterRef.current += 1;\n\t\t\t\t\tconst errorMsg: Message = {\n\t\t\t\t\t\tid: `error-${Date.now()}-${messageIdCounterRef.current}`,\n\t\t\t\t\t\ttype: \"assistant\",\n\t\t\t\t\t\tcontent: `Sorry, I encountered an error: ${error instanceof Error ? error.message : \"Unknown error\"}. Please try again.`,\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t};\n\t\t\t\t\tsetMessages((prevMsgs) => {\n\t\t\t\t\t\tconst exists = prevMsgs.some(\n\t\t\t\t\t\t\t(msg) =>\n\t\t\t\t\t\t\t\tmsg.id === errorMsg.id ||\n\t\t\t\t\t\t\t\t(msg.type === \"assistant\" &&\n\t\t\t\t\t\t\t\t\tmsg.content === errorMsg.content &&\n\t\t\t\t\t\t\t\t\tmsg.timestamp === errorMsg.timestamp),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (exists) return prevMsgs;\n\t\t\t\t\t\treturn [...prevMsgs, errorMsg];\n\t\t\t\t\t});\n\t\t\t\t\tqueueMicrotask(() => onMessage?.());\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tsetIsProcessing(false);\n\t\t\t\tprocessingRef.current = false;\n\t\t\t}\n\t\t},\n\t\t[onSubmit, onInstruction, onMessage],\n\t);\n\n\treturn {\n\t\tmessages,\n\t\tinput,\n\t\tsetInput,\n\t\tisProcessing,\n\t\thandleSubmit,\n\t};\n}\n"],"mappings":";;;AAyBA,SAAgB,QAAQ,EACvB,UACA,eACA,WACA,iBAAiB,kJACgB;CACjC,MAAM,CAAC,UAAU,eAAe,SAAoB,CACnD;EACC,IAAI;EACJ,MAAM;EACN,SAAS;EACT,WAAW,KAAK,KAAK;EACrB,CACD,CAAC;CACF,MAAM,CAAC,OAAO,YAAY,SAAS,GAAG;CACtC,MAAM,CAAC,cAAc,mBAAmB,SAAS,MAAM;CACvD,MAAM,gBAAgB,OAAO,MAAM;CACnC,MAAM,sBAAsB,OAAO,EAAE;CACrC,MAAM,WAAW,OAAO,MAAM;CAC9B,MAAM,kBAAkB,OAAO,aAAa;CAC5C,MAAM,cAAc,OAAO,SAAS;AACpC,UAAS,UAAU;AACnB,iBAAgB,UAAU;AAC1B,aAAY,UAAU;AA2FtB,QAAO;EACN;EACA;EACA;EACA;EACA,cA9FoB,YACpB,OAAO,MAAuB;AAC7B,KAAE,gBAAgB;AAClB,OACC,CAAC,SAAS,QAAQ,MAAM,IACxB,gBAAgB,WAChB,cAAc,QAEd;GAED,MAAM,cAAc,SAAS,QAAQ,MAAM;AAC3C,YAAS,GAAG;AACZ,mBAAgB,KAAK;AACrB,iBAAc,UAAU;AACxB,wBAAqB,aAAa,CAAC;AAEnC,uBAAoB,WAAW;GAC/B,MAAMA,UAAmB;IACxB,IAAI,QAAQ,KAAK,KAAK,CAAC,GAAG,oBAAoB;IAC9C,MAAM;IACN,SAAS;IACT,WAAW,KAAK,KAAK;IACrB;GAED,MAAM,kBAAkB,CAAC,GAAG,YAAY,SAAS,QAAQ;AACzD,eAAY,gBAAgB;AAC5B,wBAAqB,aAAa,CAAC;AAEnC,OAAI;IACH,MAAM,WAAW,MAAM,SAAS,aAAa,gBAAgB;AAG7D,QAAI,cAAc,SAAS;AAC1B,yBAAoB,WAAW;KAC/B,MAAMC,eAAwB;MAC7B,IAAI,aAAa,KAAK,KAAK,CAAC,GAAG,oBAAoB;MACnD,MAAM;MACN,SAAS,SAAS;MAClB,WAAW,KAAK,KAAK;MACrB;AACD,kBAAa,aAAa;AAQzB,UAPe,SAAS,MACtB,QACA,IAAI,OAAO,aAAa,MACvB,IAAI,SAAS,eACb,IAAI,YAAY,SAAS,SACzB,IAAI,cAAc,aAAa,UACjC,CACW,QAAO;AACnB,aAAO,CAAC,GAAG,UAAU,aAAa;OACjC;AACF,0BAAqB,aAAa,CAAC;AAEnC,SAAI,SAAS,gBAAgB,cAC5B,MAAK,MAAM,eAAe,SAAS,aAClC,eAAc,YAAY;;YAIrB,OAAO;AACf,QAAI,cAAc,SAAS;AAC1B,yBAAoB,WAAW;KAC/B,MAAMC,WAAoB;MACzB,IAAI,SAAS,KAAK,KAAK,CAAC,GAAG,oBAAoB;MAC/C,MAAM;MACN,SAAS,kCAAkC,iBAAiB,QAAQ,MAAM,UAAU,gBAAgB;MACpG,WAAW,KAAK,KAAK;MACrB;AACD,kBAAa,aAAa;AAQzB,UAPe,SAAS,MACtB,QACA,IAAI,OAAO,SAAS,MACnB,IAAI,SAAS,eACb,IAAI,YAAY,SAAS,WACzB,IAAI,cAAc,SAAS,UAC7B,CACW,QAAO;AACnB,aAAO,CAAC,GAAG,UAAU,SAAS;OAC7B;AACF,0BAAqB,aAAa,CAAC;;aAE3B;AACT,oBAAgB,MAAM;AACtB,kBAAc,UAAU;;KAG1B;GAAC;GAAU;GAAe;GAAU,CACpC;EAQA"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Ai11yProvider } from "./components/Ai11yProvider.mjs";
|
|
2
|
+
import { Marker } from "./components/Marker.mjs";
|
|
3
|
+
import { useAi11yContext } from "./hooks/useAi11yContext.mjs";
|
|
4
|
+
import { useChat } from "./hooks/useChat.mjs";
|
|
5
|
+
export { Ai11yProvider, Marker, useAi11yContext, useChat };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Ai11yProvider } from "./components/Ai11yProvider.mjs";
|
|
2
|
+
import { Marker } from "./components/Marker.mjs";
|
|
3
|
+
import { useAi11yContext } from "./hooks/useAi11yContext.mjs";
|
|
4
|
+
import { useChat } from "./hooks/useChat.mjs";
|
|
5
|
+
|
|
6
|
+
export { Ai11yProvider, Marker, useAi11yContext, useChat };
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ai11y/react",
|
|
3
|
+
"author": "maerzhase3000",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.mjs",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.mts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.mts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"default": "./dist/index.mjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@ai11y/core": "0.0.1"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"react": "19.2.3",
|
|
22
|
+
"react-dom": "19.2.3"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@tsconfig/create-react-app": "2.0.10",
|
|
26
|
+
"@types/react": "19.2.7",
|
|
27
|
+
"@types/react-dom": "19.2.3",
|
|
28
|
+
"react": "19.2.3",
|
|
29
|
+
"react-dom": "19.2.3",
|
|
30
|
+
"tsdown": "0.18.1",
|
|
31
|
+
"typescript": "5.9.3"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsdown",
|
|
38
|
+
"watch": "tsdown --watch",
|
|
39
|
+
"dev": "tsdown --watch"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AgentConfig,
|
|
3
|
+
type Ai11yError,
|
|
4
|
+
type Ai11yEvent,
|
|
5
|
+
type Ai11yState,
|
|
6
|
+
createClient,
|
|
7
|
+
getError,
|
|
8
|
+
getEvents,
|
|
9
|
+
getRoute,
|
|
10
|
+
getState,
|
|
11
|
+
setState,
|
|
12
|
+
subscribe,
|
|
13
|
+
subscribeToStore,
|
|
14
|
+
} from "@ai11y/core";
|
|
15
|
+
import type React from "react";
|
|
16
|
+
import { createContext, useEffect, useRef, useState } from "react";
|
|
17
|
+
|
|
18
|
+
export interface Ai11yProviderContextValue {
|
|
19
|
+
state: Ai11yState;
|
|
20
|
+
currentRoute: string;
|
|
21
|
+
lastError: Ai11yError | null;
|
|
22
|
+
events: Ai11yEvent[];
|
|
23
|
+
onNavigate?: (route: string) => void;
|
|
24
|
+
track: (event: string, payload?: unknown) => void;
|
|
25
|
+
reportError: (
|
|
26
|
+
error: Error,
|
|
27
|
+
meta?: { surface?: string; markerId?: string },
|
|
28
|
+
) => void;
|
|
29
|
+
describe: () => import("@ai11y/core").Ai11yContext;
|
|
30
|
+
act: (instruction: import("@ai11y/core").Instruction) => void;
|
|
31
|
+
agentConfig: AgentConfig | null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const Ai11yProviderContext = createContext<Ai11yProviderContextValue | null>(
|
|
35
|
+
null,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
interface Ai11yProviderProps {
|
|
39
|
+
children: React.ReactNode;
|
|
40
|
+
initialState?: Ai11yState;
|
|
41
|
+
onNavigate?: (route: string) => void;
|
|
42
|
+
agentConfig?: AgentConfig;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function Ai11yProvider({
|
|
46
|
+
children,
|
|
47
|
+
initialState = {},
|
|
48
|
+
onNavigate,
|
|
49
|
+
agentConfig,
|
|
50
|
+
}: Ai11yProviderProps) {
|
|
51
|
+
const clientRef = useRef(
|
|
52
|
+
createClient({
|
|
53
|
+
onNavigate,
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (Object.keys(initialState).length > 0) {
|
|
59
|
+
setState(initialState);
|
|
60
|
+
}
|
|
61
|
+
}, [initialState]);
|
|
62
|
+
|
|
63
|
+
const [currentRoute, setCurrentRoute] = useState<string>(
|
|
64
|
+
() => getRoute() || "/",
|
|
65
|
+
);
|
|
66
|
+
const [uiState, setUIState] = useState<Ai11yState>(() => {
|
|
67
|
+
const coreState = getState();
|
|
68
|
+
return coreState || {};
|
|
69
|
+
});
|
|
70
|
+
const [lastError, setLastError] = useState<Ai11yError | null>(() => {
|
|
71
|
+
const coreError = getError();
|
|
72
|
+
return coreError || null;
|
|
73
|
+
});
|
|
74
|
+
const [events, setEvents] = useState<Ai11yEvent[]>(() => getEvents());
|
|
75
|
+
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
const unsubscribe = subscribeToStore(
|
|
78
|
+
(type: "route" | "state" | "error", value: unknown) => {
|
|
79
|
+
if (type === "route") {
|
|
80
|
+
setCurrentRoute((value as string) || "/");
|
|
81
|
+
} else if (type === "state") {
|
|
82
|
+
setUIState((value as Ai11yState) || {});
|
|
83
|
+
} else if (type === "error") {
|
|
84
|
+
setLastError((value as Ai11yError | null) || null);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
return unsubscribe;
|
|
89
|
+
}, []);
|
|
90
|
+
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
const unsubscribe = subscribe(() => {
|
|
93
|
+
setEvents([...getEvents()]);
|
|
94
|
+
});
|
|
95
|
+
return unsubscribe;
|
|
96
|
+
}, []);
|
|
97
|
+
|
|
98
|
+
const value: Ai11yProviderContextValue = {
|
|
99
|
+
state: uiState,
|
|
100
|
+
currentRoute,
|
|
101
|
+
lastError,
|
|
102
|
+
events,
|
|
103
|
+
onNavigate,
|
|
104
|
+
track: clientRef.current.track.bind(clientRef.current),
|
|
105
|
+
reportError: clientRef.current.reportError.bind(clientRef.current),
|
|
106
|
+
describe: clientRef.current.describe.bind(clientRef.current),
|
|
107
|
+
act: clientRef.current.act.bind(clientRef.current),
|
|
108
|
+
agentConfig: agentConfig ?? null,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<Ai11yProviderContext.Provider value={value}>
|
|
113
|
+
{children}
|
|
114
|
+
</Ai11yProviderContext.Provider>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export { Ai11yProviderContext };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ATTRIBUTE_ID,
|
|
3
|
+
ATTRIBUTE_INTENT,
|
|
4
|
+
ATTRIBUTE_LABEL,
|
|
5
|
+
ATTRIBUTE_SENSITIVE,
|
|
6
|
+
} from "@ai11y/core";
|
|
7
|
+
import React from "react";
|
|
8
|
+
|
|
9
|
+
interface MarkerProps {
|
|
10
|
+
id: string;
|
|
11
|
+
label: string;
|
|
12
|
+
intent: string;
|
|
13
|
+
children: React.ReactElement;
|
|
14
|
+
/** Mark this element as containing sensitive data that should be redacted from the UI context */
|
|
15
|
+
sensitive?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function assignRef<T>(ref: React.Ref<T> | undefined, value: T | null) {
|
|
19
|
+
if (!ref) return;
|
|
20
|
+
if (typeof ref === "function") {
|
|
21
|
+
ref(value);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
(ref as React.MutableRefObject<T | null>).current = value;
|
|
26
|
+
} catch {
|
|
27
|
+
// ignore read-only refs
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function Marker({
|
|
32
|
+
id,
|
|
33
|
+
label,
|
|
34
|
+
intent,
|
|
35
|
+
children,
|
|
36
|
+
sensitive,
|
|
37
|
+
}: MarkerProps) {
|
|
38
|
+
const existingRef = (children.props as { ref?: React.Ref<HTMLElement> }).ref;
|
|
39
|
+
const childWithRef = React.cloneElement(children, {
|
|
40
|
+
ref: (node: HTMLElement | null) => {
|
|
41
|
+
assignRef(existingRef, node);
|
|
42
|
+
},
|
|
43
|
+
[ATTRIBUTE_ID]: id,
|
|
44
|
+
...(label && { [ATTRIBUTE_LABEL]: label }),
|
|
45
|
+
...(intent && { [ATTRIBUTE_INTENT]: intent }),
|
|
46
|
+
...(sensitive && { [ATTRIBUTE_SENSITIVE]: "true" }),
|
|
47
|
+
} as React.HTMLAttributes<HTMLElement> & {
|
|
48
|
+
[ATTRIBUTE_ID]: string;
|
|
49
|
+
[ATTRIBUTE_LABEL]?: string;
|
|
50
|
+
[ATTRIBUTE_INTENT]?: string;
|
|
51
|
+
[ATTRIBUTE_SENSITIVE]?: string;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return <>{childWithRef}</>;
|
|
55
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { Ai11yProviderContext } from "../components/Ai11yProvider.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook to access the AI accessibility context
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* const ctx = useAi11yContext()
|
|
10
|
+
* const ui = ctx.describe()
|
|
11
|
+
* ctx.act({ action: "click", id: "save_button" })
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export function useAi11yContext() {
|
|
15
|
+
const context = useContext(Ai11yProviderContext);
|
|
16
|
+
if (!context) {
|
|
17
|
+
throw new Error("useAi11yContext must be used within Ai11yProvider");
|
|
18
|
+
}
|
|
19
|
+
return context;
|
|
20
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { AgentResponse, Instruction } from "@ai11y/core";
|
|
2
|
+
import { useCallback, useRef, useState } from "react";
|
|
3
|
+
|
|
4
|
+
export interface Message {
|
|
5
|
+
id: string;
|
|
6
|
+
type: "user" | "assistant" | "system";
|
|
7
|
+
content: string;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface UseChatOptions {
|
|
12
|
+
onSubmit: (message: string, messages: Message[]) => Promise<AgentResponse>;
|
|
13
|
+
onInstruction?: (instruction: Instruction) => void;
|
|
14
|
+
onMessage?: () => void;
|
|
15
|
+
initialMessage?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface UseChatReturn {
|
|
19
|
+
messages: Message[];
|
|
20
|
+
input: string;
|
|
21
|
+
setInput: (value: string) => void;
|
|
22
|
+
isProcessing: boolean;
|
|
23
|
+
handleSubmit: (e: React.FormEvent) => Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function useChat({
|
|
27
|
+
onSubmit,
|
|
28
|
+
onInstruction,
|
|
29
|
+
onMessage,
|
|
30
|
+
initialMessage = "Hi! I'm your AI agent. I can help you navigate, click buttons, and highlight elements. Try saying 'go to billing' or 'click connect stripe'.",
|
|
31
|
+
}: UseChatOptions): UseChatReturn {
|
|
32
|
+
const [messages, setMessages] = useState<Message[]>([
|
|
33
|
+
{
|
|
34
|
+
id: "welcome",
|
|
35
|
+
type: "assistant",
|
|
36
|
+
content: initialMessage,
|
|
37
|
+
timestamp: Date.now(),
|
|
38
|
+
},
|
|
39
|
+
]);
|
|
40
|
+
const [input, setInput] = useState("");
|
|
41
|
+
const [isProcessing, setIsProcessing] = useState(false);
|
|
42
|
+
const processingRef = useRef(false);
|
|
43
|
+
const messageIdCounterRef = useRef(0);
|
|
44
|
+
const inputRef = useRef(input);
|
|
45
|
+
const isProcessingRef = useRef(isProcessing);
|
|
46
|
+
const messagesRef = useRef(messages);
|
|
47
|
+
inputRef.current = input;
|
|
48
|
+
isProcessingRef.current = isProcessing;
|
|
49
|
+
messagesRef.current = messages;
|
|
50
|
+
|
|
51
|
+
const handleSubmit = useCallback(
|
|
52
|
+
async (e: React.FormEvent) => {
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
if (
|
|
55
|
+
!inputRef.current.trim() ||
|
|
56
|
+
isProcessingRef.current ||
|
|
57
|
+
processingRef.current
|
|
58
|
+
)
|
|
59
|
+
return;
|
|
60
|
+
|
|
61
|
+
const userMessage = inputRef.current.trim();
|
|
62
|
+
setInput("");
|
|
63
|
+
setIsProcessing(true);
|
|
64
|
+
processingRef.current = true;
|
|
65
|
+
queueMicrotask(() => onMessage?.());
|
|
66
|
+
|
|
67
|
+
messageIdCounterRef.current += 1;
|
|
68
|
+
const userMsg: Message = {
|
|
69
|
+
id: `user-${Date.now()}-${messageIdCounterRef.current}`,
|
|
70
|
+
type: "user",
|
|
71
|
+
content: userMessage,
|
|
72
|
+
timestamp: Date.now(),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const updatedMessages = [...messagesRef.current, userMsg];
|
|
76
|
+
setMessages(updatedMessages);
|
|
77
|
+
queueMicrotask(() => onMessage?.());
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const response = await onSubmit(userMessage, updatedMessages);
|
|
81
|
+
|
|
82
|
+
// Add agent reply only if we're still processing (prevent duplicates)
|
|
83
|
+
if (processingRef.current) {
|
|
84
|
+
messageIdCounterRef.current += 1;
|
|
85
|
+
const assistantMsg: Message = {
|
|
86
|
+
id: `assistant-${Date.now()}-${messageIdCounterRef.current}`,
|
|
87
|
+
type: "assistant",
|
|
88
|
+
content: response.reply,
|
|
89
|
+
timestamp: Date.now(),
|
|
90
|
+
};
|
|
91
|
+
setMessages((prevMsgs) => {
|
|
92
|
+
const exists = prevMsgs.some(
|
|
93
|
+
(msg) =>
|
|
94
|
+
msg.id === assistantMsg.id ||
|
|
95
|
+
(msg.type === "assistant" &&
|
|
96
|
+
msg.content === response.reply &&
|
|
97
|
+
msg.timestamp === assistantMsg.timestamp),
|
|
98
|
+
);
|
|
99
|
+
if (exists) return prevMsgs;
|
|
100
|
+
return [...prevMsgs, assistantMsg];
|
|
101
|
+
});
|
|
102
|
+
queueMicrotask(() => onMessage?.());
|
|
103
|
+
|
|
104
|
+
if (response.instructions && onInstruction) {
|
|
105
|
+
for (const instruction of response.instructions) {
|
|
106
|
+
onInstruction(instruction);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
if (processingRef.current) {
|
|
112
|
+
messageIdCounterRef.current += 1;
|
|
113
|
+
const errorMsg: Message = {
|
|
114
|
+
id: `error-${Date.now()}-${messageIdCounterRef.current}`,
|
|
115
|
+
type: "assistant",
|
|
116
|
+
content: `Sorry, I encountered an error: ${error instanceof Error ? error.message : "Unknown error"}. Please try again.`,
|
|
117
|
+
timestamp: Date.now(),
|
|
118
|
+
};
|
|
119
|
+
setMessages((prevMsgs) => {
|
|
120
|
+
const exists = prevMsgs.some(
|
|
121
|
+
(msg) =>
|
|
122
|
+
msg.id === errorMsg.id ||
|
|
123
|
+
(msg.type === "assistant" &&
|
|
124
|
+
msg.content === errorMsg.content &&
|
|
125
|
+
msg.timestamp === errorMsg.timestamp),
|
|
126
|
+
);
|
|
127
|
+
if (exists) return prevMsgs;
|
|
128
|
+
return [...prevMsgs, errorMsg];
|
|
129
|
+
});
|
|
130
|
+
queueMicrotask(() => onMessage?.());
|
|
131
|
+
}
|
|
132
|
+
} finally {
|
|
133
|
+
setIsProcessing(false);
|
|
134
|
+
processingRef.current = false;
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
[onSubmit, onInstruction, onMessage],
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
messages,
|
|
142
|
+
input,
|
|
143
|
+
setInput,
|
|
144
|
+
isProcessing,
|
|
145
|
+
handleSubmit,
|
|
146
|
+
};
|
|
147
|
+
}
|
package/src/index.ts
ADDED
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "nodenext",
|
|
6
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"declarationMap": true,
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"outDir": "dist",
|
|
12
|
+
"rootDir": "src",
|
|
13
|
+
"strict": true,
|
|
14
|
+
"esModuleInterop": true,
|
|
15
|
+
"skipLibCheck": true,
|
|
16
|
+
"forceConsistentCasingInFileNames": true,
|
|
17
|
+
"noImplicitReturns": true,
|
|
18
|
+
"noUnusedLocals": false
|
|
19
|
+
},
|
|
20
|
+
"include": ["src/**/*"],
|
|
21
|
+
"exclude": ["node_modules", "dist"]
|
|
22
|
+
}
|
package/tsdown.config.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineConfig } from "tsdown";
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: ["src/index.ts"],
|
|
5
|
+
unbundle: true,
|
|
6
|
+
format: ["esm"],
|
|
7
|
+
dts: true,
|
|
8
|
+
clean: true,
|
|
9
|
+
outDir: "dist",
|
|
10
|
+
external: [
|
|
11
|
+
"react",
|
|
12
|
+
"react-dom",
|
|
13
|
+
"react/jsx-runtime",
|
|
14
|
+
"@ai11y/ui",
|
|
15
|
+
"@ai11y/core",
|
|
16
|
+
],
|
|
17
|
+
});
|