@designbasekorea/figma-ui 0.6.10 → 0.7.1

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/index.js CHANGED
@@ -37,6 +37,7 @@ const FIGMA_UI_TRANSLATIONS = {
37
37
  remainingShort: '남음',
38
38
  usageCountCaption: '오늘 사용',
39
39
  remainingCopy: '{{count}}회 남음 · 매일 리셋',
40
+ lowUsageWarning: '무료 사용량이 {{count}}회({{percent}}%) 남았습니다. 멤버십으로 무제한 이용해보세요.',
40
41
  usageUnavailable: '사용량 제한이 없습니다',
41
42
  unlimitedUsageTooltip: '모든 기능을 제한 없이 사용할 수 있습니다',
42
43
  betaUsageDescription: '베타 기간에는 사용량 제한이 적용되지 않습니다',
@@ -51,12 +52,16 @@ const FIGMA_UI_TRANSLATIONS = {
51
52
  'form.submit': '제출',
52
53
  'form.verifying': '확인 중...',
53
54
  'license.youtube_membership': 'YouTube 멤버십',
55
+ 'license.current_plan': '현재 플랜',
56
+ 'license.pro_plan': '프로 플랜',
57
+ 'license.membership': '멤버십',
54
58
  'license.pro_account': '프로 계정',
55
59
  'license.upgrade_to_pro': '프로로 업그레이드',
56
60
  'license.all_features_available': '모든 기능을 사용할 수 있습니다.',
57
61
  'license.youtube_membership_description': 'YouTube 멤버십에 가입하면 Designbase 플러그인을 무제한으로 사용할 수 있습니다.',
58
62
  'license.purchase_for_unlimited': '디자인베이스 멤버십 또는 라이선스로 무제한 이용이 가능합니다.',
59
63
  'license.usage_unlimited': '사용 횟수 제한 없음',
64
+ 'license.active_until_expiry': '만료일까지 모든 기능을 제한 없이 사용할 수 있습니다.',
60
65
  'license.usage_daily': '오늘 남은 횟수 {{remaining}}/{{limit}}',
61
66
  'license.usage_weekly': '이번 주 남은 횟수 {{remaining}}/{{limit}}',
62
67
  'license.usage_monthly': '이번 달 남은 횟수 {{remaining}}/{{limit}}',
@@ -66,6 +71,7 @@ const FIGMA_UI_TRANSLATIONS = {
66
71
  'license.spots_remaining': '자리 남음',
67
72
  'license.deactivating': '비활성화 중...',
68
73
  'license.deactivate': '라이선스 비활성화',
74
+ 'license.deactivate_hint': '이 기기에서 라이선스 연결을 해제합니다.',
69
75
  'license.designbase_account': '디자인베이스 계정',
70
76
  'license.designbase_account_hint': 'WordPress 계정으로 로그인하거나 멤버십을 구매하면 플러그인이 자동으로 활성화됩니다.',
71
77
  'license.authenticating': '인증 중...',
@@ -169,6 +175,7 @@ const FIGMA_UI_TRANSLATIONS = {
169
175
  remainingShort: 'left',
170
176
  usageCountCaption: 'Used today',
171
177
  remainingCopy: '{{count}} remaining · resets daily',
178
+ lowUsageWarning: 'Only {{count}} free uses ({{percent}}%) remain. Upgrade for unlimited access.',
172
179
  usageUnavailable: 'No usage limit',
173
180
  unlimitedUsageTooltip: 'Use every feature without limits',
174
181
  betaUsageDescription: 'Usage limits are not applied during the beta period',
@@ -183,12 +190,16 @@ const FIGMA_UI_TRANSLATIONS = {
183
190
  'form.submit': 'Activate',
184
191
  'form.verifying': 'Verifying...',
185
192
  'license.youtube_membership': 'YouTube Membership',
193
+ 'license.current_plan': 'Current plan',
194
+ 'license.pro_plan': 'Pro plan',
195
+ 'license.membership': 'Membership',
186
196
  'license.pro_account': 'Pro account',
187
197
  'license.upgrade_to_pro': 'Upgrade to Pro',
188
198
  'license.all_features_available': 'All features are available.',
189
199
  'license.youtube_membership_description': 'Join YouTube Membership to use Designbase plugins without limits.',
190
200
  'license.purchase_for_unlimited': 'Get unlimited access with a Designbase membership or license.',
191
201
  'license.usage_unlimited': 'Unlimited usage',
202
+ 'license.active_until_expiry': 'All features remain available without limits until expiration.',
192
203
  'license.usage_daily': '{{remaining}}/{{limit}} remaining today',
193
204
  'license.usage_weekly': '{{remaining}}/{{limit}} remaining this week',
194
205
  'license.usage_monthly': '{{remaining}}/{{limit}} remaining this month',
@@ -198,6 +209,7 @@ const FIGMA_UI_TRANSLATIONS = {
198
209
  'license.spots_remaining': 'spots remaining',
199
210
  'license.deactivating': 'Deactivating...',
200
211
  'license.deactivate': 'Deactivate license',
212
+ 'license.deactivate_hint': 'Disconnects this license from the current device.',
201
213
  'license.designbase_account': 'Designbase account',
202
214
  'license.designbase_account_hint': 'Sign in with your WordPress account or purchase a membership to activate the plugin automatically.',
203
215
  'license.authenticating': 'Authenticating...',
@@ -301,6 +313,7 @@ const FIGMA_UI_TRANSLATIONS = {
301
313
  remainingShort: '残り',
302
314
  usageCountCaption: '今日の使用',
303
315
  remainingCopy: '{{count}}回残り · 毎日リセット',
316
+ lowUsageWarning: '無料使用回数は残り{{count}}回({{percent}}%)です。無制限利用にアップグレードできます。',
304
317
  usageUnavailable: '使用制限はありません',
305
318
  unlimitedUsageTooltip: 'すべての機能を無制限で使用できます',
306
319
  betaUsageDescription: 'ベータ期間中は使用制限がありません',
@@ -315,12 +328,16 @@ const FIGMA_UI_TRANSLATIONS = {
315
328
  'form.submit': '有効化',
316
329
  'form.verifying': '確認中...',
317
330
  'license.youtube_membership': 'YouTubeメンバーシップ',
331
+ 'license.current_plan': '現在のプラン',
332
+ 'license.pro_plan': 'Proプラン',
333
+ 'license.membership': 'メンバーシップ',
318
334
  'license.pro_account': 'Proアカウント',
319
335
  'license.upgrade_to_pro': 'Proにアップグレード',
320
336
  'license.all_features_available': 'すべての機能を使用できます。',
321
337
  'license.youtube_membership_description': 'YouTubeメンバーシップに参加すると、Designbaseプラグインを無制限で使用できます。',
322
338
  'license.purchase_for_unlimited': 'Designbaseのメンバーシップまたはライセンスで無制限に利用できます。',
323
339
  'license.usage_unlimited': '使用回数の制限なし',
340
+ 'license.active_until_expiry': '有効期限まで、すべての機能を制限なく使用できます。',
324
341
  'license.usage_daily': '今日の残り {{remaining}}/{{limit}}回',
325
342
  'license.usage_weekly': '今週の残り {{remaining}}/{{limit}}回',
326
343
  'license.usage_monthly': '今月の残り {{remaining}}/{{limit}}回',
@@ -330,6 +347,7 @@ const FIGMA_UI_TRANSLATIONS = {
330
347
  'license.spots_remaining': '枠残り',
331
348
  'license.deactivating': '無効化中...',
332
349
  'license.deactivate': 'ライセンスを無効化',
350
+ 'license.deactivate_hint': 'このデバイスからライセンスの接続を解除します。',
333
351
  'license.designbase_account': 'Designbaseアカウント',
334
352
  'license.designbase_account_hint': 'WordPressアカウントでサインインするか、メンバーシップを購入するとプラグインが自動的に有効化されます。',
335
353
  'license.authenticating': '認証中...',
@@ -978,7 +996,7 @@ const fallbackText = (t, key, fallback) => {
978
996
  const translated = t?.(key);
979
997
  return translated && translated !== key ? translated : fallback;
980
998
  };
981
- const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20, showUsage = true, showPlan = true, isLoading = false, onLicensePageClick, t, locale, className, }) => {
999
+ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, remainingUsage, maxDailyUsage = 20, showUsage = true, showPlan = true, isLoading = false, onLicensePageClick, licenseSource, accessScope, expiresAt, lastVerifiedAt, t, locale, className, }) => {
982
1000
  const { t: translate } = useFigmaUiI18n(locale, t);
983
1001
  const [isOpen, setIsOpen] = React.useState(false);
984
1002
  const rootRef = React.useRef(null);
@@ -988,9 +1006,11 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
988
1006
  const isBeta = paymentStatus === 'BETA';
989
1007
  const hasDailyLimit = !isPaid && maxDailyUsage > 0;
990
1008
  const limit = Math.max(1, Math.round(maxDailyUsage));
991
- const used = Math.min(limit, Math.max(0, Math.round(usageCount)));
992
- const remaining = Math.max(0, limit - used);
993
- const usedPercent = hasDailyLimit ? Math.round((used / limit) * 100) : 0;
1009
+ const hasRemainingUsage = typeof remainingUsage === 'number' && Number.isFinite(remainingUsage);
1010
+ const remaining = hasRemainingUsage
1011
+ ? Math.min(limit, Math.max(0, Math.round(remainingUsage)))
1012
+ : Math.max(0, limit - Math.min(limit, Math.max(0, Math.round(usageCount))));
1013
+ const remainingPercent = hasDailyLimit ? Math.round((remaining / limit) * 100) : 0;
994
1014
  const hasControl = showUsage || showPlan;
995
1015
  const planLabel = isPaid
996
1016
  ? fallbackText(translate, 'proUser', '프로')
@@ -999,9 +1019,12 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
999
1019
  : paymentStatus === 'TRIAL'
1000
1020
  ? fallbackText(translate, 'trialPlan', '체험판')
1001
1021
  : fallbackText(translate, 'free', '무료');
1002
- const planTitle = isBeta
1022
+ const planSuffix = fallbackText(translate, 'planSuffix', '플랜');
1023
+ const normalizedPlanLabel = planLabel.toLocaleLowerCase();
1024
+ const normalizedPlanSuffix = planSuffix.toLocaleLowerCase();
1025
+ const planTitle = isBeta || normalizedPlanLabel.includes(normalizedPlanSuffix)
1003
1026
  ? planLabel
1004
- : `${planLabel} ${fallbackText(translate, 'planSuffix', '플랜')}`;
1027
+ : `${planLabel} ${planSuffix}`;
1005
1028
  const triggerLabel = isPaid
1006
1029
  ? `${planLabel}, ${fallbackText(translate, 'unlimitedUsage', '무제한 사용 가능')}`
1007
1030
  : isBeta
@@ -1012,6 +1035,41 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
1012
1035
  const upgradeLabel = isPaid
1013
1036
  ? fallbackText(translate, 'managePlan', '플랜 관리')
1014
1037
  : fallbackText(translate, 'upgradePlan', '플랜 업그레이드');
1038
+ const resolvedLocale = locale || 'ko';
1039
+ const formatDate = (value) => {
1040
+ if (!value)
1041
+ return undefined;
1042
+ const date = new Date(value);
1043
+ if (Number.isNaN(date.getTime()))
1044
+ return value;
1045
+ return new Intl.DateTimeFormat(resolvedLocale, { dateStyle: 'medium' }).format(date);
1046
+ };
1047
+ const formattedSource = licenseSource === 'youtube_membership' || licenseSource === 'membership'
1048
+ ? fallbackText(translate, 'license.source_youtube_membership', 'YouTube 멤버십')
1049
+ : licenseSource === 'lemonsqueezy'
1050
+ ? fallbackText(translate, 'license.source_purchase', '구매 라이선스')
1051
+ : licenseSource;
1052
+ const formattedScope = accessScope === 'all-products' || accessScope === 'all_products'
1053
+ ? fallbackText(translate, 'license.all_plugins', '모든 플러그인')
1054
+ : accessScope;
1055
+ const paidMetadata = [
1056
+ formattedSource && {
1057
+ label: fallbackText(translate, 'license.membership', '멤버십'),
1058
+ value: formattedSource,
1059
+ },
1060
+ expiresAt && {
1061
+ label: fallbackText(translate, 'license.expires', '만료일'),
1062
+ value: formatDate(expiresAt),
1063
+ },
1064
+ formattedScope && {
1065
+ label: fallbackText(translate, 'license.access', '접근 범위'),
1066
+ value: formattedScope,
1067
+ },
1068
+ lastVerifiedAt && {
1069
+ label: fallbackText(translate, 'license.last_checked', '최근 확인'),
1070
+ value: formatDate(lastVerifiedAt),
1071
+ },
1072
+ ].filter(Boolean);
1015
1073
  React.useEffect(() => {
1016
1074
  if (!isOpen)
1017
1075
  return undefined;
@@ -1046,9 +1104,12 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
1046
1104
  'designbase-figma-usage-status--loading': isLoading,
1047
1105
  }, className) },
1048
1106
  React.createElement("div", { className: "designbase-figma-usage-status__control" },
1049
- showUsage && (React.createElement("button", { ref: triggerRef, type: "button", className: "designbase-figma-usage-status__trigger", "aria-expanded": isOpen, "aria-controls": popoverId, "aria-haspopup": "dialog", "aria-label": isLoading ? fallbackText(translate, 'loading', '불러오는 중') : triggerLabel, disabled: isLoading, onClick: togglePopover }, isLoading ? (React.createElement("span", { className: "designbase-figma-usage-status__skeleton", "aria-hidden": "true" })) : isPaid ? (React.createElement("span", { className: "designbase-figma-usage-status__unlimited-mark", "aria-hidden": "true" }, "\u221E")) : hasDailyLimit ? (React.createElement(React.Fragment, null,
1107
+ showUsage && (React.createElement("button", { ref: triggerRef, type: "button", className: "designbase-figma-usage-status__trigger", "aria-expanded": isOpen, "aria-controls": popoverId, "aria-haspopup": "dialog", "aria-label": isLoading ? fallbackText(translate, 'loading', '불러오는 중') : triggerLabel, disabled: isLoading, onClick: togglePopover }, isLoading ? (React.createElement("span", { className: "designbase-figma-usage-status__skeleton", "aria-hidden": "true" })) : isPaid ? (React.createElement(React.Fragment, null,
1108
+ React.createElement("span", { className: "designbase-figma-usage-status__mini-progress", "aria-hidden": "true" },
1109
+ React.createElement("span", { style: { width: '100%' } })),
1110
+ React.createElement("span", { className: "designbase-figma-usage-status__unlimited-mark", "aria-hidden": "true" }, "\u221E"))) : hasDailyLimit ? (React.createElement(React.Fragment, null,
1050
1111
  React.createElement("span", { className: "designbase-figma-usage-status__mini-progress", "aria-hidden": "true" },
1051
- React.createElement("span", { style: { width: `${usedPercent}%` } })),
1112
+ React.createElement("span", { style: { width: `${remainingPercent}%` } })),
1052
1113
  React.createElement("span", { className: "designbase-figma-usage-status__usage-value" },
1053
1114
  React.createElement("strong", null, remaining),
1054
1115
  React.createElement("span", null,
@@ -1057,43 +1118,34 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
1057
1118
  showPlan && !isLoading && (React.createElement("button", { type: "button", className: "designbase-figma-usage-status__plan-badge", "aria-expanded": isOpen, "aria-controls": popoverId, "aria-haspopup": "dialog", "aria-label": planTitle, onClick: togglePopover }, planLabel))),
1058
1119
  isOpen && (React.createElement("div", { id: popoverId, className: "designbase-figma-usage-status__popover", role: "dialog", "aria-label": fallbackText(translate, 'usageDetails', '사용량 상세') },
1059
1120
  React.createElement("div", { className: "designbase-figma-usage-status__popover-header" },
1060
- React.createElement("span", { className: "designbase-figma-usage-status__plan-title" }, planTitle),
1121
+ React.createElement("span", { className: "designbase-figma-usage-status__plan-heading" },
1122
+ React.createElement("span", null, fallbackText(translate, 'license.current_plan', '현재 플랜')),
1123
+ React.createElement("strong", null, planTitle)),
1061
1124
  !isBeta && (React.createElement(react.Button, { className: "designbase-figma-usage-status__upgrade", variant: isPaid ? 'secondary' : 'primary', size: "s", endIcon: "chevron-right", onPress: handleManageClick }, upgradeLabel))),
1062
- isPaid ? (React.createElement("div", { className: "designbase-figma-usage-status__unlimited-copy" },
1063
- React.createElement("span", { className: "designbase-figma-usage-status__unlimited-mark", "aria-hidden": "true" }, "\u221E"),
1064
- React.createElement("span", null, fallbackText(translate, 'unlimitedUsageTooltip', '모든 기능을 제한 없이 사용할 수 있습니다')))) : isBeta ? (React.createElement("p", { className: "designbase-figma-usage-status__note" }, fallbackText(translate, 'betaUsageDescription', '베타 기간에는 사용량 제한이 적용되지 않습니다'))) : hasDailyLimit ? (React.createElement("div", { className: "designbase-figma-usage-status__usage-detail" },
1125
+ isPaid ? (React.createElement(React.Fragment, null,
1126
+ React.createElement("div", { className: "designbase-figma-usage-status__unlimited-copy" },
1127
+ React.createElement("span", { className: "designbase-figma-usage-status__unlimited-mark", "aria-hidden": "true" }, "\u221E"),
1128
+ React.createElement("span", null,
1129
+ React.createElement("strong", null, fallbackText(translate, 'unlimitedUsage', '무제한 사용')),
1130
+ React.createElement("small", null, fallbackText(translate, 'unlimitedUsageTooltip', '모든 기능을 제한 없이 사용할 수 있습니다')))),
1131
+ paidMetadata.length > 0 && (React.createElement("dl", { className: "designbase-figma-usage-status__license-meta" }, paidMetadata.map((item) => (React.createElement("div", { key: item.label },
1132
+ React.createElement("dt", null, item.label),
1133
+ React.createElement("dd", null, item.value)))))))) : isBeta ? (React.createElement("p", { className: "designbase-figma-usage-status__note" }, fallbackText(translate, 'betaUsageDescription', '베타 기간에는 사용량 제한이 적용되지 않습니다'))) : hasDailyLimit ? (React.createElement("div", { className: "designbase-figma-usage-status__usage-detail" },
1065
1134
  React.createElement("div", { className: "designbase-figma-usage-status__usage-summary" },
1066
1135
  React.createElement("strong", { className: "designbase-figma-usage-status__usage-count" },
1067
- used,
1136
+ remaining,
1068
1137
  React.createElement("span", { className: "designbase-figma-usage-status__usage-divider" }, "/"),
1069
1138
  limit),
1070
- React.createElement("span", { className: "designbase-figma-usage-status__usage-caption" }, fallbackText(translate, 'usageCountCaption', '오늘 사용'))),
1071
- React.createElement("div", { className: "designbase-figma-usage-status__progress", role: "progressbar", "aria-label": fallbackText(translate, 'usageCountCaption', '오늘 사용'), "aria-valuemin": 0, "aria-valuemax": limit, "aria-valuenow": used, "aria-valuetext": `${used} / ${limit}` },
1072
- React.createElement("span", { style: { width: `${usedPercent}%` } })),
1139
+ React.createElement("span", { className: "designbase-figma-usage-status__usage-caption" }, fallbackText(translate, 'remainingToday', '오늘 남은 사용량')),
1140
+ React.createElement("strong", { className: "designbase-figma-usage-status__usage-percent" },
1141
+ remainingPercent,
1142
+ "%")),
1143
+ React.createElement("div", { className: "designbase-figma-usage-status__progress", role: "progressbar", "aria-label": fallbackText(translate, 'remainingToday', '오늘 남은 사용량'), "aria-valuemin": 0, "aria-valuemax": limit, "aria-valuenow": remaining, "aria-valuetext": `${remaining} / ${limit}, ${remainingPercent}%` },
1144
+ React.createElement("span", { style: { width: `${remainingPercent}%` } })),
1073
1145
  React.createElement("p", { className: "designbase-figma-usage-status__remaining-copy" }, resolveText(translate, { key: 'remainingCopy', values: { count: remaining } }, `${remaining}회 남음 · 매일 리셋`)))) : (React.createElement("p", { className: "designbase-figma-usage-status__note" }, fallbackText(translate, 'usageUnavailable', '사용량 제한이 없습니다')))))));
1074
1146
  };
1075
1147
  UsageStatusPopover.displayName = 'UsageStatusPopover';
1076
1148
 
1077
- const FigmaFooter = ({ onLicensePageClick, paymentStatus = 'FREE', usageCount = 0, isLoading = false, showPaymentStatus = true, showUsageInfo = true, maxDailyUsage = 20, showPaymentBadge = true, showDonation = true, donationUrl = 'https://buymeacoffee.com/designbase', donationText = { key: 'donation.buy_me_a_coffee' }, showDonationText = true, donationPromptText, showLanguageSelector = true, currentLanguage = 'ko', languages, onLanguageChange, logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', logoLinks, className, t, children, }) => {
1078
- const { t: translate } = useFigmaUiI18n(currentLanguage, t);
1079
- const resolvedShowUsageInfo = showPaymentStatus && showUsageInfo;
1080
- const resolvedShowPaymentBadge = showPaymentStatus && showPaymentBadge;
1081
- const resolvedLicensePageClick = paymentStatus === 'BETA' ? undefined : onLicensePageClick;
1082
- const hasChildren = React.Children.count(children) > 0;
1083
- const classes = clsx('designbase-figma-footer', {
1084
- 'designbase-figma-footer--has-children': hasChildren,
1085
- }, className);
1086
- return (React.createElement("footer", { className: classes },
1087
- React.createElement("div", { className: "designbase-figma-footer__wrap" },
1088
- React.createElement("div", { className: "designbase-figma-footer__left" },
1089
- React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, languages: languages, currentLanguage: currentLanguage, showLanguageSelector: showLanguageSelector, onLanguageChange: onLanguageChange, t: translate })),
1090
- React.createElement("div", { className: "designbase-figma-footer__right" },
1091
- (resolvedShowUsageInfo || resolvedShowPaymentBadge) && (React.createElement(UsageStatusPopover, { paymentStatus: paymentStatus, usageCount: usageCount, maxDailyUsage: maxDailyUsage, showUsage: resolvedShowUsageInfo, showPlan: resolvedShowPaymentBadge, isLoading: isLoading, onLicensePageClick: resolvedLicensePageClick, t: translate })),
1092
- showDonation && (React.createElement(DonationBadge, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s", iconOnly: true, showPrompt: false, t: translate })))),
1093
- children));
1094
- };
1095
- FigmaFooter.displayName = 'FigmaFooter';
1096
-
1097
1149
  const FigmaToastStack = ({ toasts, position, maxToasts, onRemoveToast, }) => {
1098
1150
  const visibleToasts = [...toasts]
1099
1151
  .sort((a, b) => b.createdAt - a.createdAt)
@@ -1154,26 +1206,195 @@ const FigmaToast = ({ message, isVisible, onClose, type = 'info', duration = 300
1154
1206
  React.createElement(react.Toast, { toastId: "figma-toast-simple", title: message, status: type, duration: duration, showProgress: false, showClose: true, onClose: onClose })));
1155
1207
  };
1156
1208
  const useFigmaToast = () => {
1157
- const [toast, setToast] = React.useState({ message: '', isVisible: false });
1158
- const showToast = React.useCallback((message, _type = 'info') => {
1159
- setToast({ message, isVisible: true });
1160
- setTimeout(() => setToast({ message: '', isVisible: false }), 5000);
1209
+ const [toast, setToast] = React.useState({
1210
+ message: '',
1211
+ isVisible: false,
1212
+ type: 'info',
1213
+ });
1214
+ const timeoutRef = React.useRef(null);
1215
+ const showToast = React.useCallback((message, type = 'info') => {
1216
+ if (timeoutRef.current)
1217
+ clearTimeout(timeoutRef.current);
1218
+ setToast({ message, isVisible: true, type });
1219
+ timeoutRef.current = setTimeout(() => {
1220
+ setToast({ message: '', isVisible: false, type: 'info' });
1221
+ timeoutRef.current = null;
1222
+ }, 5000);
1223
+ }, []);
1224
+ React.useEffect(() => () => {
1225
+ if (timeoutRef.current)
1226
+ clearTimeout(timeoutRef.current);
1161
1227
  }, []);
1162
- const toastComponent = toast.isVisible ? (React.createElement(FigmaToast, { message: toast.message, isVisible: toast.isVisible, onClose: () => setToast({ message: '', isVisible: false }), type: "info", duration: 5000 })) : null;
1228
+ const toastComponent = toast.isVisible ? (React.createElement(FigmaToast, { message: toast.message, isVisible: toast.isVisible, onClose: () => setToast({ message: '', isVisible: false, type: 'info' }), type: toast.type, duration: 5000 })) : null;
1163
1229
  return { showToast, toastComponent };
1164
1230
  };
1165
1231
  FigmaToastProvider.displayName = 'FigmaToastProvider';
1166
1232
  FigmaToast.displayName = 'FigmaToast';
1167
1233
 
1234
+ const DEFAULT_LOW_USAGE_WARNING = { key: 'lowUsageWarning' };
1235
+ const FigmaFooter = ({ onLicensePageClick, paymentStatus = 'FREE', usageCount = 0, remainingUsage, listenForUsageMessages = true, isLoading = false, showPaymentStatus = true, showUsageInfo = true, maxDailyUsage = 20, licenseSource, accessScope, expiresAt, lastVerifiedAt, showLowUsageWarning = true, lowUsageThresholdPercent = 10, lowUsageWarningText = DEFAULT_LOW_USAGE_WARNING, onLowUsageWarning, showPaymentBadge = true, showDonation = true, donationUrl = 'https://buymeacoffee.com/designbase', donationText = { key: 'donation.buy_me_a_coffee' }, showDonationText = true, donationPromptText, showLanguageSelector = true, currentLanguage = 'ko', languages, onLanguageChange, logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', logoLinks, className, t, children, }) => {
1236
+ const { t: translate } = useFigmaUiI18n(currentLanguage, t);
1237
+ const { showToast, toastComponent } = useFigmaToast();
1238
+ const [liveUsage, setLiveUsage] = React.useState({});
1239
+ const previousRemainingRef = React.useRef(null);
1240
+ const hasWarnedLowUsageRef = React.useRef(false);
1241
+ const effectivePaymentStatus = liveUsage.paymentStatus ?? paymentStatus;
1242
+ const effectiveUsageCount = liveUsage.usageCount ?? usageCount;
1243
+ const effectiveRemainingUsage = liveUsage.remainingUsage ?? remainingUsage;
1244
+ const effectiveMaxDailyUsage = liveUsage.maxDailyUsage ?? maxDailyUsage;
1245
+ const effectiveLicenseSource = liveUsage.licenseSource ?? licenseSource;
1246
+ const effectiveAccessScope = liveUsage.accessScope ?? accessScope;
1247
+ const effectiveExpiresAt = liveUsage.expiresAt ?? expiresAt;
1248
+ const effectiveLastVerifiedAt = liveUsage.lastVerifiedAt ?? lastVerifiedAt;
1249
+ const limit = Math.max(1, Math.round(effectiveMaxDailyUsage));
1250
+ const resolvedRemaining = typeof effectiveRemainingUsage === 'number' && Number.isFinite(effectiveRemainingUsage)
1251
+ ? Math.min(limit, Math.max(0, Math.round(effectiveRemainingUsage)))
1252
+ : Math.max(0, limit - Math.min(limit, Math.max(0, Math.round(effectiveUsageCount))));
1253
+ const remainingPercent = Math.round((resolvedRemaining / limit) * 100);
1254
+ const resolvedShowUsageInfo = showPaymentStatus
1255
+ && (showUsageInfo || effectivePaymentStatus === 'FREE' || effectivePaymentStatus === 'PAID');
1256
+ const resolvedShowPaymentBadge = showPaymentStatus && showPaymentBadge;
1257
+ const resolvedLicensePageClick = effectivePaymentStatus === 'BETA' ? undefined : onLicensePageClick;
1258
+ const hasChildren = React.Children.count(children) > 0;
1259
+ React.useEffect(() => {
1260
+ if (!listenForUsageMessages || typeof window === 'undefined')
1261
+ return undefined;
1262
+ const handleUsageMessage = (event) => {
1263
+ const message = event.data?.pluginMessage;
1264
+ if (!message || !['initialize', 'update-plugin-status', 'usage-consume-result'].includes(message.type)) {
1265
+ return;
1266
+ }
1267
+ const nextLimit = [message.usageLimit, message.freeDailyLimit, message.maxDailyUsage]
1268
+ .find((value) => typeof value === 'number' && Number.isFinite(value) && value > 0);
1269
+ const nextUsed = [message.usageUsed, message.dailyUsage]
1270
+ .find((value) => typeof value === 'number' && Number.isFinite(value) && value >= 0);
1271
+ const nextRemaining = typeof message.remainingUsage === 'number' && Number.isFinite(message.remainingUsage)
1272
+ ? message.remainingUsage
1273
+ : typeof nextLimit === 'number' && typeof nextUsed === 'number'
1274
+ ? Math.max(0, nextLimit - nextUsed)
1275
+ : undefined;
1276
+ const nextPaymentStatus = message.paymentStatus === 'PAID'
1277
+ ? 'PAID'
1278
+ : message.paymentStatus === 'FREE' || message.paymentStatus === 'UNPAID'
1279
+ ? 'FREE'
1280
+ : undefined;
1281
+ const nextLicenseSource = typeof message.licenseSource === 'string' ? message.licenseSource : undefined;
1282
+ const nextAccessScope = typeof message.accessScope === 'string' ? message.accessScope : undefined;
1283
+ const nextExpiresAt = typeof message.expiresAt === 'string' ? message.expiresAt : undefined;
1284
+ const rawLastVerifiedAt = message.lastVerifiedAt;
1285
+ const numericLastVerifiedAt = typeof rawLastVerifiedAt === 'number' && Number.isFinite(rawLastVerifiedAt)
1286
+ ? rawLastVerifiedAt < 1000000000000
1287
+ ? rawLastVerifiedAt * 1000
1288
+ : rawLastVerifiedAt
1289
+ : undefined;
1290
+ const parsedLastVerifiedAt = numericLastVerifiedAt === undefined
1291
+ ? undefined
1292
+ : new Date(numericLastVerifiedAt);
1293
+ const nextLastVerifiedAt = typeof rawLastVerifiedAt === 'string'
1294
+ ? rawLastVerifiedAt
1295
+ : parsedLastVerifiedAt && !Number.isNaN(parsedLastVerifiedAt.getTime())
1296
+ ? parsedLastVerifiedAt.toISOString()
1297
+ : undefined;
1298
+ if (nextPaymentStatus === undefined
1299
+ && nextLimit === undefined
1300
+ && nextUsed === undefined
1301
+ && nextRemaining === undefined
1302
+ && nextLicenseSource === undefined
1303
+ && nextAccessScope === undefined
1304
+ && nextExpiresAt === undefined
1305
+ && nextLastVerifiedAt === undefined) {
1306
+ return;
1307
+ }
1308
+ setLiveUsage((previous) => ({
1309
+ ...previous,
1310
+ ...(nextPaymentStatus ? { paymentStatus: nextPaymentStatus } : {}),
1311
+ ...(typeof nextLimit === 'number' ? { maxDailyUsage: nextLimit } : {}),
1312
+ ...(typeof nextUsed === 'number' ? { usageCount: nextUsed } : {}),
1313
+ ...(typeof nextRemaining === 'number' ? { remainingUsage: nextRemaining } : {}),
1314
+ ...(nextLicenseSource ? { licenseSource: nextLicenseSource } : {}),
1315
+ ...(nextAccessScope ? { accessScope: nextAccessScope } : {}),
1316
+ ...(nextExpiresAt ? { expiresAt: nextExpiresAt } : {}),
1317
+ ...(nextLastVerifiedAt ? { lastVerifiedAt: nextLastVerifiedAt } : {}),
1318
+ }));
1319
+ };
1320
+ window.addEventListener('message', handleUsageMessage);
1321
+ return () => window.removeEventListener('message', handleUsageMessage);
1322
+ }, [listenForUsageMessages]);
1323
+ React.useEffect(() => {
1324
+ const previousRemaining = previousRemainingRef.current;
1325
+ const shouldTrack = showLowUsageWarning && effectivePaymentStatus === 'FREE' && effectiveMaxDailyUsage > 0;
1326
+ if (!shouldTrack) {
1327
+ previousRemainingRef.current = resolvedRemaining;
1328
+ hasWarnedLowUsageRef.current = false;
1329
+ return;
1330
+ }
1331
+ if (remainingPercent >= lowUsageThresholdPercent) {
1332
+ hasWarnedLowUsageRef.current = false;
1333
+ }
1334
+ else if (previousRemaining !== null
1335
+ && resolvedRemaining < previousRemaining
1336
+ && !hasWarnedLowUsageRef.current) {
1337
+ hasWarnedLowUsageRef.current = true;
1338
+ const warningText = typeof lowUsageWarningText === 'string'
1339
+ ? lowUsageWarningText
1340
+ : {
1341
+ ...lowUsageWarningText,
1342
+ values: {
1343
+ ...(lowUsageWarningText.values || {}),
1344
+ count: resolvedRemaining,
1345
+ percent: remainingPercent,
1346
+ },
1347
+ };
1348
+ onLowUsageWarning?.({
1349
+ remaining: resolvedRemaining,
1350
+ percent: remainingPercent,
1351
+ limit,
1352
+ });
1353
+ showToast(resolveText(translate, warningText, `무료 사용량이 ${resolvedRemaining}회(${remainingPercent}%) 남았습니다.`) || `무료 사용량이 ${resolvedRemaining}회(${remainingPercent}%) 남았습니다.`, 'warning');
1354
+ }
1355
+ previousRemainingRef.current = resolvedRemaining;
1356
+ }, [
1357
+ lowUsageThresholdPercent,
1358
+ lowUsageWarningText,
1359
+ effectiveMaxDailyUsage,
1360
+ effectivePaymentStatus,
1361
+ onLowUsageWarning,
1362
+ remainingPercent,
1363
+ resolvedRemaining,
1364
+ showLowUsageWarning,
1365
+ showToast,
1366
+ translate,
1367
+ ]);
1368
+ const classes = clsx('designbase-figma-footer', {
1369
+ 'designbase-figma-footer--has-children': hasChildren,
1370
+ }, className);
1371
+ return (React.createElement("footer", { className: classes },
1372
+ React.createElement("div", { className: "designbase-figma-footer__wrap" },
1373
+ React.createElement("div", { className: "designbase-figma-footer__left" },
1374
+ React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, languages: languages, currentLanguage: currentLanguage, showLanguageSelector: showLanguageSelector, onLanguageChange: onLanguageChange, t: translate })),
1375
+ React.createElement("div", { className: "designbase-figma-footer__right" },
1376
+ (resolvedShowUsageInfo || resolvedShowPaymentBadge) && (React.createElement(UsageStatusPopover, { paymentStatus: effectivePaymentStatus, usageCount: effectiveUsageCount, remainingUsage: effectiveRemainingUsage, maxDailyUsage: effectiveMaxDailyUsage, showUsage: resolvedShowUsageInfo, showPlan: resolvedShowPaymentBadge, isLoading: isLoading, onLicensePageClick: resolvedLicensePageClick, licenseSource: effectiveLicenseSource, accessScope: effectiveAccessScope, expiresAt: effectiveExpiresAt, lastVerifiedAt: effectiveLastVerifiedAt, locale: currentLanguage, t: translate })),
1377
+ showDonation && (React.createElement(DonationBadge, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s", iconOnly: true, showPrompt: false, t: translate })))),
1378
+ children,
1379
+ toastComponent));
1380
+ };
1381
+ FigmaFooter.displayName = 'FigmaFooter';
1382
+
1168
1383
  const FormWithSubmit = ({ onLicenseSubmit, disabled = false, isSubmitting = false, value = '', onValueChange, label = { key: 'form.license_key' }, placeholder = { key: 'form.license_placeholder' }, submitText = { key: 'form.submit' }, submittingText = { key: 'form.verifying' }, helperText, className, t, locale, }) => {
1169
1384
  const { t: translate } = useFigmaUiI18n(locale, t);
1170
1385
  const [inputValue, setInputValue] = React.useState(value);
1171
1386
  React.useEffect(() => {
1172
1387
  setInputValue(value);
1173
1388
  }, [value]);
1389
+ const submitLicense = async () => {
1390
+ const normalizedValue = inputValue.trim();
1391
+ if (!normalizedValue || disabled || isSubmitting)
1392
+ return;
1393
+ await onLicenseSubmit(normalizedValue);
1394
+ };
1174
1395
  const handleSubmit = async (e) => {
1175
1396
  e.preventDefault();
1176
- await onLicenseSubmit(inputValue);
1397
+ await submitLicense();
1177
1398
  };
1178
1399
  const handleChange = (value) => {
1179
1400
  setInputValue(value);
@@ -1193,7 +1414,9 @@ const FormWithSubmit = ({ onLicenseSubmit, disabled = false, isSubmitting = fals
1193
1414
  handleChange(value);
1194
1415
  }
1195
1416
  }, placeholder: displayPlaceholder, helperText: displayHelperText, disabled: disabled || isSubmitting, size: "m" })),
1196
- React.createElement(react.Button, { type: "submit", variant: "primary", size: "m", loading: isSubmitting, disabled: !inputValue.trim() || isSubmitting || disabled, fullWidth: true }, isSubmitting ? displaySubmittingText : displaySubmitText)));
1417
+ React.createElement(react.Button, { type: "button", variant: "primary", size: "m", loading: isSubmitting, disabled: !inputValue.trim() || isSubmitting || disabled, fullWidth: true, onPress: () => {
1418
+ void submitLicense();
1419
+ } }, isSubmitting ? displaySubmittingText : displaySubmitText)));
1197
1420
  };
1198
1421
  FormWithSubmit.displayName = 'FormWithSubmit';
1199
1422
 
@@ -1316,7 +1539,7 @@ function formatSource(value, t) {
1316
1539
  }
1317
1540
  return value;
1318
1541
  }
1319
- const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSource, accessScope, expiresAt, lastVerifiedAt, validationMessage, allowRevealKey = false, t, locale, className, }) => {
1542
+ const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSource, accessScope, expiresAt, lastVerifiedAt, validationMessage, showHeader = true, allowRevealKey = false, t, locale, className, }) => {
1320
1543
  const { locale: resolvedLocale, t: translate } = useFigmaUiI18n(locale, t);
1321
1544
  const [isKeyRevealed, setIsKeyRevealed] = React.useState(false);
1322
1545
  const normalizedLicenseStatus = licenseStatus?.toLowerCase();
@@ -1327,13 +1550,15 @@ const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSour
1327
1550
  : resolveText(translate, { key: 'license.status_inactive' }, 'License check failed');
1328
1551
  const statusClass = isActive ? 'active' : 'inactive';
1329
1552
  return (React.createElement("section", { className: `designbase-figma-license-status-card designbase-figma-license-status-card--${statusClass} ${className || ''}`, "aria-label": statusText },
1330
- React.createElement("div", { className: "designbase-figma-license-status-card__header" },
1553
+ showHeader && (React.createElement("div", { className: "designbase-figma-license-status-card__header" },
1331
1554
  React.createElement("div", null,
1332
1555
  React.createElement("div", { className: "designbase-figma-license-status-card__eyebrow" }, isActive
1333
1556
  ? resolveText(translate, { key: 'license.activated' }, '라이선스 활성화됨')
1334
1557
  : resolveText(translate, { key: 'license.status_label' }, '라이선스 상태')),
1335
1558
  React.createElement("h3", null, statusText)),
1336
- React.createElement("span", { className: "designbase-figma-license-status-card__badge" }, licenseStatus || (isActive ? 'active' : 'inactive'))),
1559
+ React.createElement("span", { className: "designbase-figma-license-status-card__badge" }, isActive
1560
+ ? resolveText(translate, { key: 'license.status_active' }, '활성')
1561
+ : resolveText(translate, { key: 'license.status_inactive' }, '비활성')))),
1337
1562
  validationMessage && !isActive && (React.createElement("p", { className: "designbase-figma-license-status-card__message" }, validationMessage)),
1338
1563
  licenseKey && (React.createElement("div", { className: "designbase-figma-license-status-card__key-row" },
1339
1564
  React.createElement("span", null, resolveText(translate, { key: 'license.key_label' }, '라이선스 키')),
@@ -1728,7 +1953,7 @@ function formatUsageLabel(state, t) {
1728
1953
  }
1729
1954
  return resolveText(t, { key: 'license.usage_generic', values: { remaining, limit } }, `남은 횟수 ${remaining}/${limit}`) ?? `남은 횟수 ${remaining}/${limit}`;
1730
1955
  }
1731
- const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicenseKey = '', wordpressLinks, paymentPageUrl, mode = 'pricing', membershipUrl, enableAccountAuth = true, showLicenseKeyInput = true, pricingFeatures, pricing, paymentType = 'subscription', messageTypes, onStateChange, setPaymentStatus, setUsageCount, onLicenseSubmit, usageCount: initialUsageCount = 0, t, locale, className, }) => {
1956
+ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicenseKey = '', licenseStatus: initialLicenseStatus, licenseSource: initialLicenseSource, accessScope: initialAccessScope, expiresAt: initialExpiresAt, lastVerifiedAt: initialLastVerifiedAt, wordpressLinks, paymentPageUrl, mode = 'pricing', membershipUrl, enableAccountAuth = true, showLicenseKeyInput = true, pricingFeatures, pricing, paymentType = 'subscription', messageTypes, onStateChange, setPaymentStatus, setUsageCount, onLicenseSubmit, usageCount: initialUsageCount = 0, t, locale, className, }) => {
1732
1957
  const { t: translate } = useFigmaUiI18n(locale, t);
1733
1958
  const links = React.useMemo(() => ({
1734
1959
  purchaseUrl: wordpressLinks?.purchaseUrl ?? paymentPageUrl,
@@ -1741,13 +1966,21 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
1741
1966
  initialState: {
1742
1967
  paymentStatus: initialStatus,
1743
1968
  licenseKey: initialLicenseKey,
1969
+ hasLicenseKey: Boolean(initialLicenseKey),
1970
+ isLicenseRegistered: initialStatus === 'PAID',
1971
+ planTier: initialStatus === 'PAID' ? 'PRO' : 'FREE',
1972
+ usageLimit: initialStatus === 'PAID' ? 'unlimited' : 0,
1973
+ licenseStatus: initialLicenseStatus,
1974
+ licenseSource: initialLicenseSource,
1975
+ accessScope: initialAccessScope,
1976
+ expiresAt: initialExpiresAt,
1977
+ lastVerifiedAt: initialLastVerifiedAt,
1744
1978
  usageCount: initialUsageCount,
1745
1979
  },
1746
1980
  onStateChange,
1747
1981
  onPaymentStatusChange: setPaymentStatus,
1748
1982
  onUsageCountChange: setUsageCount,
1749
1983
  });
1750
- const [showDetails, setShowDetails] = React.useState(false);
1751
1984
  const [licenseKeyDraft, setLicenseKeyDraft] = React.useState(initialLicenseKey);
1752
1985
  const isPaid = state.isLicenseRegistered && state.paymentStatus === 'PAID';
1753
1986
  const isMembershipMode = mode === 'youtube-membership';
@@ -1772,7 +2005,7 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
1772
2005
  : isPaid
1773
2006
  ? resolveText(translate, { key: 'license.pro_account' }, '프로 계정')
1774
2007
  : resolveText(translate, { key: 'license.upgrade_to_pro' }, '프로로 업그레이드')),
1775
- state.planTier && state.planTier !== 'FREE' && (React.createElement("span", { className: "designbase-figma-page-license__tier" }, state.planTier))),
2008
+ isPaid && (React.createElement("span", { className: "designbase-figma-page-license__tier" }, resolveText(translate, { key: 'license.pro_plan' }, '프로 플랜')))),
1776
2009
  React.createElement("p", { className: "designbase-figma-page-license__description" }, isPaid
1777
2010
  ? resolveText(translate, { key: 'license.all_features_available' }, '모든 기능을 사용할 수 있습니다.')
1778
2011
  : isMembershipMode
@@ -1780,16 +2013,16 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
1780
2013
  : resolveText(translate, { key: 'license.purchase_for_unlimited' }, '디자인베이스 멤버십 또는 라이선스로 무제한 이용이 가능합니다.')),
1781
2014
  !isPaid && !isMembershipMode && (React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel))),
1782
2015
  !isMembershipMode && (React.createElement(PricingComparison, { features: pricingFeatures, pricing: pricing, paymentType: paymentType, t: translate })),
1783
- state.lastMessage && !state.deactivateMessage && (React.createElement("p", { className: `designbase-figma-page-license__feedback ${state.lastErrorCode ? 'designbase-figma-page-license__feedback--error' : ''}`, "aria-live": "polite" }, state.lastMessage)),
1784
- isPaid && (React.createElement("div", { className: "designbase-figma-page-license__section" },
1785
- React.createElement("div", { className: "designbase-figma-page-license__section-header" },
1786
- React.createElement("h3", null, resolveText(translate, { key: 'license.activated' }, '라이선스 활성화됨')),
1787
- React.createElement(react.Button, { onPress: () => setShowDetails(!showDetails), variant: "tertiary", size: "s", iconOnly: true },
1788
- React.createElement(icons.MoreHorizontalIcon, { size: 16 }))),
1789
- React.createElement(LicenseStatusCard, { status: state.paymentStatus, licenseKey: state.licenseKey, licenseStatus: state.licenseStatus, licenseSource: state.licenseSource, accessScope: state.accessScope, expiresAt: state.expiresAt, lastVerifiedAt: state.lastVerifiedAt, validationMessage: state.validationMessage, allowRevealKey: false, t: translate }),
1790
- React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel),
1791
- showDetails && (React.createElement("div", { className: "designbase-figma-page-license__details" },
1792
- React.createElement("div", { className: "designbase-figma-page-license__activation-info" },
2016
+ state.lastMessage && state.lastErrorCode && !state.deactivateMessage && (React.createElement("p", { className: `designbase-figma-page-license__feedback ${state.lastErrorCode ? 'designbase-figma-page-license__feedback--error' : ''}`, "aria-live": "polite" }, state.lastMessage)),
2017
+ isPaid && (React.createElement("div", { className: "designbase-figma-page-license__paid-summary" },
2018
+ React.createElement("div", { className: "designbase-figma-page-license__paid-hero" },
2019
+ React.createElement("span", { "aria-hidden": "true" }, "\u221E"),
2020
+ React.createElement("div", null,
2021
+ React.createElement("strong", null, resolveText(translate, { key: 'license.usage_unlimited' }, '무제한 사용')),
2022
+ React.createElement("p", null, resolveText(translate, { key: 'license.active_until_expiry' }, '만료일까지 모든 기능을 제한 없이 사용할 수 있습니다.')))),
2023
+ React.createElement(LicenseStatusCard, { status: state.paymentStatus, licenseKey: state.licenseKey, licenseStatus: state.licenseStatus, licenseSource: state.licenseSource, accessScope: state.accessScope, expiresAt: state.expiresAt, lastVerifiedAt: state.lastVerifiedAt, validationMessage: state.validationMessage, allowRevealKey: false, showHeader: false, t: translate }),
2024
+ React.createElement("div", { className: "designbase-figma-page-license__paid-actions" },
2025
+ state.activationLimit > 0 && (React.createElement("div", { className: "designbase-figma-page-license__activation-info" },
1793
2026
  React.createElement("div", { className: "designbase-figma-page-license__remaining" },
1794
2027
  Math.max(0, state.activationLimit - state.activationUsage),
1795
2028
  ' ',
@@ -1799,12 +2032,12 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
1799
2032
  React.createElement("div", { className: "designbase-figma-page-license__usage-count" },
1800
2033
  state.activationUsage,
1801
2034
  "/",
1802
- state.activationLimit)),
1803
- React.createElement("div", { className: "designbase-figma-page-license__action" },
1804
- React.createElement(react.Button, { onPress: deactivateLicense, variant: "secondary", size: "s", fullWidth: true, disabled: isDeactivating }, isDeactivating
1805
- ? resolveText(translate, { key: 'license.deactivating' }, '비활성화 중...')
1806
- : resolveText(translate, { key: 'license.deactivate' }, '라이선스 비활성화')),
1807
- state.deactivateMessage && (React.createElement("p", { className: `designbase-figma-page-license__action-feedback designbase-figma-page-license__action-feedback--${resolvePaymentFeedbackVariant(state.deactivateErrorCode)}`, role: state.deactivateErrorCode ? 'alert' : 'status', "aria-live": "polite" }, state.deactivateMessage))))))),
2035
+ state.activationLimit))),
2036
+ React.createElement(react.Button, { onPress: deactivateLicense, variant: "secondary", size: "m", fullWidth: true, disabled: isDeactivating }, isDeactivating
2037
+ ? resolveText(translate, { key: 'license.deactivating' }, '비활성화 중...')
2038
+ : resolveText(translate, { key: 'license.deactivate' }, '라이선스 비활성화')),
2039
+ state.deactivateMessage && state.deactivateErrorCode && (React.createElement("p", { className: `designbase-figma-page-license__action-feedback designbase-figma-page-license__action-feedback--${resolvePaymentFeedbackVariant(state.deactivateErrorCode)}`, role: "alert", "aria-live": "polite" }, state.deactivateMessage)),
2040
+ React.createElement("p", { className: "designbase-figma-page-license__deactivate-hint" }, resolveText(translate, { key: 'license.deactivate_hint' }, '이 기기에서 라이선스 연결을 해제합니다.'))))),
1808
2041
  !isPaid && (React.createElement(React.Fragment, null,
1809
2042
  isMembershipMode && (React.createElement(LicenseOffer, { joinUrl: links.membershipUrl || links.purchaseUrl, guideUrl: links.helpUrl || links.purchaseUrl, t: translate })),
1810
2043
  !isMembershipMode && (enableAccountAuth || links.purchaseUrl || links.accountUrl) && (React.createElement("div", { className: "designbase-figma-page-license__section" },