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

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.8",
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,19 @@ 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
1324
  disabled={ai.busy}
1325
+ leftSection={
1326
+ selectedLanguageOption?.countryCode ? (
1327
+ <LanguageFlag
1328
+ countryCode={selectedLanguageOption.countryCode}
1329
+ size={18}
1330
+ />
1331
+ ) : null
1332
+ }
1295
1333
  onChange={(value) => {
1296
1334
  const nextLanguage = normalizeLang(value);
1297
1335
 
@@ -1312,33 +1350,57 @@ Follow these additional instructions: ${instructions}`
1312
1350
 
1313
1351
  return (
1314
1352
  <Group gap="xs" wrap="nowrap">
1315
- <span aria-hidden="true">{languageOption.flag}</span>
1316
- <span>{I18n.get(languageOption.countryName)}</span>
1353
+ {languageOption.countryCode ? (
1354
+ <LanguageFlag
1355
+ countryCode={languageOption.countryCode}
1356
+ size={18}
1357
+ />
1358
+ ) : null}
1359
+ <span>{I18n.get(languageOption.label)}</span>
1317
1360
  </Group>
1318
1361
  );
1319
1362
  }}
1363
+ rightSection={null}
1320
1364
  searchable={false}
1321
1365
  size={size}
1322
1366
  styles={{
1367
+ dropdown: {
1368
+ minWidth: 220,
1369
+ },
1323
1370
  input: {
1371
+ backgroundColor: "transparent",
1372
+ border: 0,
1373
+ boxShadow: "none",
1374
+ color: "inherit",
1324
1375
  fontSize: 18,
1325
1376
  lineHeight: 1,
1326
1377
  minHeight: size === "sm" ? 36 : 28,
1327
- paddingInlineStart: 10,
1328
- paddingInlineEnd: 28,
1329
- textAlign: "center",
1330
- width: size === "sm" ? 72 : 60,
1378
+ paddingInlineStart: 0,
1379
+ paddingInlineEnd: 0,
1380
+ backgroundImage: "none",
1381
+ width: size === "sm" ? 44 : 36,
1382
+ },
1383
+ option: {
1384
+ color: "inherit",
1331
1385
  },
1332
1386
  section: {
1333
- width: 24,
1387
+ color: "inherit",
1388
+ alignItems: "center",
1389
+ display: "flex",
1390
+ height: "100%",
1391
+ insetInlineStart: 0,
1392
+ justifyContent: "center",
1393
+ pointerEvents: "none",
1394
+ width: "100%",
1334
1395
  },
1335
1396
  }}
1336
1397
  value={selectedLanguageOption?.value || null}
1337
- w={size === "sm" ? 72 : 60}
1398
+ w={size === "sm" ? 44 : 36}
1338
1399
  />
1339
1400
  ),
1340
1401
  [
1341
1402
  ai.busy,
1403
+ selectedLanguageOption?.countryCode,
1342
1404
  selectedLanguageOption?.value,
1343
1405
  setLanguageOverride,
1344
1406
  supportedLanguageOptions,
@@ -1408,15 +1470,23 @@ Follow these additional instructions: ${instructions}`
1408
1470
  {variation === "modal" && <Modal.Overlay />}
1409
1471
  <ContentComponent
1410
1472
  w="100%"
1473
+ dir={effectiveDirection}
1411
1474
  style={{
1412
1475
  left: 0,
1413
1476
  }}
1414
1477
  >
1415
1478
  {variation === "modal" && (
1416
- <Modal.Header style={{ zIndex: 1000 }}>
1479
+ <Modal.Header dir={effectiveDirection} style={{ zIndex: 1000 }}>
1417
1480
  {getOpenButtonDefaultIcon("ai-feature-title-icon")}
1418
1481
  <Modal.Title>{I18n.get(defaultTitle)}</Modal.Title>
1419
- <div style={{ marginLeft: "auto", marginRight: 8 }}>
1482
+ <div
1483
+ style={{
1484
+ display: "flex",
1485
+ flex: 1,
1486
+ justifyContent: "flex-start",
1487
+ marginRight: 8,
1488
+ }}
1489
+ >
1420
1490
  {renderLanguageOverrideSelect("xs")}
1421
1491
  </div>
1422
1492
  <Modal.CloseButton />
@@ -2080,7 +2150,7 @@ Follow these additional instructions: ${instructions}`
2080
2150
  mode !== "generatePostMetadata" &&
2081
2151
  typeof generated === "string" && (
2082
2152
  <MarkdownResult
2083
- contentLanguage={renderedOutputLanguage}
2153
+ contentLanguage={generatedContentLanguage}
2084
2154
  value={generated}
2085
2155
  editable={!!editable}
2086
2156
  onChange={(v) => {
@@ -2092,7 +2162,7 @@ Follow these additional instructions: ${instructions}`
2092
2162
  )}
2093
2163
  {generated === "" && (
2094
2164
  <MarkdownResult
2095
- contentLanguage={renderedOutputLanguage}
2165
+ contentLanguage={generatedContentLanguage}
2096
2166
  value={generated}
2097
2167
  editable={false}
2098
2168
  />
@@ -2193,7 +2263,12 @@ function MarkdownResult(props: {
2193
2263
 
2194
2264
  if (editable) {
2195
2265
  return (
2196
- <Stack p={0} gap="sm">
2266
+ <Stack
2267
+ p={0}
2268
+ gap="sm"
2269
+ dir={contentDirection}
2270
+ style={{ textAlign: contentAlign }}
2271
+ >
2197
2272
  <Input.Label>{I18n.get("Generated content")}</Input.Label>
2198
2273
  <Textarea
2199
2274
  value={value}
@@ -2203,6 +2278,9 @@ function MarkdownResult(props: {
2203
2278
  maxRows={12}
2204
2279
  p={0}
2205
2280
  className="ai-feature-generated-content ai-feature-editor"
2281
+ styles={{
2282
+ input: { direction: contentDirection, textAlign: contentAlign },
2283
+ }}
2206
2284
  />
2207
2285
 
2208
2286
  <Input.Label>{I18n.get("Preview")}</Input.Label>