@designbasekorea/figma-ui 0.6.6 → 0.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,6 +6,14 @@ var icons = require('@designbasekorea/icons');
6
6
 
7
7
  function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
8
8
 
9
+ function interpolateText(template, values) {
10
+ if (!values)
11
+ return template;
12
+ return Object.entries(values).reduce((result, [key, value]) => {
13
+ const replacement = value == null ? '' : String(value);
14
+ return result.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), replacement);
15
+ }, template);
16
+ }
9
17
  function resolveText(t, input, fallback) {
10
18
  if (input == null)
11
19
  return fallback;
@@ -15,10 +23,13 @@ function resolveText(t, input, fallback) {
15
23
  if (t) {
16
24
  const translated = t(input.key, input.values);
17
25
  if (translated && translated !== input.key) {
18
- return translated;
26
+ return interpolateText(translated, input.values);
19
27
  }
20
28
  }
21
- return fallback ?? input.key;
29
+ if (fallback) {
30
+ return interpolateText(fallback, input.values);
31
+ }
32
+ return input.key;
22
33
  }
23
34
 
24
35
  const DONATION_ICON_NAMES = {
@@ -384,62 +395,63 @@ const FigmaSidebar = ({ categories, categoryGroups = {}, categoryLabels = {}, ac
384
395
  };
385
396
  FigmaSidebar.displayName = 'FigmaSidebar';
386
397
 
387
- const PaymentBadge = ({ isActive, onClick, isLoading = false, text, t = (key) => key, className, }) => {
388
- const getBadgeText = () => {
389
- if (text)
390
- return text;
391
- return isActive
392
- ? (t('proUser') || '프로 계정')
393
- : (t('usingFree') || '무료 사용');
394
- };
395
- const getBadgeVariant = () => {
396
- return isActive ? 'success' : 'secondary';
397
- };
398
- return (React.createElement("div", { className: `designbase-figma-payment-badge ${className || ''} ${isActive ? 'active' : 'free'}`, onClick: onClick, style: { cursor: onClick ? 'pointer' : 'default' } }, isLoading ? (React.createElement(react.Spinner, { size: "s" })) : (React.createElement(react.Badge, { variant: getBadgeVariant(), size: "s", className: "designbase-figma-payment-badge__badge" }, getBadgeText()))));
399
- };
400
- PaymentBadge.displayName = 'PaymentBadge';
401
-
402
- let useTranslation = null;
398
+ let useTranslation$2 = null;
403
399
  try {
404
400
  const reactI18next = require('react-i18next');
405
- useTranslation = reactI18next.useTranslation;
401
+ useTranslation$2 = reactI18next.useTranslation;
406
402
  }
407
403
  catch (e) {
408
404
  }
409
- const DEFAULT_LANGUAGES = [
410
- { code: 'ko', label: 'KO' },
411
- { code: 'en', label: 'EN' },
405
+ const DEFAULT_LANGUAGES$1 = [
406
+ { code: 'ko', label: '한국어' },
407
+ { code: 'en', label: 'English' },
408
+ { code: 'ja', label: '日本語' },
412
409
  ];
413
- const LanguageSelector = ({ currentLanguage, languages = DEFAULT_LANGUAGES, onLanguageChange, size = 's', className, t }) => {
414
- const i18n = useTranslation ? useTranslation().i18n : null;
415
- const activeLanguage = currentLanguage || (i18n?.language) || 'ko';
416
- const handleLanguageChange = (languageCode) => {
410
+ const defaultT$2 = (key) => {
411
+ const translations = {
412
+ languageSettings: '언어 설정',
413
+ languageSettingsDescription: '플러그인에 표시할 언어를 선택하세요.',
414
+ 'language.korean': '한국어',
415
+ 'language.english': 'English',
416
+ 'language.japanese': '日本語',
417
+ };
418
+ return translations[key] || key;
419
+ };
420
+ const LanguageSettingsModal = ({ isOpen, onClose, languages = DEFAULT_LANGUAGES$1, currentLanguage, onLanguageChange, title, description, className, t = defaultT$2, }) => {
421
+ const i18n = useTranslation$2 ? useTranslation$2().i18n : null;
422
+ const activeLanguage = currentLanguage || i18n?.language || languages[0]?.code;
423
+ const translate = (key, fallback) => {
424
+ const translated = t(key);
425
+ return translated && translated !== key ? translated : fallback;
426
+ };
427
+ const handleSelect = (languageCode) => {
417
428
  if (i18n) {
418
429
  i18n.changeLanguage(languageCode);
419
430
  }
420
- if (onLanguageChange) {
421
- onLanguageChange(languageCode);
422
- }
431
+ onLanguageChange?.(languageCode);
432
+ onClose();
423
433
  };
424
- const segmentOptions = languages.map((language) => ({
425
- value: language.code,
426
- label: resolveText(t, language.label),
427
- disabled: false,
428
- }));
429
- return (React.createElement("div", { className: `designbase-figma-language-selector ${className || ''}` },
430
- React.createElement(react.SegmentControl, { options: segmentOptions, value: activeLanguage, onChange: (event) => {
431
- const detail = event.detail;
432
- const value = typeof detail === 'object' && detail !== null && 'value' in detail &&
433
- typeof detail.value === 'string'
434
- ? detail.value
435
- : undefined;
436
- if (value) {
437
- handleLanguageChange(value);
438
- }
439
- }, size: size, className: "designbase-figma-language-selector__segment" })));
434
+ return (React.createElement(react.Modal, { isOpen: isOpen, onClose: onClose, title: title || translate('languageSettings', '언어 설정'), size: "s", className: clsx('designbase-figma-language-settings-modal', className) },
435
+ React.createElement("div", { className: "designbase-figma-language-settings-modal__content" },
436
+ React.createElement("p", { className: "designbase-figma-language-settings-modal__description" }, description || translate('languageSettingsDescription', '플러그인에 표시할 언어를 선택하세요.')),
437
+ React.createElement("ul", { className: "designbase-figma-language-settings-modal__list", role: "listbox", "aria-label": translate('languageSettings', '언어 설정') }, languages.map((language) => {
438
+ const label = resolveText(t, language.label, language.code.toUpperCase());
439
+ const isActive = language.code === activeLanguage;
440
+ return (React.createElement("li", { key: language.code, role: "presentation" },
441
+ React.createElement("button", { type: "button", className: clsx('designbase-figma-language-settings-modal__option', { 'designbase-figma-language-settings-modal__option--active': isActive }), role: "option", "aria-selected": isActive, onClick: () => handleSelect(language.code) },
442
+ React.createElement("span", { className: "designbase-figma-language-settings-modal__option-label" }, label),
443
+ React.createElement("span", { className: "designbase-figma-language-settings-modal__option-indicator", "aria-hidden": "true" }))));
444
+ })))));
440
445
  };
441
- LanguageSelector.displayName = 'LanguageSelector';
446
+ LanguageSettingsModal.displayName = 'LanguageSettingsModal';
442
447
 
448
+ let useTranslation$1 = null;
449
+ try {
450
+ const reactI18next = require('react-i18next');
451
+ useTranslation$1 = reactI18next.useTranslation;
452
+ }
453
+ catch (e) {
454
+ }
443
455
  const defaultLinks = [
444
456
  {
445
457
  name: 'officialWebsite',
@@ -473,50 +485,219 @@ const defaultTranslations$1 = {
473
485
  instagram: '인스타그램',
474
486
  figmaCommunity: '피그마 커뮤니티',
475
487
  contact: '문의하기',
488
+ languageSettings: '언어 설정',
476
489
  };
477
490
  const defaultT$1 = (key) => defaultTranslations$1[key] || key;
478
- const LogoDropdown = ({ logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', links = defaultLinks, position = 'top-left', className, t = defaultT$1, }) => {
491
+ const defaultLanguages = [
492
+ { code: 'ko', label: '한국어' },
493
+ { code: 'en', label: 'English' },
494
+ { code: 'ja', label: '日本語' },
495
+ ];
496
+ const LogoDropdown = ({ logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', links = defaultLinks, position = 'top-left', className, t = defaultT$1, showLanguageSelector = true, languages = defaultLanguages, currentLanguage, onLanguageChange, }) => {
479
497
  const [isOpen, setIsOpen] = React.useState(false);
480
- const toggleDropdown = () => setIsOpen(!isOpen);
498
+ const [isLanguageModalOpen, setIsLanguageModalOpen] = React.useState(false);
499
+ const i18n = useTranslation$1 ? useTranslation$1().i18n : null;
500
+ const rootRef = React.useRef(null);
501
+ const triggerRef = React.useRef(null);
502
+ const menuId = `designbase-figma-logo-menu-${React.useId().replace(/:/g, '')}`;
503
+ const activeLanguage = currentLanguage || i18n?.language || languages[0]?.code;
504
+ const translate = (key, fallback) => {
505
+ const translated = t(key);
506
+ return translated && translated !== key ? translated : fallback;
507
+ };
508
+ React.useEffect(() => {
509
+ if (!isOpen) {
510
+ return undefined;
511
+ }
512
+ const handlePointerDown = (event) => {
513
+ if (!rootRef.current?.contains(event.target)) {
514
+ setIsOpen(false);
515
+ }
516
+ };
517
+ const handleKeyDown = (event) => {
518
+ if (event.key === 'Escape') {
519
+ setIsOpen(false);
520
+ triggerRef.current?.focus();
521
+ }
522
+ };
523
+ document.addEventListener('mousedown', handlePointerDown);
524
+ document.addEventListener('keydown', handleKeyDown);
525
+ return () => {
526
+ document.removeEventListener('mousedown', handlePointerDown);
527
+ document.removeEventListener('keydown', handleKeyDown);
528
+ };
529
+ }, [isOpen]);
530
+ const toggleDropdown = () => setIsOpen((value) => !value);
481
531
  const handleLinkClick = (url) => {
532
+ setIsOpen(false);
482
533
  if (url.startsWith('mailto:')) {
483
534
  return;
484
535
  }
485
536
  window.open(url, '_blank', 'noopener,noreferrer');
486
537
  };
538
+ const handleLanguageSettingsClick = () => {
539
+ setIsOpen(false);
540
+ setIsLanguageModalOpen(true);
541
+ };
542
+ const handleLanguageChange = (languageCode) => {
543
+ if (i18n) {
544
+ i18n.changeLanguage(languageCode);
545
+ }
546
+ onLanguageChange?.(languageCode);
547
+ };
487
548
  const classes = clsx('designbase-figma-logo-dropdown', `designbase-figma-logo-dropdown--${position}`, {
488
549
  'designbase-figma-logo-dropdown--open': isOpen,
489
550
  }, className);
490
551
  const renderLogo = () => {
491
- if (logoSrc) {
492
- return (React.createElement("img", { className: "designbase-figma-logo-dropdown__logo", src: logoSrc, alt: logoAlt, onClick: toggleDropdown }));
493
- }
494
- return (React.createElement("div", { className: "designbase-figma-logo-dropdown__logo", onClick: toggleDropdown },
495
- React.createElement(react.Logo, { type: logoType, size: logoSize, variant: "default" })));
552
+ return (React.createElement("button", { ref: triggerRef, type: "button", className: "designbase-figma-logo-dropdown__logo", "aria-label": logoAlt, "aria-expanded": isOpen, "aria-controls": menuId, "aria-haspopup": "menu", onClick: toggleDropdown }, logoSrc ? (React.createElement("img", { src: logoSrc, alt: "" })) : (React.createElement(react.Logo, { type: logoType, size: logoSize, variant: "default" }))));
496
553
  };
497
- return (React.createElement("div", { className: classes },
554
+ return (React.createElement("div", { ref: rootRef, className: classes },
498
555
  renderLogo(),
499
- isOpen && (React.createElement("ul", { className: "designbase-figma-logo-dropdown__menu" }, links.map((link, index) => (React.createElement("li", { key: index, className: "designbase-figma-logo-dropdown__item" },
500
- React.createElement("button", { type: "button", className: "designbase-figma-logo-dropdown__link", onClick: () => handleLinkClick(link.url) },
501
- link.icon && (React.createElement("span", { className: "designbase-figma-logo-dropdown__icon" }, link.icon)),
502
- React.createElement("span", { className: "designbase-figma-logo-dropdown__text" }, t(link.name)),
503
- React.createElement(icons.ExternalLinkIcon, { size: 16, className: "designbase-figma-logo-dropdown__arrow" })))))))));
556
+ isOpen && (React.createElement("ul", { id: menuId, className: "designbase-figma-logo-dropdown__menu", role: "menu" },
557
+ links.map((link, index) => (React.createElement("li", { key: index, className: "designbase-figma-logo-dropdown__item", role: "none" },
558
+ React.createElement("button", { type: "button", className: "designbase-figma-logo-dropdown__link", role: "menuitem", onClick: () => handleLinkClick(link.url) },
559
+ link.icon && (React.createElement("span", { className: "designbase-figma-logo-dropdown__icon" }, link.icon)),
560
+ React.createElement("span", { className: "designbase-figma-logo-dropdown__text" }, t(link.name)),
561
+ React.createElement(icons.ExternalLinkIcon, { size: 16, className: "designbase-figma-logo-dropdown__arrow" }))))),
562
+ showLanguageSelector && (React.createElement("li", { className: "designbase-figma-logo-dropdown__item designbase-figma-logo-dropdown__language-item", role: "none" },
563
+ React.createElement("button", { type: "button", className: "designbase-figma-logo-dropdown__link", role: "menuitem", onClick: handleLanguageSettingsClick },
564
+ React.createElement("span", { className: "designbase-figma-logo-dropdown__icon" },
565
+ React.createElement(icons.TranslateIcon, { size: 20, "aria-hidden": "true" })),
566
+ React.createElement("span", { className: "designbase-figma-logo-dropdown__text" }, translate('languageSettings', '언어 설정'))))))),
567
+ showLanguageSelector && (React.createElement(LanguageSettingsModal, { isOpen: isLanguageModalOpen, onClose: () => setIsLanguageModalOpen(false), languages: languages, currentLanguage: activeLanguage, onLanguageChange: handleLanguageChange, t: t }))));
504
568
  };
505
569
  LogoDropdown.displayName = 'LogoDropdown';
506
570
 
571
+ const fallbackText = (t, key, fallback) => {
572
+ const translated = t?.(key);
573
+ return translated && translated !== key ? translated : fallback;
574
+ };
575
+ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20, showUsage = true, showPlan = true, isLoading = false, onLicensePageClick, t, className, }) => {
576
+ const [isOpen, setIsOpen] = React.useState(false);
577
+ const rootRef = React.useRef(null);
578
+ const triggerRef = React.useRef(null);
579
+ const popoverId = `designbase-figma-usage-popover-${React.useId().replace(/:/g, '')}`;
580
+ const isPaid = paymentStatus === 'PAID';
581
+ const isBeta = paymentStatus === 'BETA';
582
+ const hasDailyLimit = !isPaid && maxDailyUsage > 0;
583
+ const limit = Math.max(1, Math.round(maxDailyUsage));
584
+ const used = Math.min(limit, Math.max(0, Math.round(usageCount)));
585
+ const remaining = Math.max(0, limit - used);
586
+ const usedPercent = hasDailyLimit ? Math.round((used / limit) * 100) : 0;
587
+ const hasControl = showUsage || showPlan;
588
+ const planLabel = isPaid
589
+ ? fallbackText(t, 'proUser', '프로')
590
+ : isBeta
591
+ ? fallbackText(t, 'betaStatus', 'BETA')
592
+ : paymentStatus === 'TRIAL'
593
+ ? fallbackText(t, 'trialPlan', '체험판')
594
+ : fallbackText(t, 'free', '무료');
595
+ const planTitle = isBeta
596
+ ? planLabel
597
+ : `${planLabel} ${fallbackText(t, 'planSuffix', '플랜')}`;
598
+ const triggerLabel = isPaid
599
+ ? `${planLabel}, ${fallbackText(t, 'unlimitedUsage', '무제한 사용 가능')}`
600
+ : isBeta
601
+ ? planLabel
602
+ : hasDailyLimit
603
+ ? `${remaining}/${limit} ${fallbackText(t, 'remainingShort', '남음')}`
604
+ : planLabel;
605
+ const upgradeLabel = isPaid
606
+ ? fallbackText(t, 'managePlan', '플랜 관리')
607
+ : fallbackText(t, 'upgradePlan', '플랜 업그레이드');
608
+ React.useEffect(() => {
609
+ if (!isOpen)
610
+ return undefined;
611
+ const handlePointerDown = (event) => {
612
+ if (!rootRef.current?.contains(event.target)) {
613
+ setIsOpen(false);
614
+ }
615
+ };
616
+ const handleKeyDown = (event) => {
617
+ if (event.key === 'Escape') {
618
+ setIsOpen(false);
619
+ triggerRef.current?.focus();
620
+ }
621
+ };
622
+ document.addEventListener('mousedown', handlePointerDown);
623
+ document.addEventListener('keydown', handleKeyDown);
624
+ return () => {
625
+ document.removeEventListener('mousedown', handlePointerDown);
626
+ document.removeEventListener('keydown', handleKeyDown);
627
+ };
628
+ }, [isOpen]);
629
+ if (!hasControl)
630
+ return null;
631
+ const handleManageClick = () => {
632
+ setIsOpen(false);
633
+ onLicensePageClick?.();
634
+ };
635
+ const togglePopover = () => setIsOpen((value) => !value);
636
+ return (React.createElement("div", { ref: rootRef, className: clsx('designbase-figma-usage-status', {
637
+ 'designbase-figma-usage-status--open': isOpen,
638
+ 'designbase-figma-usage-status--unlimited': isPaid,
639
+ 'designbase-figma-usage-status--loading': isLoading,
640
+ }, className) },
641
+ React.createElement("div", { className: "designbase-figma-usage-status__control" },
642
+ 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(t, '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,
643
+ React.createElement("span", { className: "designbase-figma-usage-status__mini-progress", "aria-hidden": "true" },
644
+ React.createElement("span", { style: { width: `${usedPercent}%` } })),
645
+ React.createElement("span", { className: "designbase-figma-usage-status__usage-value" },
646
+ React.createElement("strong", null, remaining),
647
+ React.createElement("span", null,
648
+ "/",
649
+ limit)))) : (React.createElement("span", { className: "designbase-figma-usage-status__plan-label" }, planLabel)))),
650
+ 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))),
651
+ isOpen && (React.createElement("div", { id: popoverId, className: "designbase-figma-usage-status__popover", role: "dialog", "aria-label": fallbackText(t, 'usageDetails', '사용량 상세') },
652
+ React.createElement("div", { className: "designbase-figma-usage-status__popover-header" },
653
+ React.createElement("span", { className: "designbase-figma-usage-status__plan-title" }, planTitle),
654
+ !isBeta && (React.createElement(react.Button, { className: "designbase-figma-usage-status__upgrade", variant: isPaid ? 'secondary' : 'primary', size: "s", endIcon: "chevron-right", onPress: handleManageClick }, upgradeLabel))),
655
+ isPaid ? (React.createElement("div", { className: "designbase-figma-usage-status__unlimited-copy" },
656
+ React.createElement("span", { className: "designbase-figma-usage-status__unlimited-mark", "aria-hidden": "true" }, "\u221E"),
657
+ React.createElement("span", null, fallbackText(t, 'unlimitedUsageTooltip', '모든 기능을 제한 없이 사용할 수 있습니다')))) : isBeta ? (React.createElement("p", { className: "designbase-figma-usage-status__note" }, fallbackText(t, 'betaUsageDescription', '베타 기간에는 사용량 제한이 적용되지 않습니다'))) : hasDailyLimit ? (React.createElement("div", { className: "designbase-figma-usage-status__usage-detail" },
658
+ React.createElement("div", { className: "designbase-figma-usage-status__usage-summary" },
659
+ React.createElement("strong", { className: "designbase-figma-usage-status__usage-count" },
660
+ used,
661
+ React.createElement("span", { className: "designbase-figma-usage-status__usage-divider" }, "/"),
662
+ limit),
663
+ React.createElement("span", { className: "designbase-figma-usage-status__usage-caption" }, fallbackText(t, 'usageCountCaption', '오늘 사용'))),
664
+ React.createElement("div", { className: "designbase-figma-usage-status__progress", role: "progressbar", "aria-label": fallbackText(t, 'usageCountCaption', '오늘 사용'), "aria-valuemin": 0, "aria-valuemax": limit, "aria-valuenow": used, "aria-valuetext": `${used} / ${limit}` },
665
+ React.createElement("span", { style: { width: `${usedPercent}%` } })),
666
+ React.createElement("p", { className: "designbase-figma-usage-status__remaining-copy" }, resolveText(t, { key: 'remainingCopy', values: { count: remaining } }, `${remaining}회 남음 · 매일 리셋`)))) : (React.createElement("p", { className: "designbase-figma-usage-status__note" }, fallbackText(t, 'usageUnavailable', '사용량 제한이 없습니다')))))));
667
+ };
668
+ UsageStatusPopover.displayName = 'UsageStatusPopover';
669
+
507
670
  const defaultTranslations = {
508
671
  unlimitedUsage: '무제한 사용',
672
+ proUser: '프로',
673
+ free: '무료',
674
+ trialPlan: '체험판',
675
+ planSuffix: '플랜',
676
+ usageDetails: '사용량 상세',
677
+ dailyUsage: '오늘 사용량',
678
+ usedToday: '오늘 사용',
679
+ remainingToday: '오늘 남은 사용량',
680
+ remainingUsage: '오늘 남은 사용량',
681
+ managePlan: '플랜 관리',
682
+ upgradePlan: '플랜 업그레이드',
683
+ remainingShort: '남음',
684
+ usageCountCaption: '오늘 사용',
685
+ remainingCopy: '{{count}}회 남음 · 매일 리셋',
686
+ usageUnavailable: '사용량 제한이 없습니다',
687
+ betaUsageDescription: '베타 기간에는 사용량 제한이 적용되지 않습니다',
509
688
  perDay: '일',
510
689
  resetsDaily: '매일 리셋',
511
690
  donationPrompt: '이 플러그인이 유용했나요?',
512
691
  betaStatus: 'BETA',
513
692
  };
514
- const defaultT = (key) => defaultTranslations[key] || key;
515
- 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 = 'Buy me a coffee', showDonationText = true, donationPromptText, showLanguageSelector = false, currentLanguage = 'ko', languages, onLanguageChange, logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', logoLinks, className, t = defaultT, children, }) => {
516
- const isActive = paymentStatus === 'PAID';
517
- const isBeta = paymentStatus === 'BETA';
518
- const resolvedShowUsageInfo = showUsageInfo && !isLoading && !isBeta;
693
+ const defaultT = (key, values) => {
694
+ const template = defaultTranslations[key] || key;
695
+ return interpolateText(template, values);
696
+ };
697
+ 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 = 'Buy me a coffee', showDonationText = true, donationPromptText, showLanguageSelector = true, currentLanguage = 'ko', languages, onLanguageChange, logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', logoLinks, className, t = defaultT, children, }) => {
698
+ const resolvedShowUsageInfo = showPaymentStatus && showUsageInfo;
519
699
  const resolvedShowPaymentBadge = showPaymentStatus && showPaymentBadge;
700
+ const resolvedLicensePageClick = paymentStatus === 'BETA' ? undefined : onLicensePageClick;
520
701
  const hasChildren = React.Children.count(children) > 0;
521
702
  const classes = clsx('designbase-figma-footer', {
522
703
  'designbase-figma-footer--has-children': hasChildren,
@@ -524,17 +705,9 @@ const FigmaFooter = ({ onLicensePageClick, paymentStatus = 'FREE', usageCount =
524
705
  return (React.createElement("footer", { className: classes },
525
706
  React.createElement("div", { className: "designbase-figma-footer__wrap" },
526
707
  React.createElement("div", { className: "designbase-figma-footer__left" },
527
- React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, t: t }),
528
- showLanguageSelector && (React.createElement(LanguageSelector, { currentLanguage: currentLanguage, languages: languages, onLanguageChange: onLanguageChange, size: "s" }))),
708
+ React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, languages: languages, currentLanguage: currentLanguage, showLanguageSelector: showLanguageSelector, onLanguageChange: onLanguageChange, t: t })),
529
709
  React.createElement("div", { className: "designbase-figma-footer__right" },
530
- showPaymentStatus && resolvedShowUsageInfo && (React.createElement("div", { className: "designbase-figma-footer__usage-info" }, isActive ? (React.createElement("span", { className: "designbase-figma-footer__unlimited-usage designbase-figma-footer__unlimited-usage--tooltip", title: t('unlimitedUsageTooltip') }, t('unlimitedUsage'))) : (React.createElement("span", { className: "designbase-figma-footer__usage-info designbase-figma-footer__usage-info--tooltip", title: t('resetsDaily') },
531
- React.createElement("span", { className: "designbase-figma-footer__usage-count" }, usageCount),
532
- React.createElement("span", { className: "designbase-figma-footer__max-daily-usage" },
533
- "/",
534
- maxDailyUsage,
535
- " ",
536
- t('perDay')))))),
537
- showPaymentStatus && resolvedShowPaymentBadge && (React.createElement(PaymentBadge, { isActive: isActive, onClick: isBeta ? undefined : onLicensePageClick, isLoading: isLoading, text: isBeta ? (t('betaStatus') || 'BETA') : undefined, t: t })),
710
+ (resolvedShowUsageInfo || resolvedShowPaymentBadge) && (React.createElement(UsageStatusPopover, { paymentStatus: paymentStatus, usageCount: usageCount, maxDailyUsage: maxDailyUsage, showUsage: resolvedShowUsageInfo, showPlan: resolvedShowPaymentBadge, isLoading: isLoading, onLicensePageClick: resolvedLicensePageClick, t: t })),
538
711
  showDonation && (React.createElement(DonationBadge, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s", iconOnly: true, showPrompt: false, t: t })))),
539
712
  children));
540
713
  };
@@ -658,6 +831,47 @@ const InteractionFeedback = ({ status = 'default', message, statusMessage, leftC
658
831
  };
659
832
  InteractionFeedback.displayName = 'InteractionFeedback';
660
833
 
834
+ let useTranslation = null;
835
+ try {
836
+ const reactI18next = require('react-i18next');
837
+ useTranslation = reactI18next.useTranslation;
838
+ }
839
+ catch (e) {
840
+ }
841
+ const DEFAULT_LANGUAGES = [
842
+ { code: 'ko', label: 'KO' },
843
+ { code: 'en', label: 'EN' },
844
+ ];
845
+ const LanguageSelector = ({ currentLanguage, languages = DEFAULT_LANGUAGES, onLanguageChange, size = 's', className, t }) => {
846
+ const i18n = useTranslation ? useTranslation().i18n : null;
847
+ const activeLanguage = currentLanguage || (i18n?.language) || 'ko';
848
+ const handleLanguageChange = (languageCode) => {
849
+ if (i18n) {
850
+ i18n.changeLanguage(languageCode);
851
+ }
852
+ if (onLanguageChange) {
853
+ onLanguageChange(languageCode);
854
+ }
855
+ };
856
+ const segmentOptions = languages.map((language) => ({
857
+ value: language.code,
858
+ label: resolveText(t, language.label),
859
+ disabled: false,
860
+ }));
861
+ return (React.createElement("div", { className: `designbase-figma-language-selector ${className || ''}` },
862
+ React.createElement(react.SegmentControl, { options: segmentOptions, value: activeLanguage, onChange: (event) => {
863
+ const detail = event.detail;
864
+ const value = typeof detail === 'object' && detail !== null && 'value' in detail &&
865
+ typeof detail.value === 'string'
866
+ ? detail.value
867
+ : undefined;
868
+ if (value) {
869
+ handleLanguageChange(value);
870
+ }
871
+ }, size: size, className: "designbase-figma-language-selector__segment" })));
872
+ };
873
+ LanguageSelector.displayName = 'LanguageSelector';
874
+
661
875
  function postPluginMessage(message) {
662
876
  if (typeof parent === 'undefined') {
663
877
  return;
@@ -673,17 +887,27 @@ function openExternalUrl(url) {
673
887
  }
674
888
  }
675
889
 
676
- const LicenseOffer = ({ membershipUrl, t, className }) => (React.createElement("section", { className: `designbase-figma-license-offer ${className || ''}`, "aria-labelledby": "designbase-license-offer-title" },
677
- React.createElement("div", { className: "designbase-figma-license-offer__eyebrow" }, "YouTube Membership"),
678
- React.createElement("h3", { id: "designbase-license-offer-title" }, resolveText(t, { key: 'license.youtube_membership_title' }, 'YouTube 멤버십으로 전체 플러그인 이용')),
679
- React.createElement("p", null, resolveText(t, { key: 'license.youtube_membership_description' }, 'YouTube 멤버십에 가입하면 Designbase 플러그인을 무제한으로 사용할 수 있습니다.')),
680
- React.createElement("ol", null,
681
- React.createElement("li", null, "Designbase YouTube \uCC44\uB110\uC758 \uBA64\uBC84\uC2ED\uC5D0 \uAC00\uC785\uD569\uB2C8\uB2E4."),
682
- React.createElement("li", null, "\uBA64\uBC84 \uC804\uC6A9 \uCEE4\uBBA4\uB2C8\uD2F0 \uAC8C\uC2DC\uAE00\uC5D0\uC11C \uC774\uBC88 \uB2EC \uB77C\uC774\uC120\uC2A4 \uD0A4\uB97C \uD655\uC778\uD569\uB2C8\uB2E4."),
683
- React.createElement("li", null, "\uC544\uB798 \uC785\uB825\uCC3D\uC5D0 \uD0A4\uB97C \uBD99\uC5EC \uB123\uACE0 \uD65C\uC131\uD654\uD569\uB2C8\uB2E4.")),
684
- membershipUrl && (React.createElement(react.Button, { onPress: () => openExternalUrl(membershipUrl), variant: "secondary", size: "m", fullWidth: true },
685
- React.createElement("span", null, resolveText(t, { key: 'license.youtube_membership_cta' }, '멤버십 안내 보기')),
686
- React.createElement(icons.ExternalLinkIcon, { size: 16 })))));
890
+ const LicenseOffer = ({ joinUrl, guideUrl, membershipUrl, t, className, }) => {
891
+ const titleId = `designbase-license-offer-title-${React.useId().replace(/:/g, '')}`;
892
+ const resolvedJoinUrl = joinUrl || membershipUrl;
893
+ const steps = [
894
+ resolveText(t, { key: 'license.youtube_membership_step_1' }, 'Designbase YouTube 채널 멤버십에 가입합니다.'),
895
+ resolveText(t, { key: 'license.youtube_membership_step_2' }, '멤버 전용 커뮤니티에서 이번 달 키를 복사합니다.'),
896
+ resolveText(t, { key: 'license.youtube_membership_step_3' }, '아래 입력창에 키를 붙여 넣고 활성화합니다.'),
897
+ ];
898
+ return (React.createElement("section", { className: `designbase-figma-license-offer ${className || ''}`, "aria-labelledby": titleId },
899
+ React.createElement("h3", { id: titleId, className: "designbase-figma-license-offer__title" }, resolveText(t, { key: 'license.youtube_membership_howto' }, '이용 방법')),
900
+ React.createElement("ol", { className: "designbase-figma-license-offer__steps" }, steps.map((step, index) => (React.createElement("li", { key: index },
901
+ React.createElement("span", { className: "designbase-figma-license-offer__step-index", "aria-hidden": "true" },
902
+ index + 1,
903
+ "."),
904
+ React.createElement("span", null, step))))),
905
+ (resolvedJoinUrl || guideUrl) && (React.createElement("div", { className: "designbase-figma-license-offer__actions" },
906
+ resolvedJoinUrl && (React.createElement(react.Button, { onPress: () => openExternalUrl(resolvedJoinUrl), variant: "primary", size: "m", fullWidth: true }, resolveText(t, { key: 'license.youtube_membership_join' }, '멤버십 가입'))),
907
+ guideUrl && (React.createElement(react.Button, { onPress: () => openExternalUrl(guideUrl), variant: "secondary", size: "m", fullWidth: true },
908
+ React.createElement("span", null, resolveText(t, { key: 'license.youtube_membership_guide' }, '안내보기')),
909
+ React.createElement(icons.ExternalLinkIcon, { size: 16, "aria-hidden": "true" })))))));
910
+ };
687
911
  LicenseOffer.displayName = 'LicenseOffer';
688
912
 
689
913
  function maskLicenseKey(value) {
@@ -699,16 +923,30 @@ function formatDate(value) {
699
923
  return value;
700
924
  return new Intl.DateTimeFormat(undefined, { dateStyle: 'medium' }).format(date);
701
925
  }
702
- function formatScope(value) {
926
+ function formatScope(value, t) {
703
927
  if (!value)
704
928
  return undefined;
705
- if (value === 'all-products' || value === 'all_products')
706
- return '전체 제품';
929
+ if (value === 'all-products' || value === 'all_products') {
930
+ return resolveText(t, { key: 'license.all_plugins' }, '모든 플러그인');
931
+ }
932
+ return value;
933
+ }
934
+ function formatSource(value, t) {
935
+ if (!value)
936
+ return undefined;
937
+ if (value === 'youtube_membership' || value === 'membership') {
938
+ return resolveText(t, { key: 'license.source_youtube_membership' }, 'YouTube 멤버십');
939
+ }
940
+ if (value === 'lemonsqueezy') {
941
+ return resolveText(t, { key: 'license.source_purchase' }, '구매 라이선스');
942
+ }
707
943
  return value;
708
944
  }
709
- const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSource, accessScope, expiresAt, lastVerifiedAt, validationMessage, allowRevealKey = true, t, className, }) => {
945
+ const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSource, accessScope, expiresAt, lastVerifiedAt, validationMessage, allowRevealKey = false, t, className, }) => {
710
946
  const [isKeyRevealed, setIsKeyRevealed] = React.useState(false);
711
- const isActive = status === 'PAID';
947
+ const normalizedLicenseStatus = licenseStatus?.toLowerCase();
948
+ const isInactiveStatus = ['expired', 'revoked', 'invalid', 'inactive'].includes(normalizedLicenseStatus || '');
949
+ const isActive = status === 'PAID' && !isInactiveStatus;
712
950
  const statusText = isActive
713
951
  ? resolveText(t, { key: 'license.status_active' }, 'Active')
714
952
  : resolveText(t, { key: 'license.status_inactive' }, 'License check failed');
@@ -716,30 +954,82 @@ const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSour
716
954
  return (React.createElement("section", { className: `designbase-figma-license-status-card designbase-figma-license-status-card--${statusClass} ${className || ''}`, "aria-label": statusText },
717
955
  React.createElement("div", { className: "designbase-figma-license-status-card__header" },
718
956
  React.createElement("div", null,
719
- React.createElement("div", { className: "designbase-figma-license-status-card__eyebrow" }, isActive ? 'License Activated' : 'License Status'),
957
+ React.createElement("div", { className: "designbase-figma-license-status-card__eyebrow" }, isActive
958
+ ? resolveText(t, { key: 'license.activated' }, '라이선스 활성화됨')
959
+ : resolveText(t, { key: 'license.status_label' }, '라이선스 상태')),
720
960
  React.createElement("h3", null, statusText)),
721
961
  React.createElement("span", { className: "designbase-figma-license-status-card__badge" }, licenseStatus || (isActive ? 'active' : 'inactive'))),
722
962
  validationMessage && !isActive && (React.createElement("p", { className: "designbase-figma-license-status-card__message" }, validationMessage)),
723
963
  licenseKey && (React.createElement("div", { className: "designbase-figma-license-status-card__key-row" },
724
964
  React.createElement("span", null, resolveText(t, { key: 'license.key_label' }, '라이선스 키')),
725
965
  React.createElement("code", null, allowRevealKey && isKeyRevealed ? licenseKey : maskLicenseKey(licenseKey)),
726
- allowRevealKey && (React.createElement(react.Button, { type: "button", onPress: () => setIsKeyRevealed((value) => !value), variant: "tertiary", size: "s" }, isKeyRevealed ? '숨기기' : '보기')))),
966
+ allowRevealKey && (React.createElement(react.Button, { type: "button", onPress: () => setIsKeyRevealed((value) => !value), variant: "tertiary", size: "s" }, isKeyRevealed
967
+ ? resolveText(t, { key: 'license.hide_key' }, '숨기기')
968
+ : resolveText(t, { key: 'license.reveal_key' }, '보기'))))),
727
969
  (licenseSource || accessScope || expiresAt || lastVerifiedAt) && (React.createElement("dl", { className: "designbase-figma-license-status-card__meta" },
728
- licenseSource && React.createElement(React.Fragment, null,
729
- React.createElement("dt", null, "Source"),
730
- React.createElement("dd", null, licenseSource)),
731
- formatScope(accessScope) && React.createElement(React.Fragment, null,
732
- React.createElement("dt", null, "Access"),
733
- React.createElement("dd", null, formatScope(accessScope))),
970
+ formatSource(licenseSource, t) && React.createElement(React.Fragment, null,
971
+ React.createElement("dt", null, resolveText(t, { key: 'license.source' }, '출처')),
972
+ React.createElement("dd", null, formatSource(licenseSource, t))),
973
+ formatScope(accessScope, t) && React.createElement(React.Fragment, null,
974
+ React.createElement("dt", null, resolveText(t, { key: 'license.access' }, '접근 범위')),
975
+ React.createElement("dd", null, formatScope(accessScope, t))),
734
976
  expiresAt && React.createElement(React.Fragment, null,
735
- React.createElement("dt", null, "Expires"),
977
+ React.createElement("dt", null, resolveText(t, { key: 'license.expires' }, '만료일')),
736
978
  React.createElement("dd", null, formatDate(expiresAt) || expiresAt)),
737
979
  lastVerifiedAt && React.createElement(React.Fragment, null,
738
- React.createElement("dt", null, "Last checked"),
980
+ React.createElement("dt", null, resolveText(t, { key: 'license.last_checked' }, '최근 확인')),
739
981
  React.createElement("dd", null, formatDate(lastVerifiedAt) || lastVerifiedAt))))));
740
982
  };
741
983
  LicenseStatusCard.displayName = 'LicenseStatusCard';
742
984
 
985
+ function resolvePaymentFeedbackVariant(errorCode, explicitVariant) {
986
+ if (explicitVariant)
987
+ return explicitVariant;
988
+ if (!errorCode)
989
+ return 'success';
990
+ const normalized = errorCode.toLowerCase();
991
+ if (normalized.includes('network')
992
+ || normalized.includes('server')
993
+ || normalized.includes('rate')
994
+ || normalized.includes('timeout')) {
995
+ return 'warning';
996
+ }
997
+ if (normalized.includes('info')) {
998
+ return 'info';
999
+ }
1000
+ return 'error';
1001
+ }
1002
+ const PaymentStatusSection = ({ activationLimit, activationUsage, licenseKey, onDeactivate, isDeactivating = false, showDetails = false, actionMessage, actionMessageVariant, errorCode, className, t, }) => {
1003
+ const remainingActivations = Math.max(0, activationLimit - activationUsage);
1004
+ const resolvedActionMessage = actionMessage ? resolveText(t, actionMessage) : undefined;
1005
+ const feedbackVariant = resolvedActionMessage
1006
+ ? resolvePaymentFeedbackVariant(errorCode, actionMessageVariant)
1007
+ : undefined;
1008
+ return (React.createElement("div", { className: clsx('designbase-figma-payment-status', className) },
1009
+ React.createElement("div", { className: "designbase-figma-payment-status__license-key" },
1010
+ resolveText(t, { key: 'payment.license_key_label' }, '라이선스 키'),
1011
+ ": ",
1012
+ React.createElement("span", null, licenseKey)),
1013
+ showDetails && (React.createElement("div", { className: "designbase-figma-payment-status__details" },
1014
+ React.createElement("div", { className: "designbase-figma-payment-status__activation-info" },
1015
+ React.createElement("div", { className: "designbase-figma-payment-status__remaining" },
1016
+ remainingActivations,
1017
+ ' ',
1018
+ resolveText(t, { key: 'payment.activations_remaining' }, '활성'),
1019
+ ' ',
1020
+ resolveText(t, { key: 'payment.spots_remaining' }, '자리 남음')),
1021
+ React.createElement("div", { className: "designbase-figma-payment-status__usage" },
1022
+ activationUsage,
1023
+ "/",
1024
+ activationLimit)),
1025
+ React.createElement("div", { className: "designbase-figma-payment-status__action" },
1026
+ React.createElement(react.Button, { onPress: onDeactivate, variant: "secondary", size: "s", fullWidth: true, disabled: isDeactivating }, isDeactivating
1027
+ ? resolveText(t, { key: 'payment.deactivating' }, '비활성화중...')
1028
+ : resolveText(t, { key: 'payment.deactivate_license' }, '라이선스 비활성화')),
1029
+ resolvedActionMessage && feedbackVariant && (React.createElement("p", { className: clsx('designbase-figma-payment-status__feedback', `designbase-figma-payment-status__feedback--${feedbackVariant}`), role: feedbackVariant === 'error' ? 'alert' : 'status', "aria-live": "polite" }, resolvedActionMessage)))))));
1030
+ };
1031
+ PaymentStatusSection.displayName = 'PaymentStatusSection';
1032
+
743
1033
  const defaultFeatures = [
744
1034
  { name: '사용 제한', free: '일일 제한', pro: '무제한' },
745
1035
  { name: '모든 주제 사용', free: false, pro: true },
@@ -934,15 +1224,27 @@ function useFigmaLicenseBridge(options = {}) {
934
1224
  if (type === messageTypes.verifyResult) {
935
1225
  setIsVerifying(false);
936
1226
  setIsStartingAuth(false);
937
- applyPatch(parseLicenseMessage(payload));
1227
+ applyPatch({
1228
+ ...parseLicenseMessage(payload),
1229
+ deactivateMessage: '',
1230
+ deactivateErrorCode: '',
1231
+ });
938
1232
  if (payload.success === true) {
939
1233
  applyPatch({ paymentStatus: 'PAID', isLicenseRegistered: true });
940
1234
  }
941
1235
  }
942
1236
  if (type === messageTypes.deactivateResult) {
943
1237
  setIsDeactivating(false);
944
- applyPatch(parseLicenseMessage(payload));
945
- if (payload.success === true) {
1238
+ const parsed = parseLicenseMessage(payload);
1239
+ const isSuccess = payload.success === true;
1240
+ applyPatch({
1241
+ ...parsed,
1242
+ deactivateMessage: parsed.lastMessage,
1243
+ deactivateErrorCode: isSuccess ? undefined : parsed.lastErrorCode,
1244
+ lastMessage: isSuccess ? undefined : parsed.lastMessage,
1245
+ lastErrorCode: isSuccess ? undefined : parsed.lastErrorCode,
1246
+ });
1247
+ if (isSuccess) {
946
1248
  applyPatch({
947
1249
  paymentStatus: 'UNPAID',
948
1250
  licenseKey: '',
@@ -950,6 +1252,8 @@ function useFigmaLicenseBridge(options = {}) {
950
1252
  isLicenseRegistered: false,
951
1253
  activationLimit: 0,
952
1254
  activationUsage: 0,
1255
+ deactivateMessage: parsed.lastMessage || '라이선스가 비활성화되었습니다.',
1256
+ deactivateErrorCode: undefined,
953
1257
  });
954
1258
  }
955
1259
  }
@@ -976,8 +1280,12 @@ function useFigmaLicenseBridge(options = {}) {
976
1280
  }, [messageTypes.verifyLicense]);
977
1281
  const deactivateLicense = React.useCallback(() => {
978
1282
  setIsDeactivating(true);
1283
+ applyPatch({
1284
+ deactivateMessage: '',
1285
+ deactivateErrorCode: '',
1286
+ });
979
1287
  postPluginMessage({ type: messageTypes.deactivateLicense });
980
- }, [messageTypes.deactivateLicense]);
1288
+ }, [applyPatch, messageTypes.deactivateLicense]);
981
1289
  const startDesignbaseAuth = React.useCallback(() => {
982
1290
  setIsStartingAuth(true);
983
1291
  postPluginMessage({ type: START_DESIGNBASE_AUTH_MESSAGE });
@@ -1047,7 +1355,7 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
1047
1355
  setLicenseKeyDraft(value);
1048
1356
  setLicenseKeyInput(value);
1049
1357
  };
1050
- return (React.createElement("div", { className: `designbase-figma-page-license ${className || ''}` },
1358
+ return (React.createElement("div", { className: `designbase-figma-page-license designbase-figma-page-license--${mode} ${className || ''}`, "data-license-mode": mode },
1051
1359
  React.createElement("div", { className: "designbase-figma-page-license__content" },
1052
1360
  React.createElement("div", { className: "designbase-figma-page-license__header" },
1053
1361
  React.createElement("div", { className: "designbase-figma-page-license__title" },
@@ -1062,15 +1370,15 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
1062
1370
  : isMembershipMode
1063
1371
  ? resolveText(t, { key: 'license.youtube_membership_description' }, 'YouTube 멤버십에 가입하면 Designbase 플러그인을 무제한으로 사용할 수 있습니다.')
1064
1372
  : resolveText(t, { key: 'license.purchase_for_unlimited' }, '디자인베이스 멤버십 또는 라이선스로 무제한 이용이 가능합니다.')),
1065
- !isPaid && (React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel))),
1373
+ !isPaid && !isMembershipMode && (React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel))),
1066
1374
  !isMembershipMode && (React.createElement(PricingComparison, { features: pricingFeatures, pricing: pricing, paymentType: paymentType, t: t })),
1067
- state.lastMessage && (React.createElement("p", { className: `designbase-figma-page-license__feedback ${state.lastErrorCode ? 'designbase-figma-page-license__feedback--error' : ''}` }, state.lastMessage)),
1375
+ 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)),
1068
1376
  isPaid && (React.createElement("div", { className: "designbase-figma-page-license__section" },
1069
1377
  React.createElement("div", { className: "designbase-figma-page-license__section-header" },
1070
1378
  React.createElement("h3", null, resolveText(t, { key: 'license.activated' }, '라이선스 활성화됨')),
1071
1379
  React.createElement(react.Button, { onPress: () => setShowDetails(!showDetails), variant: "tertiary", size: "s", iconOnly: true },
1072
1380
  React.createElement(icons.MoreHorizontalIcon, { size: 16 }))),
1073
- 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, t: t }),
1381
+ 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: t }),
1074
1382
  React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel),
1075
1383
  showDetails && (React.createElement("div", { className: "designbase-figma-page-license__details" },
1076
1384
  React.createElement("div", { className: "designbase-figma-page-license__activation-info" },
@@ -1084,11 +1392,13 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
1084
1392
  state.activationUsage,
1085
1393
  "/",
1086
1394
  state.activationLimit)),
1087
- React.createElement(react.Button, { onPress: deactivateLicense, variant: "tertiary", size: "s", disabled: isDeactivating }, isDeactivating
1088
- ? resolveText(t, { key: 'license.deactivating' }, '비활성화 중...')
1089
- : resolveText(t, { key: 'license.deactivate' }, '라이선스 비활성화')))))),
1395
+ React.createElement("div", { className: "designbase-figma-page-license__action" },
1396
+ React.createElement(react.Button, { onPress: deactivateLicense, variant: "secondary", size: "s", fullWidth: true, disabled: isDeactivating }, isDeactivating
1397
+ ? resolveText(t, { key: 'license.deactivating' }, '비활성화 중...')
1398
+ : resolveText(t, { key: 'license.deactivate' }, '라이선스 비활성화')),
1399
+ 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))))))),
1090
1400
  !isPaid && (React.createElement(React.Fragment, null,
1091
- isMembershipMode && (React.createElement(LicenseOffer, { membershipUrl: links.membershipUrl || links.purchaseUrl, t: t })),
1401
+ isMembershipMode && (React.createElement(LicenseOffer, { joinUrl: links.membershipUrl || links.purchaseUrl, guideUrl: links.helpUrl || links.purchaseUrl, t: t })),
1092
1402
  !isMembershipMode && (enableAccountAuth || links.purchaseUrl || links.accountUrl) && (React.createElement("div", { className: "designbase-figma-page-license__section" },
1093
1403
  React.createElement("div", { className: "designbase-figma-page-license__section-header" },
1094
1404
  React.createElement("h3", null, resolveText(t, { key: 'license.designbase_account' }, '디자인베이스 계정'))),
@@ -1103,41 +1413,33 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
1103
1413
  links.accountUrl && (React.createElement(react.Button, { onPress: () => openExternalUrl(links.accountUrl), variant: "tertiary", size: "s", fullWidth: true },
1104
1414
  React.createElement("span", null, resolveText(t, { key: 'license.manage_account' }, '계정·멤버십 관리')),
1105
1415
  React.createElement(icons.ExternalLinkIcon, { size: 16 })))))),
1106
- showLicenseKeyInput && (React.createElement("div", { className: "designbase-figma-page-license__section" },
1416
+ showLicenseKeyInput && (React.createElement("div", { className: isMembershipMode
1417
+ ? 'designbase-figma-page-license__key-form'
1418
+ : 'designbase-figma-page-license__section' },
1107
1419
  React.createElement("div", { className: "designbase-figma-page-license__section-header" },
1108
1420
  React.createElement("h3", null, resolveText(t, { key: 'license.enter_key' }, '라이선스 키 입력'))),
1109
- React.createElement(FormWithSubmit, { onLicenseSubmit: handleLicenseSubmit, disabled: false, isSubmitting: isVerifying, value: licenseKeyDraft, onValueChange: handleLicenseKeyChange, label: { key: 'license.key' }, submitText: { key: 'license.submit' }, submittingText: { key: 'license.verifying' }, helperText: { key: 'license.enter_from_email' }, t: t }))))),
1110
- links.helpUrl && (React.createElement("p", { className: "designbase-figma-page-license__disclaimer" },
1421
+ React.createElement(FormWithSubmit, { onLicenseSubmit: handleLicenseSubmit, disabled: false, isSubmitting: isVerifying, value: licenseKeyDraft, onValueChange: handleLicenseKeyChange, label: { key: 'license.key' }, submitText: { key: 'license.submit' }, submittingText: { key: 'license.verifying' }, helperText: { key: isMembershipMode ? 'license.enter_from_youtube' : 'license.enter_from_email' }, t: t }))))),
1422
+ links.helpUrl && !isMembershipMode && (React.createElement("p", { className: "designbase-figma-page-license__disclaimer" },
1111
1423
  React.createElement("button", { type: "button", className: "designbase-figma-page-license__link", onClick: () => openExternalUrl(links.helpUrl) }, resolveText(t, { key: 'license.help_link' }, '라이선스·결제 도움말'))))),
