@lastbrain/ai-ui-react 1.0.73 → 1.0.74

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 (83) hide show
  1. package/dist/components/AiChipLabel.d.ts.map +1 -1
  2. package/dist/components/AiChipLabel.js +10 -7
  3. package/dist/components/AiContextButton.d.ts +1 -1
  4. package/dist/components/AiContextButton.d.ts.map +1 -1
  5. package/dist/components/AiContextButton.js +25 -12
  6. package/dist/components/AiImageButton.d.ts.map +1 -1
  7. package/dist/components/AiImageButton.js +32 -16
  8. package/dist/components/AiInput.d.ts.map +1 -1
  9. package/dist/components/AiInput.js +15 -5
  10. package/dist/components/AiModelSelect.d.ts.map +1 -1
  11. package/dist/components/AiModelSelect.js +3 -1
  12. package/dist/components/AiPromptPanel.d.ts.map +1 -1
  13. package/dist/components/AiPromptPanel.js +72 -47
  14. package/dist/components/AiSelect.d.ts.map +1 -1
  15. package/dist/components/AiSelect.js +8 -3
  16. package/dist/components/AiStatusButton.d.ts.map +1 -1
  17. package/dist/components/AiStatusButton.js +23 -20
  18. package/dist/components/AiTextarea.d.ts.map +1 -1
  19. package/dist/components/AiTextarea.js +19 -6
  20. package/dist/components/ErrorToast.d.ts.map +1 -1
  21. package/dist/components/ErrorToast.js +4 -2
  22. package/dist/components/LBApiKeySelector.d.ts.map +1 -1
  23. package/dist/components/LBApiKeySelector.js +13 -5
  24. package/dist/components/LBConnectButton.d.ts.map +1 -1
  25. package/dist/components/LBConnectButton.js +8 -3
  26. package/dist/components/LBKeyPicker.d.ts.map +1 -1
  27. package/dist/components/LBKeyPicker.js +8 -4
  28. package/dist/components/LBSigninModal.d.ts.map +1 -1
  29. package/dist/components/LBSigninModal.js +13 -7
  30. package/dist/components/UsageToast.d.ts.map +1 -1
  31. package/dist/components/UsageToast.js +4 -2
  32. package/dist/context/I18nContext.d.ts +15 -0
  33. package/dist/context/I18nContext.d.ts.map +1 -0
  34. package/dist/context/I18nContext.js +44 -0
  35. package/dist/context/LBAuthProvider.d.ts +4 -1
  36. package/dist/context/LBAuthProvider.d.ts.map +1 -1
  37. package/dist/context/LBAuthProvider.js +3 -2
  38. package/dist/hooks/useAiCallImage.d.ts.map +1 -1
  39. package/dist/hooks/useAiCallImage.js +1 -107
  40. package/dist/hooks/useAiCallText.d.ts.map +1 -1
  41. package/dist/hooks/useAiCallText.js +1 -25
  42. package/dist/hooks/useLoadingTimer.d.ts +5 -0
  43. package/dist/hooks/useLoadingTimer.d.ts.map +1 -0
  44. package/dist/hooks/useLoadingTimer.js +27 -0
  45. package/dist/i18n/de.json +62 -0
  46. package/dist/i18n/en.json +128 -0
  47. package/dist/i18n/es.json +70 -0
  48. package/dist/i18n/fr.json +128 -0
  49. package/dist/i18n/it.json +62 -0
  50. package/dist/i18n/pt.json +62 -0
  51. package/dist/index.d.ts +2 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +2 -0
  54. package/dist/styles.css +141 -1
  55. package/package.json +3 -3
  56. package/src/components/AiChipLabel.tsx +17 -8
  57. package/src/components/AiContextButton.tsx +44 -20
  58. package/src/components/AiImageButton.tsx +52 -25
  59. package/src/components/AiInput.tsx +20 -5
  60. package/src/components/AiModelSelect.tsx +3 -1
  61. package/src/components/AiPromptPanel.tsx +177 -59
  62. package/src/components/AiSelect.tsx +8 -3
  63. package/src/components/AiStatusButton.tsx +51 -40
  64. package/src/components/AiTextarea.tsx +24 -6
  65. package/src/components/ErrorToast.tsx +4 -2
  66. package/src/components/LBApiKeySelector.tsx +33 -13
  67. package/src/components/LBConnectButton.tsx +9 -3
  68. package/src/components/LBKeyPicker.tsx +10 -4
  69. package/src/components/LBSigninModal.tsx +31 -15
  70. package/src/components/UsageToast.tsx +4 -2
  71. package/src/context/I18nContext.tsx +71 -0
  72. package/src/context/LBAuthProvider.tsx +9 -1
  73. package/src/hooks/useAiCallImage.ts +1 -149
  74. package/src/hooks/useAiCallText.ts +1 -30
  75. package/src/hooks/useLoadingTimer.ts +32 -0
  76. package/src/i18n/de.json +62 -0
  77. package/src/i18n/en.json +128 -0
  78. package/src/i18n/es.json +70 -0
  79. package/src/i18n/fr.json +128 -0
  80. package/src/i18n/it.json +62 -0
  81. package/src/i18n/pt.json +62 -0
  82. package/src/index.ts +2 -0
  83. package/src/styles.css +141 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AiChipLabel.d.ts","sourceRoot":"","sources":["../../src/components/AiChipLabel.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,KAA0C,MAAM,OAAO,CAAC;AAS/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,OAAmB,EACnB,IAAW,EACX,MAAe,EACf,SAAS,EACT,KAAK,EAAE,WAAW,GACnB,EAAE,gBAAgB,2CAiBlB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAU,EACV,QAAQ,EACR,WAAoE,EACpE,OAAO,EACP,QAAQ,EACR,eAAuB,EACvB,SAAS,EACT,YAAY,EACZ,aAAa,EACb,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,IAAW,EACX,MAAe,GAChB,EAAE,gBAAgB,2CAqMlB"}
