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