1112
- React.createElement(react.Button, { className: "designbase-figma-page-license__close", onPress: onClose, variant: "tertiary", size: "s", "aria-label": "Close" },
1113
- React.createElement(icons.CloseIcon, { size: 20 }))));
1424
+ React.createElement(react.Button, { className: "designbase-figma-page-license__close", onPress: onClose, variant: "tertiary", size: "s", iconOnly: true, "aria-label": "Close" },
1425
+ React.createElement(icons.CloseIcon, { size: 16 }))));
1114
1426
  };
1115
1427
  PageLicense.displayName = 'PageLicense';
1116
1428
 
1117
- const PaymentStatusSection = ({ status, usageCount, activationLimit, activationUsage, licenseKey, onDeactivate, isDeactivating = false, showDetails = false, className, t }) => {
1118
- const remainingActivations = activationLimit - activationUsage;
1119
- return (React.createElement("div", { className: `designbase-figma-payment-status ${className || ''}` },
1120
- React.createElement("div", { className: "designbase-figma-payment-status__license-key" },
1121
- resolveText(t, { key: 'payment.license_key_label' }, '라이선스 키'),
1122
- ": ",
1123
- React.createElement("span", null, licenseKey)),
1124
- showDetails && (React.createElement("div", { className: "designbase-figma-payment-status__details" },
1125
- React.createElement("div", { className: "designbase-figma-payment-status__activation-info" },
1126
- React.createElement("div", { className: "designbase-figma-payment-status__remaining" },
1127
- remainingActivations,
1128
- " ",
1129
- resolveText(t, { key: 'payment.activations_remaining' }, '활성'),
1130
- " ",
1131
- resolveText(t, { key: 'payment.spots_remaining' }, '자리 남음')),
1132
- React.createElement("div", { className: "designbase-figma-payment-status__usage" },
1133
- activationUsage,
1134
- "/",
1135
- activationLimit)),
1136
- React.createElement(react.Button, { onPress: onDeactivate, variant: "tertiary", size: "s", disabled: isDeactivating }, isDeactivating
1137
- ? resolveText(t, { key: 'payment.deactivating' }, '비활성화중...')
1138
- : resolveText(t, { key: 'payment.deactivate_license' }, '라이선스 비활성화'))))));
1429
+ const PaymentBadge = ({ isActive, onClick, isLoading = false, text, t = (key) => key, className, }) => {
1430
+ const getBadgeText = () => {
1431
+ if (text)
1432
+ return text;
1433
+ return isActive
1434
+ ? (t('proUser') || '프로 계정')
1435
+ : (t('usingFree') || '무료 사용');
1436
+ };
1437
+ const getBadgeVariant = () => {
1438
+ return isActive ? 'success' : 'secondary';
1439
+ };
1440
+ return (React.createElement("div", { className: `designbase-figma-payment-badge ${className || ''} ${isActive ? 'active' : 'free'}`, onClick: onClick, style: { cursor: onClick ? 'pointer' : 'default' } }, isLoading ? (React.createElement(react.Spinner, { size: "s" })) : (React.createElement(react.Badge, { variant: getBadgeVariant(), size: "s", className: "designbase-figma-payment-badge__badge" }, getBadgeText()))));
1139
1441
  };
1140
- PaymentStatusSection.displayName = 'PaymentStatusSection';
1442
+ PaymentBadge.displayName = 'PaymentBadge';
1141
1443
 
1142
1444
  const ProgressModal = ({ isOpen, onClose, progress, title = { key: 'progress.working' }, completedMessage = { key: 'progress.completed' }, processingMessage = { key: 'progress.processing' }, stopButtonText = { key: 'progress.stop' }, confirmButtonText = { key: 'progress.confirm' }, helpText, onStop, onComplete, loadingIcon, completeIcon, className, t, autoComplete = true, autoCloseOnComplete = false }) => {
1143
1445
  const [isCompleted, setIsCompleted] = React.useState(false);
@@ -1181,7 +1483,7 @@ const ProgressModal = ({ isOpen, onClose, progress, title = { key: 'progress.wor
1181
1483
  React.createElement(react.Spinner, { size: "m" }))))),
1182
1484
  React.createElement("p", { className: "designbase-figma-progress-modal__message" }, displayMessage),
1183
1485
  React.createElement("div", { className: "designbase-figma-progress-modal__status" }, `${processedNodes} / ${totalNodes} (${progressPercentage.toFixed(1)}%)`),
1184
- React.createElement(react.Progressbar, { value: progressPercentage, size: "m", variant: isCompleted ? 'success' : 'primary', style: "animated", fullWidth: true }),
1486
+ React.createElement(react.Progressbar, { value: progressPercentage, size: "m", variant: isCompleted ? 'success' : 'primary', barStyle: "animated", fullWidth: true }),
1185
1487
  displayHelpText && !isCompleted && (React.createElement("p", { className: "designbase-figma-progress-modal__help-text" }, displayHelpText)),
1186
1488
  React.createElement("div", { className: "designbase-figma-progress-modal__actions" }, isCompleted ? (React.createElement(react.Button, { onPress: handleConfirm, variant: "primary", size: "m", fullWidth: true }, displayConfirmText)) : (React.createElement(react.Button, { onPress: handleStop, variant: "secondary", size: "m", fullWidth: true }, displayStopText))))));