1
+ {"version":3,"file":"AiChipLabel.d.ts","sourceRoot":"","sources":["../../src/components/AiChipLabel.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,KAA0C,MAAM,OAAO,CAAC;AAS/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGjD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,OAAmB,EACnB,IAAW,EACX,MAAe,EACf,SAAS,EACT,KAAK,EAAE,WAAW,GACnB,EAAE,gBAAgB,2CAiBlB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAU,EACV,QAAQ,EACR,WAAW,EACX,OAAO,EACP,QAAQ,EACR,eAAuB,EACvB,SAAS,EACT,YAAY,EACZ,aAAa,EACb,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,IAAW,EACX,MAAe,GAChB,EAAE,gBAAgB,2CA6MlB"}
@@ -10,6 +10,7 @@ import { useAiModels } from "../hooks/useAiModels";
10
10
  import { useAiContext } from "../context/AiProvider";
11
11
  import { useLB } from "../context/LBAuthProvider";
12
12
  import { handleAIError } from "../utils/errorHandler";
13
+ import { useI18n } from "../context/I18nContext";
13
14
  export function AiChipLabel({ children, variant = "default", size = "md", radius = "full", className, style: customStyle, }) {
14
15
  const variantClassMap = {
15
16
  default: "",
@@ -20,7 +21,8 @@ export function AiChipLabel({ children, variant = "default", size = "md", radius
20
21
  };
21
22
  return (_jsx("span", { style: customStyle, className: `ai-chip ai-size-${size} ai-radius-${radius} ${variantClassMap[variant]} ${className || ""}`, children: children }));
22
23
  }
23
- export function AiChipInput({ value = [], onChange, placeholder = "Tapez et appuyez sur Entrée pour ajouter des tags...", context, maxChips, allowDuplicates = false, className, storeOutputs, artifactTitle, baseUrl: propBaseUrl, apiKeyId: propApiKeyId, size = "md", radius = "full", }) {
24
+ export function AiChipInput({ value = [], onChange, placeholder, context, maxChips, allowDuplicates = false, className, storeOutputs, artifactTitle, baseUrl: propBaseUrl, apiKeyId: propApiKeyId, size = "md", radius = "full", }) {
25
+ const { t } = useI18n();
24
26
  const [inputValue, setInputValue] = useState("");
25
27
  const [showPromptPanel, setShowPromptPanel] = useState(false);
26
28
  const [showSigninModal, setShowSigninModal] = useState(false);
@@ -91,7 +93,7 @@ Exemple de réponse attendue: javascript, react, frontend, api, development`;
91
93
  model,
92
94
  prompt: instruction,
93
95
  storeOutputs,
94
- artifactTitle: artifactTitle || "Tags générés par IA",
96
+ artifactTitle: artifactTitle || t("ai.chips.artifactTitle", "AI generated tags"),
95
97
  });
96
98
  const chips = parseChipsFromResponse(response.text);
97
99
  // Fermer le modal immédiatement
@@ -125,7 +127,8 @@ Exemple de réponse attendue: javascript, react, frontend, api, development`;
125
127
  };
126
128
  const sizeClass = `ai-size-${size}`;
127
129
  const radiusClass = `ai-radius-${radius}`;
128
- return (_jsxs("div", { className: className, children: [_jsx("div", { className: "ai-control-group ai-glow mb-2", children: _jsxs("div", { className: `ai-shell ${sizeClass} ${radiusClass}`, children: [_jsx("input", { ref: inputRef, type: "text", value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyDown: handleKeyDown, placeholder: placeholder, className: `ai-control ai-control-input ai-control-input--with-action ${sizeClass} ${radiusClass}` }), _jsx("button", { onClick: () => {
130
+ return (_jsxs("div", { className: className, children: [_jsx("div", { className: "ai-control-group ai-glow mb-2", children: _jsxs("div", { className: `ai-shell ${sizeClass} ${radiusClass}`, children: [_jsx("input", { ref: inputRef, type: "text", value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyDown: handleKeyDown, placeholder: placeholder ||
131
+ t("ai.chips.placeholder", "Type and press Enter to add tags..."), className: `ai-control ai-control-input ai-control-input--with-action ${sizeClass} ${radiusClass}` }), _jsx("button", { onClick: () => {
129
132
  if (isAuthenticated) {
130
133
  handleGenerateChips();
131
134
  }
@@ -133,8 +136,8 @@ Exemple de réponse attendue: javascript, react, frontend, api, development`;
133
136
  setShowSigninModal(true);
134
137
  }
135
138
  }, className: `ai-control-action ai-spark ${sizeClass} ${radiusClass}`, "aria-label": isAuthenticated
136
- ? "Générer des tags avec l'IA"
137
- : "Connexion requise", title: isAuthenticated
138
- ? "Générer des tags avec l'IA"
139
- : "Se connecter pour utiliser l'IA", children: isAuthenticated ? _jsx(Sparkles, { size: 16 }) : _jsx(Lock, { size: 16 }) })] }) }), value.length > 0 && (_jsx("div", { className: "ai-chip-input", children: value.map((chip, index) => (_jsxs("div", { className: `ai-chip ai-chip--with-close ${sizeClass} ${radiusClass}`, children: [_jsx("span", { children: chip }), _jsx("button", { onClick: () => removeChip(index), className: "ai-chip-remover", title: "Supprimer", children: _jsx(X, { size: 14 }) })] }, index))) })), _jsx(AiPromptPanel, { isOpen: showPromptPanel, onClose: () => setShowPromptPanel(false), onSubmit: handlePromptSubmit, models: models || undefined, baseUrl: baseUrl, sourceText: context ? `Contexte: ${context}` : undefined, enableModelManagement: true, showOnlyUserModels: true }), _jsx(LBSigninModal, { isOpen: showSigninModal, onClose: () => setShowSigninModal(false) })] }));
139
+ ? t("ai.chips.generate", "Generate tags with AI")
140
+ : t("auth.connectRequired", "Connection required"), title: isAuthenticated
141
+ ? t("ai.chips.generate", "Generate tags with AI")
142
+ : t("auth.connectToUseAi", "Sign in to use AI"), children: isAuthenticated ? _jsx(Sparkles, { size: 16 }) : _jsx(Lock, { size: 16 }) })] }) }), value.length > 0 && (_jsx("div", { className: "ai-chip-input", children: value.map((chip, index) => (_jsxs("div", { className: `ai-chip ai-chip--with-close ${sizeClass} ${radiusClass}`, children: [_jsx("span", { children: chip }), _jsx("button", { onClick: () => removeChip(index), className: "ai-chip-remover", title: t("ai.chips.remove", "Remove"), children: _jsx(X, { size: 14 }) })] }, index))) })), _jsx(AiPromptPanel, { isOpen: showPromptPanel, onClose: () => setShowPromptPanel(false), onSubmit: handlePromptSubmit, models: models || undefined, baseUrl: baseUrl, sourceText: context ? `Contexte: ${context}` : undefined, enableModelManagement: true, showOnlyUserModels: true }), _jsx(LBSigninModal, { isOpen: showSigninModal, onClose: () => setShowSigninModal(false) })] }));
140
143
  }
@@ -22,6 +22,6 @@ export interface AiContextButtonProps extends Omit<BaseAiProps, "onValue" | "typ
22
22
  radius?: AiRadius;
23
23
  variant?: AiVariant;
24
24
  }
25
- export declare function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode, contextData, contextDescription, onResult, onToast, disabled, className, children, resultModalTitle, size, radius, variant, context: _context, ...buttonProps }: AiContextButtonProps): import("react/jsx-runtime").JSX.Element;
25
+ export declare function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode, contextData, contextDescription, onResult, onToast, disabled, className, children, resultModalTitle, storeOutputs, artifactTitle, size, radius, variant, context: _context, ...buttonProps }: AiContextButtonProps): import("react/jsx-runtime").JSX.Element;
26
26
  export {};
27
27
  //# sourceMappingURL=AiContextButton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AiContextButton.d.ts","sourceRoot":"","sources":["../../src/components/AiContextButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAW5D,KAAK,WAAW,GACZ,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,OAAO,EAAE,GACT;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAE/B,MAAM,WAAW,oBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACvE,WAAW,EAAE,WAAW,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,CACT,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC7C,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,WAAW,EACX,kBAAyC,EACzC,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,gBAA0C,EAC1C,IAAW,EACX,MAAe,EACf,OAAmB,EACnB,OAAO,EAAE,QAAQ,EACjB,GAAG,WAAW,EACf,EAAE,oBAAoB,2CA+QtB"}
1
+ {"version":3,"file":"AiContextButton.d.ts","sourceRoot":"","sources":["../../src/components/AiContextButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAY5D,KAAK,WAAW,GACZ,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,OAAO,EAAE,GACT;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAE/B,MAAM,WAAW,oBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACvE,WAAW,EAAE,WAAW,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,CACT,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC7C,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,IAAW,EACX,MAAe,EACf,OAAmB,EACnB,OAAO,EAAE,QAAQ,EACjB,GAAG,WAAW,EACf,EAAE,oBAAoB,2CAoStB"}
@@ -11,13 +11,16 @@ import { useAiContext } from "../context/AiProvider";
11
11
  import { handleAIError } from "../utils/errorHandler";
12
12
  import { useLB } from "../context/LBAuthProvider";
13
13
  import { LBSigninModal } from "./LBSigninModal";
14
- export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode = "modal", contextData, contextDescription = "Données à analyser", onResult, onToast, disabled, className, children, resultModalTitle = "Résultat de l'analyse", size = "md", radius = "full", variant = "default", context: _context, ...buttonProps }) {
14
+ import { useI18n } from "../context/I18nContext";
15
+ export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode = "modal", contextData, contextDescription, onResult, onToast, disabled, className, children, resultModalTitle, storeOutputs, artifactTitle, size = "md", radius = "full", variant = "default", context: _context, ...buttonProps }) {
16
+ const { t, lang } = useI18n();
15
17
  const [isOpen, setIsOpen] = useState(false);
16
18
  const [showAuthModal, setShowAuthModal] = useState(false);
17
19
  const [isResultOpen, setIsResultOpen] = useState(false);
18
20
  const [analysisResult, setAnalysisResult] = useState(null);
19
21
  const { showUsageToast } = useUsageToast();
20
22
  const { showErrorToast, errorData, errorKey, clearError } = useErrorToast();
23
+ const resolvedContextDescription = contextDescription || t("ai.context.description", "Data to analyze");
21
24
  let lbStatus;
22
25
  try {
23
26
  const lbContext = useLB();
@@ -53,13 +56,15 @@ export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId,
53
56
  const handleSubmit = async (selectedModel, selectedPrompt) => {
54
57
  try {
55
58
  const contextString = formatContextData(contextData);
56
- const fullPrompt = `${selectedPrompt}\n\nCONTEXTE (${contextDescription}):\n${contextString}\n\nAnalyse ces données et réponds de manière structurée et claire.`;
59
+ const fullPrompt = `${selectedPrompt}\n\nCONTEXTE (${resolvedContextDescription}):\n${contextString}\n\n${t("ai.context.analyzeStructured", "Analyze this data and respond in a clear, structured way.")}`;
57
60
  const result = await callText({
58
61
  prompt: fullPrompt,
59
62
  model: selectedModel || "gpt-4o-mini",
60
63
  context: _context || undefined,
61
64
  maxTokens: 4000,
62
65
  temperature: 0.7,
66
+ storeOutputs,
67
+ artifactTitle,
63
68
  });
64
69
  if (!result.text) {
65
70
  return;
@@ -83,7 +88,9 @@ export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId,
83
88
  });
84
89
  onToast?.({
85
90
  type: "success",
86
- message: `Analyse terminée - Coût: $${(apiKeyId?.includes("dev") ? 0 : actualCost).toFixed(6)}`,
91
+ message: t("ai.analysisDoneCost", "Analysis completed - Cost: {cost}", {
92
+ cost: `$${(apiKeyId?.includes("dev") ? 0 : actualCost).toFixed(6)}`,
93
+ }),
87
94
  });
88
95
  showUsageToast({
89
96
  requestId: result.requestId,
@@ -108,14 +115,18 @@ export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId,
108
115
  if (!analysisResult) {
109
116
  return;
110
117
  }
118
+ const locale = lang === "fr" ? "fr-FR" : "en-US";
111
119
  const currentDate = new Date()
112
- .toLocaleDateString("fr-FR")
120
+ .toLocaleDateString(locale)
113
121
  .replace(/\//g, "-");
114
- const defaultName = `analyse-${currentDate}.txt`;
115
- const fileName = prompt("Nom du fichier :", defaultName) || defaultName;
116
- const content = `ANALYSE DES DONNÉES - ${new Date().toLocaleString("fr-FR")}\n\nPROMPT UTILISÉ :\n${analysisResult.prompt}\n\nRÉSULTAT DE L'ANALYSE :\n${analysisResult.content}\n\n--- MÉTADONNÉES ---\nTokens utilisés: ${analysisResult.tokens.toLocaleString()}\nCoût: $${(apiKeyId?.includes("dev")
122
+ const defaultName = t("ai.context.fileNameBase", "analysis-{date}.txt", {
123
+ date: currentDate,
124
+ });
125
+ const fileName = prompt(t("ai.context.saveFileNamePrompt", "File name:"), defaultName) ||
126
+ defaultName;
127
+ const content = `${t("ai.context.fileHeader", "DATA ANALYSIS")} - ${new Date().toLocaleString(locale)}\n\n${t("common.promptUsed", "Prompt used").toUpperCase()} :\n${analysisResult.prompt}\n\n${t("common.result", "Result").toUpperCase()} :\n${analysisResult.content}\n\n--- ${t("ai.context.metadata", "METADATA")} ---\n${t("ai.context.tokensUsed", "Tokens used")}: ${analysisResult.tokens.toLocaleString()}\n${t("ai.context.cost", "Cost")}: $${(apiKeyId?.includes("dev")
117
128
  ? 0
118
- : analysisResult.cost).toFixed(6)}\nID de requête: ${analysisResult.requestId || "N/A"}`;
129
+ : analysisResult.cost).toFixed(6)}\n${t("ai.context.requestId", "Request ID")}: ${analysisResult.requestId || "N/A"}`;
119
130
  const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
120
131
  const url = URL.createObjectURL(blob);
121
132
  const anchor = document.createElement("a");
@@ -129,15 +140,17 @@ export function AiContextButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId,
129
140
  const sizeClass = `ai-size-${size}`;
130
141
  const radiusClass = `ai-radius-${radius}`;
131
142
  const variantClass = variant === "light" ? "ai-btn--light" : "";
132
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative inline-block ai-glow", children: [_jsx("button", { ...buttonProps, onClick: handleOpenPanel, disabled: disabled || loading || !isAuthReady, className: `ai-btn ai-context-btn ${variantClass} ${sizeClass} ${radiusClass} ${className || ""}`, title: !isAuthReady ? "Authentication required" : "Analyser avec l'IA", children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 16, className: "ai-spinner" }), _jsx("span", { children: "Analyse..." })] })) : !isAuthReady ? (_jsxs(_Fragment, { children: [_jsx(Lock, { size: 16 }), children || _jsx("span", { children: "Connexion requise" })] })) : (_jsxs(_Fragment, { children: [_jsx(Sparkles, { size: 16 }), children || _jsx("span", { children: "Analyser" })] })) }), isOpen ? (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: () => setIsOpen(false), onSubmit: handleSubmit, uiMode: uiMode, models: [], enableModelManagement: true, modelCategory: "text", baseUrl: baseUrl, apiKey: apiKeyId })) : null] }), isResultOpen && analysisResult ? (_jsx("div", { className: "ai-signin-overlay ai-overlay-panel", onClick: (e) => {
143
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative inline-block ai-glow", children: [_jsx("button", { ...buttonProps, onClick: handleOpenPanel, disabled: disabled || loading || !isAuthReady, className: `ai-btn ai-context-btn ${variantClass} ${sizeClass} ${radiusClass} ${className || ""}`, title: !isAuthReady
144
+ ? t("auth.required", "Authentication required")
145
+ : t("ai.analyze", "Analyze with AI"), children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 16, className: "ai-spinner" }), _jsx("span", { children: t("ai.analyzing", "Analyzing...") })] })) : !isAuthReady ? (_jsxs(_Fragment, { children: [_jsx(Lock, { size: 16 }), children || (_jsx("span", { children: t("auth.connectRequired", "Connection required") }))] })) : (_jsxs(_Fragment, { children: [_jsx(Sparkles, { size: 16 }), children || _jsx("span", { children: t("ai.context.buttonAnalyze", "Analyze") })] })) }), isOpen ? (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: () => setIsOpen(false), onSubmit: handleSubmit, uiMode: uiMode, models: [], enableModelManagement: true, modelCategory: "text", baseUrl: baseUrl, apiKey: apiKeyId })) : null] }), isResultOpen && analysisResult ? (_jsx("div", { className: "ai-signin-overlay ai-overlay-panel", onClick: (e) => {
133
146
  if (e.target === e.currentTarget) {
134
147
  setIsResultOpen(false);
135
148
  setAnalysisResult(null);
136
149
  }
137
- }, children: _jsxs("div", { className: "ai-popover ai-result-modal", onClick: (e) => e.stopPropagation(), children: [_jsxs("div", { className: "ai-result-header", children: [_jsxs("div", { className: "ai-row", children: [_jsx(FileText, { size: 18 }), _jsx("h2", { className: "ai-result-title", children: resultModalTitle })] }), _jsxs("div", { className: "ai-row", children: [_jsxs("button", { type: "button", className: `ai-btn ai-btn--ghost ai-btn--compact ${sizeClass} ${radiusClass}`, onClick: saveToFile, children: [_jsx(Download, { size: 14 }), "Sauvegarder"] }), _jsx("button", { type: "button", className: "ai-icon-btn", onClick: () => {
150
+ }, children: _jsxs("div", { className: "ai-popover ai-result-modal", onClick: (e) => e.stopPropagation(), children: [_jsxs("div", { className: "ai-result-header", children: [_jsxs("div", { className: "ai-row", children: [_jsx(FileText, { size: 18 }), _jsx("h2", { className: "ai-result-title", children: resultModalTitle || t("ai.context.resultTitle", "Analysis result") })] }), _jsxs("div", { className: "ai-row", children: [_jsxs("button", { type: "button", className: `ai-btn ai-btn--ghost ai-btn--compact ${sizeClass} ${radiusClass}`, onClick: saveToFile, children: [_jsx(Download, { size: 14 }), t("common.save", "Save")] }), _jsx("button", { type: "button", className: "ai-icon-btn", onClick: () => {
138
151
  setIsResultOpen(false);
139
152
  setAnalysisResult(null);
140
- }, "aria-label": "Fermer", children: _jsx(X, { size: 16 }) })] })] }), _jsxs("div", { className: "ai-result-body", children: [_jsxs("div", { className: "ai-result-block", children: [_jsx("h3", { className: "ai-result-subtitle", children: "Prompt utilis\u00E9" }), _jsx("pre", { className: "ai-result-code", children: analysisResult.prompt })] }), _jsxs("div", { className: "ai-result-block", children: [_jsx("h3", { className: "ai-result-subtitle", children: "R\u00E9sultat" }), _jsx("div", { className: "ai-result-content", children: analysisResult.content })] }), _jsxs("div", { className: "ai-result-meta ai-between", children: [_jsxs("span", { children: ["Co\u00FBt: $", (apiKeyId?.includes("dev")
153
+ }, "aria-label": t("common.closeLabel", "Close"), children: _jsx(X, { size: 16 }) })] })] }), _jsxs("div", { className: "ai-result-body", children: [_jsxs("div", { className: "ai-result-block", children: [_jsx("h3", { className: "ai-result-subtitle", children: t("common.promptUsed", "Prompt used") }), _jsx("pre", { className: "ai-result-code", children: analysisResult.prompt })] }), _jsxs("div", { className: "ai-result-block", children: [_jsx("h3", { className: "ai-result-subtitle", children: t("common.result", "Result") }), _jsx("div", { className: "ai-result-content", children: analysisResult.content })] }), _jsxs("div", { className: "ai-result-meta ai-between", children: [_jsxs("span", { children: [t("ai.context.cost", "Cost"), ": $", (apiKeyId?.includes("dev")
141
154
  ? 0
142
- : analysisResult.cost).toFixed(6)] }), _jsxs("span", { children: ["ID: ", analysisResult.requestId?.slice(-8) || "N/A"] })] })] })] }) })) : null, _jsx(LBSigninModal, { isOpen: showAuthModal, onClose: () => setShowAuthModal(false) }), _jsx(ErrorToast, { error: errorData, onComplete: clearError }, errorKey)] }));
155
+ : analysisResult.cost).toFixed(6)] }), _jsxs("span", { children: [t("ai.context.requestId", "Request ID"), ":", " ", analysisResult.requestId?.slice(-8) || "N/A"] })] })] })] }) })) : null, _jsx(LBSigninModal, { isOpen: showAuthModal, onClose: () => setShowAuthModal(false) }), _jsx(ErrorToast, { error: errorData, onComplete: clearError }, errorKey)] }));
143
156
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AiImageButton.d.ts","sourceRoot":"","sources":["../../src/components/AiImageButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAS5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAU5D,MAAM,WAAW,kBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACvE,OAAO,CAAC,EAAE,CACR,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC7C,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,aAAoB,EACpB,WAAW,EACX,YAAY,EACZ,aAAa,EACb,IAAW,EACX,MAAe,EACf,OAAmB,EACnB,GAAG,WAAW,EACf,EAAE,kBAAkB,2CAqPpB"}
1
+ {"version":3,"file":"AiImageButton.d.ts","sourceRoot":"","sources":["../../src/components/AiImageButton.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAAY,KAAK,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAS5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAY5D,MAAM,WAAW,kBACf,SACE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC,EACrC,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACvE,OAAO,CAAC,EAAE,CACR,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC7C,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,aAAoB,EACpB,WAAW,EACX,YAAY,EACZ,aAAa,EACb,IAAW,EACX,MAAe,EACf,OAAmB,EACnB,GAAG,WAAW,EACf,EAAE,kBAAkB,2CA8QpB"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import "../styles/register";
4
4
  import { useState } from "react";
5
5
  import { ImageIcon, Loader2, Download, ExternalLink, X, Lock, } from "lucide-react";
@@ -11,7 +11,10 @@ import { useAiContext } from "../context/AiProvider";
11
11
  import { handleAIError } from "../utils/errorHandler";
12
12
  import { useLB } from "../context/LBAuthProvider";
13
13
  import { LBSigninModal } from "./LBSigninModal";
14
+ import { useI18n } from "../context/I18nContext";
15
+ import { useLoadingTimer } from "../hooks/useLoadingTimer";
14
16
  export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode = "modal", context: _context, model: _model, prompt: _prompt, onImage, onToast, disabled, className, children, showImageCard = true, onImageSave, storeOutputs, artifactTitle, size = "md", radius = "full", variant = "default", ...buttonProps }) {
17
+ const { t } = useI18n();
15
18
  const [isOpen, setIsOpen] = useState(false);
16
19
  const [showAuthModal, setShowAuthModal] = useState(false);
17
20
  const [generatedImage, setGeneratedImage] = useState(null);
@@ -32,6 +35,7 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
32
35
  const baseUrl = propBaseUrl ?? aiContext.baseUrl;
33
36
  const apiKeyId = propApiKeyId ?? aiContext.apiKeyId;
34
37
  const { generateImage, loading } = useAiCallImage({ baseUrl, apiKeyId });
38
+ const { formatted: loadingElapsed } = useLoadingTimer(loading);
35
39
  const isAuthReady = lbStatus === "ready" || Boolean(process.env.LB_API_KEY);
36
40
  const handleOpenPanel = () => {
37
41
  if (!isAuthReady) {
@@ -57,10 +61,13 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
57
61
  return;
58
62
  try {
59
63
  await onImageSave(generatedImage.url);
60
- onToast?.({ type: "success", message: "Image sauvegardée" });
64
+ onToast?.({ type: "success", message: t("ai.image.savedSuccess", "Image saved") });
61
65
  }
62
66
  catch (_error) {
63
- onToast?.({ type: "error", message: "Erreur lors de la sauvegarde" });
67
+ onToast?.({
68
+ type: "error",
69
+ message: t("ai.image.saveError", "Error while saving"),
70
+ });
64
71
  }
65
72
  };
66
73
  const handleCloseImage = () => {
@@ -82,30 +89,35 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
82
89
  requestId: result.requestId,
83
90
  });
84
91
  if (result.url) {
92
+ const safeRequestId = result.requestId || `img-${Date.now()}`;
93
+ const safeTokens = result.debitTokens || 0;
85
94
  // Stocker l'image générée
86
95
  const imageData = {
87
96
  url: result.url,
88
97
  prompt: selectedPrompt,
89
- requestId: result.requestId,
90
- tokens: result.debitTokens,
98
+ requestId: safeRequestId,
99
+ tokens: safeTokens,
91
100
  };
92
101
  setGeneratedImage(imageData);
93
102
  console.log("[AiImageButton] Image data stored:", imageData);
94
103
  onImage?.(result.url, {
95
- requestId: result.requestId,
96
- tokens: result.debitTokens,
104
+ requestId: safeRequestId,
105
+ tokens: safeTokens,
106
+ });
107
+ onToast?.({
108
+ type: "success",
109
+ message: t("ai.image.generatedSuccess", "Image generated successfully"),
97
110
  });
98
- onToast?.({ type: "success", message: "Image générée avec succès" });
99
111
  // Afficher le toast de coût même en mode dev
100
112
  showUsageToast({
101
- requestId: result.requestId,
102
- debitTokens: result.debitTokens,
113
+ requestId: safeRequestId,
114
+ debitTokens: safeTokens,
103
115
  usage: {
104
- total_tokens: result.debitTokens,
105
- prompt_tokens: Math.floor(result.debitTokens * 0.8),
106
- completion_tokens: Math.floor(result.debitTokens * 0.2),
116
+ total_tokens: safeTokens,
117
+ prompt_tokens: Math.floor(safeTokens * 0.8),
118
+ completion_tokens: Math.floor(safeTokens * 0.2),
107
119
  },
108
- cost: apiKeyId?.includes("dev") ? 0 : result.debitTokens * 0.002, // Coût simulé
120
+ cost: apiKeyId?.includes("dev") ? 0 : safeTokens * 0.002, // Coût simulé
109
121
  });
110
122
  }
111
123
  }
@@ -119,7 +131,11 @@ export function AiImageButton({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, ui
119
131
  const sizeClass = `ai-size-${size}`;
120
132
  const radiusClass = `ai-radius-${radius}`;
121
133
  const variantClass = variant === "light" ? "ai-btn--light" : "";
122
- return (_jsxs("div", { className: "flex items-start gap-4", children: [_jsxs("div", { className: "relative inline-block ai-glow", children: [_jsx("button", { ...buttonProps, onClick: handleOpenPanel, disabled: disabled || loading || !isAuthReady, className: `ai-btn ai-image-btn ${variantClass} ${sizeClass} ${radiusClass} ${className || ""}`, style: buttonProps.style, "data-ai-image-button": true, title: !isAuthReady ? "Authentication required" : "Générer une image", children: loading ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 18, className: "ai-spinner" }), _jsx("span", { className: "ai-text-microtracking", children: "G\u00E9n\u00E9ration..." })] })) : !isAuthReady ? (_jsxs(_Fragment, { children: [_jsx(Lock, { size: 18 }), children || _jsx("span", { children: "Connexion requise" })] })) : (_jsxs(_Fragment, { children: [_jsx(ImageIcon, { size: 18 }), _jsx("span", { className: "ai-text-microtracking", children: children || "Générer une image" })] })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, enableModelManagement: true, modelCategory: "image", baseUrl: baseUrl, apiKey: apiKeyId, models: [] }))] }), showImageCard && generatedImage && (_jsxs("div", { className: "ai-surface ai-image-card relative", children: [_jsxs("div", { className: "flex items-start justify-between mb-3", children: [_jsx("h3", { className: "font-medium text-sm leading-tight max-w-[calc(100%-32px)]", title: generatedImage.prompt, children: generatedImage.prompt.length > 60
134
+ return (_jsxs("div", { className: "flex items-start gap-4", children: [_jsxs("div", { className: "relative inline-block ai-glow", children: [_jsx("button", { ...buttonProps, onClick: handleOpenPanel, disabled: disabled || loading || !isAuthReady, className: `ai-btn ai-image-btn ${variantClass} ${sizeClass} ${radiusClass} ${className || ""}`, style: buttonProps.style, "data-ai-image-button": true, title: !isAuthReady
135
+ ? t("auth.required", "Authentication required")
136
+ : t("ai.image.generate", "Generate image"), children: loading ? (_jsxs("span", { className: "ai-loading-stack", children: [_jsxs("span", { className: "ai-loading-row", children: [_jsx(Loader2, { size: 18, className: "ai-spinner" }), _jsx("span", { className: "ai-text-microtracking", children: t("ai.image.generating", "Generating...") })] }), _jsx("span", { className: "ai-loading-meta", children: t("ai.loading.elapsed", "{seconds}", {
137
+ seconds: loadingElapsed,
138
+ }) })] })) : !isAuthReady ? (_jsxs(_Fragment, { children: [_jsx(Lock, { size: 18 }), children || (_jsx("span", { children: t("auth.connectRequired", "Connection required") }))] })) : (_jsxs(_Fragment, { children: [_jsx(ImageIcon, { size: 18 }), _jsx("span", { className: "ai-text-microtracking", children: children || t("ai.image.generate", "Generate image") })] })) }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, enableModelManagement: true, modelCategory: "image", baseUrl: baseUrl, apiKey: apiKeyId, models: [] }))] }), showImageCard && generatedImage && (_jsxs("div", { className: "ai-surface ai-image-card relative", children: [_jsxs("div", { className: "flex items-start justify-between mb-3", children: [_jsx("h3", { className: "font-medium text-sm leading-tight max-w-[calc(100%-32px)]", title: generatedImage.prompt, children: generatedImage.prompt.length > 60
123
139
  ? `${generatedImage.prompt.substring(0, 60)}...`
124
- : generatedImage.prompt }), _jsx("button", { onClick: handleCloseImage, className: "ai-icon-btn flex-shrink-0", children: _jsx(X, { size: 16 }) })] }), _jsx("div", { className: "ai-image-frame", children: _jsx("img", { src: generatedImage.url, alt: generatedImage.prompt, className: "ai-image-preview" }) }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsxs("button", { onClick: handleDownload, className: "ai-btn ai-btn--primary", title: "T\u00E9l\u00E9charger l'image", children: [_jsx(Download, { size: 16 }), "T\u00E9l\u00E9charger l'image"] }), onImageSave && (_jsxs("button", { onClick: handleSave, className: "ai-btn", title: "Sauvegarder en base", children: [_jsx(ExternalLink, { size: 14 }), "Sauvegarder"] }))] }), _jsx("div", { className: "ai-image-meta", children: _jsx("div", { className: "flex justify-center", children: _jsxs("span", { children: ["ID: ", generatedImage.requestId.slice(-8)] }) }) })] })), _jsx(LBSigninModal, { isOpen: showAuthModal, onClose: () => setShowAuthModal(false) }), _jsx(ErrorToast, { error: errorData, onComplete: clearError }, errorKey)] }));
140
+ : generatedImage.prompt }), _jsx("button", { onClick: handleCloseImage, className: "ai-icon-btn flex-shrink-0", children: _jsx(X, { size: 16 }) })] }), _jsx("div", { className: "ai-image-frame", children: _jsx("img", { src: generatedImage.url, alt: generatedImage.prompt, className: "ai-image-preview" }) }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsxs("button", { onClick: handleDownload, className: "ai-btn ai-btn--primary", title: t("ai.image.download", "Download image"), children: [_jsx(Download, { size: 16 }), t("ai.image.download", "Download image")] }), onImageSave && (_jsxs("button", { onClick: handleSave, className: "ai-btn", title: t("ai.image.saveToDb", "Save to database"), children: [_jsx(ExternalLink, { size: 14 }), t("common.save", "Save")] }))] }), _jsx("div", { className: "ai-image-meta", children: _jsx("div", { className: "flex justify-center", children: _jsxs("span", { children: ["ID: ", (generatedImage.requestId || "N/A").slice(-8)] }) }) })] })), _jsx(LBSigninModal, { isOpen: showAuthModal, onClose: () => setShowAuthModal(false) }), _jsx(ErrorToast, { error: errorData, onComplete: clearError }, errorKey)] }));
125
141
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AiInput.d.ts","sourceRoot":"","sources":["../../src/components/AiInput.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAc,EAAoB,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAE1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAU9D,MAAM,WAAW,YACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IACjE,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,wBAAgB,OAAO,CAAC,EACtB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,IAAW,EACX,MAAe,EACf,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAgB,EAChB,qBAA4B,EAC5B,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,UAAU,EACd,EAAE,YAAY,2CA4Md"}
1
+ {"version":3,"file":"AiInput.d.ts","sourceRoot":"","sources":["../../src/components/AiInput.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAc,EAAoB,KAAK,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAE1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAY9D,MAAM,WAAW,YACf,SACE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EACzB,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IACjE,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED,wBAAgB,OAAO,CAAC,EACtB,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,MAAgB,EAChB,IAAW,EACX,MAAe,EACf,OAAO,EACP,KAAK,EACL,MAAM,EACN,QAAgB,EAChB,qBAA4B,EAC5B,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACT,GAAG,UAAU,EACd,EAAE,YAAY,2CAyNd"}
@@ -11,7 +11,10 @@ import { handleAIError } from "../utils/errorHandler";
11
11
  import { useLB } from "../context/LBAuthProvider";
12
12
  import { LBSigninModal } from "./LBSigninModal";
13
13
  import { useAiContext } from "../context/AiProvider";
14
+ import { useI18n } from "../context/I18nContext";
15
+ import { useLoadingTimer } from "../hooks/useLoadingTimer";
14
16
  export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode = "modal", size = "md", radius = "full", context, model, prompt, editMode = false, enableModelManagement = true, storeOutputs, artifactTitle, onValue, onToast, disabled, className, ...inputProps }) {
17
+ const { t } = useI18n();
15
18
  const [isOpen, setIsOpen] = useState(false);
16
19
  const [showAuthModal, setShowAuthModal] = useState(false);
17
20
  const [inputValue, setInputValue] = useState(inputProps.value?.toString() || inputProps.defaultValue?.toString() || "");
@@ -46,6 +49,7 @@ export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode =
46
49
  modelType: "text-or-language",
47
50
  });
48
51
  const { generateText, loading } = useAiCallText({ baseUrl, apiKeyId });
52
+ const { formatted: loadingElapsed } = useLoadingTimer(loading);
49
53
  const hasConfiguration = Boolean(model && prompt);
50
54
  const isAuthReady = lbStatus === "ready" || Boolean(process.env.LB_API_KEY);
51
55
  const shouldShowSparkles = isAuthReady && !disabled;
@@ -80,7 +84,10 @@ export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode =
80
84
  inputRef.current.value = result.text;
81
85
  }
82
86
  onValue?.(result.text);
83
- onToast?.({ type: "success", message: "AI generation successful" });
87
+ onToast?.({
88
+ type: "success",
89
+ message: t("ai.generationSuccess", "AI generation successful"),
90
+ });
84
91
  }
85
92
  }
86
93
  catch (error) {
@@ -112,7 +119,10 @@ export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode =
112
119
  inputRef.current.value = result.text;
113
120
  }
114
121
  onValue?.(result.text);
115
- onToast?.({ type: "success", message: "AI generation successful" });
122
+ onToast?.({
123
+ type: "success",
124
+ message: t("ai.generationSuccess", "AI generation successful"),
125
+ });
116
126
  }
117
127
  }
118
128
  catch (error) {
@@ -132,8 +142,8 @@ export function AiInput({ baseUrl: propBaseUrl, apiKeyId: propApiKeyId, uiMode =
132
142
  }, onBlur: (e) => {
133
143
  inputProps.onBlur?.(e);
134
144
  }, "aria-invalid": Boolean(inputProps["aria-invalid"]), disabled: disabled || loading }), _jsx("button", { className: `ai-control-action ai-spark ${sizeClass} ${radiusClass}`, onClick: hasConfiguration ? handleQuickGenerate : handleOpenPanel, disabled: disabled || loading, type: "button", title: !isAuthReady
135
- ? "Authentication required"
145
+ ? t("auth.required", "Authentication required")
136
146
  : hasConfiguration
137
- ? "Generate with AI"
138
- : "Setup AI", children: loading ? (_jsx(Loader2, { size: 16, className: "ai-spinner" })) : shouldShowSparkles ? (_jsx(Sparkles, { size: 16 })) : (_jsx(Lock, { size: 16 })) })] }), isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: [], modelCategory: "text", sourceText: inputValue || undefined, apiKey: apiKeyId, baseUrl: baseUrl, enableModelManagement: enableModelManagement, showOnlyUserModels: true })), _jsx(LBSigninModal, { isOpen: showAuthModal, onClose: () => setShowAuthModal(false) }), Boolean(toastData) && (_jsx(UsageToast, { result: toastData, position: "bottom-right", onComplete: clearToast }, toastKey))] }));
147
+ ? t("ai.generate", "Generate with AI")
148
+ : t("ai.setup", "Setup AI"), children: loading ? (_jsx(Loader2, { size: 16, className: "ai-spinner" })) : shouldShowSparkles ? (_jsx(Sparkles, { size: 16 })) : (_jsx(Lock, { size: 16 })) })] }), loading ? (_jsx("span", { className: "ai-control-timer", children: t("ai.loading.elapsed", "{seconds}", { seconds: loadingElapsed }) })) : null, isOpen && (_jsx(AiPromptPanel, { isOpen: isOpen, onClose: handleClosePanel, onSubmit: handleSubmit, uiMode: uiMode, models: [], modelCategory: "text", sourceText: inputValue || undefined, apiKey: apiKeyId, baseUrl: baseUrl, enableModelManagement: enableModelManagement, showOnlyUserModels: true })), _jsx(LBSigninModal, { isOpen: showAuthModal, onClose: () => setShowAuthModal(false) }), Boolean(toastData) && (_jsx(UsageToast, { result: toastData, position: "bottom-right", onComplete: clearToast }, toastKey))] }));
139
149
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AiModelSelect.d.ts","sourceRoot":"","sources":["../../src/components/AiModelSelect.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,KAAK,EACL,QAAQ,EACR,SAAS,EACT,QAAQ,GACT,EAAE,kBAAkB,2CAiBpB"}
1
+ {"version":3,"file":"AiModelSelect.d.ts","sourceRoot":"","sources":["../../src/components/AiModelSelect.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,KAAK,EACL,QAAQ,EACR,SAAS,EACT,QAAQ,GACT,EAAE,kBAAkB,2CAkBpB"}
@@ -1,6 +1,8 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import "../styles/register";
4
+ import { useI18n } from "../context/I18nContext";
4
5
  export function AiModelSelect({ models, value, onChange, className, disabled, }) {
5
- return (_jsxs("select", { value: value, onChange: (e) => onChange(e.target.value), className: className, disabled: disabled, "data-ai-model-select": true, children: [_jsx("option", { value: "", children: "Select a model" }), models.map((model) => (_jsx("option", { value: model.id, children: model.name }, model.id)))] }));
6
+ const { t } = useI18n();
7
+ return (_jsxs("select", { value: value, onChange: (e) => onChange(e.target.value), className: className, disabled: disabled, "data-ai-model-select": true, children: [_jsx("option", { value: "", children: t("ai.select.modelPlaceholder", "Select a model") }), models.map((model) => (_jsx("option", { value: model.id, children: model.name }, model.id)))] }));
6
8
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AiPromptPanel.d.ts","sourceRoot":"","sources":["../../src/components/AiPromptPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AASvC,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAGrD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,SAAS,CAAC;IAE1D,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,IAAI,CAAC;IAExB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,2CA0BtD"}
1
+ {"version":3,"file":"AiPromptPanel.d.ts","sourceRoot":"","sources":["../../src/components/AiPromptPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAC;AAC5B,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAWf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AASvC,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAKrD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,SAAS,CAAC;IAE1D,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,IAAI,CAAC;IAExB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,2CA0BtD"}