@parhelia/localization 0.1.10884 → 0.1.10979
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/dist/LocalizeItemCommand.d.ts.map +1 -1
- package/dist/LocalizeItemCommand.js +4 -2
- package/dist/LocalizeItemDialog.d.ts.map +1 -1
- package/dist/LocalizeItemDialog.js +13 -19
- package/dist/index.js +2 -5
- package/dist/setup/LocalizationSetupStep.d.ts.map +1 -1
- package/dist/setup/LocalizationSetupStep.js +5 -7
- package/dist/translation-center/BatchTranslationView.d.ts.map +1 -1
- package/dist/translation-center/BatchTranslationView.js +15 -7
- package/package.json +6 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalizeItemCommand.d.ts","sourceRoot":"","sources":["../src/LocalizeItemCommand.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"LocalizeItemCommand.d.ts","sourceRoot":"","sources":["../src/LocalizeItemCommand.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAUhF,MAAM,MAAM,uBAAuB,GAAG,WAAW,GAAG;IAClD,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG,cAAc,CAAC,uBAAuB,CAAC,CAAC;AACjF,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;AAEnE,eAAO,MAAM,mBAAmB,EAAE,mBAgDjC,CAAC"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
2
3
|
import { Globe as LucideGlobe } from "lucide-react";
|
|
3
4
|
import { LocalizeItemDialog } from "./LocalizeItemDialog";
|
|
4
|
-
//
|
|
5
|
-
|
|
5
|
+
// Icon wrapper to avoid React 19 JSX type issues with forwardRef components.
|
|
6
|
+
// Use React.createElement so we don't call the forwardRef objects as functions.
|
|
7
|
+
const GlobeIcon = (props) => React.createElement(LucideGlobe, props);
|
|
6
8
|
export const localizeItemCommand = {
|
|
7
9
|
id: "localizeItem",
|
|
8
10
|
label: "Localize",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalizeItemDialog.d.ts","sourceRoot":"","sources":["../src/LocalizeItemDialog.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAI7C,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"LocalizeItemDialog.d.ts","sourceRoot":"","sources":["../src/LocalizeItemDialog.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAI7C,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,eAAe,CAAC;AAiBvB,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,uBAAuB,GAAG,WAAW,CAAC,uBAAuB,CAAC,2CA2PtE"}
|
|
@@ -4,8 +4,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
4
4
|
import { ChevronRight as LucideChevronRight } from "lucide-react";
|
|
5
5
|
import { useTranslationWizard } from "./hooks/useTranslationWizard";
|
|
6
6
|
import { performDefaultTranslation, generateBatchId } from "./LocalizeItemUtils";
|
|
7
|
-
|
|
8
|
-
const ChevronRightIcon = (props) => LucideChevronRight(props);
|
|
7
|
+
const ChevronRightIcon = LucideChevronRight;
|
|
9
8
|
// Note: DialogButtons is an internal component that might need to be added to core exports
|
|
10
9
|
// For now, we'll implement it inline
|
|
11
10
|
const DialogButtons = ({ children, ...props }) => (_jsx("div", { className: "flex justify-end gap-2 pt-4 border-t mt-auto px-6 pb-6", ...props, children: children }));
|
|
@@ -77,24 +76,19 @@ export function LocalizeItemDialog(props) {
|
|
|
77
76
|
includeSubitems: currentWizardData.includeSubitems,
|
|
78
77
|
discoveredItems: currentWizardData.discoveredItems,
|
|
79
78
|
};
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
try {
|
|
80
|
+
const batchId = generateBatchId();
|
|
81
|
+
const translationResult = await performDefaultTranslation(currentWizardData, editContext, batchId);
|
|
82
|
+
console.log("Translation completed successfully:", translationResult);
|
|
83
|
+
// Include batchId in result for navigation to translation management
|
|
84
|
+
const resultWithBatch = { ...result, batchId };
|
|
85
|
+
// Close dialog and let parent component handle navigation to translation management
|
|
86
|
+
props.onClose?.(resultWithBatch);
|
|
82
87
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
console.log("Translation completed successfully:", translationResult);
|
|
88
|
-
// Include batchId in result for navigation to translation management
|
|
89
|
-
const resultWithBatch = { ...result, batchId };
|
|
90
|
-
// Close dialog and let parent component handle navigation to translation management
|
|
91
|
-
props.onClose?.(resultWithBatch);
|
|
92
|
-
}
|
|
93
|
-
catch (error) {
|
|
94
|
-
console.error("Translation failed:", error);
|
|
95
|
-
// Still close the dialog even on error
|
|
96
|
-
props.onClose?.(result);
|
|
97
|
-
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
console.error("Translation failed:", error);
|
|
90
|
+
// Still close the dialog even on error
|
|
91
|
+
props.onClose?.(result);
|
|
98
92
|
}
|
|
99
93
|
}
|
|
100
94
|
catch (error) {
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import React from "react";
|
|
3
2
|
import { ServiceLanguageSelectionStep } from "./steps/ServiceLanguageSelectionStep";
|
|
4
3
|
import { SubitemDiscoveryStep } from "./steps/SubitemDiscoveryStep";
|
|
5
4
|
import { localizeItemCommand } from "./LocalizeItemCommand";
|
|
@@ -9,10 +8,8 @@ import { RecentTranslations } from "./translation-center/RecentTranslations";
|
|
|
9
8
|
import { LocalizationSetupStep } from "./setup/LocalizationSetupStep";
|
|
10
9
|
import { Languages as LucideLanguages, LayoutGrid as LucideLayoutGrid, } from "lucide-react";
|
|
11
10
|
import { pageEditorViewBase } from "@parhelia/core";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const LanguagesIcon = (props) => React.createElement(LucideLanguages, props);
|
|
15
|
-
const LayoutGridIcon = (props) => React.createElement(LucideLayoutGrid, props);
|
|
11
|
+
const LanguagesIcon = LucideLanguages;
|
|
12
|
+
const LayoutGridIcon = LucideLayoutGrid;
|
|
16
13
|
const DEFAULT_TRANSLATION_WIZARD_CONFIGURATION = {
|
|
17
14
|
steps: [
|
|
18
15
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalizationSetupStep.d.ts","sourceRoot":"","sources":["../../src/setup/LocalizationSetupStep.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"LocalizationSetupStep.d.ts","sourceRoot":"","sources":["../../src/setup/LocalizationSetupStep.tsx"],"names":[],"mappings":"AAgCA,wBAAgB,qBAAqB,4CAwNpC;AAED,eAAe,qBAAqB,CAAC"}
|
|
@@ -3,13 +3,11 @@ import React, { useCallback, useState } from "react";
|
|
|
3
3
|
import { Card, Button, useEditContext } from "@parhelia/core";
|
|
4
4
|
import { CheckCircle as LucideCheckCircle, AlertCircle as LucideAlertCircle, Languages as LucideLanguages, RefreshCw as LucideRefreshCw, Plus as LucidePlus, } from "lucide-react";
|
|
5
5
|
import { getAvailableTranslationServices, createProviderSettings, } from "../services/translationService";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const RefreshCwIcon = (props) => React.createElement(LucideRefreshCw, props);
|
|
12
|
-
const PlusIcon = (props) => React.createElement(LucidePlus, props);
|
|
6
|
+
const CheckCircleIcon = LucideCheckCircle;
|
|
7
|
+
const AlertCircleIcon = LucideAlertCircle;
|
|
8
|
+
const LanguagesIcon = LucideLanguages;
|
|
9
|
+
const RefreshCwIcon = LucideRefreshCw;
|
|
10
|
+
const PlusIcon = LucidePlus;
|
|
13
11
|
export function LocalizationSetupStep() {
|
|
14
12
|
const editContext = useEditContext();
|
|
15
13
|
const [state, setState] = useState("checking");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BatchTranslationView.d.ts","sourceRoot":"","sources":["../../src/translation-center/BatchTranslationView.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"BatchTranslationView.d.ts","sourceRoot":"","sources":["../../src/translation-center/BatchTranslationView.tsx"],"names":[],"mappings":"AA2BA,UAAU,yBAAyB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAoCD,wBAAgB,oBAAoB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,yBAAyB,2CAyzBlF"}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import React, { useEffect, useState, useMemo, useRef, useCallback } from "react";
|
|
2
|
+
import React, { useEffect, useState, useMemo, useRef, useCallback, } from "react";
|
|
3
3
|
import { useDebouncedCallback } from "use-debounce";
|
|
4
4
|
import { useEditContext, SimpleTable, Progress as CoreProgress, Button, } from "@parhelia/core";
|
|
5
5
|
import { listBatchTranslationJobs, subscribeToBatch, unsubscribeFromBatch, getBatchInfo, getTranslationProviders } from "../services/translationService";
|
|
6
6
|
// Wrapper so React 19 sees a plain function component instead of a forwardRef exotic component.
|
|
7
|
-
// Use React.createElement so we don't call the forwardRef object as a function at runtime.
|
|
8
7
|
const Progress = (props) => React.createElement(CoreProgress, props);
|
|
9
8
|
// Normalize API shape/casing to what the UI expects
|
|
10
9
|
function normalizeBatchInfo(raw) {
|
|
@@ -414,13 +413,22 @@ export function BatchTranslationView({ batchId, onBack }) {
|
|
|
414
413
|
};
|
|
415
414
|
}, [batchJobs, batchInfo, translationProgress]);
|
|
416
415
|
const overallProgress = progressSegments.actualProgress;
|
|
417
|
-
|
|
416
|
+
// Treat the batch as Completed if all jobs are completed and there are no errors/in-progress,
|
|
417
|
+
// even if the backend status is still "In Progress".
|
|
418
|
+
const effectiveStatus = batchInfo?.status === "In Progress" &&
|
|
419
|
+
progressSegments.total > 0 &&
|
|
420
|
+
progressSegments.completedCount === progressSegments.total &&
|
|
421
|
+
progressSegments.errorCount === 0 &&
|
|
422
|
+
progressSegments.inProgressCount === 0
|
|
423
|
+
? "Completed"
|
|
424
|
+
: batchInfo?.status;
|
|
425
|
+
return (_jsxs("div", { className: "flex h-full flex-col min-h-0", "data-testid": "batch-translation-view", children: [_jsxs("div", { className: "border-b p-4 flex-shrink-0", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [onBack && (_jsxs(Button, { size: "sm", variant: "outline", onClick: onBack, children: [_jsx("i", { className: "pi pi-arrow-left" }), "Back"] })), _jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Translation Batch" }), _jsxs("p", { className: "text-sm text-gray-600 mt-1", children: ["Batch ID: ", _jsx("span", { className: "font-mono text-xs bg-gray-100 px-2 py-0.5 rounded", children: batchId })] }), batchInfo && (_jsxs("div", { className: "mt-2 flex flex-wrap gap-2 items-center text-xs", children: [effectiveStatus && (_jsx("span", { "data-testid": "translation-job-status", className: `inline-flex items-center rounded-full px-2 py-1 font-medium ${effectiveStatus === 'Completed'
|
|
418
426
|
? 'bg-green-100 text-green-700'
|
|
419
|
-
:
|
|
427
|
+
: effectiveStatus === 'In Progress'
|
|
420
428
|
? 'bg-blue-100 text-blue-700'
|
|
421
|
-
:
|
|
429
|
+
: effectiveStatus === 'Error'
|
|
422
430
|
? 'bg-red-100 text-red-700'
|
|
423
|
-
: 'bg-gray-100 text-gray-700'}`, children:
|
|
431
|
+
: 'bg-gray-100 text-gray-700'}`, children: effectiveStatus })), batchInfo.provider && (_jsxs("span", { className: "text-gray-700", children: ["Provider: ", _jsx("span", { className: "font-medium", children: getProviderDisplayName(batchInfo.provider) })] })), batchInfo.initiatedByUser && (_jsxs("span", { className: "text-gray-700", children: ["By: ", _jsx("span", { className: "font-medium", children: batchInfo.initiatedByUser })] })), batchInfo.includeSubitems !== undefined && batchInfo.includeSubitems !== null && (_jsxs("span", { className: "text-gray-700", children: ["Include subitems: ", _jsx("span", { className: "font-medium", children: batchInfo.includeSubitems ? 'Yes' : 'No' })] })), batchInfo.scopeItemPath && (_jsxs("span", { className: "text-gray-700 truncate max-w-[40rem]", children: ["Scope: ", _jsx("span", { className: "font-mono", children: batchInfo.scopeItemPath })] })), (batchInfo.startedAtUtc || batchInfo.completedAtUtc || batchInfo.lastUpdatedUtc) && (_jsxs("span", { className: "text-gray-500", children: [batchInfo.startedAtUtc && (_jsxs(_Fragment, { children: ["Started: ", new Date(batchInfo.startedAtUtc).toLocaleString()] })), batchInfo.completedAtUtc && (_jsxs(_Fragment, { children: [batchInfo.startedAtUtc ? ' • ' : '', "Completed: ", new Date(batchInfo.completedAtUtc).toLocaleString()] })), !batchInfo.completedAtUtc && batchInfo.lastUpdatedUtc && (_jsxs(_Fragment, { children: [batchInfo.startedAtUtc ? ' • ' : '', "Updated: ", new Date(batchInfo.lastUpdatedUtc).toLocaleString()] }))] }))] }))] })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("div", { className: "text-sm text-gray-600", children: [`${progressSegments.completedCount}/${progressSegments.total} completed`, progressSegments.errorCount ? `, ${progressSegments.errorCount} errors` : ''] }), _jsxs(Button, { size: "sm", onClick: loadBatchJobs, disabled: isLoading, title: "Manual refresh - normally updates come through websocket subscriptions", children: [_jsx("i", { className: `pi pi-refresh ${isLoading ? 'pi-spin' : ''}` }), "Refresh"] })] })] }), _jsxs("div", { className: "mt-4", children: [_jsxs("div", { className: "flex items-center justify-between mb-2", children: [_jsx("span", { className: "text-sm font-medium", children: "Overall Progress" }), _jsxs("span", { className: "text-sm text-gray-600", children: [progressSegments.completedCount, " / ", progressSegments.total, " completed"] })] }), _jsxs("div", { className: "relative h-4 bg-gray-200 rounded-full overflow-hidden", children: [_jsxs("div", { className: "absolute inset-0 flex", children: [progressSegments.completed > 0 && (_jsx("div", { className: "bg-green-500 h-full transition-all duration-300", style: { width: `${progressSegments.completed}%` } })), progressSegments.inProgress > 0 && (_jsx("div", { className: "bg-blue-500 h-full transition-all duration-300", style: { width: `${progressSegments.inProgress}%` } })), progressSegments.error > 0 && (_jsx("div", { className: "bg-red-500 h-full transition-all duration-300", style: { width: `${progressSegments.error}%` } })), progressSegments.pending > 0 && (_jsx("div", { className: "bg-gray-300 h-full transition-all duration-300", style: { width: `${progressSegments.pending}%` } }))] }), _jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: _jsxs("span", { className: "text-xs font-medium text-white drop-shadow-sm", children: [overallProgress, "%"] }) })] }), _jsxs("div", { className: "flex gap-3 mt-2 text-xs", children: [progressSegments.completedCount > 0 && (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx("div", { className: "w-3 h-3 bg-green-500 rounded-sm" }), _jsxs("span", { className: "text-gray-700", children: [progressSegments.completedCount, " completed"] })] })), progressSegments.inProgressCount > 0 && (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx("div", { className: "w-3 h-3 bg-blue-500 rounded-sm" }), _jsxs("span", { className: "text-gray-700", children: [progressSegments.inProgressCount, " in progress"] })] })), progressSegments.errorCount > 0 && (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx("div", { className: "w-3 h-3 bg-red-500 rounded-sm" }), _jsxs("span", { className: "text-gray-700", children: [progressSegments.errorCount, " errors"] })] })), progressSegments.pendingCount > 0 && (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx("div", { className: "w-3 h-3 bg-gray-300 rounded-sm" }), _jsxs("span", { className: "text-gray-700", children: [progressSegments.pendingCount, " pending"] })] })), typeof batchInfo?.expectedJobs === 'number' && (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx("div", { className: "w-3 h-3 bg-gray-300 rounded-sm" }), _jsxs("span", { className: "text-gray-700", children: [(batchInfo.expectedJobs || 0) - (batchInfo.completedJobs || 0) - (batchInfo.errorJobs || 0), " pending (server)"] })] }))] })] })] }), _jsx("div", { className: "flex-1 overflow-auto p-4 min-h-0", children: isLoading && batchJobs.length === 0 ? (_jsx("div", { className: "flex items-center justify-center h-32", children: _jsxs("div", { className: "flex items-center gap-2 text-gray-500", children: [_jsx("i", { className: "pi pi-spin pi-spinner" }), "Loading batch translations..."] }) })) : batchJobs.length === 0 ? (_jsx("div", { className: "flex items-center justify-center h-32", children: _jsxs("div", { className: "text-center text-gray-500", children: [_jsx("i", { className: "pi pi-language text-2xl block mb-2" }), _jsx("p", { children: "No translations found for this batch" }), _jsx("p", { className: "text-sm", children: "The batch may not exist or has no translation jobs" })] }) })) : (_jsx("div", { className: "space-y-6", children: sortedItemGroups.map(([itemId, jobs]) => {
|
|
424
432
|
const itemName = itemNameById[itemId] || itemId;
|
|
425
433
|
const itemCompleted = jobs.filter(j => j.status === "Completed").length;
|
|
426
434
|
// Calculate item progress including in-progress job progress
|
|
@@ -463,7 +471,7 @@ export function BatchTranslationView({ batchId, onBack }) {
|
|
|
463
471
|
? "bg-blue-100 text-blue-700"
|
|
464
472
|
: job.status === "Error"
|
|
465
473
|
? "bg-red-100 text-red-700"
|
|
466
|
-
: "bg-gray-100 text-gray-700"}`, children: job.status }), job.status === "In Progress" && progress && Math.round(progress.progress || 0) === 0 && (_jsx("div", { className: "mt-1 text-xs text-gray-600", children: "Version created" })), progress && job.status === "In Progress" && (_jsxs("div", { className: "mt-1", children: [_jsxs("div", { className: "flex items-center justify-between mb-1", children: [_jsx("span", { className: "text-xs text-gray-500 truncate", children: progress.message }), _jsxs("span", { className: "text-xs font-medium text-gray-700 ml-1", children: [Math.round(progress.progress), "%"] })] }), _jsx(Progress, { value: progress.progress, className: "h-2", indicatorClassName: "bg-blue-500", showValue: false })] }))] }));
|
|
474
|
+
: "bg-gray-100 text-gray-700"}`, children: job.status }), job.status === "In Progress" && progress && Math.round(progress.progress || 0) === 0 && (_jsx("div", { className: "mt-1 text-xs text-gray-600", children: "Version created" })), progress && job.status === "In Progress" && (_jsxs("div", { className: "mt-1", children: [_jsxs("div", { className: "flex items-center justify-between mb-1", children: [_jsx("span", { className: "text-xs text-gray-500 truncate", "data-testid": "translation-progress-text", children: progress.message }), _jsxs("span", { className: "text-xs font-medium text-gray-700 ml-1", children: [Math.round(progress.progress), "%"] })] }), _jsx(Progress, { value: progress.progress, className: "h-2", indicatorClassName: "bg-blue-500", showValue: false, "data-testid": "translation-progress-bar" })] }))] }));
|
|
467
475
|
}
|
|
468
476
|
},
|
|
469
477
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parhelia/localization",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10979",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"generate:component": "turbo gen react-component",
|
|
29
29
|
"check-types": "tsc --noEmit"
|
|
30
30
|
},
|
|
31
|
-
"devDependencies": {
|
|
31
|
+
"devDependencies": {
|
|
32
32
|
"@repo/eslint-config": "*",
|
|
33
33
|
"@repo/typescript-config": "*",
|
|
34
34
|
"@turbo/gen": "^2.4.4",
|
|
@@ -41,10 +41,11 @@
|
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@parhelia/core": "*",
|
|
43
43
|
"@tailwindcss/postcss": "^4.0.14",
|
|
44
|
-
"
|
|
44
|
+
"lucide-react": "^0.486.0",
|
|
45
|
+
"next": "16.0.3",
|
|
45
46
|
"postcss": "^8.5.3",
|
|
46
|
-
"react": "
|
|
47
|
-
"react-dom": "
|
|
47
|
+
"react": "19.2.0",
|
|
48
|
+
"react-dom": "19.2.0",
|
|
48
49
|
"tailwindcss": "^4.0.14"
|
|
49
50
|
},
|
|
50
51
|
"overrides": {
|