@smart-cloud/ai-kit-ui 1.4.7 → 1.4.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smart-cloud/ai-kit-ui",
3
- "version": "1.4.7",
3
+ "version": "1.4.9",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -24,7 +24,7 @@
24
24
  "@smart-cloud/wpsuite-core": "^2.2.10",
25
25
  "@tabler/icons-react": "^3.40.0",
26
26
  "chroma-js": "^3.2.0",
27
- "country-data-list": "^1.6.0",
27
+ "country-flag-icons": "^1.6.15",
28
28
  "react-markdown": "^10.1.0",
29
29
  "rehype-parse": "^9.0.1",
30
30
  "rehype-raw": "^7.0.0",
@@ -41,7 +41,7 @@ import {
41
41
  type WriteArgs,
42
42
  } from "@smart-cloud/ai-kit-core";
43
43
  import { I18n } from "aws-amplify/utils";
44
- import { countries } from "country-data-list";
44
+ import * as CountryFlagIcons from "country-flag-icons/react/3x2";
45
45
  import {
46
46
  type ComponentProps,
47
47
  FC,
@@ -93,8 +93,6 @@ type LanguageSelectorOption = {
93
93
  value: AiKitLanguageCode;
94
94
  label: string;
95
95
  countryCode: string;
96
- countryName: string;
97
- flag: string;
98
96
  };
99
97
 
100
98
  type WriterTone = "neutral" | "formal" | "casual";
@@ -137,12 +135,33 @@ const LANGUAGE_TO_COUNTRY_CODE: Record<AiKitLanguageCode, string> = {
137
135
 
138
136
  const RTL_LANGUAGES = new Set<AiKitLanguageCode>(["ar", "he"]);
139
137
 
140
- const countryLookup = countries as Record<string, { name?: string }>;
138
+ function LanguageFlag(props: { countryCode?: string; size?: number }) {
139
+ const { countryCode, size = 18 } = props;
141
140
 
142
- function getFlagEmoji(countryCode: string): string {
143
- return countryCode
144
- .toUpperCase()
145
- .replace(/./g, (char) => String.fromCodePoint(127397 + char.charCodeAt(0)));
141
+ if (!countryCode) {
142
+ return null;
143
+ }
144
+
145
+ const Flag = CountryFlagIcons[
146
+ countryCode as keyof typeof CountryFlagIcons
147
+ ] as FC<ComponentProps<"svg">> | undefined;
148
+
149
+ if (!Flag) {
150
+ return null;
151
+ }
152
+
153
+ return (
154
+ <Flag
155
+ aria-hidden="true"
156
+ focusable="false"
157
+ style={{
158
+ display: "block",
159
+ flex: "none",
160
+ height: size,
161
+ width: Math.round(size * (4 / 3)),
162
+ }}
163
+ />
164
+ );
146
165
  }
147
166
 
148
167
  function isRtlLanguage(code?: AiKitLanguageCode | "" | null): boolean {
@@ -157,8 +176,6 @@ function getSupportedLanguageOptions(): LanguageSelectorOption[] {
157
176
  value: languageOption.value,
158
177
  label: languageOption.label,
159
178
  countryCode,
160
- countryName: countryLookup[countryCode]?.name || languageOption.label,
161
- flag: getFlagEmoji(countryCode),
162
179
  };
163
180
  });
164
181
  }
@@ -407,6 +424,8 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
407
424
  >();
408
425
 
409
426
  const effectiveUiLanguage = languageOverride ?? normalizeLang(language);
427
+ I18n.setLanguage(effectiveUiLanguage || "en");
428
+ const currentUiLanguage = effectiveUiLanguage || "en";
410
429
  const effectiveDirection = useMemo(() => {
411
430
  const activeDirection = languageOverride ? directionOverride : direction;
412
431
 
@@ -475,6 +494,9 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
475
494
  defaults?.tone,
476
495
  );
477
496
  const [type, setType] = useState<SummarizerType | undefined>(defaults?.type);
497
+ const [generatedContentLanguage, setGeneratedContentLanguage] = useState<
498
+ AiKitLanguageCode | ""
499
+ >("");
478
500
 
479
501
  const autoRunOnceRef = useRef(false);
480
502
  const supportedLanguageOptions = useMemo(
@@ -621,6 +643,18 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
621
643
  const statusText =
622
644
  formatAiKitStatus(ai.statusEvent) ?? (state ? I18n.get(state) : null);
623
645
 
646
+ const setGeneratedResultLanguage = useCallback(
647
+ (nextLanguage?: AiKitLanguageCode | "auto" | "" | null) => {
648
+ if (nextLanguage && nextLanguage !== "auto") {
649
+ setGeneratedContentLanguage(nextLanguage);
650
+ return;
651
+ }
652
+
653
+ setGeneratedContentLanguage(readDefaultOutputLanguage() || "");
654
+ },
655
+ [],
656
+ );
657
+
624
658
  const runGenerate = useCallback(
625
659
  async (modeOverride?: AiModePreference) => {
626
660
  if (!canGenerate) {
@@ -639,11 +673,11 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
639
673
  typeof input === "function" ? await Promise.resolve(input()) : input;
640
674
  switch (mode) {
641
675
  case "summarize": {
676
+ const outLang =
677
+ (outputLanguage && outputLanguage !== "auto"
678
+ ? outputLanguage
679
+ : null) || readDefaultOutputLanguage();
642
680
  const res = await ai.run(async ({ signal, onStatus }) => {
643
- const outLang =
644
- (outputLanguage && outputLanguage !== "auto"
645
- ? outputLanguage
646
- : null) || readDefaultOutputLanguage();
647
681
  const args: SummarizeArgs = {
648
682
  text: text!.trim(),
649
683
  format:
@@ -665,6 +699,7 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
665
699
  const out = await summarize(args, featureOptions);
666
700
  return out.result;
667
701
  });
702
+ setGeneratedResultLanguage(outLang || "");
668
703
  setGenerated((res as never) ?? "");
669
704
  break;
670
705
  }
@@ -707,6 +742,10 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
707
742
  break;
708
743
  }
709
744
  case "translate": {
745
+ const outLang =
746
+ (outputLanguage && outputLanguage !== "auto"
747
+ ? outputLanguage
748
+ : null) || readDefaultOutputLanguage();
710
749
  const res = await ai.run(async ({ signal, onStatus }) => {
711
750
  let inputLang = inputLanguage ?? "auto";
712
751
  if (inputLang === "auto") {
@@ -722,10 +761,6 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
722
761
  } else {
723
762
  setDetectedLanguage(inputLang);
724
763
  }
725
- const outLang =
726
- (outputLanguage && outputLanguage !== "auto"
727
- ? outputLanguage
728
- : null) || readDefaultOutputLanguage();
729
764
  if (outLang === inputLang) {
730
765
  console.warn(
731
766
  "AI Kit: input and output languages are the same",
@@ -759,10 +794,12 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
759
794
  const out = await translate(args, featureOptions);
760
795
  return out.result;
761
796
  });
797
+ setGeneratedResultLanguage(outLang || "");
762
798
  setGenerated((res as never) ?? "");
763
799
  break;
764
800
  }
765
801
  case "rewrite": {
802
+ let generatedLanguage: AiKitLanguageCode | "" = "";
766
803
  const res = await ai.run(async ({ signal, onStatus }) => {
767
804
  let outLang =
768
805
  (outputLanguage && outputLanguage !== "auto"
@@ -779,6 +816,7 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
779
816
  });
780
817
  setOutputLanguage(outLang);
781
818
  }
819
+ generatedLanguage = outLang || "";
782
820
  const args: RewriteArgs = {
783
821
  text: text!.trim(),
784
822
  context: instructions?.trim() || undefined,
@@ -801,6 +839,7 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
801
839
  const out = await rewrite(args, featureOptions);
802
840
  return out.result;
803
841
  });
842
+ setGeneratedResultLanguage(generatedLanguage);
804
843
  setGenerated((res as never) ?? "");
805
844
  break;
806
845
  }
@@ -864,6 +903,7 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
864
903
  });
865
904
  return out.result;
866
905
  });
906
+ setGeneratedResultLanguage(outLang || "");
867
907
  setGenerated((res as never) ?? "");
868
908
  break;
869
909
  }
@@ -1265,20 +1305,11 @@ Follow these additional instructions: ${instructions}`
1265
1305
  const selectedLanguageOption = useMemo(
1266
1306
  () =>
1267
1307
  supportedLanguageOptions.find(
1268
- (option) =>
1269
- option.value === (languageOverride ?? normalizeLang(language)),
1308
+ (option) => option.value === currentUiLanguage,
1270
1309
  ) ?? supportedLanguageOptions[0],
1271
- [language, languageOverride, supportedLanguageOptions],
1310
+ [currentUiLanguage, supportedLanguageOptions],
1272
1311
  );
1273
1312
 
1274
- const renderedOutputLanguage = useMemo(() => {
1275
- if (outputLanguage && outputLanguage !== "auto") {
1276
- return outputLanguage;
1277
- }
1278
-
1279
- return languageOverride || readDefaultOutputLanguage() || "";
1280
- }, [languageOverride, outputLanguage]);
1281
-
1282
1313
  const renderLanguageOverrideSelect = useCallback(
1283
1314
  (size: "xs" | "sm" = "sm") => (
1284
1315
  <Select
@@ -1286,12 +1317,18 @@ Follow these additional instructions: ${instructions}`
1286
1317
  aria-label={I18n.get("Interface language")}
1287
1318
  checkIconPosition="right"
1288
1319
  className="ai-feature-language-selector"
1289
- comboboxProps={{ width: 220 }}
1290
1320
  data={supportedLanguageOptions.map((option) => ({
1291
1321
  value: option.value,
1292
- label: option.flag,
1322
+ label: "",
1293
1323
  }))}
1294
- disabled={ai.busy}
1324
+ leftSection={
1325
+ selectedLanguageOption?.countryCode ? (
1326
+ <LanguageFlag
1327
+ countryCode={selectedLanguageOption.countryCode}
1328
+ size={18}
1329
+ />
1330
+ ) : null
1331
+ }
1295
1332
  onChange={(value) => {
1296
1333
  const nextLanguage = normalizeLang(value);
1297
1334
 
@@ -1312,33 +1349,57 @@ Follow these additional instructions: ${instructions}`
1312
1349
 
1313
1350
  return (
1314
1351
  <Group gap="xs" wrap="nowrap">
1315
- <span aria-hidden="true">{languageOption.flag}</span>
1316
- <span>{I18n.get(languageOption.countryName)}</span>
1352
+ {languageOption.countryCode ? (
1353
+ <LanguageFlag
1354
+ countryCode={languageOption.countryCode}
1355
+ size={18}
1356
+ />
1357
+ ) : null}
1358
+ <span>{I18n.get(languageOption.label)}</span>
1317
1359
  </Group>
1318
1360
  );
1319
1361
  }}
1362
+ rightSection={null}
1320
1363
  searchable={false}
1321
1364
  size={size}
1322
1365
  styles={{
1366
+ dropdown: {
1367
+ minWidth: 220,
1368
+ },
1323
1369
  input: {
1370
+ backgroundColor: "transparent",
1371
+ border: 0,
1372
+ boxShadow: "none",
1373
+ color: "inherit",
1324
1374
  fontSize: 18,
1325
1375
  lineHeight: 1,
1326
1376
  minHeight: size === "sm" ? 36 : 28,
1327
- paddingInlineStart: 10,
1328
- paddingInlineEnd: 28,
1329
- textAlign: "center",
1330
- width: size === "sm" ? 72 : 60,
1377
+ paddingInlineStart: 0,
1378
+ paddingInlineEnd: 0,
1379
+ backgroundImage: "none",
1380
+ width: size === "sm" ? 44 : 36,
1381
+ },
1382
+ option: {
1383
+ color: "inherit",
1331
1384
  },
1332
1385
  section: {
1333
- width: 24,
1386
+ color: "inherit",
1387
+ alignItems: "center",
1388
+ display: "flex",
1389
+ height: "100%",
1390
+ insetInlineStart: 0,
1391
+ justifyContent: "center",
1392
+ pointerEvents: "none",
1393
+ width: "100%",
1334
1394
  },
1335
1395
  }}
1336
1396
  value={selectedLanguageOption?.value || null}
1337
- w={size === "sm" ? 72 : 60}
1397
+ w={size === "sm" ? 44 : 36}
1338
1398
  />
1339
1399
  ),
1340
1400
  [
1341
1401
  ai.busy,
1402
+ selectedLanguageOption?.countryCode,
1342
1403
  selectedLanguageOption?.value,
1343
1404
  setLanguageOverride,
1344
1405
  supportedLanguageOptions,
@@ -1408,15 +1469,23 @@ Follow these additional instructions: ${instructions}`
1408
1469
  {variation === "modal" && <Modal.Overlay />}
1409
1470
  <ContentComponent
1410
1471
  w="100%"
1472
+ dir={effectiveDirection}
1411
1473
  style={{
1412
1474
  left: 0,
1413
1475
  }}
1414
1476
  >
1415
1477
  {variation === "modal" && (
1416
- <Modal.Header style={{ zIndex: 1000 }}>
1478
+ <Modal.Header dir={effectiveDirection} style={{ zIndex: 1000 }}>
1417
1479
  {getOpenButtonDefaultIcon("ai-feature-title-icon")}
1418
1480
  <Modal.Title>{I18n.get(defaultTitle)}</Modal.Title>
1419
- <div style={{ marginLeft: "auto", marginRight: 8 }}>
1481
+ <div
1482
+ style={{
1483
+ display: "flex",
1484
+ flex: 1,
1485
+ justifyContent: "flex-start",
1486
+ marginRight: 8,
1487
+ }}
1488
+ >
1420
1489
  {renderLanguageOverrideSelect("xs")}
1421
1490
  </div>
1422
1491
  <Modal.CloseButton />
@@ -2080,7 +2149,7 @@ Follow these additional instructions: ${instructions}`
2080
2149
  mode !== "generatePostMetadata" &&
2081
2150
  typeof generated === "string" && (
2082
2151
  <MarkdownResult
2083
- contentLanguage={renderedOutputLanguage}
2152
+ contentLanguage={generatedContentLanguage}
2084
2153
  value={generated}
2085
2154
  editable={!!editable}
2086
2155
  onChange={(v) => {
@@ -2092,7 +2161,7 @@ Follow these additional instructions: ${instructions}`
2092
2161
  )}
2093
2162
  {generated === "" && (
2094
2163
  <MarkdownResult
2095
- contentLanguage={renderedOutputLanguage}
2164
+ contentLanguage={generatedContentLanguage}
2096
2165
  value={generated}
2097
2166
  editable={false}
2098
2167
  />
@@ -2193,7 +2262,12 @@ function MarkdownResult(props: {
2193
2262
 
2194
2263
  if (editable) {
2195
2264
  return (
2196
- <Stack p={0} gap="sm">
2265
+ <Stack
2266
+ p={0}
2267
+ gap="sm"
2268
+ dir={contentDirection}
2269
+ style={{ textAlign: contentAlign }}
2270
+ >
2197
2271
  <Input.Label>{I18n.get("Generated content")}</Input.Label>
2198
2272
  <Textarea
2199
2273
  value={value}
@@ -2203,6 +2277,9 @@ function MarkdownResult(props: {
2203
2277
  maxRows={12}
2204
2278
  p={0}
2205
2279
  className="ai-feature-generated-content ai-feature-editor"
2280
+ styles={{
2281
+ input: { direction: contentDirection, textAlign: contentAlign },
2282
+ }}
2206
2283
  />
2207
2284
 
2208
2285
  <Input.Label>{I18n.get("Preview")}</Input.Label>