1187
1489
  };
@@ -1933,6 +2235,7 @@ exports.FigmaToastProvider = FigmaToastProvider;
1933
2235
  exports.FormWithSubmit = FormWithSubmit;
1934
2236
  exports.InteractionFeedback = InteractionFeedback;
1935
2237
  exports.LanguageSelector = LanguageSelector;
2238
+ exports.LanguageSettingsModal = LanguageSettingsModal;
1936
2239
  exports.LicenseOffer = LicenseOffer;
1937
2240
  exports.LicenseStatusCard = LicenseStatusCard;
1938
2241
  exports.LogoDropdown = LogoDropdown;
@@ -1946,7 +2249,10 @@ exports.START_DESIGNBASE_AUTH_MESSAGE = START_DESIGNBASE_AUTH_MESSAGE;
1946
2249
  exports.SettingsModal = SettingsModal;
1947
2250
  exports.TitleDescription = TitleDescription;
1948
2251
  exports.UpgradeBanner = UpgradeBanner;
2252
+ exports.UsageStatusPopover = UsageStatusPopover;
1949
2253
  exports.getActiveSection = getActiveSection;
2254
+ exports.interpolateText = interpolateText;
2255
+ exports.resolvePaymentFeedbackVariant = resolvePaymentFeedbackVariant;
1950
2256
  exports.resolveText = resolveText;
1951
2257
  exports.scrollToSection = scrollToSection;
1952
2258
  exports.useFigmaLicenseBridge = useFigmaLicenseBridge;