a2ui-react 0.4.0 → 0.5.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/README.md CHANGED
@@ -20,32 +20,19 @@ yarn add a2ui-react
20
20
 
21
21
  ## Setup
22
22
 
23
- ### 1. Configure Tailwind v4 (Critical)
24
-
25
- Add the `@source` directive to your CSS so Tailwind scans the component classes:
23
+ Add the theme import and `@source` directive to your main CSS file:
26
24
 
27
25
  ```css
28
- @import 'tailwindcss';
29
- @source "../node_modules/a2ui-react/dist";
30
- ```
31
-
32
- Without this, Tailwind v4 won't scan node_modules and components render unstyled.
33
-
34
- ### 2. Import Theme CSS
26
+ /* Import the a2ui-react theme (includes Tailwind and all theme variables) */
27
+ @import 'a2ui-react/theme.css';
35
28
 
36
- Import the theme CSS in your app entry point:
37
-
38
- ```tsx
39
- import 'a2ui-react/theme.css'
29
+ /* Tell Tailwind to scan the package for class usage */
30
+ @source "../node_modules/a2ui-react/dist";
40
31
  ```
41
32
 
42
- > **Vite users**: If the import doesn't resolve, copy the CSS locally:
43
- > ```bash
44
- > cp node_modules/a2ui-react/dist/theme.css src/a2ui-theme.css
45
- > ```
46
- > Then import `'./a2ui-theme.css'` instead.
47
-
48
- Or if using your own Tailwind setup, ensure your CSS includes the [shadcn/ui theme variables](https://ui.shadcn.com/docs/theming).
33
+ Both lines are required:
34
+ - `theme.css` provides Tailwind, shadcn/ui theme variables, and alert color tokens
35
+ - `@source` ensures Tailwind includes the component classes (v4 doesn't scan node_modules by default)
49
36
 
50
37
  ## Quick Start
51
38
 
@@ -243,14 +230,7 @@ The `id` must be present both in the update wrapper AND inside the component obj
243
230
 
244
231
  ### a2ui-go Compatibility
245
232
 
246
- If using [a2ui-go](https://github.com/burka/a2ui-go) as your backend, you'll need to transform messages:
247
-
248
- | a2ui-go format | a2ui-react format |
249
- |----------------|-------------------|
250
- | `surfaceUpdate.components` | `surfaceUpdate.updates` |
251
- | `{"id":"x", "Text":{...}}` | `{"id":"x", "component":{"type":"Text",...}}` |
252
- | `text` field | `content` field |
253
- | `usageHint` field | `style` field |
233
+ Works out of the box with [a2ui-go](https://github.com/burka/a2ui-go) - both use the A2UI v0.9 message format.
254
234
 
255
235
  ## License
256
236
 
package/dist/index.js CHANGED
@@ -696,8 +696,13 @@ function A2UISurface({
696
696
  const surface = useSurface(surfaceId);
697
697
  const [loading, setLoading] = React7.useState(false);
698
698
  const [error, setError] = React7.useState(null);
699
+ const processedMessagesRef = React7.useRef(null);
699
700
  React7.useEffect(() => {
700
701
  if (messages) {
702
+ if (messages === processedMessagesRef.current) {
703
+ return;
704
+ }
705
+ processedMessagesRef.current = messages;
701
706
  try {
702
707
  for (const message of messages) {
703
708
  processMessage(message, store);
@@ -4771,14 +4776,14 @@ var TextScrambleRenderer = {
4771
4776
  type: "TextScramble",
4772
4777
  render: ({ component }) => {
4773
4778
  const [displayText, setDisplayText] = React7.useState(component.content);
4774
- const [isAnimating, setIsAnimating] = React7.useState(false);
4779
+ const isAnimatingRef = React7.useRef(false);
4775
4780
  const prefersReducedMotion = useReducedMotion();
4776
4781
  const speed = component.speed || 30;
4777
4782
  const characters = component.characters || defaultCharacters;
4778
4783
  const trigger = component.trigger || "mount";
4779
- const scramble = () => {
4780
- if (prefersReducedMotion || isAnimating) return;
4781
- setIsAnimating(true);
4784
+ const scramble = React7.useCallback(() => {
4785
+ if (prefersReducedMotion || isAnimatingRef.current) return;
4786
+ isAnimatingRef.current = true;
4782
4787
  const target = component.content;
4783
4788
  const length = target.length;
4784
4789
  let iteration = 0;
@@ -4797,10 +4802,10 @@ var TextScrambleRenderer = {
4797
4802
  if (iteration >= maxIterations) {
4798
4803
  clearInterval(interval);
4799
4804
  setDisplayText(target);
4800
- setIsAnimating(false);
4805
+ isAnimatingRef.current = false;
4801
4806
  }
4802
4807
  }, speed);
4803
- };
4808
+ }, [component.content, characters, speed, prefersReducedMotion]);
4804
4809
  React7.useEffect(() => {
4805
4810
  if (trigger === "mount") {
4806
4811
  scramble();