@bravostudioai/react 0.1.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.
Files changed (127) hide show
  1. package/bin/encore-lib.js +3 -0
  2. package/dist/_virtual/_commonjsHelpers.js +7 -0
  3. package/dist/_virtual/_commonjsHelpers.js.map +1 -0
  4. package/dist/_virtual/main.js +8 -0
  5. package/dist/_virtual/main.js.map +1 -0
  6. package/dist/_virtual/main2.js +5 -0
  7. package/dist/_virtual/main2.js.map +1 -0
  8. package/dist/app.js +9 -0
  9. package/dist/app.js.map +1 -0
  10. package/dist/cli/commands/download.js +82 -0
  11. package/dist/cli/commands/download.js.map +1 -0
  12. package/dist/cli/commands/generate.js +1526 -0
  13. package/dist/cli/commands/generate.js.map +1 -0
  14. package/dist/cli.js +25 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/components/DynamicComponent.js +24 -0
  17. package/dist/components/DynamicComponent.js.map +1 -0
  18. package/dist/components/EncoreApp.js +259 -0
  19. package/dist/components/EncoreApp.js.map +1 -0
  20. package/dist/components/EncoreErrorBoundary.js +33 -0
  21. package/dist/components/EncoreErrorBoundary.js.map +1 -0
  22. package/dist/components/EncoreLoadingFallback.js +20 -0
  23. package/dist/components/EncoreLoadingFallback.js.map +1 -0
  24. package/dist/components.js +1454 -0
  25. package/dist/components.js.map +1 -0
  26. package/dist/constants.d.ts +3 -0
  27. package/dist/constants.d.ts.map +1 -0
  28. package/dist/contexts/EncoreActionContext.js +6 -0
  29. package/dist/contexts/EncoreActionContext.js.map +1 -0
  30. package/dist/contexts/EncoreAppContext.js +9 -0
  31. package/dist/contexts/EncoreAppContext.js.map +1 -0
  32. package/dist/contexts/EncoreBindingContext.js +6 -0
  33. package/dist/contexts/EncoreBindingContext.js.map +1 -0
  34. package/dist/contexts/EncoreComponentIdContext.js +8 -0
  35. package/dist/contexts/EncoreComponentIdContext.js.map +1 -0
  36. package/dist/contexts/EncoreRepeatingContainerContext.js +6 -0
  37. package/dist/contexts/EncoreRepeatingContainerContext.js.map +1 -0
  38. package/dist/hooks/usePusherUpdates.js +60 -0
  39. package/dist/hooks/usePusherUpdates.js.map +1 -0
  40. package/dist/index.js +16 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/lib/dynamicModules.js +132 -0
  43. package/dist/lib/dynamicModules.js.map +1 -0
  44. package/dist/lib/fetcher.js +58 -0
  45. package/dist/lib/fetcher.js.map +1 -0
  46. package/dist/lib/localMode.js +21 -0
  47. package/dist/lib/localMode.js.map +1 -0
  48. package/dist/lib/packages.js +18 -0
  49. package/dist/lib/packages.js.map +1 -0
  50. package/dist/node_modules/dotenv/lib/main.js +198 -0
  51. package/dist/node_modules/dotenv/lib/main.js.map +1 -0
  52. package/dist/node_modules/dotenv/package.json.js +8 -0
  53. package/dist/node_modules/dotenv/package.json.js.map +1 -0
  54. package/dist/packages/encore-lib/constants.js +6 -0
  55. package/dist/packages/encore-lib/constants.js.map +1 -0
  56. package/dist/src/app.d.ts +5 -0
  57. package/dist/src/app.d.ts.map +1 -0
  58. package/dist/src/cli/commands/download.d.ts +2 -0
  59. package/dist/src/cli/commands/download.d.ts.map +1 -0
  60. package/dist/src/cli/commands/generate.d.ts +2 -0
  61. package/dist/src/cli/commands/generate.d.ts.map +1 -0
  62. package/dist/src/cli/index.d.ts +2 -0
  63. package/dist/src/cli/index.d.ts.map +1 -0
  64. package/dist/src/components/DynamicComponent.d.ts +12 -0
  65. package/dist/src/components/DynamicComponent.d.ts.map +1 -0
  66. package/dist/src/components/EncoreApp.d.ts +27 -0
  67. package/dist/src/components/EncoreApp.d.ts.map +1 -0
  68. package/dist/src/components/EncoreErrorBoundary.d.ts +17 -0
  69. package/dist/src/components/EncoreErrorBoundary.d.ts.map +1 -0
  70. package/dist/src/components/EncoreLoadingFallback.d.ts +4 -0
  71. package/dist/src/components/EncoreLoadingFallback.d.ts.map +1 -0
  72. package/dist/src/components.d.ts +4 -0
  73. package/dist/src/components.d.ts.map +1 -0
  74. package/dist/src/contexts/EncoreActionContext.d.ts +13 -0
  75. package/dist/src/contexts/EncoreActionContext.d.ts.map +1 -0
  76. package/dist/src/contexts/EncoreAppContext.d.ts +8 -0
  77. package/dist/src/contexts/EncoreAppContext.d.ts.map +1 -0
  78. package/dist/src/contexts/EncoreBindingContext.d.ts +5 -0
  79. package/dist/src/contexts/EncoreBindingContext.d.ts.map +1 -0
  80. package/dist/src/contexts/EncoreComponentIdContext.d.ts +8 -0
  81. package/dist/src/contexts/EncoreComponentIdContext.d.ts.map +1 -0
  82. package/dist/src/contexts/EncoreRepeatingContainerContext.d.ts +21 -0
  83. package/dist/src/contexts/EncoreRepeatingContainerContext.d.ts.map +1 -0
  84. package/dist/src/hooks/useAuthRedirect.d.ts +3 -0
  85. package/dist/src/hooks/useAuthRedirect.d.ts.map +1 -0
  86. package/dist/src/hooks/usePusherUpdates.d.ts +18 -0
  87. package/dist/src/hooks/usePusherUpdates.d.ts.map +1 -0
  88. package/dist/src/index.d.ts +8 -0
  89. package/dist/src/index.d.ts.map +1 -0
  90. package/dist/src/lib/dynamicModules.d.ts +8 -0
  91. package/dist/src/lib/dynamicModules.d.ts.map +1 -0
  92. package/dist/src/lib/fetcher.d.ts +5 -0
  93. package/dist/src/lib/fetcher.d.ts.map +1 -0
  94. package/dist/src/lib/localMode.d.ts +3 -0
  95. package/dist/src/lib/localMode.d.ts.map +1 -0
  96. package/dist/src/lib/packages.d.ts +6 -0
  97. package/dist/src/lib/packages.d.ts.map +1 -0
  98. package/dist/src/stores/useEncoreState.d.ts +33 -0
  99. package/dist/src/stores/useEncoreState.d.ts.map +1 -0
  100. package/dist/stores/useEncoreState.js +70 -0
  101. package/dist/stores/useEncoreState.js.map +1 -0
  102. package/package.json +60 -0
  103. package/src/AGENTS.md +161 -0
  104. package/src/README.md +110 -0
  105. package/src/app.ts +5 -0
  106. package/src/cli/commands/download.ts +133 -0
  107. package/src/cli/commands/generate.ts +3045 -0
  108. package/src/cli/index.ts +35 -0
  109. package/src/components/DynamicComponent.tsx +40 -0
  110. package/src/components/EncoreApp.tsx +759 -0
  111. package/src/components/EncoreErrorBoundary.tsx +49 -0
  112. package/src/components/EncoreLoadingFallback.tsx +25 -0
  113. package/src/components.tsx +3155 -0
  114. package/src/contexts/EncoreActionContext.ts +18 -0
  115. package/src/contexts/EncoreAppContext.ts +13 -0
  116. package/src/contexts/EncoreBindingContext.ts +6 -0
  117. package/src/contexts/EncoreComponentIdContext.ts +12 -0
  118. package/src/contexts/EncoreRepeatingContainerContext.ts +30 -0
  119. package/src/hooks/useAuthRedirect.ts +63 -0
  120. package/src/hooks/usePusherUpdates.ts +156 -0
  121. package/src/index.ts +16 -0
  122. package/src/lib/dynamicModules.ts +193 -0
  123. package/src/lib/fetcher.ts +108 -0
  124. package/src/lib/localMode.ts +30 -0
  125. package/src/lib/moduleRegistry.ts +24 -0
  126. package/src/lib/packages.ts +33 -0
  127. package/src/stores/useEncoreState.ts +121 -0
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ type Props = {
3
+ children: React.ReactNode;
4
+ fallback?: React.ReactNode;
5
+ };
6
+ type State = {
7
+ hasError: boolean;
8
+ error: unknown;
9
+ };
10
+ export default class EncoreErrorBoundary extends React.Component<Props, State> {
11
+ constructor(props: Props);
12
+ static getDerivedStateFromError(error: unknown): State;
13
+ componentDidCatch(error: unknown): void;
14
+ render(): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
15
+ }
16
+ export {};
17
+ //# sourceMappingURL=EncoreErrorBoundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EncoreErrorBoundary.d.ts","sourceRoot":"","sources":["../../../src/components/EncoreErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,KAAK,KAAK,GAAG;IACT,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC9B,CAAC;AAEF,KAAK,KAAK,GAAG;IACT,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,KAAK,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC;gBAC9D,KAAK,EAAE,KAAK;IAKxB,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK;IAItD,iBAAiB,CAAC,KAAK,EAAE,OAAO;IAKhC,MAAM;CAqBT"}
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ declare const EncoreLoadingFallback: React.FC;
3
+ export default EncoreLoadingFallback;
4
+ //# sourceMappingURL=EncoreLoadingFallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EncoreLoadingFallback.d.ts","sourceRoot":"","sources":["../../../src/components/EncoreLoadingFallback.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,QAAA,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAoBlC,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ declare const components: Record<string, React.ComponentType<any>>;
3
+ export default components;
4
+ //# sourceMappingURL=components.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../src/components.tsx"],"names":[],"mappings":"AACA,OAAO,KAMN,MAAM,OAAO,CAAC;AAqhGf,QAAA,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAoDxD,CAAC;AAEF,eAAe,UAAU,CAAC"}
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+ export type EncoreActionPayload = {
3
+ bravo: {
4
+ cancel: () => void;
5
+ action: any;
6
+ };
7
+ };
8
+ type EncoreActionContextType = {
9
+ onAction?: (payload: EncoreActionPayload) => void | Promise<void>;
10
+ };
11
+ declare const EncoreActionContext: React.Context<EncoreActionContextType>;
12
+ export default EncoreActionContext;
13
+ //# sourceMappingURL=EncoreActionContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EncoreActionContext.d.ts","sourceRoot":"","sources":["../../../src/contexts/EncoreActionContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,IAAI,CAAC;QACnB,MAAM,EAAE,GAAG,CAAC;KACb,CAAC;CACH,CAAC;AAEF,KAAK,uBAAuB,GAAG;IAC7B,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnE,CAAC;AAEF,QAAA,MAAM,mBAAmB,wCAAmD,CAAC;AAE7E,eAAe,mBAAmB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ type EncoreAppContext = {
3
+ app: any;
4
+ baseURL: string;
5
+ };
6
+ declare const EncoreAppContext: React.Context<EncoreAppContext>;
7
+ export default EncoreAppContext;
8
+ //# sourceMappingURL=EncoreAppContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EncoreAppContext.d.ts","sourceRoot":"","sources":["../../../src/contexts/EncoreAppContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,KAAK,gBAAgB,GAAG;IACtB,GAAG,EAAE,GAAG,CAAC;IACT,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,QAAA,MAAM,gBAAgB,iCAGpB,CAAC;AAEH,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import React from "react";
2
+ type EncoreBindingContext = {};
3
+ declare const EncoreBindingContext: React.Context<{}>;
4
+ export default EncoreBindingContext;
5
+ //# sourceMappingURL=EncoreBindingContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EncoreBindingContext.d.ts","sourceRoot":"","sources":["../../../src/contexts/EncoreBindingContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,KAAK,oBAAoB,GAAG,EAAE,CAAC;AAC/B,QAAA,MAAM,oBAAoB,mBAA0B,CAAC;AAErD,eAAe,oBAAoB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ type EncoreComponentIdContext = {
3
+ componentId?: string;
4
+ statefulSetId?: string;
5
+ };
6
+ declare const EncoreComponentIdContext: React.Context<EncoreComponentIdContext>;
7
+ export default EncoreComponentIdContext;
8
+ //# sourceMappingURL=EncoreComponentIdContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EncoreComponentIdContext.d.ts","sourceRoot":"","sources":["../../../src/contexts/EncoreComponentIdContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,KAAK,wBAAwB,GAAG;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,QAAA,MAAM,wBAAwB,yCAE7B,CAAC;AAEF,eAAe,wBAAwB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ export type RepeatingContainerControl = {
3
+ currentIndex?: number;
4
+ onIndexChange?: (index: number, containerId: string) => void;
5
+ goToIndex: (index: number) => void;
6
+ getCurrentIndex: () => number;
7
+ };
8
+ type ControlProps = {
9
+ currentIndex?: number;
10
+ onIndexChange?: (index: number) => void;
11
+ };
12
+ type EncoreRepeatingContainerContextValue = {
13
+ registerContainer: (id: string, control: RepeatingContainerControl) => void;
14
+ unregisterContainer: (id: string) => void;
15
+ getControl: (id: string) => RepeatingContainerControl | undefined;
16
+ setControlProps: (id: string, props: ControlProps | ((prev: ControlProps) => ControlProps)) => void;
17
+ getControlProps: (id: string) => ControlProps | undefined;
18
+ };
19
+ declare const EncoreRepeatingContainerContext: React.Context<EncoreRepeatingContainerContextValue | null>;
20
+ export default EncoreRepeatingContainerContext;
21
+ //# sourceMappingURL=EncoreRepeatingContainerContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EncoreRepeatingContainerContext.d.ts","sourceRoot":"","sources":["../../../src/contexts/EncoreRepeatingContainerContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,MAAM,yBAAyB,GAAG;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7D,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,eAAe,EAAE,MAAM,MAAM,CAAC;CAC/B,CAAC;AAEF,KAAK,YAAY,GAAG;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC,CAAC;AAEF,KAAK,oCAAoC,GAAG;IAC1C,iBAAiB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,yBAAyB,KAAK,IAAI,CAAC;IAC5E,mBAAmB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,yBAAyB,GAAG,SAAS,CAAC;IAClE,eAAe,EAAE,CACf,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,YAAY,GAAG,CAAC,CAAC,IAAI,EAAE,YAAY,KAAK,YAAY,CAAC,KACzD,IAAI,CAAC;IACV,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,YAAY,GAAG,SAAS,CAAC;CAC3D,CAAC;AAEF,QAAA,MAAM,+BAA+B,4DACmC,CAAC;AAEzE,eAAe,+BAA+B,CAAC"}
@@ -0,0 +1,3 @@
1
+ declare const useAuthRedirect: () => void;
2
+ export default useAuthRedirect;
3
+ //# sourceMappingURL=useAuthRedirect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAuthRedirect.d.ts","sourceRoot":"","sources":["../../../src/hooks/useAuthRedirect.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,eAAe,YAuDpB,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -0,0 +1,18 @@
1
+ import Pusher from "pusher-js";
2
+ type UsePusherUpdatesOptions = {
3
+ appId: string;
4
+ pageId?: string;
5
+ enabled?: boolean;
6
+ onUpdate?: () => void;
7
+ };
8
+ /**
9
+ * Hook to listen for EncoreApp component updates via Pusher
10
+ * Channel name format: `${appId}-${pageId}`
11
+ * When an update event is received, it invalidates SWR cache and triggers a reload
12
+ */
13
+ export declare function usePusherUpdates({ appId, pageId, enabled, onUpdate, }: UsePusherUpdatesOptions): {
14
+ channel: import("pusher-js").Channel | null;
15
+ pusher: Pusher | null;
16
+ };
17
+ export {};
18
+ //# sourceMappingURL=usePusherUpdates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePusherUpdates.d.ts","sourceRoot":"","sources":["../../../src/hooks/usePusherUpdates.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,WAAW,CAAC;AAU/B,KAAK,uBAAuB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC;AAWF;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,MAAM,EACN,OAAc,EACd,QAAQ,GACT,EAAE,uBAAuB;;;EAsHzB"}
@@ -0,0 +1,8 @@
1
+ import EncoreApp from "./components/EncoreApp";
2
+ import EncoreErrorBoundary from "./components/EncoreErrorBoundary";
3
+ import EncoreLoadingFallback from "./components/EncoreLoadingFallback";
4
+ import EncoreAppContext from "./contexts/EncoreAppContext";
5
+ import EncoreBindingContext from "./contexts/EncoreBindingContext";
6
+ import useEncoreState from "./stores/useEncoreState";
7
+ export { EncoreApp, EncoreErrorBoundary, EncoreLoadingFallback, EncoreAppContext, EncoreBindingContext, useEncoreState, };
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,SAAS,MAAM,wBAAwB,CAAC;AAC/C,OAAO,mBAAmB,MAAM,kCAAkC,CAAC;AACnE,OAAO,qBAAqB,MAAM,oCAAoC,CAAC;AACvE,OAAO,gBAAgB,MAAM,6BAA6B,CAAC;AAC3D,OAAO,oBAAoB,MAAM,iCAAiC,CAAC;AACnE,OAAO,cAAc,MAAM,yBAAyB,CAAC;AAErD,OAAO,EACL,SAAS,EACT,mBAAmB,EACnB,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,GACf,CAAC"}
@@ -0,0 +1,8 @@
1
+ export declare function loadAMDModule(name: string, code: string): Promise<any>;
2
+ export declare function fetchDep(name: string): Promise<any>;
3
+ /**
4
+ * Clear a module from the cache to force reload on next fetch
5
+ * @param name Module name (e.g., `${appId}/draft/components/${pageId}`)
6
+ */
7
+ export declare function clearModuleCache(name: string): void;
8
+ //# sourceMappingURL=dynamicModules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamicModules.d.ts","sourceRoot":"","sources":["../../../src/lib/dynamicModules.ts"],"names":[],"mappings":"AAwBA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,gBAyGvD;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,gBAoE1C;AAOD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAKnD"}
@@ -0,0 +1,5 @@
1
+ declare const getAppsServiceUrl: () => string;
2
+ declare const fetcher: (url: string) => any;
3
+ export default fetcher;
4
+ export { getAppsServiceUrl as appsServiceUrl };
5
+ //# sourceMappingURL=fetcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../../../src/lib/fetcher.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,iBAAiB,cAEtB,CAAC;AAIF,QAAA,MAAM,OAAO,GAAI,KAAK,MAAM,QA6F3B,CAAC;AAEF,eAAe,OAAO,CAAC;AACvB,OAAO,EAAE,iBAAiB,IAAI,cAAc,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function setLocalModeOverride(value: "local" | "remote" | null): void;
2
+ export declare function isLocalMode(): boolean;
3
+ //# sourceMappingURL=localMode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localMode.d.ts","sourceRoot":"","sources":["../../../src/lib/localMode.ts"],"names":[],"mappings":"AAGA,wBAAgB,oBAAoB,CAChC,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,GACjC,IAAI,CAEN;AAED,wBAAgB,WAAW,IAAI,OAAO,CAkBrC"}
@@ -0,0 +1,6 @@
1
+ export type Package = {
2
+ exports: unknown;
3
+ };
4
+ declare const Packages: Record<string, () => Package>;
5
+ export default Packages;
6
+ //# sourceMappingURL=packages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packages.d.ts","sourceRoot":"","sources":["../../../src/lib/packages.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,OAAO,GAAG;IAClB,OAAO,EAAE,OAAO,CAAC;CACpB,CAAC;AAGF,QAAA,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAO3C,CAAC;AAQF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,33 @@
1
+ type EncoreState = {
2
+ app: any;
3
+ baseURL: string;
4
+ accessToken?: {
5
+ expireAt: number;
6
+ token: string;
7
+ refreshToken: string;
8
+ params: string;
9
+ };
10
+ appId?: string;
11
+ formInputs: Record<string, Record<string, any>>;
12
+ pageId?: string;
13
+ statefulSetVariants: Record<string, string>;
14
+ inputGroups: Record<string, string>;
15
+ assetsById: Record<string, any>;
16
+ fontsById: Record<string, string>;
17
+ fontsByIdFull: Record<string, {
18
+ family: string;
19
+ postScriptName?: string;
20
+ }>;
21
+ resetFormInputs: () => void;
22
+ setAccessToken: (accessToken: EncoreState["accessToken"]) => void;
23
+ setApp: (app: any) => void;
24
+ setAppId: (appId: string) => void;
25
+ setFormInputValue: (nodeId: string, value: any) => void;
26
+ setPageId: (pageId: string) => void;
27
+ setStatefulSetVariant: (statefulSetId: string, variant: string) => void;
28
+ setInputGroupValue: (groupName: string, elementName: string) => void;
29
+ setBaseURL: (baseURL: string) => void;
30
+ };
31
+ declare const useEncoreState: import("zustand").UseBoundStore<import("zustand").StoreApi<EncoreState>>;
32
+ export default useEncoreState;
33
+ //# sourceMappingURL=useEncoreState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEncoreState.d.ts","sourceRoot":"","sources":["../../../src/stores/useEncoreState.ts"],"names":[],"mappings":"AAGA,KAAK,WAAW,GAAG;IACjB,GAAG,EAAE,GAAG,CAAC;IACT,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,YAAY,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE3E,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,cAAc,EAAE,CAAC,WAAW,EAAE,WAAW,CAAC,aAAa,CAAC,KAAK,IAAI,CAAC;IAClE,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IACxD,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,qBAAqB,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACxE,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC,CAAC;AAEF,QAAA,MAAM,cAAc,0EAsFjB,CAAC;AAEJ,eAAe,cAAc,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { create as u } from "zustand";
2
+ import { CONST_APPS_SERVICE_URL as r } from "../packages/encore-lib/constants.js";
3
+ const f = u((o) => ({
4
+ app: void 0,
5
+ baseURL: r,
6
+ accessToken: void 0,
7
+ appId: void 0,
8
+ formInputs: {},
9
+ pageId: void 0,
10
+ statefulSetVariants: {},
11
+ inputGroups: {},
12
+ assetsById: {},
13
+ fontsById: {},
14
+ fontsByIdFull: {},
15
+ resetFormInputs: () => o((s) => ({ ...s, formInputs: {} })),
16
+ setAccessToken: (s) => o((t) => ({ ...t, accessToken: s })),
17
+ setApp: (s) => o((t) => ({
18
+ ...t,
19
+ app: s,
20
+ assetsById: s.app?.assets?.reduce?.(
21
+ (e, n) => ({
22
+ ...e,
23
+ [n.id]: n
24
+ }),
25
+ {}
26
+ ) ?? {},
27
+ fontsById: s.app?.fonts?.reduce?.(
28
+ (e, n) => ({
29
+ ...e,
30
+ [n.id]: n.fontName?.family || "sans-serif"
31
+ }),
32
+ {}
33
+ ) ?? {},
34
+ fontsByIdFull: s.app?.fonts?.reduce?.(
35
+ (e, n) => ({
36
+ ...e,
37
+ [n.id]: {
38
+ family: n.fontName?.family || "sans-serif",
39
+ postScriptName: n.fontName?.postScriptName
40
+ }
41
+ }),
42
+ {}
43
+ ) ?? {}
44
+ })),
45
+ setAppId: (s) => o((t) => ({ ...t, appId: s })),
46
+ setFormInputValue: (s, t) => o((e) => {
47
+ const p = { ...e.formInputs[e.pageId] || {}, [s]: t }, a = { ...e.formInputs, [e.pageId]: p };
48
+ return { ...e, formInputs: a };
49
+ }),
50
+ setPageId: (s) => o((t) => ({ ...t, pageId: s })),
51
+ setStatefulSetVariant: (s, t) => o((e) => ({
52
+ ...e,
53
+ statefulSetVariants: {
54
+ ...e.statefulSetVariants,
55
+ [s]: t
56
+ }
57
+ })),
58
+ setInputGroupValue: (s, t) => o((e) => ({
59
+ ...e,
60
+ inputGroups: {
61
+ ...e.inputGroups,
62
+ [s]: t
63
+ }
64
+ })),
65
+ setBaseURL: (s) => o((t) => ({ ...t, baseURL: s }))
66
+ }));
67
+ export {
68
+ f as default
69
+ };
70
+ //# sourceMappingURL=useEncoreState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEncoreState.js","sources":["../../src/stores/useEncoreState.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { CONST_APPS_SERVICE_URL } from \"../../constants\";\n\ntype EncoreState = {\n app: any;\n baseURL: string;\n accessToken?: {\n expireAt: number;\n token: string;\n refreshToken: string;\n params: string;\n };\n appId?: string;\n formInputs: Record<string, Record<string, any>>;\n pageId?: string;\n statefulSetVariants: Record<string, string>;\n inputGroups: Record<string, string>; // Maps group name to active element name\n assetsById: Record<string, any>;\n fontsById: Record<string, string>;\n fontsByIdFull: Record<string, { family: string; postScriptName?: string }>;\n\n resetFormInputs: () => void;\n setAccessToken: (accessToken: EncoreState[\"accessToken\"]) => void;\n setApp: (app: any) => void;\n setAppId: (appId: string) => void;\n setFormInputValue: (nodeId: string, value: any) => void;\n setPageId: (pageId: string) => void;\n setStatefulSetVariant: (statefulSetId: string, variant: string) => void;\n setInputGroupValue: (groupName: string, elementName: string) => void;\n setBaseURL: (baseURL: string) => void;\n};\n\nconst useEncoreState = create<EncoreState>((set) => ({\n app: undefined,\n baseURL:\n import.meta.env.VITE_APPS_SERVICE_URL || CONST_APPS_SERVICE_URL || \n \"https://apps-service-dev.bravostudio.app\",\n accessToken: undefined,\n appId: undefined,\n formInputs: {},\n pageId: undefined,\n statefulSetVariants: {},\n inputGroups: {},\n assetsById: {},\n fontsById: {},\n fontsByIdFull: {},\n\n resetFormInputs: () => set((state) => ({ ...state, formInputs: {} })),\n\n setAccessToken: (accessToken) => set((state) => ({ ...state, accessToken })),\n\n setApp: (app: any) =>\n set((state) => ({\n ...state,\n app,\n assetsById:\n app.app?.assets?.reduce?.(\n (acc: Record<string, any>, asset: any) => ({\n ...acc,\n [asset.id]: asset,\n }),\n {}\n ) ?? {},\n fontsById:\n app.app?.fonts?.reduce?.(\n (acc: Record<string, string>, font: any) => ({\n ...acc,\n [font.id]: font.fontName?.family || \"sans-serif\",\n }),\n {}\n ) ?? {},\n fontsByIdFull:\n app.app?.fonts?.reduce?.(\n (\n acc: Record<string, { family: string; postScriptName?: string }>,\n font: any\n ) => ({\n ...acc,\n [font.id]: {\n family: font.fontName?.family || \"sans-serif\",\n postScriptName: font.fontName?.postScriptName,\n },\n }),\n {}\n ) ?? {},\n })),\n\n setAppId: (appId) => set((state) => ({ ...state, appId })),\n\n setFormInputValue: (nodeId, value) =>\n set((state) => {\n const oldPageInputs = state.formInputs[state.pageId!] || {};\n const newPageInputs = { ...oldPageInputs, [nodeId]: value };\n const newInputs = { ...state.formInputs, [state.pageId!]: newPageInputs };\n return { ...state, formInputs: newInputs };\n }),\n\n setPageId: (pageId) => set((state) => ({ ...state, pageId })),\n\n setStatefulSetVariant: (statefulSetId, variant) =>\n set((state) => ({\n ...state,\n statefulSetVariants: {\n ...state.statefulSetVariants,\n [statefulSetId]: variant,\n },\n })),\n\n setInputGroupValue: (groupName, elementName) =>\n set((state) => ({\n ...state,\n inputGroups: {\n ...state.inputGroups,\n [groupName]: elementName,\n },\n })),\n\n setBaseURL: (baseURL) => set((state) => ({ ...state, baseURL })),\n}));\n\nexport default useEncoreState;\n"],"names":["useEncoreState","create","set","CONST_APPS_SERVICE_URL","state","accessToken","app","acc","asset","font","appId","nodeId","value","newPageInputs","newInputs","pageId","statefulSetId","variant","groupName","elementName","baseURL"],"mappings":";;AAgCA,MAAMA,IAAiBC,EAAoB,CAACC,OAAS;AAAA,EACnD,KAAK;AAAA,EACL,SAC2CC;AAAA,EAE3C,aAAa;AAAA,EACb,OAAO;AAAA,EACP,YAAY,CAAA;AAAA,EACZ,QAAQ;AAAA,EACR,qBAAqB,CAAA;AAAA,EACrB,aAAa,CAAA;AAAA,EACb,YAAY,CAAA;AAAA,EACZ,WAAW,CAAA;AAAA,EACX,eAAe,CAAA;AAAA,EAEf,iBAAiB,MAAMD,EAAI,CAACE,OAAW,EAAE,GAAGA,GAAO,YAAY,CAAA,EAAC,EAAI;AAAA,EAEpE,gBAAgB,CAACC,MAAgBH,EAAI,CAACE,OAAW,EAAE,GAAGA,GAAO,aAAAC,EAAA,EAAc;AAAA,EAE3E,QAAQ,CAACC,MACPJ,EAAI,CAACE,OAAW;AAAA,IACd,GAAGA;AAAA,IACH,KAAAE;AAAA,IACA,YACEA,EAAI,KAAK,QAAQ;AAAA,MACf,CAACC,GAA0BC,OAAgB;AAAA,QACzC,GAAGD;AAAA,QACH,CAACC,EAAM,EAAE,GAAGA;AAAA,MAAA;AAAA,MAEd,CAAA;AAAA,IAAC,KACE,CAAA;AAAA,IACP,WACEF,EAAI,KAAK,OAAO;AAAA,MACd,CAACC,GAA6BE,OAAe;AAAA,QAC3C,GAAGF;AAAA,QACH,CAACE,EAAK,EAAE,GAAGA,EAAK,UAAU,UAAU;AAAA,MAAA;AAAA,MAEtC,CAAA;AAAA,IAAC,KACE,CAAA;AAAA,IACP,eACEH,EAAI,KAAK,OAAO;AAAA,MACd,CACEC,GACAE,OACI;AAAA,QACJ,GAAGF;AAAA,QACH,CAACE,EAAK,EAAE,GAAG;AAAA,UACT,QAAQA,EAAK,UAAU,UAAU;AAAA,UACjC,gBAAgBA,EAAK,UAAU;AAAA,QAAA;AAAA,MACjC;AAAA,MAEF,CAAA;AAAA,IAAC,KACE,CAAA;AAAA,EAAC,EACR;AAAA,EAEJ,UAAU,CAACC,MAAUR,EAAI,CAACE,OAAW,EAAE,GAAGA,GAAO,OAAAM,EAAA,EAAQ;AAAA,EAEzD,mBAAmB,CAACC,GAAQC,MAC1BV,EAAI,CAACE,MAAU;AAEb,UAAMS,IAAgB,EAAE,GADFT,EAAM,WAAWA,EAAM,MAAO,KAAK,CAAA,GACf,CAACO,CAAM,GAAGC,EAAA,GAC9CE,IAAY,EAAE,GAAGV,EAAM,YAAY,CAACA,EAAM,MAAO,GAAGS,EAAA;AAC1D,WAAO,EAAE,GAAGT,GAAO,YAAYU,EAAA;AAAA,EACjC,CAAC;AAAA,EAEH,WAAW,CAACC,MAAWb,EAAI,CAACE,OAAW,EAAE,GAAGA,GAAO,QAAAW,EAAA,EAAS;AAAA,EAE5D,uBAAuB,CAACC,GAAeC,MACrCf,EAAI,CAACE,OAAW;AAAA,IACd,GAAGA;AAAA,IACH,qBAAqB;AAAA,MACnB,GAAGA,EAAM;AAAA,MACT,CAACY,CAAa,GAAGC;AAAA,IAAA;AAAA,EACnB,EACA;AAAA,EAEJ,oBAAoB,CAACC,GAAWC,MAC9BjB,EAAI,CAACE,OAAW;AAAA,IACd,GAAGA;AAAA,IACH,aAAa;AAAA,MACX,GAAGA,EAAM;AAAA,MACT,CAACc,CAAS,GAAGC;AAAA,IAAA;AAAA,EACf,EACA;AAAA,EAEJ,YAAY,CAACC,MAAYlB,EAAI,CAACE,OAAW,EAAE,GAAGA,GAAO,SAAAgB,IAAU;AACjE,EAAE;"}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@bravostudioai/react",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/src/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./components": {
13
+ "types": "./dist/src/components.d.ts",
14
+ "import": "./dist/components.js"
15
+ },
16
+ "./contexts/*": "./dist/contexts/*",
17
+ "./hooks/*": "./dist/hooks/*",
18
+ "./stores/*": "./dist/stores/*",
19
+ "./lib/*": "./dist/lib/*"
20
+ },
21
+ "bin": "./bin/encore-lib.js",
22
+ "files": [
23
+ "dist",
24
+ "src",
25
+ "bin"
26
+ ],
27
+ "scripts": {
28
+ "build": "vite build && tsc --emitDeclarationOnly",
29
+ "download-bravo": "tsx scripts/download-bravo.ts",
30
+ "reverse-bravo": "tsx scripts/reverse-bravo.ts",
31
+ "generate-wrapper": "tsx scripts/generate-wrapper.ts",
32
+ "lint": "eslint ."
33
+ },
34
+ "dependencies": {
35
+ "axios": "^1.13.0",
36
+ "dotenv": "^16.0.0",
37
+ "pusher-js": "^8.4.0",
38
+ "swr": "^2.3.6",
39
+ "zustand": "^5.0.8"
40
+ },
41
+ "devDependencies": {
42
+ "@eslint/js": "^9.39.1",
43
+ "@types/node": "^24.6.0",
44
+ "@types/react": "^19.1.16",
45
+ "@vitejs/plugin-react": "^5.1.1",
46
+ "eslint": "^9.39.1",
47
+ "eslint-plugin-react-hooks": "^7.0.1",
48
+ "eslint-plugin-react-refresh": "^0.4.24",
49
+ "globals": "^16.5.0",
50
+ "react-element-to-jsx-string": "^17.0.1",
51
+ "tsx": "^4.19.2",
52
+ "typescript": "~5.9.3",
53
+ "typescript-eslint": "^8.46.4",
54
+ "vite": "^7.2.4"
55
+ },
56
+ "peerDependencies": {
57
+ "react": "^19.2.0",
58
+ "react-dom": "^19.2.0"
59
+ }
60
+ }
package/src/AGENTS.md ADDED
@@ -0,0 +1,161 @@
1
+ # Guidance for AI Agents working in `src/bravo/`
2
+
3
+ This document lists practical rules and gotchas observed while implementing the Bravo renderer. Follow these to avoid layout regressions and broken renders.
4
+
5
+ ## Golden Rules
6
+
7
+ - Do NOT use Tailwind here. All styling must be inline (see `README.md`).
8
+ - Respect the library boundary: changes in `src/bravo/**` should not depend on app-only tooling.
9
+ - Prefer small, local changes and verify with the default app (`01KA23JMNBQ2V9NR7K0VXKT5TF`).
10
+
11
+ ## Build Process (CRITICAL)
12
+
13
+ > **⚠️ Changes to `encore-lib` are NOT automatically reflected in consuming apps!**
14
+
15
+ - After making changes to any file in `packages/encore-lib/src/`, you **MUST** run:
16
+ ```bash
17
+ cd packages/encore-lib
18
+ yarn build
19
+ ```
20
+ - The library builds to `packages/encore-lib/dist/` which is what consuming apps (like `test-app-2`, `test-app-3`) import.
21
+ - Dev servers in consuming apps will pick up the rebuilt library after a browser refresh.
22
+ - **No restart** of the consuming app's dev server is required, just rebuild `encore-lib` and refresh the browser.
23
+ - If you're not seeing your changes reflected, the most likely cause is forgetting to rebuild `encore-lib`.
24
+
25
+ ## Layout & Positioning
26
+
27
+ - Positioning context:
28
+ - Containers that host absolutely positioned children must set `position: 'relative'`.
29
+ - Background layers must be `position: 'absolute'` with `zIndex: -1` and should not participate in layout.
30
+ - Prevent zero‑size containers:
31
+ - Use `ResizeObserver` to measure and pass dimensions via `BravoContainerContext`.
32
+ - For container wrappers, default `width: '100%'`, `height: '100%'` if computed size is not yet known.
33
+ - Percent sizing:
34
+ - Convert percentages using the nearest container’s measured width/height, not `window.innerWidth/innerHeight`.
35
+ - Guard conversions when dimensions are `0` to avoid collapsing to `0px`.
36
+
37
+ ## Typography & Units
38
+
39
+ - When mapping numeric style fields to CSS, add units where required:
40
+ - `lineHeightPx` → `lineHeight = (value * scaleFactor) + 'px'`
41
+ - `fontSize` uses numeric pixels (React handles units when used directly as numbers).
42
+ - Re-check any new numeric style property; if it corresponds to a CSS length, ensure a unit.
43
+
44
+ ## Backgrounds & Layers
45
+
46
+ - Color layers:
47
+ - Never skip rendering color layers by name (e.g., “SOLID”). Honor background color if not fully transparent.
48
+ - Z-order:
49
+ - Action hit-areas or overlays should use explicit `zIndex` to avoid covering content unintentionally.
50
+
51
+ ## Scaffolding & Scrolling
52
+
53
+ - Page structure:
54
+ - Keep a non-scrollable group (backgrounds, top bars) separate from the scrollable page area.
55
+ - The scrollable container should fill its parent: `width: '100%'`, `height: '100%'`, `overflow: 'auto'`.
56
+ - Scale factor:
57
+ - `scaleFactor` must be derived from the measured container rectangle (via `ResizeObserver`), not the window.
58
+ - Use a fit strategy: `min(containerWidth / originalWidth, containerHeight / originalHeight)`.
59
+ - This guarantees the render scales responsively to whatever rectangle it is placed in, with edge-to-edge layout.
60
+
61
+ ## Spacing & Layout Mapping
62
+
63
+ - Scale layout paddings and gaps by `scaleFactor`:
64
+ - In `useBravoStyle`, map `style.layout.padding.{top,right,bottom,left}` and `style.layout.itemSpacing` to CSS by multiplying each by `scaleFactor`.
65
+ - This keeps card paddings and inter-card spacing visually consistent with Figma across container sizes.
66
+ - Border radii:
67
+ - For percent-based radii from Bravo, convert using the nearest container’s width (see `percentOfParentWidthToPx`).
68
+ - If `cornerRadii` is present, set per-corner radii and enable `overflow: 'hidden'` to clip content.
69
+
70
+ ## Images & SVGs
71
+
72
+ - Images:
73
+ - Honor `scaleMode`: `fill → objectFit='cover'`, `fit → objectFit='contain'`, center with `objectPosition='center'`.
74
+ - Maintain aspect ratio within absolute insets when both left/right or top/bottom are set:
75
+ - Compute pixel width/height from container dimensions and node `aspectRatio`.
76
+ - Anchor from `top`/`left` and clear the opposite sides to avoid stretch.
77
+ - SVG “BG” layers:
78
+ - Treat like images only if a specific node requires aspect rules; do not apply global image-like behavior to all SVGs.
79
+ - Verify corner radii after percent → px conversion; adjust on a per-node basis if Figma expects masking behavior.
80
+
81
+ ## Asset Loading
82
+
83
+ - Image preloading must not block rendering:
84
+ - Use `Promise.allSettled` and resolve on `onerror` in dev mode.
85
+
86
+ ## Change Review Checklist
87
+
88
+ - Will any absolutely positioned child lack a positioned ancestor? If yes, add `position: 'relative'`.
89
+ - Could a wrapper render at `0×0` initially? If yes, default to `width/height: '100%'` until measured.
90
+ - Are you converting percent sizes using container dimensions (not window)?
91
+ - Are all numeric CSS lengths expressed with units?
92
+ - Are backgrounds visible (not fully transparent) and behind content?
93
+ - Do action wrappers have the correct `zIndex` and not block clicks unexpectedly?
94
+
95
+ ## Debugging Tips
96
+
97
+ - Inspect `[data-type="ColorComponent"]` for `background-color`, size, and absolute insets.
98
+ - Inspect containers’ computed `width/height` and bounding rect; if `0×0`, confirm `ResizeObserver` and defaults.
99
+ - For missing text, verify computed `line-height`, `font-size`, and clipping (`overflow`).
100
+
101
+ ## Local Flex-Layout Mode (for offline testing)
102
+
103
+ - Toggle via query `?useLocal=1` or env `VITE_BRAVO_LOCAL=1`.
104
+ - App JSON source: `/flex-layout/<appId>/<appId>.json`.
105
+ - Component modules: `/flex-layout/<appId>/<pageId>.jsx` (fallback to `.js`).
106
+ - `BravoApp` page list uses `app.data.pages[].id` when local mode is on.
107
+ - Keep all styling inline; no Tailwind in `src/bravo/**`.
108
+
109
+ ## Interactive Components & Click Handling
110
+
111
+ ### Clickable Overlay Positioning (CRITICAL)
112
+
113
+ **Problem:** When `EncoreLinkActionWrapper` creates an absolutely positioned overlay div for click handling, the overlay must be scoped to the correct parent element to avoid overlapping with other interactive elements.
114
+
115
+ **Root Cause:** If the overlay is positioned relative to a parent container (like `StatefulSetComponent`) that contains multiple buttons, all buttons' overlays will overlap at the same position, causing clicks on one button to trigger handlers on other buttons.
116
+
117
+ **Solution:**
118
+ - The clickable overlay div from `EncoreLinkActionWrapper` must be positioned relative to the **button element itself**, not the parent container.
119
+ - In `StatefulCompoundComponent`, wrap the button's content with `EncoreLinkActionWrapper`, then place the button's container div **outside** the wrapper:
120
+ ```tsx
121
+ // ✅ CORRECT: Overlay is scoped to button element
122
+ <div style={containerStyle}>
123
+ <EncoreLinkActionWrapper>
124
+ {children}
125
+ </EncoreLinkActionWrapper>
126
+ </div>
127
+
128
+ // ❌ WRONG: Overlay is scoped to parent container
129
+ <EncoreLinkActionWrapper>
130
+ <div style={containerStyle}>
131
+ {children}
132
+ </div>
133
+ </EncoreLinkActionWrapper>
134
+ ```
135
+
136
+ **Testing:**
137
+ - When debugging click handling issues, check if clickable overlays overlap by:
138
+ 1. Inspecting the bounding boxes of overlay divs (look for `zIndex >= 9999` and `position: absolute`)
139
+ 2. Verifying that overlays from different buttons have different positions
140
+ 3. Using `document.elementsFromPoint()` to see which elements are at a click point
141
+
142
+ **Key Principle:** Interactive overlays should be scoped to the smallest containing element that represents the clickable area, not a parent that contains multiple interactive elements.
143
+
144
+ ### StatefulSetComponent Variant Selection
145
+
146
+ - `StatefulSetComponent` uses its own `id` as the `stateSetId` for variant lookup in the Zustand store.
147
+ - Always use the component's `id` directly in the selector function to avoid closure issues:
148
+ ```tsx
149
+ // ✅ CORRECT: Use id directly
150
+ const variant = useEncoreState((state) => state.statefulSetVariants[id]);
151
+
152
+ // ❌ WRONG: Using a variable can cause closure issues
153
+ const stateSetId = id;
154
+ const variant = useEncoreState((state) => state.statefulSetVariants[stateSetId]);
155
+ ```
156
+
157
+ ### Parent StatefulSetId Propagation
158
+
159
+ - When `StatefulSetComponent` renders a child button (`StatefulCompoundComponent`), it must pass its own `id` as `_parentStatefulSetId` prop.
160
+ - This ensures the button's click handler targets the correct parent's state, not the button's own state.
161
+ - The `EncoreLinkActionWrapper` prioritizes `_parentStatefulSetId` prop over context values for reliability.