@designbasekorea/ui-wc 0.13.0 → 0.15.0

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.
Files changed (168) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +21 -0
  3. package/README.en.md +45 -0
  4. package/README.md +13 -5
  5. package/THIRD-PARTY-NOTICES.md +5 -0
  6. package/dist/esm/components/db-ad-banner.d.ts +6 -0
  7. package/dist/esm/components/db-ad-banner.js +1 -1
  8. package/dist/esm/components/db-animation-background.d.ts +7 -0
  9. package/dist/esm/components/db-animation-background.js +1 -1
  10. package/dist/esm/components/db-avatar.d.ts +2 -0
  11. package/dist/esm/components/db-avatar.js +1 -1
  12. package/dist/esm/components/db-banner.d.ts +2 -0
  13. package/dist/esm/components/db-banner.js +1 -1
  14. package/dist/esm/components/db-bottom-navigation.d.ts +2 -0
  15. package/dist/esm/components/db-bottom-navigation.js +1 -1
  16. package/dist/esm/components/db-card.d.ts +2 -0
  17. package/dist/esm/components/db-card.js +1 -1
  18. package/dist/esm/components/db-carousel.d.ts +2 -0
  19. package/dist/esm/components/db-carousel.js +1 -1
  20. package/dist/esm/components/db-code-block.d.ts +3 -0
  21. package/dist/esm/components/db-code-block.js +1 -1
  22. package/dist/esm/components/db-color-picker.d.ts +5 -0
  23. package/dist/esm/components/db-color-picker.js +1 -1
  24. package/dist/esm/components/db-color-swatch.d.ts +2 -0
  25. package/dist/esm/components/db-color-swatch.js +1 -1
  26. package/dist/esm/components/db-confirm.d.ts +2 -0
  27. package/dist/esm/components/db-confirm.js +1 -1
  28. package/dist/esm/components/db-context-menu.d.ts +6 -2
  29. package/dist/esm/components/db-context-menu.js +1 -1
  30. package/dist/esm/components/db-cookie-banner.d.ts +2 -0
  31. package/dist/esm/components/db-cookie-banner.js +1 -1
  32. package/dist/esm/components/db-drawer.d.ts +2 -0
  33. package/dist/esm/components/db-drawer.js +1 -1
  34. package/dist/esm/components/db-dropdown.d.ts +2 -0
  35. package/dist/esm/components/db-dropdown.js +1 -1
  36. package/dist/esm/components/db-easing-editor.d.ts +5 -0
  37. package/dist/esm/components/db-empty-state.d.ts +2 -0
  38. package/dist/esm/components/db-empty-state.js +1 -1
  39. package/dist/esm/components/db-file-uploader.d.ts +4 -0
  40. package/dist/esm/components/db-file-uploader.js +1 -1
  41. package/dist/esm/components/db-footer.d.ts +2 -0
  42. package/dist/esm/components/db-footer.js +1 -1
  43. package/dist/esm/components/db-gantt.d.ts +2 -0
  44. package/dist/esm/components/db-gantt.js +1 -1
  45. package/dist/esm/components/db-image-list.d.ts +3 -0
  46. package/dist/esm/components/db-image-list.js +1 -1
  47. package/dist/esm/components/db-keyframe-editor.d.ts +5 -0
  48. package/dist/esm/components/db-layer-list.d.ts +71 -0
  49. package/dist/esm/components/db-layer-list.js +1 -0
  50. package/dist/esm/components/db-lightbox.d.ts +4 -0
  51. package/dist/esm/components/db-lightbox.js +1 -1
  52. package/dist/esm/components/db-list.d.ts +2 -0
  53. package/dist/esm/components/db-list.js +1 -1
  54. package/dist/esm/components/db-menu-item.d.ts +2 -0
  55. package/dist/esm/components/db-menu-item.js +1 -1
  56. package/dist/esm/components/db-modal.d.ts +3 -0
  57. package/dist/esm/components/db-modal.js +1 -1
  58. package/dist/esm/components/db-navbar.d.ts +5 -0
  59. package/dist/esm/components/db-navbar.js +1 -1
  60. package/dist/esm/components/db-onboarding-modal.d.ts +3 -0
  61. package/dist/esm/components/db-onboarding-modal.js +1 -1
  62. package/dist/esm/components/db-page-header.d.ts +3 -0
  63. package/dist/esm/components/db-page-header.js +1 -1
  64. package/dist/esm/components/db-pagination.d.ts +2 -0
  65. package/dist/esm/components/db-pagination.js +1 -1
  66. package/dist/esm/components/db-popover.d.ts +2 -0
  67. package/dist/esm/components/db-popover.js +1 -1
  68. package/dist/esm/components/db-search-bar.d.ts +3 -0
  69. package/dist/esm/components/db-search-bar.js +1 -1
  70. package/dist/esm/components/db-section-app-download.d.ts +2 -0
  71. package/dist/esm/components/db-section-app-download.js +1 -1
  72. package/dist/esm/components/db-section-contact.d.ts +2 -0
  73. package/dist/esm/components/db-section-contact.js +1 -1
  74. package/dist/esm/components/db-section-cta.d.ts +3 -0
  75. package/dist/esm/components/db-section-cta.js +1 -1
  76. package/dist/esm/components/db-section-faq.d.ts +2 -0
  77. package/dist/esm/components/db-section-faq.js +1 -1
  78. package/dist/esm/components/db-section-feature.d.ts +3 -0
  79. package/dist/esm/components/db-section-feature.js +1 -1
  80. package/dist/esm/components/db-section-hero.d.ts +2 -0
  81. package/dist/esm/components/db-section-hero.js +1 -1
  82. package/dist/esm/components/db-section-logo-cloud.d.ts +2 -0
  83. package/dist/esm/components/db-section-logo-cloud.js +1 -1
  84. package/dist/esm/components/db-section-pricing.d.ts +3 -0
  85. package/dist/esm/components/db-section-pricing.js +1 -1
  86. package/dist/esm/components/db-section-stats.d.ts +2 -0
  87. package/dist/esm/components/db-section-stats.js +1 -1
  88. package/dist/esm/components/db-section-testimonials.d.ts +4 -0
  89. package/dist/esm/components/db-section-testimonials.js +1 -1
  90. package/dist/esm/components/db-section.d.ts +2 -0
  91. package/dist/esm/components/db-section.js +1 -1
  92. package/dist/esm/components/db-share.d.ts +5 -0
  93. package/dist/esm/components/db-share.js +1 -1
  94. package/dist/esm/components/db-sidebar.d.ts +7 -0
  95. package/dist/esm/components/db-sidebar.js +1 -1
  96. package/dist/esm/components/db-stat.d.ts +2 -0
  97. package/dist/esm/components/db-stat.js +1 -1
  98. package/dist/esm/components/db-table.d.ts +5 -0
  99. package/dist/esm/components/db-table.js +1 -1
  100. package/dist/esm/components/db-testimonial.d.ts +3 -0
  101. package/dist/esm/components/db-testimonial.js +1 -1
  102. package/dist/esm/components/db-time-picker.d.ts +1 -0
  103. package/dist/esm/components/db-time-picker.js +1 -1
  104. package/dist/esm/components/db-top-banner.d.ts +3 -0
  105. package/dist/esm/components/db-top-banner.js +1 -1
  106. package/dist/esm/components/db-tutorial.d.ts +3 -0
  107. package/dist/esm/components/db-tutorial.js +1 -1
  108. package/dist/esm/components/db-video-timeline.d.ts +34 -2
  109. package/dist/esm/components/db-video-timeline.js +1 -1
  110. package/dist/esm/components/db-world-map.d.ts +5 -0
  111. package/dist/esm/components/media-editor-model.d.ts +10 -1
  112. package/dist/esm/core/i18n-en.js +1 -1
  113. package/dist/esm/core/i18n.d.ts +11 -0
  114. package/dist/esm/core/i18n.js +1 -1
  115. package/dist/esm/index.d.ts +2 -1
  116. package/dist/esm/index.js +1 -1
  117. package/dist/index.cjs +1 -1
  118. package/dist/index.css +1 -1
  119. package/dist/index.d.ts +619 -486
  120. package/dist/index.esm.js +1 -1
  121. package/dist/react/index.cjs +1 -1
  122. package/dist/react/index.d.ts +47 -3
  123. package/dist/react/index.esm.js +1 -1
  124. package/docs/ACCORDION.md +17 -0
  125. package/docs/ACHIEVEMENTS.md +15 -0
  126. package/docs/AI-ELEMENTS.md +86 -0
  127. package/docs/APP-UPDATE.md +60 -0
  128. package/docs/ATTACHMENTS.md +32 -0
  129. package/docs/BUTTON.md +17 -0
  130. package/docs/CDN-GUIDE.md +82 -0
  131. package/docs/COMPONENT-CHECKLIST.md +98 -0
  132. package/docs/CONTEXT-MENU.md +22 -0
  133. package/docs/CREDIT-AI.md +136 -0
  134. package/docs/CREDIT-CARD.md +52 -0
  135. package/docs/CROSS_PLATFORM_COMPONENT_STRATEGY.md +81 -0
  136. package/docs/EASING-EDITOR.md +42 -0
  137. package/docs/EFFECT-KEYFRAMES.md +60 -0
  138. package/docs/LAYER-LIST.md +31 -0
  139. package/docs/MEDIA-EDITORS.md +63 -0
  140. package/docs/MODULE-REUSE.md +43 -0
  141. package/docs/MULTI-PLATFORM-GUIDE.md +246 -0
  142. package/docs/NOTIFICATIONS.md +23 -0
  143. package/docs/PAPER-CARDS.md +61 -0
  144. package/docs/QUALITY-GUIDELINES.md +196 -0
  145. package/docs/RANGE-SLIDER.md +29 -0
  146. package/docs/REACT-WC-INTEGRATION.md +96 -0
  147. package/docs/RECENT-COMPONENTS-QUALITY.md +96 -0
  148. package/docs/SELECT.md +26 -0
  149. package/docs/SELECTION-CONTROLS.md +33 -0
  150. package/docs/SITE-SHELL.md +37 -0
  151. package/docs/STYLING.md +60 -0
  152. package/docs/SUPPORT.md +39 -0
  153. package/docs/TABS.md +19 -0
  154. package/docs/TEXT-FIELDS.md +28 -0
  155. package/docs/TIME-PICKER-DEVELOPMENT.md +115 -0
  156. package/docs/TOGGLE.md +40 -0
  157. package/docs/TRAVEL.md +30 -0
  158. package/docs/UI-NATIVE.md +90 -0
  159. package/docs/VIDEO-EXPORT.md +12 -0
  160. package/docs/VOICE-INPUT.md +24 -0
  161. package/docs/VUE-ANGULAR-INTEGRATION.md +197 -0
  162. package/docs/WORDPRESS-INTEGRATION.md +250 -0
  163. package/docs/WORKSPACE.md +27 -0
  164. package/docs/quality/AXE-REPORT.md +35 -0
  165. package/docs/quality/CREDIT-AI-RELEASE-2026-10-18.md +34 -0
  166. package/docs/quality/ENUM-MIGRATION-WORKSHEET.md +288 -0
  167. package/docs/quality/SCORECARD.md +389 -0
  168. package/package.json +136 -130
@@ -1 +1 @@
1
- import{inheritThemeScope as t}from"../core/theme-scope.js";import{subscribeDbLocale as e,dbText as i}from"../core/i18n.js";import{createDbIcon as s,getDbComponentIconSize as n}from"./db-icons.js";import"./db-button.js";import{TIME_PICKER_FORMATS as o,TIME_PICKER_SIZES as r,TIME_PICKER_DROPDOWN_POSITIONS as h}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";import{coerceToContract as d}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";const a="designbase-wc-time-picker";class DbTimePicker extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.isOpen=!1,this.hours=12,this.minutes=0,this.seconds=0,this.period="PM",this.inputText="",this.tempHours=12,this.tempMinutes=0,this.tempSeconds=0,this.tempPeriod="PM",this.boundDocumentMouseDown=t=>this.handleDocumentMouseDown(t),this.boundViewportChange=()=>this.handleViewportChange(),this.viewportChangeFrame=null,this.handleDocumentKeyDown=t=>{"Escape"===t.key&&this.isOpen&&(t.preventDefault(),this.closePicker(),this.inputEl?.focus())},this.boundDocumentKeyDown=t=>this.handleDocumentKeyDown(t)}static get observedAttributes(){return["value","default-value","type","format","minute-step","size","disabled","readonly","label","full-width","error","dropdown-position","open"]}connectedCallback(){this.built||this.build(),this.unsubscribeLocale?.(),this.unsubscribeLocale=e(()=>this.syncLocale()),this.parseTimeString(this.getAttribute("value")??this.getAttribute("default-value")??"12:00"),this.syncInputText(),this.render(),document.addEventListener("mousedown",this.boundDocumentMouseDown),document.addEventListener("keydown",this.boundDocumentKeyDown),window.addEventListener("resize",this.boundViewportChange),window.addEventListener("scroll",this.boundViewportChange,{capture:!0,passive:!0})}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0,document.removeEventListener("mousedown",this.boundDocumentMouseDown),document.removeEventListener("keydown",this.boundDocumentKeyDown),window.removeEventListener("resize",this.boundViewportChange),window.removeEventListener("scroll",this.boundViewportChange,{capture:!0}),null!==this.viewportChangeFrame&&cancelAnimationFrame(this.viewportChangeFrame),this.viewportChangeFrame=null,this.dropdownEl?.remove()}syncLocale(){if(!this.built)return;if(this.inputEl?.setAttribute("aria-label",i("시간 입력")),this.triggerEl?.querySelectorAll(`.${a}__icon-display, .${a}__toggle`).forEach(t=>t.setAttribute("aria-label",i("시간 선택 열기"))),this.is12h&&this.inputEl&&document.activeElement!==this.inputEl){const t=`${this.pad2(this.hours)}:${this.pad2(this.minutes)}${this.showSeconds?`:${this.pad2(this.seconds)}`:""}`;("AM"===this.period?["오전","AM"]:["오후","PM"]).some(e=>this.inputText===`${e} ${t}`)&&this.syncInputText()}this.dropdownEl?.setAttribute("aria-label",i("시간 선택"));const t=this.querySelector("db-modal");t&&t.setAttribute("title",i("시간 선택"));const e=[this.dropdownEl,t].filter(t=>!!t);for(const t of e){const e=["시간","분",...this.showSeconds?["초"]:[],...this.is12h?["오전/오후"]:[]];t.querySelectorAll(`.${a}__wheel-container`).forEach((t,s)=>{const n=e[s];if(!n)return;const o=t.querySelector(`.${a}__wheel-label`);o&&(o.textContent=i(n)),t.querySelector('[role="listbox"]')?.setAttribute("aria-label",i(n)),"오전/오후"===n&&t.querySelectorAll('[role="option"]').forEach((t,e)=>{t.textContent=i(0===e?"오전":"오후")})});const s=t.querySelector(`.${a}__preview-time`);s&&(s.textContent=this.formatTimeString(this.hours,this.minutes,this.seconds,this.period));const n=t.querySelector(`.${a}__dropdown-actions db-button`);n&&(n.textContent=i("선택"));const o=t.querySelectorAll(`.${a}__modal-footer db-button`);o[0]&&(o[0].textContent=i("취소")),o[1]&&(o[1].textContent=i("적용"))}}attributeChangedCallback(t){if(this.built){if("value"===t){const t=this.getAttribute("value");t&&(this.parseTimeString(t),this.syncInputText())}if("open"===t)return this.isOpen=this.hasAttribute("open"),void this.render();this.render()}}get value(){return this.getAttribute("value")||""}set value(t){t?this.setAttribute("value",t):this.removeAttribute("value")}get format(){const t=this.getAttribute("format")||"24h";return o.includes(t)?t:"24h"}set format(t){this.setAttribute("format",t)}get type(){return"modal"===this.getAttribute("type")?"modal":"dropdown"}get is12h(){return"12h"===this.format||"12h-with-seconds"===this.format}get showSeconds(){return"12h-with-seconds"===this.format||"24h-with-seconds"===this.format}get minuteStep(){const t=Number(this.getAttribute("minute-step")||"1");return Number.isFinite(t)&&t>0?t:1}get normalizedSize(){return d(this.getAttribute("size"),r,"m")}get dropdownPosition(){return d(this.getAttribute("dropdown-position"),h,"auto")}get itemHeight(){return"s"===this.normalizedSize?32:"l"===this.normalizedSize?40:28}get disabled(){return this.hasAttribute("disabled")}set disabled(t){this.toggleAttribute("disabled",Boolean(t))}get readonly(){return this.hasAttribute("readonly")}set readonly(t){this.toggleAttribute("readonly",Boolean(t))}build(){this.built=!0,this.labelEl=document.createElement("label"),this.labelEl.className=`${a}__label`,this.triggerEl=document.createElement("div"),this.triggerEl.className=`${a}__trigger`;const t=document.createElement("button");t.type="button",t.className=`${a}__icon-display`,t.setAttribute("aria-label",i("시간 선택 열기")),t.addEventListener("click",()=>this.toggleOpen()),this.inputEl=document.createElement("input"),this.inputEl.type="text",this.inputEl.className=`${a}__input`,this.inputEl.setAttribute("aria-label",i("시간 입력")),this.inputEl.addEventListener("click",()=>this.openPicker()),this.inputEl.addEventListener("blur",()=>this.handleInputBlur()),this.inputEl.addEventListener("input",()=>{this.inputText=this.inputEl?.value||""});const e=document.createElement("button");e.type="button",e.className=`${a}__toggle`,e.setAttribute("aria-label",i("시간 선택 열기")),e.addEventListener("click",()=>this.toggleOpen()),this.triggerEl.append(t,this.inputEl,e),this.dropdownEl=document.createElement("div"),this.dropdownEl.className=`${a}__dropdown`,this.dropdownEl.setAttribute("role","dialog"),this.dropdownEl.setAttribute("aria-modal","false")}pad2(t){return String(t).padStart(2,"0")}to24Hour(t,e){return"AM"===e?12===t?0:t:12===t?12:t+12}formatTimeString(t,e,s,n){const o=n||this.period,r=i("AM"===o?"오전":"오후");return this.is12h?`${r} ${this.pad2(t)}:${this.pad2(e)}${this.showSeconds?`:${this.pad2(s)}`:""}`:`${this.pad2(t)}:${this.pad2(e)}${this.showSeconds?`:${this.pad2(s)}`:""}`}toValue24(t,e,i,s){const n=this.is12h?this.to24Hour(t,s||this.period):t;return`${this.pad2(n)}:${this.pad2(e)}${this.showSeconds?`:${this.pad2(i)}`:""}`}parseTimeString(t){const e=t.trim();if(this.is12h){const t=this.showSeconds?/^(오전|오후|AM|PM)?\s*(0?[1-9]|1[0-2]):([0-5]\d):([0-5]\d)\s*(AM|PM|오전|오후)?$/i:/^(오전|오후|AM|PM)?\s*(0?[1-9]|1[0-2]):([0-5]\d)\s*(AM|PM|오전|오후)?$/i,i=e.match(t);if(i){const t=i[1],e=this.showSeconds?i[5]:i[4],s=(t||e||"AM").toUpperCase();return this.hours=parseInt(i[2],10),this.minutes=parseInt(i[3],10),this.seconds=this.showSeconds?parseInt(i[4],10):0,void(this.period="오후"===s||"PM"===s?"PM":"AM")}}else{const t=this.showSeconds?/^([01]?\d|2[0-3]):([0-5]\d):([0-5]\d)$/:/^([01]?\d|2[0-3]):([0-5]\d)$/,i=e.match(t);i&&(this.hours=parseInt(i[1],10),this.minutes=parseInt(i[2],10),this.seconds=this.showSeconds?parseInt(i[3],10):0)}}syncInputText(){this.inputText=this.formatTimeString(this.hours,this.minutes,this.seconds),this.inputEl&&(this.inputEl.value=this.inputText)}emitChange(t){this.setAttribute("value",t),this.dispatchEvent(new CustomEvent("db-change",{detail:{value:t},bubbles:!0,composed:!0}))}handleTimeChange(t,e,i=this.seconds,s){this.hours=t,this.minutes=e,this.seconds=i,s&&(this.period=s);const n=this.formatTimeString(t,e,i,s);this.inputText=n,this.inputEl&&(this.inputEl.value=n),this.emitChange(this.toValue24(t,e,i,s))}handleInputBlur(){if(!this.inputEl)return;const t=this.inputText;this.parseTimeString(this.inputEl.value);this.formatTimeString(this.hours,this.minutes,this.seconds)!==t||this.inputEl.value.trim()?this.handleTimeChange(this.hours,this.minutes,this.seconds,this.period):this.syncInputText()}openPicker(){this.disabled||this.readonly||(this.isOpen=!0,"modal"===this.type&&(this.tempHours=this.hours,this.tempMinutes=this.minutes,this.tempSeconds=this.seconds,this.tempPeriod=this.period),this.render())}closePicker(){this.isOpen=!1,this.render()}toggleOpen(){this.isOpen?this.closePicker():this.openPicker()}handleDocumentMouseDown(t){if(!this.isOpen)return;const e=t.target;this.contains(e)||this.dropdownEl?.contains(e)||this.closePicker()}handleViewportChange(){this.isOpen&&"dropdown"===this.type&&null===this.viewportChangeFrame&&(this.viewportChangeFrame=requestAnimationFrame(()=>{this.viewportChangeFrame=null,this.isOpen&&"dropdown"===this.type&&this.positionDropdown()}))}positionDropdown(){if(!this.isOpen||"dropdown"!==this.type||!this.triggerEl||!this.dropdownEl||this.dropdownEl.hidden)return;const t=this.dropdownEl,e=this.triggerEl.getBoundingClientRect();t.style.width="";const i=t.getBoundingClientRect(),s=Math.max(0,window.innerWidth-16),n=Math.min(i.width,s);t.style.width=`${Math.round(n)}px`;const o=this.dropdownPosition,r=e.bottom+4+i.height<=window.innerHeight-8,h=e.top-4-i.height>=8,d="auto"===o?r||!h?"bottom":"top":o.startsWith("top")?"top":"bottom",a="auto"===o?e.left+n>window.innerWidth-8?"end":"start":o.endsWith("end")?"end":"start",l="end"===a?e.right-n:e.left,u=Math.max(8,Math.min(l,window.innerWidth-n-8)),c="top"===d?e.top-i.height-4:e.bottom+4,p=Math.max(8,Math.min(c,window.innerHeight-i.height-8));t.style.left=`${Math.round(u)}px`,t.style.top=`${Math.round(p)}px`,t.dataset.placement=`${d}-${a}`}createWheel(t){const{label:e,items:i,value:s,onChange:n,formatter:o}=t,r=document.createElement("div");r.className=`${a}__wheel-container`;const h=document.createElement("div");h.className=`${a}__wheel-label`,h.textContent=e,r.appendChild(h);const d=document.createElement("div");d.className=`${a}__wheel-viewport`;const l=document.createElement("div");l.className=`${a}__wheel`,l.setAttribute("role","listbox"),l.setAttribute("aria-label",e);let u=null,c=!1,p=!1,m=0,b=0;const indexOfValue=t=>i.findIndex(e=>e===t),scrollToIndex=(t,e=!0)=>{const i=t*this.itemHeight;c=!0,l.scrollTo({top:i,behavior:e?"smooth":"auto"}),window.setTimeout(()=>{c=!1},150)},fmt=t=>o?o(t):"number"==typeof t?this.pad2(t):String(t);i.forEach(t=>{const e=document.createElement("div");e.className=[`${a}__wheel-item`,t===s&&`${a}__wheel-item--selected`].filter(Boolean).join(" "),e.setAttribute("role","option"),e.setAttribute("aria-selected",String(t===s)),e.textContent=fmt(t),e.addEventListener("click",()=>{const e=indexOfValue(t);scrollToIndex(e,!0),t!==s&&n(t)}),l.appendChild(e)});const handleScroll=()=>{c||(u&&window.clearTimeout(u),u=window.setTimeout(()=>{const t=l.scrollTop,e=Math.round(t/this.itemHeight),o=Math.max(0,Math.min(i.length-1,e)),r=i[o];scrollToIndex(o,!0),r!==s&&n(r)},120))};l.addEventListener("scroll",handleScroll,{passive:!0}),l.addEventListener("pointerdown",t=>{p=!0,m=t.clientY,b=l.scrollTop,l.setPointerCapture(t.pointerId),l.style.cursor="grabbing"}),l.addEventListener("pointermove",t=>{p&&(l.scrollTop=b-(t.clientY-m))});const endDrag=t=>{p&&(p=!1,l.releasePointerCapture(t.pointerId),l.style.cursor="grab",handleScroll())};l.addEventListener("pointerup",endDrag),l.addEventListener("pointercancel",endDrag);const w=indexOfValue(s);w>=0&&requestAnimationFrame(()=>scrollToIndex(w,!1));const g=document.createElement("div");g.className=`${a}__wheel-center-indicator`,g.setAttribute("aria-hidden","true");const E=document.createElement("div");E.className=`${a}__wheel-fade wheel-fade--top`,E.setAttribute("aria-hidden","true");const v=document.createElement("div");return v.className=`${a}__wheel-fade wheel-fade--bottom`,v.setAttribute("aria-hidden","true"),d.append(l,g,E,v),r.appendChild(d),r}renderSelector(t=!1){const e=document.createElement("div");e.className=`${a}__selector`;const n=document.createElement("div");n.className=`${a}__picker-wheels`;const o=this.is12h?Array.from({length:12},(t,e)=>e+1):Array.from({length:24},(t,e)=>e),r=Array.from({length:Math.floor(60/this.minuteStep)},(t,e)=>e*this.minuteStep),h=Array.from({length:60},(t,e)=>e),d=t?this.tempHours:this.hours,l=t?this.tempMinutes:this.minutes,u=t?this.tempSeconds:this.seconds,c=t?this.tempPeriod:this.period,onChange=(e,i,s,n)=>{if(t)return this.tempHours=e,this.tempMinutes=i,this.tempSeconds=s,void(this.tempPeriod=n);this.handleTimeChange(e,i,s,n)};if(n.appendChild(this.createWheel({label:i("시간"),items:o,value:d,onChange:t=>onChange(t,l,u,c)})),n.appendChild(this.createWheel({label:i("분"),items:r,value:r.includes(l)?l:r[0],onChange:t=>onChange(d,t,u,c)})),this.showSeconds&&n.appendChild(this.createWheel({label:i("초"),items:h,value:u,onChange:t=>onChange(d,l,t,c)})),this.is12h&&n.appendChild(this.createWheel({label:i("오전/오후"),items:["AM","PM"],value:c,onChange:t=>onChange(d,l,u,t),formatter:t=>i("AM"===t?"오전":"오후")})),e.appendChild(n),t)return e;const p=document.createElement("div");p.className=`${a}__dropdown-actions`;const m=document.createElement("div");m.className=`${a}__preview`,m.setAttribute("aria-live","polite"),m.appendChild(s("clock",16));const b=document.createElement("div");b.className=`${a}__preview-time`,b.textContent=this.formatTimeString(d,l,u,c),m.appendChild(b);const w=document.createElement("db-button");return w.setAttribute("variant","primary"),w.setAttribute("size","m"),w.textContent=i("선택"),w.addEventListener("click",()=>{this.handleTimeChange(d,l,u,c),this.closePicker()}),p.append(m,w),e.appendChild(p),e}render(){const e=this.normalizedSize,o=this.dropdownPosition,r=n(e);if(this.className=[a,`${a}--${e}`,this.hasAttribute("full-width")&&`${a}--full-width`,this.disabled&&`${a}--disabled`,this.readonly&&`${a}--readonly`,this.isOpen&&`${a}--open`,this.hasAttribute("error")&&`${a}--error`].filter(Boolean).join(" "),this.inputEl?.setAttribute("aria-invalid",String(this.hasAttribute("error"))),this.dropdownEl?.parentElement===document.body&&this.dropdownEl.remove(),this.replaceChildren(),!(this.labelEl&&this.triggerEl&&this.inputEl&&this.dropdownEl))return;const h=this.getAttribute("label")||"";this.labelEl.textContent=h,this.labelEl.hidden=!h,this.inputEl.value=this.inputText,this.inputEl.placeholder=this.is12h?this.showSeconds?"12:00:00 PM":"12:00 PM":this.showSeconds?"12:00:00":"12:00",this.inputEl.disabled=this.disabled,this.inputEl.readOnly=this.readonly;const d=this.triggerEl.querySelector(`.${a}__icon-display`);d&&(d.innerHTML="",d.appendChild(s("clock",r)),d.disabled=this.disabled);const l=this.triggerEl.querySelector(`.${a}__toggle`);if(l&&(l.innerHTML="",l.appendChild(s("chevron-down",r)),l.disabled=this.disabled),this.dropdownEl.className=[`${a}__dropdown`,`${a}__dropdown--${e}`,`${a}__dropdown--${o}`].filter(Boolean).join(" "),this.dropdownEl.setAttribute("aria-label",i("시간 선택")),this.dropdownEl.hidden=!this.isOpen,this.isOpen&&"dropdown"===this.type&&(this.dropdownEl.replaceChildren(this.renderSelector(!1)),t(this,this.dropdownEl),document.body.appendChild(this.dropdownEl),this.dropdownEl.hidden=!1,this.positionDropdown(),requestAnimationFrame(()=>this.positionDropdown())),this.append(this.labelEl,this.triggerEl),this.isOpen&&"modal"===this.type){const t=document.createElement("db-modal");t.setAttribute("open","true"),t.setAttribute("title",i("시간 선택")),t.setAttribute("size","s"),t.addEventListener("db-close",()=>this.closePicker());const e=this.renderSelector(!0),s=document.createElement("div");s.className=`${a}__modal-footer`;const n=document.createElement("db-button");n.setAttribute("variant","secondary"),n.setAttribute("size","m"),n.textContent=i("취소"),n.addEventListener("click",()=>this.closePicker());const o=document.createElement("db-button");o.setAttribute("variant","primary"),o.setAttribute("size","m"),o.textContent=i("적용"),o.addEventListener("click",()=>{this.handleTimeChange(this.tempHours,this.tempMinutes,this.tempSeconds,this.tempPeriod),this.closePicker()}),s.append(n,o),t.append(e,s),this.appendChild(t)}}}customElements.get("db-time-picker")||customElements.define("db-time-picker",DbTimePicker);export{DbTimePicker};
1
+ import"./db-modal.js";import{inheritThemeScope as t}from"../core/theme-scope.js";import{subscribeDbLocale as e,dbText as i}from"../core/i18n.js";import{createDbIcon as s,getDbComponentIconSize as n}from"./db-icons.js";import"./db-button.js";import{TIME_PICKER_FORMATS as o,TIME_PICKER_SIZES as r,TIME_PICKER_DROPDOWN_POSITIONS as h}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";import{coerceToContract as d}from"../node_modules/@designbasekorea/ui-contracts/dist/primitives.js";const a="designbase-wc-time-picker";class DbTimePicker extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.isOpen=!1,this.hours=12,this.minutes=0,this.seconds=0,this.period="PM",this.inputText="",this.tempHours=12,this.tempMinutes=0,this.tempSeconds=0,this.tempPeriod="PM",this.boundDocumentMouseDown=t=>this.handleDocumentMouseDown(t),this.boundViewportChange=()=>this.handleViewportChange(),this.viewportChangeFrame=null,this.handleDocumentKeyDown=t=>{"Escape"===t.key&&this.isOpen&&(t.preventDefault(),this.closePicker(),this.inputEl?.focus())},this.boundDocumentKeyDown=t=>this.handleDocumentKeyDown(t)}static get observedAttributes(){return["value","default-value","type","format","minute-step","size","disabled","readonly","label","full-width","error","dropdown-position","open"]}connectedCallback(){this.built||this.build(),this.unsubscribeLocale?.(),this.unsubscribeLocale=e(()=>this.syncLocale()),this.parseTimeString(this.getAttribute("value")??this.getAttribute("default-value")??"12:00"),this.syncInputText(),this.render(),document.addEventListener("mousedown",this.boundDocumentMouseDown),document.addEventListener("keydown",this.boundDocumentKeyDown),window.addEventListener("resize",this.boundViewportChange),window.addEventListener("scroll",this.boundViewportChange,{capture:!0,passive:!0})}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0,document.removeEventListener("mousedown",this.boundDocumentMouseDown),document.removeEventListener("keydown",this.boundDocumentKeyDown),window.removeEventListener("resize",this.boundViewportChange),window.removeEventListener("scroll",this.boundViewportChange,{capture:!0}),null!==this.viewportChangeFrame&&cancelAnimationFrame(this.viewportChangeFrame),this.viewportChangeFrame=null,this.dropdownEl?.remove()}syncLocale(){if(!this.built)return;if(this.inputEl?.setAttribute("aria-label",i("시간 입력")),this.triggerEl?.querySelectorAll(`.${a}__icon-display, .${a}__toggle`).forEach(t=>t.setAttribute("aria-label",i("시간 선택 열기"))),this.is12h&&this.inputEl&&document.activeElement!==this.inputEl){const t=`${this.pad2(this.hours)}:${this.pad2(this.minutes)}${this.showSeconds?`:${this.pad2(this.seconds)}`:""}`;("AM"===this.period?["오전","AM"]:["오후","PM"]).some(e=>this.inputText===`${e} ${t}`)&&this.syncInputText()}this.dropdownEl?.setAttribute("aria-label",i("시간 선택"));const t=this.querySelector("db-modal");t&&t.setAttribute("title",i("시간 선택"));const e=[this.dropdownEl,t].filter(t=>!!t);for(const t of e){const e=["시간","분",...this.showSeconds?["초"]:[],...this.is12h?["오전/오후"]:[]];t.querySelectorAll(`.${a}__wheel-container`).forEach((t,s)=>{const n=e[s];if(!n)return;const o=t.querySelector(`.${a}__wheel-label`);o&&(o.textContent=i(n)),t.querySelector('[role="listbox"]')?.setAttribute("aria-label",i(n)),"오전/오후"===n&&t.querySelectorAll('[role="option"]').forEach((t,e)=>{t.textContent=i(0===e?"오전":"오후")})});const s=t.querySelector(`.${a}__preview-time`);s&&(s.textContent=this.formatTimeString(this.hours,this.minutes,this.seconds,this.period));const n=t.querySelector(`.${a}__dropdown-actions db-button`);n&&(n.textContent=i("선택"));const o=t.querySelectorAll(`.${a}__modal-footer db-button`);o[0]&&(o[0].textContent=i("취소")),o[1]&&(o[1].textContent=i("적용"))}}attributeChangedCallback(t){if(this.built){if("value"===t){const t=this.getAttribute("value");t&&(this.parseTimeString(t),this.syncInputText())}if("open"===t)return this.isOpen=this.hasAttribute("open"),void this.render();this.render()}}get value(){return this.getAttribute("value")||""}set value(t){t?this.setAttribute("value",t):this.removeAttribute("value")}get format(){const t=this.getAttribute("format")||"24h";return o.includes(t)?t:"24h"}set format(t){this.setAttribute("format",t)}get type(){return"modal"===this.getAttribute("type")?"modal":"dropdown"}get is12h(){return"12h"===this.format||"12h-with-seconds"===this.format}get showSeconds(){return"12h-with-seconds"===this.format||"24h-with-seconds"===this.format}get minuteStep(){const t=Number(this.getAttribute("minute-step")||"1");return Number.isFinite(t)&&t>0?t:1}get normalizedSize(){return d(this.getAttribute("size"),r,"m")}get dropdownPosition(){return d(this.getAttribute("dropdown-position"),h,"auto")}get itemHeight(){return"s"===this.normalizedSize?32:"l"===this.normalizedSize?40:28}get disabled(){return this.hasAttribute("disabled")}set disabled(t){this.toggleAttribute("disabled",Boolean(t))}get readonly(){return this.hasAttribute("readonly")}set readonly(t){this.toggleAttribute("readonly",Boolean(t))}build(){this.built=!0,this.labelEl=document.createElement("label"),this.labelEl.className=`${a}__label`,this.triggerEl=document.createElement("div"),this.triggerEl.className=`${a}__trigger`;const t=document.createElement("button");t.type="button",t.className=`${a}__icon-display`,t.setAttribute("aria-label",i("시간 선택 열기")),t.addEventListener("click",()=>this.toggleOpen()),this.inputEl=document.createElement("input"),this.inputEl.type="text",this.inputEl.className=`${a}__input`,this.inputEl.setAttribute("aria-label",i("시간 입력")),this.inputEl.addEventListener("click",()=>this.openPicker()),this.inputEl.addEventListener("blur",()=>this.handleInputBlur()),this.inputEl.addEventListener("input",()=>{this.inputText=this.inputEl?.value||""});const e=document.createElement("button");e.type="button",e.className=`${a}__toggle`,e.setAttribute("aria-label",i("시간 선택 열기")),e.addEventListener("click",()=>this.toggleOpen()),this.triggerEl.append(t,this.inputEl,e),this.dropdownEl=document.createElement("div"),this.dropdownEl.className=`${a}__dropdown`,this.dropdownEl.setAttribute("role","dialog"),this.dropdownEl.setAttribute("aria-modal","false")}pad2(t){return String(t).padStart(2,"0")}to24Hour(t,e){return"AM"===e?12===t?0:t:12===t?12:t+12}formatTimeString(t,e,s,n){const o=n||this.period,r=i("AM"===o?"오전":"오후");return this.is12h?`${r} ${this.pad2(t)}:${this.pad2(e)}${this.showSeconds?`:${this.pad2(s)}`:""}`:`${this.pad2(t)}:${this.pad2(e)}${this.showSeconds?`:${this.pad2(s)}`:""}`}toValue24(t,e,i,s){const n=this.is12h?this.to24Hour(t,s||this.period):t;return`${this.pad2(n)}:${this.pad2(e)}${this.showSeconds?`:${this.pad2(i)}`:""}`}parseTimeString(t){const e=t.trim();if(this.is12h){const t=this.showSeconds?/^(오전|오후|AM|PM)?\s*(0?[1-9]|1[0-2]):([0-5]\d):([0-5]\d)\s*(AM|PM|오전|오후)?$/i:/^(오전|오후|AM|PM)?\s*(0?[1-9]|1[0-2]):([0-5]\d)\s*(AM|PM|오전|오후)?$/i,i=e.match(t);if(i){const t=i[1],e=this.showSeconds?i[5]:i[4],s=(t||e||"AM").toUpperCase();return this.hours=parseInt(i[2],10),this.minutes=parseInt(i[3],10),this.seconds=this.showSeconds?parseInt(i[4],10):0,void(this.period="오후"===s||"PM"===s?"PM":"AM")}}else{const t=this.showSeconds?/^([01]?\d|2[0-3]):([0-5]\d):([0-5]\d)$/:/^([01]?\d|2[0-3]):([0-5]\d)$/,i=e.match(t);i&&(this.hours=parseInt(i[1],10),this.minutes=parseInt(i[2],10),this.seconds=this.showSeconds?parseInt(i[3],10):0)}}syncInputText(){this.inputText=this.formatTimeString(this.hours,this.minutes,this.seconds),this.inputEl&&(this.inputEl.value=this.inputText)}emitChange(t){this.setAttribute("value",t),this.dispatchEvent(new CustomEvent("db-change",{detail:{value:t},bubbles:!0,composed:!0}))}handleTimeChange(t,e,i=this.seconds,s){this.hours=t,this.minutes=e,this.seconds=i,s&&(this.period=s);const n=this.formatTimeString(t,e,i,s);this.inputText=n,this.inputEl&&(this.inputEl.value=n),this.emitChange(this.toValue24(t,e,i,s))}handleInputBlur(){if(!this.inputEl)return;const t=this.inputText;this.parseTimeString(this.inputEl.value);this.formatTimeString(this.hours,this.minutes,this.seconds)!==t||this.inputEl.value.trim()?this.handleTimeChange(this.hours,this.minutes,this.seconds,this.period):this.syncInputText()}openPicker(){this.disabled||this.readonly||(this.isOpen=!0,"modal"===this.type&&(this.tempHours=this.hours,this.tempMinutes=this.minutes,this.tempSeconds=this.seconds,this.tempPeriod=this.period),this.render())}closePicker(){this.isOpen=!1,this.render()}toggleOpen(){this.isOpen?this.closePicker():this.openPicker()}handleDocumentMouseDown(t){if(!this.isOpen)return;const e=t.target;this.contains(e)||this.dropdownEl?.contains(e)||this.closePicker()}handleViewportChange(){this.isOpen&&"dropdown"===this.type&&null===this.viewportChangeFrame&&(this.viewportChangeFrame=requestAnimationFrame(()=>{this.viewportChangeFrame=null,this.isOpen&&"dropdown"===this.type&&this.positionDropdown()}))}positionDropdown(){if(!this.isOpen||"dropdown"!==this.type||!this.triggerEl||!this.dropdownEl||this.dropdownEl.hidden)return;const t=this.dropdownEl,e=this.triggerEl.getBoundingClientRect();t.style.width="";const i=t.getBoundingClientRect(),s=Math.max(0,window.innerWidth-16),n=Math.min(i.width,s);t.style.width=`${Math.round(n)}px`;const o=this.dropdownPosition,r=e.bottom+4+i.height<=window.innerHeight-8,h=e.top-4-i.height>=8,d="auto"===o?r||!h?"bottom":"top":o.startsWith("top")?"top":"bottom",a="auto"===o?e.left+n>window.innerWidth-8?"end":"start":o.endsWith("end")?"end":"start",l="end"===a?e.right-n:e.left,u=Math.max(8,Math.min(l,window.innerWidth-n-8)),c="top"===d?e.top-i.height-4:e.bottom+4,p=Math.max(8,Math.min(c,window.innerHeight-i.height-8));t.style.left=`${Math.round(u)}px`,t.style.top=`${Math.round(p)}px`,t.dataset.placement=`${d}-${a}`}createWheel(t){const{label:e,items:i,value:s,onChange:n,formatter:o}=t,r=document.createElement("div");r.className=`${a}__wheel-container`;const h=document.createElement("div");h.className=`${a}__wheel-label`,h.textContent=e,r.appendChild(h);const d=document.createElement("div");d.className=`${a}__wheel-viewport`;const l=document.createElement("div");l.className=`${a}__wheel`,l.setAttribute("role","listbox"),l.setAttribute("aria-label",e);let u=null,c=!1,p=!1,m=0,b=0;const indexOfValue=t=>i.findIndex(e=>e===t),scrollToIndex=(t,e=!0)=>{const i=t*this.itemHeight;c=!0,l.scrollTo({top:i,behavior:e?"smooth":"auto"}),window.setTimeout(()=>{c=!1},150)},fmt=t=>o?o(t):"number"==typeof t?this.pad2(t):String(t);i.forEach(t=>{const e=document.createElement("div");e.className=[`${a}__wheel-item`,t===s&&`${a}__wheel-item--selected`].filter(Boolean).join(" "),e.setAttribute("role","option"),e.setAttribute("aria-selected",String(t===s)),e.textContent=fmt(t),e.addEventListener("click",()=>{const e=indexOfValue(t);scrollToIndex(e,!0),t!==s&&n(t)}),l.appendChild(e)});const handleScroll=()=>{c||(u&&window.clearTimeout(u),u=window.setTimeout(()=>{const t=l.scrollTop,e=Math.round(t/this.itemHeight),o=Math.max(0,Math.min(i.length-1,e)),r=i[o];scrollToIndex(o,!0),r!==s&&n(r)},120))};l.addEventListener("scroll",handleScroll,{passive:!0}),l.addEventListener("pointerdown",t=>{p=!0,m=t.clientY,b=l.scrollTop,l.setPointerCapture(t.pointerId),l.style.cursor="grabbing"}),l.addEventListener("pointermove",t=>{p&&(l.scrollTop=b-(t.clientY-m))});const endDrag=t=>{p&&(p=!1,l.releasePointerCapture(t.pointerId),l.style.cursor="grab",handleScroll())};l.addEventListener("pointerup",endDrag),l.addEventListener("pointercancel",endDrag);const w=indexOfValue(s);w>=0&&requestAnimationFrame(()=>scrollToIndex(w,!1));const g=document.createElement("div");g.className=`${a}__wheel-center-indicator`,g.setAttribute("aria-hidden","true");const E=document.createElement("div");E.className=`${a}__wheel-fade wheel-fade--top`,E.setAttribute("aria-hidden","true");const v=document.createElement("div");return v.className=`${a}__wheel-fade wheel-fade--bottom`,v.setAttribute("aria-hidden","true"),d.append(l,g,E,v),r.appendChild(d),r}renderSelector(t=!1){const e=document.createElement("div");e.className=`${a}__selector`;const n=document.createElement("div");n.className=`${a}__picker-wheels`;const o=this.is12h?Array.from({length:12},(t,e)=>e+1):Array.from({length:24},(t,e)=>e),r=Array.from({length:Math.floor(60/this.minuteStep)},(t,e)=>e*this.minuteStep),h=Array.from({length:60},(t,e)=>e),d=t?this.tempHours:this.hours,l=t?this.tempMinutes:this.minutes,u=t?this.tempSeconds:this.seconds,c=t?this.tempPeriod:this.period,onChange=(e,i,s,n)=>{if(t)return this.tempHours=e,this.tempMinutes=i,this.tempSeconds=s,void(this.tempPeriod=n);this.handleTimeChange(e,i,s,n)};if(n.appendChild(this.createWheel({label:i("시간"),items:o,value:d,onChange:t=>onChange(t,l,u,c)})),n.appendChild(this.createWheel({label:i("분"),items:r,value:r.includes(l)?l:r[0],onChange:t=>onChange(d,t,u,c)})),this.showSeconds&&n.appendChild(this.createWheel({label:i("초"),items:h,value:u,onChange:t=>onChange(d,l,t,c)})),this.is12h&&n.appendChild(this.createWheel({label:i("오전/오후"),items:["AM","PM"],value:c,onChange:t=>onChange(d,l,u,t),formatter:t=>i("AM"===t?"오전":"오후")})),e.appendChild(n),t)return e;const p=document.createElement("div");p.className=`${a}__dropdown-actions`;const m=document.createElement("div");m.className=`${a}__preview`,m.setAttribute("aria-live","polite"),m.appendChild(s("clock",16));const b=document.createElement("div");b.className=`${a}__preview-time`,b.textContent=this.formatTimeString(d,l,u,c),m.appendChild(b);const w=document.createElement("db-button");return w.setAttribute("variant","primary"),w.setAttribute("size","m"),w.textContent=i("선택"),w.addEventListener("click",()=>{this.handleTimeChange(d,l,u,c),this.closePicker()}),p.append(m,w),e.appendChild(p),e}render(){const e=this.normalizedSize,o=this.dropdownPosition,r=n(e);if(this.className=[a,`${a}--${e}`,this.hasAttribute("full-width")&&`${a}--full-width`,this.disabled&&`${a}--disabled`,this.readonly&&`${a}--readonly`,this.isOpen&&`${a}--open`,this.hasAttribute("error")&&`${a}--error`].filter(Boolean).join(" "),this.inputEl?.setAttribute("aria-invalid",String(this.hasAttribute("error"))),this.dropdownEl?.parentElement===document.body&&this.dropdownEl.remove(),this.replaceChildren(),!(this.labelEl&&this.triggerEl&&this.inputEl&&this.dropdownEl))return;const h=this.getAttribute("label")||"";this.labelEl.textContent=h,this.labelEl.hidden=!h,this.inputEl.value=this.inputText,this.inputEl.placeholder=this.is12h?this.showSeconds?"12:00:00 PM":"12:00 PM":this.showSeconds?"12:00:00":"12:00",this.inputEl.disabled=this.disabled,this.inputEl.readOnly=this.readonly;const d=this.triggerEl.querySelector(`.${a}__icon-display`);d&&(d.innerHTML="",d.appendChild(s("clock",r)),d.disabled=this.disabled);const l=this.triggerEl.querySelector(`.${a}__toggle`);if(l&&(l.innerHTML="",l.appendChild(s("chevron-down",r)),l.disabled=this.disabled),this.dropdownEl.className=[`${a}__dropdown`,`${a}__dropdown--${e}`,`${a}__dropdown--${o}`].filter(Boolean).join(" "),this.dropdownEl.setAttribute("aria-label",i("시간 선택")),this.dropdownEl.hidden=!this.isOpen,this.isOpen&&"dropdown"===this.type&&(this.dropdownEl.replaceChildren(this.renderSelector(!1)),t(this,this.dropdownEl),document.body.appendChild(this.dropdownEl),this.dropdownEl.hidden=!1,this.positionDropdown(),requestAnimationFrame(()=>this.positionDropdown())),this.append(this.labelEl,this.triggerEl),this.isOpen&&"modal"===this.type){const t=document.createElement("db-modal");t.setAttribute("open","true"),t.setAttribute("title",i("시간 선택")),t.setAttribute("size","s"),t.addEventListener("db-close",()=>this.closePicker());const e=this.renderSelector(!0),s=document.createElement("div");s.className=`${a}__modal-footer`;const n=document.createElement("db-button");n.setAttribute("variant","secondary"),n.setAttribute("size","m"),n.textContent=i("취소"),n.addEventListener("click",()=>this.closePicker());const o=document.createElement("db-button");o.setAttribute("variant","primary"),o.setAttribute("size","m"),o.textContent=i("적용"),o.addEventListener("click",()=>{this.handleTimeChange(this.tempHours,this.tempMinutes,this.tempSeconds,this.tempPeriod),this.closePicker()}),s.append(n,o),t.append(e,s),this.appendChild(t)}}}customElements.get("db-time-picker")||customElements.define("db-time-picker",DbTimePicker);export{DbTimePicker};
@@ -1,3 +1,6 @@
1
+ import './db-button.js';
2
+ import './db-badge.js';
3
+
1
4
  declare class DbTopBanner extends HTMLElement {
2
5
  private unsubscribeLocale?;
3
6
  static get observedAttributes(): string[];
@@ -1 +1 @@
1
- import{subscribeDbLocale as e,dbText as t}from"../core/i18n.js";import{createDbIcon as s}from"./db-icons.js";import{TOP_BANNER_VARIANTS as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbTopBanner extends HTMLElement{static get observedAttributes(){return["badge-text","content","image","link-text","variant","fixed","dismissed"]}connectedCallback(){this.render(),this.unsubscribeLocale?.(),this.unsubscribeLocale=e(()=>this.querySelector(".designbase-wc-top-banner__close")?.setAttribute("aria-label",t("닫기")))}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0}attributeChangedCallback(){this.render()}isDarkBg(e){return"primary"===e||"dark"===e}createLinkButton(e,t){const n=this.isDarkBg(e),a=document.createElement("db-button");a.setAttribute("variant",n?"secondary":"ghost"),a.setAttribute("size","s"),a.classList.add("designbase-wc-top-banner__link");const i=document.createElement("span");i.textContent=t;const r=document.createElement("span");return r.className="designbase-wc-button__end-icon",r.append(s("chevron-right",16)),a.append(i,r),a.addEventListener("db-click",()=>{this.dispatchEvent(new CustomEvent("db-link",{bubbles:!0,composed:!0}))}),a}createCloseButton(){const e=document.createElement("db-button");return e.setAttribute("variant","ghost"),e.setAttribute("size","m"),e.setAttribute("icon-only",""),e.setAttribute("aria-label",t("닫기")),e.classList.add("designbase-wc-top-banner__close"),e.append(s("close",20)),e.addEventListener("db-click",()=>{this.setAttribute("dismissed",""),this.dispatchEvent(new CustomEvent("db-close",{bubbles:!0,composed:!0})),this.render()}),e}createBadge(e,t){const s=document.createElement("db-badge");return s.setAttribute("size","s"),s.setAttribute("variant",t?"secondary":"primary"),s.setAttribute("type","text"),s.textContent=e,s.classList.add("designbase-wc-top-banner__badge"),s}render(){if(this.hasAttribute("dismissed"))return this.replaceChildren(),this.className="designbase-wc-top-banner designbase-wc-top-banner--dismissed",void(this.style.display="none");this.style.display="";const e=this.getAttribute("badge-text")||"",t=this.getAttribute("content")||"",s=this.getAttribute("image")||"",a=this.getAttribute("link-text")||"",i=this.getAttribute("variant")||"primary",r=n.includes(i)?i:"primary",c=this.hasAttribute("fixed"),d=this.isDarkBg(r);if(this.className=["designbase-wc-top-banner",`designbase-wc-top-banner--${r}`,c&&"designbase-wc-top-banner--fixed",s&&"designbase-wc-top-banner--has-image"].filter(Boolean).join(" "),this.setAttribute("role","alert"),s?this.style.setProperty("--db-top-banner-bg-image",`url("${s}")`):this.style.removeProperty("--db-top-banner-bg-image"),this.replaceChildren(),s){const e=document.createElement("div");e.className="designbase-wc-top-banner__background",e.setAttribute("aria-hidden","true");const t=document.createElement("div");t.className="designbase-wc-top-banner__overlay",t.setAttribute("aria-hidden","true"),this.append(e,t)}const o=document.createElement("div");o.className="designbase-wc-top-banner__container";const b=document.createElement("div");b.className="designbase-wc-top-banner__content",e&&b.append(this.createBadge(e,d));const l=document.createElement("span");l.className="designbase-wc-top-banner__text",l.textContent=t,b.append(l),a&&b.append(this.createLinkButton(r,a)),o.append(b),o.append(this.createCloseButton()),this.append(o)}}customElements.get("db-top-banner")||customElements.define("db-top-banner",DbTopBanner);export{DbTopBanner};
1
+ import"./db-button.js";import"./db-badge.js";import{subscribeDbLocale as e,dbText as t}from"../core/i18n.js";import{createDbIcon as s}from"./db-icons.js";import{TOP_BANNER_VARIANTS as n}from"../node_modules/@designbasekorea/ui-contracts/dist/components.js";class DbTopBanner extends HTMLElement{static get observedAttributes(){return["badge-text","content","image","link-text","variant","fixed","dismissed"]}connectedCallback(){this.render(),this.unsubscribeLocale?.(),this.unsubscribeLocale=e(()=>this.querySelector(".designbase-wc-top-banner__close")?.setAttribute("aria-label",t("닫기")))}disconnectedCallback(){this.unsubscribeLocale?.(),this.unsubscribeLocale=void 0}attributeChangedCallback(){this.render()}isDarkBg(e){return"primary"===e||"dark"===e}createLinkButton(e,t){const n=this.isDarkBg(e),a=document.createElement("db-button");a.setAttribute("variant",n?"secondary":"ghost"),a.setAttribute("size","s"),a.classList.add("designbase-wc-top-banner__link");const i=document.createElement("span");i.textContent=t;const r=document.createElement("span");return r.className="designbase-wc-button__end-icon",r.append(s("chevron-right",16)),a.append(i,r),a.addEventListener("db-click",()=>{this.dispatchEvent(new CustomEvent("db-link",{bubbles:!0,composed:!0}))}),a}createCloseButton(){const e=document.createElement("db-button");return e.setAttribute("variant","ghost"),e.setAttribute("size","m"),e.setAttribute("icon-only",""),e.setAttribute("aria-label",t("닫기")),e.classList.add("designbase-wc-top-banner__close"),e.append(s("close",20)),e.addEventListener("db-click",()=>{this.setAttribute("dismissed",""),this.dispatchEvent(new CustomEvent("db-close",{bubbles:!0,composed:!0})),this.render()}),e}createBadge(e,t){const s=document.createElement("db-badge");return s.setAttribute("size","s"),s.setAttribute("variant",t?"secondary":"primary"),s.setAttribute("type","text"),s.textContent=e,s.classList.add("designbase-wc-top-banner__badge"),s}render(){if(this.hasAttribute("dismissed"))return this.replaceChildren(),this.className="designbase-wc-top-banner designbase-wc-top-banner--dismissed",void(this.style.display="none");this.style.display="";const e=this.getAttribute("badge-text")||"",t=this.getAttribute("content")||"",s=this.getAttribute("image")||"",a=this.getAttribute("link-text")||"",i=this.getAttribute("variant")||"primary",r=n.includes(i)?i:"primary",d=this.hasAttribute("fixed"),o=this.isDarkBg(r);if(this.className=["designbase-wc-top-banner",`designbase-wc-top-banner--${r}`,d&&"designbase-wc-top-banner--fixed",s&&"designbase-wc-top-banner--has-image"].filter(Boolean).join(" "),this.setAttribute("role","alert"),s?this.style.setProperty("--db-top-banner-bg-image",`url("${s}")`):this.style.removeProperty("--db-top-banner-bg-image"),this.replaceChildren(),s){const e=document.createElement("div");e.className="designbase-wc-top-banner__background",e.setAttribute("aria-hidden","true");const t=document.createElement("div");t.className="designbase-wc-top-banner__overlay",t.setAttribute("aria-hidden","true"),this.append(e,t)}const c=document.createElement("div");c.className="designbase-wc-top-banner__container";const b=document.createElement("div");b.className="designbase-wc-top-banner__content",e&&b.append(this.createBadge(e,o));const l=document.createElement("span");l.className="designbase-wc-top-banner__text",l.textContent=t,b.append(l),a&&b.append(this.createLinkButton(r,a)),c.append(b),c.append(this.createCloseButton()),this.append(c)}}customElements.get("db-top-banner")||customElements.define("db-top-banner",DbTopBanner);export{DbTopBanner};
@@ -1,3 +1,6 @@
1
+ import './db-button.js';
2
+ import './db-indicator.js';
3
+
1
4
  type TutorialStep = {
2
5
  id: string;
3
6
  target: string;
@@ -1 +1 @@
1
- import{inheritThemeScope as t}from"../core/theme-scope.js";import{dbText as e}from"../core/i18n.js";import{createDbIcon as i}from"./db-icons.js";import{createFocusTrap as s}from"../core/focus-trap.js";const n="designbase-wc-tutorial";class DbTutorial extends HTMLElement{constructor(){super(...arguments),this.internalStep=0,this.targetElement=null,this.keyHandler=null,this.resizeHandler=null,this.isMounted=!1}static get observedAttributes(){return["open","steps","current-step","indicator-type","indicator-size"]}connectedCallback(){this.style.display="contents",this.syncStepFromAttribute(),this.syncOpenState()}disconnectedCallback(){this.detachListeners(),this.clearTargetHighlight(),this.unmount()}attributeChangedCallback(t){this.isConnected&&("open"!==t?("current-step"===t&&this.syncStepFromAttribute(),this.isMounted&&this.render()):this.syncOpenState())}get steps(){return this.parseSteps()}set steps(t){this.setAttribute("steps","string"==typeof t?t:JSON.stringify(t))}parseSteps(){try{const t=JSON.parse(this.getAttribute("steps")||"[]");return Array.isArray(t)?t:[]}catch{return[]}}syncStepFromAttribute(){if(!this.hasAttribute("current-step"))return;const t=Number(this.getAttribute("current-step")||0);Number.isFinite(t)&&(this.internalStep=Math.max(0,t))}isOpenRequested(){return this.hasAttribute("open")&&"false"!==this.getAttribute("open")}getCurrentStepData(){const t=this.parseSteps();if(!t.length)return;const e=Math.min(Math.max(this.internalStep,0),t.length-1);return{step:t[e],index:e,steps:t}}syncOpenState(){if(this.isOpenRequested())return this.mount(),void this.attachListeners();this.detachListeners(),this.clearTargetHighlight(),this.unmount()}mount(){this.root||(this.root=document.createElement("div"),this.root.className=n,this.root.setAttribute("role","dialog"),this.root.setAttribute("aria-modal","true"),this.root.tabIndex=-1,this.ensureStructure()),this.root.isConnected||(t(this,this.root),document.body.append(this.root)),this.isMounted=!0,this.render(),this.popoverEl&&!this.focusTrap&&(this.focusTrap=s(this.popoverEl),requestAnimationFrame(()=>this.focusTrap?.activate()))}unmount(){this.isMounted=!1,this.focusTrap?.deactivate(),this.focusTrap=void 0,this.root?.remove()}ensureStructure(){if(!this.root||this.popoverEl)return;this.overlayEl=document.createElement("div"),this.overlayEl.className=`${n}__overlay`,this.overlayEl.addEventListener("click",()=>this.handleEnd()),this.popoverEl=document.createElement("div"),this.popoverEl.className=`${n}__popover`,this.closeButtonEl=document.createElement("button"),this.closeButtonEl.type="button",this.closeButtonEl.className=`${n}__close-button`,this.closeButtonEl.setAttribute("aria-label",e("튜토리얼 종료")),this.closeButtonEl.append(i("close",16)),this.closeButtonEl.addEventListener("click",()=>this.handleEnd());const t=document.createElement("div");t.className=`${n}__content`;const s=document.createElement("div");s.className=`${n}__text`,this.titleEl=document.createElement("h3"),this.titleEl.id="tutorial-title",this.titleEl.className=`${n}__title`,this.descriptionEl=document.createElement("p"),this.descriptionEl.id="tutorial-content",this.descriptionEl.className=`${n}__description`,s.append(this.titleEl,this.descriptionEl);const r=document.createElement("div");r.className=`${n}__navigation`,this.prevButtonEl=document.createElement("db-button"),this.prevButtonEl.setAttribute("variant","secondary"),this.prevButtonEl.setAttribute("size","s"),this.prevButtonEl.setAttribute("start-icon","chevron-left"),this.prevButtonEl.textContent=e("이전"),this.prevButtonEl.addEventListener("db-click",()=>this.handlePrev()),this.indicatorEl=document.createElement("db-indicator"),this.indicatorEl.addEventListener("db-step-click",t=>{const e=t.detail;"number"==typeof e.index&&this.goToStep(e.index)}),this.nextButtonEl=document.createElement("db-button"),this.nextButtonEl.setAttribute("variant","primary"),this.nextButtonEl.setAttribute("size","s"),this.nextButtonEl.addEventListener("db-click",()=>this.handleNext()),r.append(this.prevButtonEl,this.indicatorEl,this.nextButtonEl),t.append(s,r),this.popoverEl.append(this.closeButtonEl,t),this.root.append(this.overlayEl,this.popoverEl)}attachListeners(){this.keyHandler||(this.keyHandler=t=>{"Escape"===t.key?this.handleEnd():"ArrowLeft"===t.key?this.handlePrev():"ArrowRight"===t.key&&this.handleNext()},document.addEventListener("keydown",this.keyHandler)),this.resizeHandler||(this.resizeHandler=()=>this.updateTargetElement(),window.addEventListener("resize",this.resizeHandler))}detachListeners(){this.keyHandler&&(document.removeEventListener("keydown",this.keyHandler),this.keyHandler=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null)}clearTargetHighlight(){this.targetElement&&(this.targetElement.classList.remove(`${n}__target`),this.targetElement.style.position="",this.targetElement.style.zIndex="",this.targetElement=null)}updateTargetElement(){const t=this.getCurrentStepData();if(!t?.step.target||!this.popoverEl)return;this.clearTargetHighlight();const e=document.querySelector(t.step.target);if(!e)return;this.targetElement=e,e.style.position="relative",e.style.zIndex="1000",e.classList.add(`${n}__target`);const i=e.getBoundingClientRect(),s=window.innerWidth,r=window.innerHeight,o=300,h=200,a=16;let l="auto"!==t.step.placement&&t.step.placement?t.step.placement:"bottom",c=i.bottom+a,d=i.left+i.width/2-150;if("auto"===t.step.placement)i.top-h-a>0?(l="top",c=i.top-h-a):i.bottom+h+a<r?(l="bottom",c=i.bottom+a):i.left-o-a>0?(l="left",c=i.top+i.height/2-100,d=i.left-o-a):(l="right",c=i.top+i.height/2-100,d=i.right+a);else switch(l){case"top":c=i.top-h-a;break;case"bottom":c=i.bottom+a;break;case"left":c=i.top+i.height/2-100,d=i.left-o-a;break;case"right":c=i.top+i.height/2-100,d=i.right+a}d=Math.max(a,Math.min(d,s-o-a)),c=Math.max(a,Math.min(c,r-h-a)),this.popoverEl.className=[`${n}__popover`,`${n}__popover--${l}`].join(" "),this.popoverEl.style.top=`${c}px`,this.popoverEl.style.left=`${d}px`}emitStepChange(t){this.dispatchEvent(new CustomEvent("db-step-change",{bubbles:!0,composed:!0,detail:{step:t}}))}goToStep(t){const e=this.parseSteps();e.length&&(this.clearTargetHighlight(),this.internalStep=Math.min(Math.max(t,0),e.length-1),this.setAttribute("current-step",String(this.internalStep)),this.emitStepChange(this.internalStep),this.render())}handlePrev(){const t=this.getCurrentStepData();!t||t.index<=0||(this.dispatchEvent(new CustomEvent("db-prev",{bubbles:!0,composed:!0})),this.goToStep(t.index-1))}handleNext(){const t=this.getCurrentStepData();t&&(t.index>=t.steps.length-1?this.handleEnd():(this.dispatchEvent(new CustomEvent("db-next",{bubbles:!0,composed:!0})),this.goToStep(t.index+1)))}handleEnd(){this.dispatchEvent(new CustomEvent("db-end",{bubbles:!0,composed:!0})),this.setAttribute("open","false")}render(){const t=this.getCurrentStepData();if(!t||!this.root)return;const{step:e,index:i,steps:s}=t,n=0===i,r=i===s.length-1;this.titleEl&&(this.titleEl.textContent=e.title||"",this.titleEl.hidden=!e.title),this.descriptionEl&&(this.descriptionEl.textContent=e.content||"",this.descriptionEl.hidden=!e.content),this.prevButtonEl&&(this.prevButtonEl.hidden=n),this.nextButtonEl&&(this.nextButtonEl.textContent=r?"완료":"다음",r?this.nextButtonEl.removeAttribute("end-icon"):this.nextButtonEl.setAttribute("end-icon","chevron-right")),this.indicatorEl&&(this.indicatorEl.setAttribute("current",String(i)),this.indicatorEl.setAttribute("total",String(s.length)),this.indicatorEl.setAttribute("type",this.getAttribute("indicator-type")||"numbers"),this.indicatorEl.setAttribute("size",this.getAttribute("indicator-size")||"m")),this.updateTargetElement()}}customElements.get("db-tutorial")||customElements.define("db-tutorial",DbTutorial);export{DbTutorial};
1
+ import"./db-button.js";import"./db-indicator.js";import{inheritThemeScope as t}from"../core/theme-scope.js";import{dbText as e}from"../core/i18n.js";import{createDbIcon as i}from"./db-icons.js";import{createFocusTrap as s}from"../core/focus-trap.js";const n="designbase-wc-tutorial";class DbTutorial extends HTMLElement{constructor(){super(...arguments),this.internalStep=0,this.targetElement=null,this.keyHandler=null,this.resizeHandler=null,this.isMounted=!1}static get observedAttributes(){return["open","steps","current-step","indicator-type","indicator-size"]}connectedCallback(){this.style.display="contents",this.syncStepFromAttribute(),this.syncOpenState()}disconnectedCallback(){this.detachListeners(),this.clearTargetHighlight(),this.unmount()}attributeChangedCallback(t){this.isConnected&&("open"!==t?("current-step"===t&&this.syncStepFromAttribute(),this.isMounted&&this.render()):this.syncOpenState())}get steps(){return this.parseSteps()}set steps(t){this.setAttribute("steps","string"==typeof t?t:JSON.stringify(t))}parseSteps(){try{const t=JSON.parse(this.getAttribute("steps")||"[]");return Array.isArray(t)?t:[]}catch{return[]}}syncStepFromAttribute(){if(!this.hasAttribute("current-step"))return;const t=Number(this.getAttribute("current-step")||0);Number.isFinite(t)&&(this.internalStep=Math.max(0,t))}isOpenRequested(){return this.hasAttribute("open")&&"false"!==this.getAttribute("open")}getCurrentStepData(){const t=this.parseSteps();if(!t.length)return;const e=Math.min(Math.max(this.internalStep,0),t.length-1);return{step:t[e],index:e,steps:t}}syncOpenState(){if(this.isOpenRequested())return this.mount(),void this.attachListeners();this.detachListeners(),this.clearTargetHighlight(),this.unmount()}mount(){this.root||(this.root=document.createElement("div"),this.root.className=n,this.root.setAttribute("role","dialog"),this.root.setAttribute("aria-modal","true"),this.root.tabIndex=-1,this.ensureStructure()),this.root.isConnected||(t(this,this.root),document.body.append(this.root)),this.isMounted=!0,this.render(),this.popoverEl&&!this.focusTrap&&(this.focusTrap=s(this.popoverEl),requestAnimationFrame(()=>this.focusTrap?.activate()))}unmount(){this.isMounted=!1,this.focusTrap?.deactivate(),this.focusTrap=void 0,this.root?.remove()}ensureStructure(){if(!this.root||this.popoverEl)return;this.overlayEl=document.createElement("div"),this.overlayEl.className=`${n}__overlay`,this.overlayEl.addEventListener("click",()=>this.handleEnd()),this.popoverEl=document.createElement("div"),this.popoverEl.className=`${n}__popover`,this.closeButtonEl=document.createElement("button"),this.closeButtonEl.type="button",this.closeButtonEl.className=`${n}__close-button`,this.closeButtonEl.setAttribute("aria-label",e("튜토리얼 종료")),this.closeButtonEl.append(i("close",16)),this.closeButtonEl.addEventListener("click",()=>this.handleEnd());const t=document.createElement("div");t.className=`${n}__content`;const s=document.createElement("div");s.className=`${n}__text`,this.titleEl=document.createElement("h3"),this.titleEl.id="tutorial-title",this.titleEl.className=`${n}__title`,this.descriptionEl=document.createElement("p"),this.descriptionEl.id="tutorial-content",this.descriptionEl.className=`${n}__description`,s.append(this.titleEl,this.descriptionEl);const r=document.createElement("div");r.className=`${n}__navigation`,this.prevButtonEl=document.createElement("db-button"),this.prevButtonEl.setAttribute("variant","secondary"),this.prevButtonEl.setAttribute("size","s"),this.prevButtonEl.setAttribute("start-icon","chevron-left"),this.prevButtonEl.textContent=e("이전"),this.prevButtonEl.addEventListener("db-click",()=>this.handlePrev()),this.indicatorEl=document.createElement("db-indicator"),this.indicatorEl.addEventListener("db-step-click",t=>{const e=t.detail;"number"==typeof e.index&&this.goToStep(e.index)}),this.nextButtonEl=document.createElement("db-button"),this.nextButtonEl.setAttribute("variant","primary"),this.nextButtonEl.setAttribute("size","s"),this.nextButtonEl.addEventListener("db-click",()=>this.handleNext()),r.append(this.prevButtonEl,this.indicatorEl,this.nextButtonEl),t.append(s,r),this.popoverEl.append(this.closeButtonEl,t),this.root.append(this.overlayEl,this.popoverEl)}attachListeners(){this.keyHandler||(this.keyHandler=t=>{"Escape"===t.key?this.handleEnd():"ArrowLeft"===t.key?this.handlePrev():"ArrowRight"===t.key&&this.handleNext()},document.addEventListener("keydown",this.keyHandler)),this.resizeHandler||(this.resizeHandler=()=>this.updateTargetElement(),window.addEventListener("resize",this.resizeHandler))}detachListeners(){this.keyHandler&&(document.removeEventListener("keydown",this.keyHandler),this.keyHandler=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null)}clearTargetHighlight(){this.targetElement&&(this.targetElement.classList.remove(`${n}__target`),this.targetElement.style.position="",this.targetElement.style.zIndex="",this.targetElement=null)}updateTargetElement(){const t=this.getCurrentStepData();if(!t?.step.target||!this.popoverEl)return;this.clearTargetHighlight();const e=document.querySelector(t.step.target);if(!e)return;this.targetElement=e,e.style.position="relative",e.style.zIndex="1000",e.classList.add(`${n}__target`);const i=e.getBoundingClientRect(),s=window.innerWidth,r=window.innerHeight,o=300,a=200,h=16;let l="auto"!==t.step.placement&&t.step.placement?t.step.placement:"bottom",d=i.bottom+h,c=i.left+i.width/2-150;if("auto"===t.step.placement)i.top-a-h>0?(l="top",d=i.top-a-h):i.bottom+a+h<r?(l="bottom",d=i.bottom+h):i.left-o-h>0?(l="left",d=i.top+i.height/2-100,c=i.left-o-h):(l="right",d=i.top+i.height/2-100,c=i.right+h);else switch(l){case"top":d=i.top-a-h;break;case"bottom":d=i.bottom+h;break;case"left":d=i.top+i.height/2-100,c=i.left-o-h;break;case"right":d=i.top+i.height/2-100,c=i.right+h}c=Math.max(h,Math.min(c,s-o-h)),d=Math.max(h,Math.min(d,r-a-h)),this.popoverEl.className=[`${n}__popover`,`${n}__popover--${l}`].join(" "),this.popoverEl.style.top=`${d}px`,this.popoverEl.style.left=`${c}px`}emitStepChange(t){this.dispatchEvent(new CustomEvent("db-step-change",{bubbles:!0,composed:!0,detail:{step:t}}))}goToStep(t){const e=this.parseSteps();e.length&&(this.clearTargetHighlight(),this.internalStep=Math.min(Math.max(t,0),e.length-1),this.setAttribute("current-step",String(this.internalStep)),this.emitStepChange(this.internalStep),this.render())}handlePrev(){const t=this.getCurrentStepData();!t||t.index<=0||(this.dispatchEvent(new CustomEvent("db-prev",{bubbles:!0,composed:!0})),this.goToStep(t.index-1))}handleNext(){const t=this.getCurrentStepData();t&&(t.index>=t.steps.length-1?this.handleEnd():(this.dispatchEvent(new CustomEvent("db-next",{bubbles:!0,composed:!0})),this.goToStep(t.index+1)))}handleEnd(){this.dispatchEvent(new CustomEvent("db-end",{bubbles:!0,composed:!0})),this.setAttribute("open","false")}render(){const t=this.getCurrentStepData();if(!t||!this.root)return;const{step:e,index:i,steps:s}=t,n=0===i,r=i===s.length-1;this.titleEl&&(this.titleEl.textContent=e.title||"",this.titleEl.hidden=!e.title),this.descriptionEl&&(this.descriptionEl.textContent=e.content||"",this.descriptionEl.hidden=!e.content),this.prevButtonEl&&(this.prevButtonEl.hidden=n),this.nextButtonEl&&(this.nextButtonEl.textContent=r?"완료":"다음",r?this.nextButtonEl.removeAttribute("end-icon"):this.nextButtonEl.setAttribute("end-icon","chevron-right")),this.indicatorEl&&(this.indicatorEl.setAttribute("current",String(i)),this.indicatorEl.setAttribute("total",String(s.length)),this.indicatorEl.setAttribute("type",this.getAttribute("indicator-type")||"numbers"),this.indicatorEl.setAttribute("size",this.getAttribute("indicator-size")||"m")),this.updateTargetElement()}}customElements.get("db-tutorial")||customElements.define("db-tutorial",DbTutorial);export{DbTutorial};
@@ -1,14 +1,28 @@
1
1
  import './db-button.js';
2
2
  import { VideoTrack, VideoClip, TimelineMarker } from './media-editor-model.js';
3
- export { VideoClipEffect } from './media-editor-model.js';
3
+ export { VideoClipEffect, VideoEffectProperty } from './media-editor-model.js';
4
4
 
5
5
  interface TimelineEditDetail {
6
6
  clips: VideoClip[];
7
- action: 'move' | 'trim-start' | 'trim-end' | 'fade-in' | 'fade-out' | 'delete' | 'split';
7
+ action: 'undo' | 'redo' | 'keyframe' | 'move' | 'trim-start' | 'trim-end' | 'fade-in' | 'fade-out' | 'delete' | 'split';
8
8
  clipId: string;
9
+ effectId?: string;
10
+ propertyId?: string;
9
11
  }
10
12
  /** Non-overlapping tracks; optimistic drag feedback emits input, one change per committed transaction. */
11
13
  declare class DbVideoTimeline extends HTMLElement {
14
+ private effectTreeId;
15
+ private effectKeySelection;
16
+ private expandedEffects;
17
+ private activeProperty;
18
+ private effectEditing;
19
+ private keyMenu?;
20
+ private undoClips;
21
+ private redoClips;
22
+ private committedClips;
23
+ private selectedEffectKeys;
24
+ private marquee?;
25
+ private effectDrag?;
12
26
  static get observedAttributes(): string[];
13
27
  private _tracks;
14
28
  private _clips;
@@ -64,6 +78,7 @@ declare class DbVideoTimeline extends HTMLElement {
64
78
  setZoom(value: number): void;
65
79
  private node;
66
80
  private render;
81
+ private renderEffects;
67
82
  private select;
68
83
  private timeAt;
69
84
  private down;
@@ -71,6 +86,18 @@ declare class DbVideoTimeline extends HTMLElement {
71
86
  private snapped;
72
87
  private updateDrag;
73
88
  private autoScroll;
89
+ private recordTimelineHistory;
90
+ private restoreTimelineHistory;
91
+ private makeKeyCopyIds;
92
+ private copyKeyData;
93
+ private duplicateSelectedKeys;
94
+ private effectContextMenu;
95
+ private keyMenuSelect;
96
+ private effectToken;
97
+ private syncEffectSelection;
98
+ private finishMarquee;
99
+ private editSelectedKeys;
100
+ private finishEffectDrag;
74
101
  private finish;
75
102
  private up;
76
103
  private cancel;
@@ -84,6 +111,11 @@ declare class DbVideoTimeline extends HTMLElement {
84
111
  private wheel;
85
112
  private key;
86
113
  }
114
+ declare global {
115
+ interface HTMLElementTagNameMap {
116
+ 'db-video-timeline': DbVideoTimeline;
117
+ }
118
+ }
87
119
 
88
120
  export { DbVideoTimeline, TimelineMarker, VideoClip, VideoTrack };
89
121
  export type { TimelineEditDetail };
@@ -1 +1 @@
1
- import"./db-button.js";import{t,subscribeDbLocale as e}from"../core/i18n.js";import{clamp as i,emitEditor as s,editorEditable as r,finite as a,frameTime as d,editorButton as n,editorTimecode as h}from"./media-editor-model.js";class DbVideoTimeline extends HTMLElement{constructor(){super(...arguments),this._tracks=[],this._clips=[],this._markers=[],this.raf=0,this.down=t=>{if(this.drag||this.resizeDrag)return;if(this.disabled||0!==t.button||!(t.target instanceof Element)||t.target.closest("db-button"))return;const e=t.target.closest("[data-track-resize]"),i=t.target.closest("[data-pane-resize]");if(e||i){t.preventDefault(),this.viewport?.focus({preventScroll:!0});const i=this._tracks.find(t=>t.id===e?.dataset.trackResize);return this.resizeDrag={id:t.pointerId,trackId:i?.id,startY:t.clientY,initial:i?this.trackHeight(i):this.viewport.getBoundingClientRect().height,beforeTracks:this.tracks,beforeHeight:this.height},void this.setPointerCapture(t.pointerId)}const s=t.target.closest("[data-marker]");if(s)return void this.seek(Number(s.dataset.marker));const r=t.target.closest("[data-clip]"),a=this._clips.find(t=>t.id===r?.dataset.clip);if(a){if(this.select(a.id),this._tracks.find(t=>t.id===a.trackId)?.locked)return;this.viewport?.focus({preventScroll:!0})}else if(!t.target.closest("[data-seek],[data-playhead]"))return;if("touch"===t.pointerType&&!a&&!t.target.closest(".db-vt__ruler,[data-playhead]"))return;t.preventDefault();const d=t.target.closest("[data-trim]")?.dataset.trim,n=t.target.closest("[data-fade]")?.dataset.fade;this.drag={kind:a?"in"===n?"fade-in":"out"===n?"fade-out":"start"===d?"trim-start":"end"===d?"trim-end":"move":"seek",id:t.pointerId,clipId:a?.id,originX:t.clientX,originY:t.clientY,scroll:this.viewport.scrollLeft,before:this.clips,x:t.clientX,y:t.clientY,changed:!1,shift:t.shiftKey,ripple:t.metaKey||t.ctrlKey,duplicate:a&&!d&&!n&&t.altKey?`clip-${crypto.randomUUID()}`:void 0},this.setPointerCapture(t.pointerId),a||this.seek(this.timeAt(t.clientX)),this.raf=requestAnimationFrame(this.autoScroll)},this.move=t=>{if(this.resizeDrag?.id===t.pointerId){const e=this.resizeDrag,r=Math.round(e.initial+t.clientY-e.startY);return void(e.trackId?(this._tracks=e.beforeTracks.map(t=>t.id===e.trackId?{...t,height:i(r,32,120)}:t),this.render(),s(this,"db-track-input",{tracks:this.tracks})):(this.height=i(r,120,720),s(this,"db-resize-input",{height:this.height})))}this.drag?.id===t.pointerId&&(this.drag.x=t.clientX,this.drag.y=t.clientY,this.drag.shift=t.shiftKey,this.drag.ripple=t.metaKey||t.ctrlKey,"move"===this.drag.kind&&t.altKey&&!this.drag.duplicate&&(this.drag.duplicate=`clip-${crypto.randomUUID()}`),this.updateDrag())},this.autoScroll=()=>{if(!this.drag||!this.viewport)return;const t=this.viewport.getBoundingClientRect(),e=this.drag.x,s=e>t.right-48?i((e-t.right+48)/3,0,18):e<t.left+144+32?-i((t.left+176-e)/3,0,18):0,r=this.viewport.scrollLeft;this.viewport.scrollLeft+=s,r!==this.viewport.scrollLeft&&this.updateDrag(),this.raf=requestAnimationFrame(this.autoScroll)},this.up=t=>{this.resizeDrag?.id!==t.pointerId?this.drag?.id===t.pointerId&&(this.drag.changed&&(this.drag.x=t.clientX,this.drag.y=t.clientY,this.updateDrag()),this.finish()):this.finishResize()},this.cancel=()=>{this.finish(!0),this.finishResize(!0)},this.onActionClick=t=>{const e=t.target,i=e.dataset.action;if(!this.disabled&&("scroll"===i&&this.viewport?.scrollBy({left:Math.max(160,this.viewport.clientWidth-144),behavior:matchMedia("(prefers-reduced-motion: reduce)").matches?"instant":"smooth"}),"lock"===i||"mute"===i)){const t=e.dataset.track;this._tracks=this._tracks.map(e=>e.id===t?{...e,["lock"===i?"locked":"muted"]:!e["lock"===i?"locked":"muted"]}:e),this.render(),s(this,"db-track-change",{tracks:this.tracks})}},this.doubleResize=t=>{if(this.disabled||!(t.target instanceof Element))return;const e=t.target.closest("[data-track-resize],[data-pane-resize]");e&&(e.dataset.trackResize?(this._tracks=this._tracks.map(t=>t.id===e.dataset.trackResize?{...t,height:this.rowHeight}:t),this.render(),s(this,"db-track-change",{tracks:this.tracks})):(this.height=0,s(this,"db-resize",{height:0})))},this.wheel=t=>{if(this.disabled||!this.shortcuts||this.drag||this.resizeDrag||!t.metaKey&&!t.ctrlKey)return;t.preventDefault(),t.stopPropagation();const e=Math.exp(-t.deltaY*(1===t.deltaMode?.04:.002));this.changeZoom(this.zoom*e,t.clientX-this.viewport.getBoundingClientRect().left,"wheel")},this.key=t=>{if(this.disabled||r(t.target)||t.isComposing)return;if("Escape"===t.key&&this.drag)return t.preventDefault(),void this.finish(!0);if("Escape"===t.key&&this.resizeDrag)return t.preventDefault(),void this.finishResize(!0);const e=t.target.closest("[data-track-resize],[data-pane-resize]");if(e&&["ArrowUp","ArrowDown","Home"].includes(t.key)){t.preventDefault();const r="ArrowUp"===t.key?-1:1;return void(e.dataset.trackResize?(this._tracks=this._tracks.map(s=>s.id===e.dataset.trackResize?{...s,height:"Home"===t.key?this.rowHeight:i(this.trackHeight(s)+r*(t.shiftKey?8:2),32,120)}:s),this.render(),s(this,"db-track-change",{tracks:this.tracks}),this.querySelector(`[data-track-resize="${CSS.escape(e.dataset.trackResize)}"]`)?.focus()):(this.height="Home"===t.key?0:i((this.height||this.viewport.clientHeight)+r*(t.shiftKey?24:8),120,720),s(this,"db-resize",{height:this.height}),this.paneGrip?.focus()))}if(this.shortcuts&&!t.altKey&&["+","=","-","0"].includes(t.key))return t.preventDefault(),t.stopPropagation(),void("0"===t.key?this.changeZoom((this.viewport.clientWidth-144)/this.duration/.8,void 0,"fit"):this.changeZoom(this.zoom*("-"===t.key?1/1.2:1.2)));if(this.disabled||r(t.target)||t.ctrlKey||t.metaKey||t.altKey)return;const a=t.target.closest("[data-trim],[data-fade]");if(a&&["ArrowLeft","ArrowRight"].includes(t.key)){const e=a.closest("[data-clip]"),i=this._clips.find(t=>t.id===e?.dataset.clip);if(!i||this._tracks.find(t=>t.id===i.trackId)?.locked)return;t.preventDefault();const s="start"===a.dataset.trim?"trim-start":"end"===a.dataset.trim?"trim-end":"in"===a.dataset.fade?"fade-in":"fade-out";this.drag={kind:s,id:-1,clipId:i.id,originX:0,originY:0,scroll:this.viewport.scrollLeft,before:this.clips,x:("ArrowLeft"===t.key?-1:1)*(t.shiftKey?10:1)/this.fps*this.scale,y:0,changed:!1,shift:!0,ripple:t.metaKey||t.ctrlKey},this.updateDrag(),this.finish();const r=a.dataset.focus;return void Array.from(this.querySelectorAll("[data-focus]")).find(t=>t.dataset.focus===r)?.focus({preventScroll:!0})}if(["ArrowLeft","ArrowRight","Home","End"].includes(t.key))t.preventDefault(),this.seek("Home"===t.key?0:"End"===t.key?this.duration:this.currentTime+("ArrowLeft"===t.key?-1:1)*(t.shiftKey?10:1)/this.fps),this.scrollToTime(this.currentTime);else if("s"===t.key.toLowerCase())t.preventDefault(),this.splitSelected();else if("Delete"===t.key||"Backspace"===t.key){const e=this._clips.find(t=>t.id===this.selectedClipId);if(!e||this._tracks.find(t=>t.id===e.trackId)?.locked)return;t.preventDefault(),this._clips=this._clips.filter(t=>t.id!==e.id),this.select(""),this.render(),s(this,"db-change",{clips:this.clips,clipId:e.id,action:"delete"})}else"Enter"===t.key&&t.target instanceof HTMLElement&&t.target.dataset.clip&&(t.preventDefault(),this.select(t.target.dataset.clip))}}static get observedAttributes(){return["duration","fps","current-time","zoom","snap","disabled","selected-clip-id","row-height","height","min-clip-duration","shortcuts"]}get rowHeight(){return i(this.number("row-height",42),32,120)}set rowHeight(t){this.setAttribute("row-height",String(t))}get height(){const t=this.number("height",0);return t>0?i(t,120,720):0}set height(t){t>0?this.setAttribute("height",String(t)):this.removeAttribute("height")}get minClipDuration(){return Math.min(this.duration,Math.max(1/this.fps,Math.ceil(this.number("min-clip-duration",.5)*this.fps)/this.fps))}set minClipDuration(t){this.setAttribute("min-clip-duration",String(t))}get shortcuts(){return"false"!==this.getAttribute("shortcuts")}set shortcuts(t){this.setAttribute("shortcuts",String(t))}trackHeight(t){return i(a(t.height,this.rowHeight),32,120)}number(t,e){const i=this.getAttribute(t);return i?.trim()&&Number.isFinite(Number(i))?Number(i):e}get duration(){return i(this.number("duration",30),1/this.fps,86400)}set duration(t){this.setAttribute("duration",String(t))}get fps(){return i(Math.round(this.number("fps",30)),1,120)}set fps(t){this.setAttribute("fps",String(t))}get zoom(){return i(this.number("zoom",100),10,800)}set zoom(t){this.setAttribute("zoom",String(t))}get currentTime(){return d(i(this.number("current-time",0),0,this.duration),this.fps)}set currentTime(t){this.setAttribute("current-time",String(t))}get snap(){return"false"!==this.getAttribute("snap")}set snap(t){this.setAttribute("snap",String(t))}get disabled(){return this.hasAttribute("disabled")}set disabled(t){this.toggleAttribute("disabled",t)}get selectedClipId(){return this.getAttribute("selected-clip-id")||""}set selectedClipId(t){this.setAttribute("selected-clip-id",t)}get tracks(){return this._tracks.map(t=>({...t}))}set tracks(t){const e=new Set;this._tracks=(Array.isArray(t)?t:[]).filter(t=>t&&"string"==typeof t.id&&!e.has(t.id)&&!!e.add(t.id)).map(t=>({...t,label:String(t.label||t.id),kind:["video","audio","title","mixed"].includes(t.kind)?t.kind:"video"})),this.render()}get clips(){return this._clips.map(t=>({...t,...t.effects?{effects:t.effects.map(t=>({...t}))}:{}}))}set clips(t){if(this.drag)return;const e=new Set;this._clips=(Array.isArray(t)?t:[]).filter(t=>t&&"string"==typeof t.id&&!e.has(t.id)&&!!e.add(t.id)).map(t=>{const e=d(i(a(t.start,0),0,this.duration-1/this.fps),this.fps),s=Math.min(this.duration-e,Math.max(1/this.fps,d(a(t.duration,1),this.fps))),r=d(i(a(t.fadeIn,0),0,s),this.fps),n=d(i(a(t.fadeOut,0),0,Math.max(0,s-r)),this.fps);return{...t,label:String(t.label||t.id),start:e,duration:s,sourceOffset:Math.max(0,a(t.sourceOffset,0)),...void 0!==t.fadeIn?{fadeIn:r}:{},...void 0!==t.fadeOut?{fadeOut:n}:{},...t.effects?{effects:Array.isArray(t.effects)?t.effects.filter(t=>t&&"string"==typeof t.id).map(t=>({...t,label:String(t.label||t.id)})):[]}:{}}}),this.render()}get markers(){return this._markers.map(t=>({...t}))}set markers(t){this._markers=(Array.isArray(t)?t:[]).filter(t=>t&&"string"==typeof t.id&&Number.isFinite(t.time)).map(t=>({...t,label:String(t.label),time:i(t.time,0,this.duration)})),this.render()}get scale(){return.8*this.zoom}connectedCallback(){if(this.classList.add("designbase-wc-video-timeline"),this.setAttribute("role","region"),!this.viewport){this.viewport=document.createElement("div"),this.viewport.className="db-vt__viewport",this.viewport.tabIndex=0,this.canvas=document.createElement("div"),this.canvas.className="db-vt__canvas",this.viewport.append(this.canvas),this.hint=document.createElement("div"),this.hint.className="db-vt__hint";const e=n(t("media.more"),"chevron-right","scroll");this.hint.append(e),this.paneGrip=document.createElement("div"),this.paneGrip.className="db-vt__pane-resize",this.paneGrip.dataset.paneResize="",this.paneGrip.tabIndex=0,this.paneGrip.setAttribute("role","separator"),this.paneGrip.setAttribute("aria-orientation","horizontal"),this.append(this.viewport,this.hint,this.paneGrip),this.viewport.addEventListener("scroll",()=>this.syncHint())}this.viewport.addEventListener("wheel",this.wheel,{passive:!1}),this.addEventListener("dblclick",this.doubleResize),this.addEventListener("pointerdown",this.down),this.addEventListener("pointermove",this.move),this.addEventListener("pointerup",this.up),this.addEventListener("pointercancel",this.cancel),this.addEventListener("lostpointercapture",this.cancel),this.addEventListener("keydown",this.key),this.addEventListener("db-click",this.onActionClick),this.unsubscribe=e(()=>this.render()),this.resize=new ResizeObserver(()=>this.syncHint()),this.resize.observe(this),this.render()}disconnectedCallback(){this.rollback(),this.resize?.disconnect(),this.unsubscribe?.(),this.viewport?.removeEventListener("wheel",this.wheel),this.removeEventListener("dblclick",this.doubleResize),this.finishResize(!0),this.removeEventListener("pointerdown",this.down),this.removeEventListener("pointermove",this.move),this.removeEventListener("pointerup",this.up),this.removeEventListener("pointercancel",this.cancel),this.removeEventListener("lostpointercapture",this.cancel),this.removeEventListener("keydown",this.key),this.removeEventListener("db-click",this.onActionClick)}attributeChangedCallback(t){"disabled"===t&&this.disabled&&(this.finish(!0),this.finishResize(!0)),"current-time"===t?this.syncCursor():this.render()}syncHint(){if(this.syncCursor(),this.viewport)for(const t of this.querySelectorAll("[data-marker]"))t.style.visibility=Number(t.dataset.marker)*this.scale<this.viewport.scrollLeft?"hidden":"";this.viewport&&this.hint&&(this.hint.hidden=this.viewport.scrollLeft+this.viewport.clientWidth>=this.viewport.scrollWidth-2)}syncCursor(){this.cursor&&(this.cursor.style.left=144+this.currentTime*this.scale+"px",this.cursor.style.visibility=this.currentTime*this.scale<(this.viewport?.scrollLeft||0)?"hidden":"",this.cursor.setAttribute("aria-valuenow",String(this.currentTime)),this.cursor.setAttribute("aria-valuetext",h(this.currentTime,this.fps)))}seek(t){this.currentTime=d(i(t,0,this.duration),this.fps),s(this,"db-seek",{time:this.currentTime})}scrollToTime(t){this.viewport&&(this.viewport.scrollLeft=Math.max(0,i(t,0,this.duration)*this.scale-(this.viewport.clientWidth-144)/2))}setZoom(t){const e=this.currentTime;this.zoom=t,this.scrollToTime(e)}node(t,e,i){const s=document.createElement(t);return s.className=e,void 0!==i&&(s.textContent=i),s}render(){if(!this.canvas||!this.viewport)return;this.querySelector(":scope > .db-vt__readout")?.remove(),this.viewport.style.height=this.height?`${this.height}px`:"",this.viewport.style.maxHeight=this.height?"720px":"360px",this.paneGrip&&(this.paneGrip.setAttribute("aria-label",t("media.resizeTimeline")),this.paneGrip.setAttribute("title",t("media.resizeTimelineHint")),this.paneGrip.setAttribute("aria-valuemin","120"),this.paneGrip.setAttribute("aria-valuemax","720"),this.paneGrip.setAttribute("aria-valuenow",String(this.height||this.viewport.clientHeight)));const e=this.hint?.querySelector("db-button");e?.setAttribute("aria-label",t("media.more")),e?.setAttribute("title",t("media.more"));const s=this.ownerDocument.activeElement?.dataset.focus,r=document.createDocumentFragment(),a=this.node("div","db-vt__ruler"),d=this.node("span","db-vt__corner",t("media.tracks"));a.append(d);const o=[1/this.fps,.1,.25,.5,1,2,5,10,30,60,300,600,3600].find(t=>t*this.scale>=64&&this.duration/t<=200)||3600;for(let t=0;t<=this.duration;t+=o){const e=this.node("span","db-vt__tick",h(t,this.fps));e.style.left=144+t*this.scale+"px",a.append(e)}a.dataset.seek="",r.append(a),this._tracks.forEach((e,s)=>{const a=this.node("div","db-vt__row");a.dataset.track=e.id,a.dataset.kind=e.kind,a.style.setProperty("--db-vt-track-height",`${this.trackHeight(e)}px`),a.dataset.condensed=String(this.trackHeight(e)<44),a.dataset.locked=String(!!e.locked),a.dataset.muted=String(!!e.muted);const d=this.node("div","db-vt__track");d.append(this.node("span","db-vt__track-name",e.label));for(const i of["lock","mute"]){const s=t("lock"===i?e.locked?"media.trackLocked":"media.trackUnlocked":e.muted?"media.soundOff":"media.soundOn"),r=n(`${s} · ${e.label}`,"lock"===i?e.locked?"lock":"unlock":e.muted?"mute":"volume-up",i);r.setAttribute("variant","ghost"),r.dataset.track=e.id,r.setAttribute("aria-pressed",String("lock"===i?!!e.locked:!!e.muted)),r.toggleAttribute("disabled",this.disabled),d.append(r)}a.append(d);const c=this.node("div","db-vt__lane");c.dataset.seek="",c.style.width=this.duration*this.scale+"px",c.style.setProperty("--tick",o*this.scale+"px"),this._clips.filter(t=>t.trackId===e.id).forEach(s=>{const r=this.node("div","db-vt__clip");r.dataset.clip=s.id,r.dataset.focus=s.id,r.dataset.selected=String(this.selectedClipId===s.id),r.setAttribute("role","group"),r.tabIndex=0,r.setAttribute("aria-label",`${s.label}, ${h(s.start,this.fps)}, ${t("media.duration")} ${h(s.duration,this.fps)}`),r.setAttribute("aria-disabled",String(this.disabled||!!e.locked));const a=this.selectedClipId===s.id;r.dataset.appearance=s.appearance||("audio"===(s.kind||e.kind)?"waveform":"standard"),r.dataset.narrow=String(s.duration*this.scale<100),r.dataset.editing=String(this.drag?.clipId===s.id),r.style.left=s.start*this.scale+"px",r.style.width=`${Math.max(24,s.duration*this.scale)}px`,r.dataset.hitProxy=String(s.duration*this.scale<24),r.style.setProperty("--db-vt-actual-width",s.duration*this.scale+"px");for(const i of["start","end"]){const d=this.node("span","db-vt__trim");d.dataset.trim=i,d.dataset.focus=`${s.id}-trim-${i}`,d.tabIndex=!a||this.disabled||e.locked?-1:0,d.setAttribute("role","slider"),d.setAttribute("aria-label",`${t("start"===i?"media.trimStart":"media.trimEnd")} · ${s.label}`),d.setAttribute("aria-valuemin","0"),d.setAttribute("aria-valuemax",String(this.duration)),d.setAttribute("aria-valuenow",String("start"===i?s.start:s.start+s.duration)),d.setAttribute("aria-valuetext",h("start"===i?s.start:s.start+s.duration,this.fps)),d.setAttribute("title",t("start"===i?"media.trimStart":"media.trimEnd")),d.append(this.node("i","db-vt__grip")),r.append(d)}if("filmstrip"===s.appearance&&s.thumbnail){const t=this.node("div","db-vt__filmstrip");t.setAttribute("aria-hidden","true");for(let e=0;e<Math.min(24,Math.ceil(s.duration*this.scale/64));e++){const e=document.createElement("img");e.src=s.thumbnail,e.alt="",e.draggable=!1,t.append(e)}r.append(t)}for(const d of["in","out"]){const n=i("in"===d?s.fadeIn||0:s.fadeOut||0,0,s.duration),h=this.node("span","db-vt__fade");if(h.dataset.fadeArea=d,h.style.width=n*this.scale+"px",h.setAttribute("aria-hidden","true"),r.append(h),a){const i=this.node("span","db-vt__fade-handle");i.dataset.fade=d,i.dataset.focus=`${s.id}-fade-${d}`,i.style["in"===d?"left":"right"]=n*this.scale+"px",i.tabIndex=this.disabled||e.locked?-1:0,i.setAttribute("role","slider"),i.setAttribute("aria-label",`${t("in"===d?"media.fadeIn":"media.fadeOut")} · ${s.label}`),i.setAttribute("aria-valuemin","0"),i.setAttribute("aria-valuemax",String(s.duration)),i.setAttribute("aria-valuenow",String(n)),i.setAttribute("title",t("in"===d?"media.fadeIn":"media.fadeOut")),r.append(i)}}if(r.append(this.node("span","db-vt__clip-label",s.label)),"audio"===(s.kind||e.kind)||"waveform"===s.appearance){const t=this.node("span","db-vt__wave");t.setAttribute("aria-hidden","true");for(let e=0;e<Math.min(160,Math.ceil(s.duration*this.scale/7));e++){const i=this.node("i","");i.style.height=20+75*Math.abs(Math.sin(1.7*e)*Math.cos(.4*e))+"%",t.append(i)}r.append(t)}if(s.effects?.length){const e=this.node("span","db-vt__effects");e.setAttribute("aria-label",t("media.effects"));for(const t of s.effects){const i=this.node("span","db-vt__effect",t.label);i.dataset.enabled=String(!1!==t.enabled),i.dataset.kind=t.kind,i.setAttribute("title",t.label),e.append(i)}r.append(e)}c.append(r)}),a.append(c);const l=this.node("div","db-vt__track-resize");l.dataset.trackResize=e.id,l.tabIndex=0,l.setAttribute("role","separator"),l.setAttribute("aria-orientation","horizontal"),l.setAttribute("aria-label",`${t("media.resizeTrack")} · ${e.label}`),l.setAttribute("aria-valuemin","32"),l.setAttribute("aria-valuemax","120"),l.setAttribute("aria-valuenow",String(this.trackHeight(e))),a.append(l),r.append(a)});for(const e of this._markers){const i=this.node("button","db-vt__marker");i.type="button",i.dataset.marker=String(e.time),i.style.left=144+e.time*this.scale+"px";const s=`${t("media.marker")} · ${e.label} · ${h(e.time,this.fps)}`;i.setAttribute("aria-label",s),i.setAttribute("title",s),r.append(i)}if(this.cursor=this.node("div","db-vt__playhead"),this.cursor.dataset.playhead="",this.cursor.tabIndex=0,this.cursor.setAttribute("role","slider"),this.cursor.dataset.focus="playhead",this.cursor.setAttribute("aria-label",t("media.playhead")),this.cursor.setAttribute("aria-valuemin","0"),this.cursor.setAttribute("aria-valuemax",String(this.duration)),this.cursor.setAttribute("aria-disabled",String(this.disabled)),r.append(this.cursor),this._tracks.length||r.append(this.node("p","db-vt__empty",t("media.emptyTracks"))),this.canvas.style.width=144+this.duration*this.scale+"px",this.drag?.clipId){const e=this._clips.find(t=>t.id===this.drag?.clipId);if(e){const i="fade-in"===this.drag.kind?e.fadeIn||0:"fade-out"===this.drag.kind?e.fadeOut||0:"trim-end"===this.drag.kind?e.start+e.duration:e.start,s=this.node("div","db-vt__readout",`${t("media."+("trim-start"===this.drag.kind?"trimStart":"trim-end"===this.drag.kind?"trimEnd":"fade-in"===this.drag.kind?"fadeIn":"fade-out"===this.drag.kind?"fadeOut":"time"))} ${h(i,this.fps)} / ${t("media.duration")} ${h(e.duration,this.fps)}`);this.append(s)}}this.canvas.replaceChildren(r),this.viewport.setAttribute("aria-label",t("media.timeline")),this.syncCursor(),this.syncHint(),s&&!this.drag&&Array.from(this.querySelectorAll("[data-focus]")).find(t=>t.dataset.focus===s)?.focus({preventScroll:!0})}select(t){this.selectedClipId!==t&&(this.selectedClipId=t,s(this,"db-selection-change",{clipId:t}))}timeAt(t){return(t-this.viewport.getBoundingClientRect().left+this.viewport.scrollLeft-144)/this.scale}snapped(t,e){let i=d(t,this.fps);if(this.snap&&!this.drag?.shift){const t=[0,this.currentTime,...this._markers.map(t=>t.time),...this._clips.filter(t=>t.id!==e).flatMap(t=>[t.start,t.start+t.duration])].reduce((t,e)=>Math.abs(e-i)<Math.abs(t-i)?e:t,1/0);Math.abs(t-i)*this.scale<=8&&(i=d(t,this.fps))}return i}updateDrag(){const t=this.drag;if(!t)return;if("seek"===t.kind)return void this.seek(this.timeAt(t.x));const e=t.before.find(e=>e.id===t.clipId);if(!e)return;const r=(t.x-t.originX+this.viewport.scrollLeft-t.scroll)/this.scale;let a=e.start,n=e.duration,h=e.sourceOffset||0,o=e.trackId;if("fade-in"===t.kind||"fade-out"===t.kind){const a="fade-in"===t.kind?"fadeIn":"fadeOut",n="fade-in"===t.kind?e.fadeOut||0:e.fadeIn||0,h=d(i((e[a]||0)+("fade-in"===t.kind?r:-r),0,Math.max(0,e.duration-n)),this.fps);return this._clips=t.before.map(t=>t.id===e.id?{...t,[a]:h}:t),t.changed=JSON.stringify(this._clips)!==JSON.stringify(t.before),this.render(),void s(this,"db-input",{clips:this.clips,clipId:e.id,action:t.kind})}if("move"===t.kind){a=this.snapped(a+r,e.id);const s=this.canvas.getBoundingClientRect(),d=this.trackIndexAt(t.y-s.top-36),h=this._tracks[i(d,0,this._tracks.length-1)],c=this._tracks.find(t=>t.id===e.trackId);!h||h.locked||"mixed"!==h.kind&&h.kind!==(e.kind||c?.kind)||(o=h.id),a=i(a,0,this.duration-n)}else if("trim-start"===t.kind)a=i(this.snapped(a+r,e.id),Math.max(0,e.start-h),e.start+n-Math.min(this.minClipDuration,e.start+n-Math.max(0,e.start-h))),n=e.start+e.duration-a,h+=a-e.start;else{const t=e.sourceDuration?Math.min(this.duration-a,e.sourceDuration-h):this.duration-a;n=i(this.snapped(a+n+r,e.id)-a,Math.min(this.minClipDuration,t),t)}const c=t.ripple&&("trim-start"===t.kind||"trim-end"===t.kind),l=t.before.filter(t=>t.id!==e.id&&t.trackId===e.trackId&&t.start>=e.start+e.duration-1e-6);let p=0;if(c){"trim-start"===t.kind&&(a=e.start);const i=Math.max(e.start+e.duration,...l.map(t=>t.start+t.duration));n=Math.min(n,e.duration+this.duration-i),p=d(n-e.duration,this.fps)}let u=t.before.map(t=>l.some(e=>e.id===t.id)&&c?{...t,start:d(t.start+p,this.fps)}:t);if("move"===t.kind&&t.ripple){const i=t.before.filter(i=>i.trackId===o&&(t.duplicate||i.id!==e.id)).sort((t,e)=>t.start-e.start),s=i.find(t=>a>t.start+1e-6&&a<t.start+t.duration-1e-6);s&&(a=s.start);let r=a+n;const h=new Map;for(const t of i){if(t.start<a-1e-6)continue;const e=Math.max(t.start,r);h.set(t.id,e),r=e+t.duration}if(r>this.duration+1e-6)return;u=t.before.map(t=>h.has(t.id)?{...t,start:d(h.get(t.id),this.fps)}:t)}if(u.some(i=>(t.duplicate||i.id!==e.id)&&i.trackId===o&&a<i.start+i.duration-1e-6&&a+n>i.start+1e-6))return;const f=t.duplicate||e.id;t.duplicate&&(u=[...u,{...e,id:f}]),this._clips=u.map(t=>t.id===f?{...t,kind:t.kind||("mixed"===this._tracks.find(t=>t.id===e.trackId)?.kind?"video":this._tracks.find(t=>t.id===e.trackId)?.kind),start:d(a,this.fps),duration:d(n,this.fps),sourceOffset:d(h,this.fps),trackId:o,...void 0!==t.fadeIn?{fadeIn:Math.min(t.fadeIn,n)}:{},...void 0!==t.fadeOut?{fadeOut:Math.min(t.fadeOut,Math.max(0,n-(t.fadeIn||0)))}:{}}:t),t.changed=JSON.stringify(this._clips)!==JSON.stringify(t.before),this.render(),s(this,"db-input",{clips:this.clips,clipId:e.id,action:t.kind})}finish(t=!1){const e=this.drag;e&&(cancelAnimationFrame(this.raf),this.drag=void 0,this.hasPointerCapture(e.id)&&this.releasePointerCapture(e.id),t?(this._clips=e.before,this.render(),s(this,"db-input",{clips:this.clips,clipId:e.clipId,action:e.kind,cancelled:!0})):e.changed&&e.clipId&&"seek"!==e.kind&&s(this,"db-change",{clips:this.clips,clipId:e.clipId,action:e.kind}),this.render(),this.viewport?.focus({preventScroll:!0}))}rollback(){this.finish(!0)}splitSelected(){const t=this._clips.find(t=>t.id===this.selectedClipId);if(this.disabled||!t||this._tracks.find(e=>e.id===t.trackId)?.locked||t.duration<2*this.minClipDuration||this.currentTime<t.start+this.minClipDuration||this.currentTime>t.start+t.duration-this.minClipDuration)return;const e=this.currentTime;let i=t.id+"-split";for(;this._clips.some(t=>t.id===i);)i+="-1";this._clips=this._clips.flatMap(s=>s.id===t.id?[{...s,duration:e-s.start,fadeOut:0,fadeIn:Math.min(s.fadeIn||0,e-s.start)},{...s,id:i,fadeIn:0,fadeOut:Math.min(s.fadeOut||0,s.start+s.duration-e),start:e,duration:s.start+s.duration-e,sourceOffset:(s.sourceOffset||0)+e-s.start}]:[s]),this.render(),s(this,"db-change",{clips:this.clips,action:"split",clipId:t.id})}trackIndexAt(t){let e=0;for(let i=0;i<this._tracks.length;i++)if(e+=this.trackHeight(this._tracks[i]),t<e)return i;return this._tracks.length-1}finishResize(t=!1){const e=this.resizeDrag;e&&(this.resizeDrag=void 0,this.hasPointerCapture(e.id)&&this.releasePointerCapture(e.id),t?(this._tracks=e.beforeTracks,this.height=e.beforeHeight,this.render(),s(this,e.trackId?"db-track-input":"db-resize-input",e.trackId?{tracks:this.tracks,cancelled:!0}:{height:this.height,cancelled:!0})):s(this,e.trackId?"db-track-change":"db-resize",e.trackId?{tracks:this.tracks}:{height:this.height}))}changeZoom(t,e,r="keyboard"){if(!this.viewport)return;const a=this.zoom,d=e??Math.min(this.viewport.clientWidth-8,Math.max(152,144+this.currentTime*this.scale-this.viewport.scrollLeft)),n=(this.viewport.scrollLeft+d-144)/this.scale;this.zoom=Math.round(100*i(t,10,800))/100,this.viewport.scrollLeft="fit"===r?0:Math.max(0,144+n*this.scale-d),this.zoom!==a&&s(this,"db-zoom-change",{value:this.zoom,previousValue:a,anchorTime:n,source:r})}}customElements.get("db-video-timeline")||customElements.define("db-video-timeline",DbVideoTimeline);export{DbVideoTimeline};
1
+ import"./db-button.js";import{DbContextMenu as t}from"./db-context-menu.js";import{DbKeyframeEditor as e}from"./db-keyframe-editor.js";import{t as i,subscribeDbLocale as s}from"../core/i18n.js";import{frameTime as r,emitEditor as a,clamp as d,editorEditable as n,finite as o,editorButton as c,evaluateKeyframes as h,editorTimecode as l,applyKeyframeEasing as f}from"./media-editor-model.js";class DbVideoTimeline extends HTMLElement{constructor(){super(...arguments),this.effectTreeId=`effects-${crypto.randomUUID()}`,this.effectKeySelection=new Map,this.expandedEffects=new Set,this.activeProperty=new Map,this.effectEditing=!1,this.undoClips=[],this.redoClips=[],this.committedClips=[],this.selectedEffectKeys=new Set,this._tracks=[],this._clips=[],this._markers=[],this.raf=0,this.down=t=>{if(this.drag||this.resizeDrag)return;if(this.disabled||0!==t.button||!(t.target instanceof Element)||t.target.closest("db-button,db-keyframe-editor,[data-effect-clip]"))return;const e=t.target.closest("[data-track-resize]"),i=t.target.closest("[data-pane-resize]");if(e||i){t.preventDefault(),this.viewport?.focus({preventScroll:!0});const i=this._tracks.find(t=>t.id===e?.dataset.trackResize);return this.resizeDrag={id:t.pointerId,trackId:i?.id,startY:t.clientY,initial:i?this.trackHeight(i):this.viewport.getBoundingClientRect().height,beforeTracks:this.tracks,beforeHeight:this.height},void this.setPointerCapture(t.pointerId)}const s=t.target.closest("[data-marker]");if(s)return void this.seek(Number(s.dataset.marker));const r=t.target.closest("[data-clip]"),a=this._clips.find(t=>t.id===r?.dataset.clip);if(a){if(this.selectedEffectKeys.clear(),this.select(a.id),this._tracks.find(t=>t.id===a.trackId)?.locked)return;this.viewport?.focus({preventScroll:!0})}else if(!t.target.closest("[data-seek],[data-playhead]"))return;if("touch"===t.pointerType&&!a&&!t.target.closest(".db-vt__ruler,[data-playhead]"))return;t.preventDefault();const d=t.target.closest("[data-trim]")?.dataset.trim,n=t.target.closest("[data-fade]")?.dataset.fade;this.drag={kind:a?"in"===n?"fade-in":"out"===n?"fade-out":"start"===d?"trim-start":"end"===d?"trim-end":"move":"seek",id:t.pointerId,clipId:a?.id,originX:t.clientX,originY:t.clientY,scroll:this.viewport.scrollLeft,before:this.clips,x:t.clientX,y:t.clientY,changed:!1,shift:t.shiftKey,ripple:t.metaKey||t.ctrlKey,duplicate:a&&!d&&!n&&t.altKey?`clip-${crypto.randomUUID()}`:void 0},this.setPointerCapture(t.pointerId),a||this.seek(this.timeAt(t.clientX)),this.raf=requestAnimationFrame(this.autoScroll)},this.move=t=>{if(this.marquee?.pointer===t.pointerId){const e=this.marquee,i=this.getBoundingClientRect(),s=Math.min(e.x,t.clientX),r=Math.min(e.y,t.clientY),a=Math.max(e.x,t.clientX),d=Math.max(e.y,t.clientY);Object.assign(e.box.style,{left:s-i.left+"px",top:r-i.top+"px",width:a-s+"px",height:d-r+"px"}),this.selectedEffectKeys=new Set(e.before);for(const t of this.querySelectorAll("[data-key-token]")){if(t.closest("[data-effect-clip]")?.dataset.effectClip!==e.clipId||t.hasAttribute("disabled"))continue;const i=t.getBoundingClientRect(),n=i.x+i.width/2,o=i.y+i.height/2;n>=s&&n<=a&&o>=r&&o<=d&&this.selectedEffectKeys.add(t.dataset.keyToken)}return void this.syncEffectSelection()}if(this.effectDrag?.pointer===t.pointerId){const e=this.effectDrag,i=e.before.find(t=>t.id===e.clipId);if(t.altKey&&!e.copies&&(e.copies=this.makeKeyCopyIds()),e.copies){const s=r((t.clientX-e.x)/this.scale,this.fps);if(0===s){this._clips=e.before,e.changed=!1;for(const t of this.querySelectorAll("[data-duplicate-preview]"))t.remove();return void this.syncEffectSelection()}const d=this.copyKeyData(e.before,e.clipId,s,e.copies);if(!d)return;this._clips=d,e.changed=!0;for(const[t,s]of e.copies){const[e,r,a]=JSON.parse(t),n=this.effectToken(e,r,a,s),o=Array.from(this.querySelectorAll("[data-key-token]")).find(e=>e.dataset.keyToken===t);if(!o)continue;let c=Array.from(this.querySelectorAll("[data-key-token]")).find(t=>t.dataset.keyToken===n);c||(c=o.cloneNode(!0),c.dataset.keyToken=n,c.dataset.propertyKey=s,c.dataset.duplicatePreview="true",o.parentElement.append(c));const h=d.find(t=>t.id===e)?.effects?.find(t=>t.id===r)?.properties?.find(t=>t.id===a)?.keyframes.find(t=>t.id===s);h&&(c.style.left=(i.start+h.time)*this.scale+"px"),c.setAttribute("aria-pressed","true"),o.setAttribute("aria-pressed","false")}return void a(this,"db-input",{clips:this.clips,clipId:e.clipId,action:"keyframe"})}let s=-1/0,n=1/0;for(const t of i.effects||[])for(const e of t.properties||[]){const r=[...e.keyframes].sort((t,e)=>t.time-e.time);r.forEach((a,d)=>{if(!this.selectedEffectKeys.has(this.effectToken(i.id,t.id,e.id,a.id)))return;const o=r.slice(0,d).reverse().find(s=>!this.selectedEffectKeys.has(this.effectToken(i.id,t.id,e.id,s.id))),c=r.slice(d+1).find(s=>!this.selectedEffectKeys.has(this.effectToken(i.id,t.id,e.id,s.id)));s=Math.max(s,(o?o.time+1/this.fps:0)-a.time),n=Math.min(n,(c?c.time-1/this.fps:i.duration)-a.time)})}const o=r(d((t.clientX-e.x)/this.scale,s,n),this.fps);this._clips=e.before.map(t=>t.id!==e.clipId?t:{...t,effects:t.effects?.map(e=>({...e,properties:e.properties?.map(i=>({...i,keyframes:i.keyframes.map(s=>this.selectedEffectKeys.has(this.effectToken(t.id,e.id,i.id,s.id))?{...s,time:r(s.time+o,this.fps)}:s)}))}))}),e.changed=0!==o;for(const t of this.querySelectorAll("[data-key-token]")){if(!this.selectedEffectKeys.has(t.dataset.keyToken))continue;const[e,i,s,r]=JSON.parse(t.dataset.keyToken),a=this._clips.find(t=>t.id===e),d=a?.effects?.find(t=>t.id===i)?.properties?.find(t=>t.id===s)?.keyframes.find(t=>t.id===r);a&&d&&(t.style.left=(a.start+d.time)*this.scale+"px")}return void a(this,"db-input",{clips:this.clips,clipId:e.clipId,action:"keyframe",effectId:e.effectId,propertyId:e.propertyId})}if(this.resizeDrag?.id===t.pointerId){const e=this.resizeDrag,i=Math.round(e.initial+t.clientY-e.startY);return void(e.trackId?(this._tracks=e.beforeTracks.map(t=>t.id===e.trackId?{...t,height:d(i,32,120)}:t),this.render(),a(this,"db-track-input",{tracks:this.tracks})):(this.height=d(i,120,720),a(this,"db-resize-input",{height:this.height})))}this.drag?.id===t.pointerId&&(this.drag.x=t.clientX,this.drag.y=t.clientY,this.drag.shift=t.shiftKey,this.drag.ripple=t.metaKey||t.ctrlKey,"move"===this.drag.kind&&t.altKey&&!this.drag.duplicate&&(this.drag.duplicate=`clip-${crypto.randomUUID()}`),this.updateDrag())},this.autoScroll=()=>{if(!this.drag||!this.viewport)return;const t=this.viewport.getBoundingClientRect(),e=this.drag.x,i=e>t.right-48?d((e-t.right+48)/3,0,18):e<t.left+144+32?-d((t.left+176-e)/3,0,18):0,s=this.viewport.scrollLeft;this.viewport.scrollLeft+=i,s!==this.viewport.scrollLeft&&this.updateDrag(),this.raf=requestAnimationFrame(this.autoScroll)},this.recordTimelineHistory=t=>{if(t.target!==this)return;const e=t.detail;e?.clips&&"undo"!==e.action&&"redo"!==e.action&&JSON.stringify(e.clips)!==JSON.stringify(this.committedClips)&&(this.undoClips.push(structuredClone(this.committedClips)),this.undoClips.length>100&&this.undoClips.shift(),this.redoClips=[],this.committedClips=structuredClone(e.clips))},this.effectContextMenu=e=>{const s=e.target,r=s.closest("[data-effect-clip]");if(!r)return;const a=this._clips.find(t=>t.id===r.dataset.effectClip);if(!a)return;e.preventDefault(),e.stopPropagation(),this.viewport?.focus({preventScroll:!0});const d=s.closest("[data-key-token]");d&&!this.selectedEffectKeys.has(d.dataset.keyToken)&&(this.selectedEffectKeys.clear(),this.selectedEffectKeys.add(d.dataset.keyToken),this.syncEffectSelection());const n=this.disabled||!!this._tracks.find(t=>t.id===a.trackId)?.locked||!this.selectedEffectKeys.size||!!d?.hasAttribute("disabled");this.keyMenu||(this.keyMenu=new t,this.append(this.keyMenu)),this.keyMenu.items=[{id:"duplicate",label:i("media.duplicateKeys"),icon:"copy",disabled:n},{id:"easing",label:i("media.keyEasing"),disabled:n,subItems:["linear","hold","ease-in","ease-out","ease-in-out"].map(t=>({id:`ease:${t}`,label:i("media."+t)}))},{id:"divider",divider:!0},{id:"delete",label:i("media.deleteKey"),icon:"trash",variant:"danger",disabled:n},{id:"undo",label:i("media.undo"),disabled:!this.undoClips.length}],this.keyMenu.oncontextmenu=t=>t.preventDefault(),this.keyMenu.addEventListener("db-select",this.keyMenuSelect),this.keyMenu.dataset.clipId=a.id,this.keyMenu.openAt(e.clientX,e.clientY)},this.keyMenuSelect=t=>{t.stopPropagation();const e=t.detail.item.id,i=this.keyMenu.dataset.clipId;"duplicate"===e?this.duplicateSelectedKeys(i):"delete"===e?this.editSelectedKeys(i):e.startsWith("ease:")?this.editSelectedKeys(i,e.slice(5)):"undo"===e&&this.restoreTimelineHistory()},this.up=t=>{this.marquee?.pointer!==t.pointerId?this.effectDrag?.pointer!==t.pointerId?this.resizeDrag?.id!==t.pointerId?this.drag?.id===t.pointerId&&(this.drag.changed&&(this.drag.x=t.clientX,this.drag.y=t.clientY,this.updateDrag()),this.finish()):this.finishResize():this.finishEffectDrag():this.finishMarquee()},this.cancel=t=>{[this.drag?.id,this.resizeDrag?.id,this.effectDrag?.pointer,this.marquee?.pointer].includes(t.pointerId)&&("lostpointercapture"!==t.type||t.target===this&&!this.hasPointerCapture(t.pointerId))&&(this.finishMarquee(!0),this.finishEffectDrag(!0),this.finish(!0),this.finishResize(!0))},this.onActionClick=t=>{const e=t.target,i=e.dataset.action;if(!this.disabled&&("scroll"===i&&this.viewport?.scrollBy({left:Math.max(160,this.viewport.clientWidth-144),behavior:matchMedia("(prefers-reduced-motion: reduce)").matches?"instant":"smooth"}),"lock"===i||"mute"===i)){const t=e.dataset.track;this._tracks=this._tracks.map(e=>e.id===t?{...e,["lock"===i?"locked":"muted"]:!e["lock"===i?"locked":"muted"]}:e),this.render(),a(this,"db-track-change",{tracks:this.tracks})}},this.doubleResize=t=>{if(this.disabled||!(t.target instanceof Element))return;const e=t.target.closest("[data-track-resize],[data-pane-resize]");e&&(e.dataset.trackResize?(this._tracks=this._tracks.map(t=>t.id===e.dataset.trackResize?{...t,height:this.rowHeight}:t),this.render(),a(this,"db-track-change",{tracks:this.tracks})):(this.height=0,a(this,"db-resize",{height:0})))},this.wheel=t=>{if(this.disabled||!this.shortcuts||this.drag||this.resizeDrag||!t.metaKey&&!t.ctrlKey)return;t.preventDefault(),t.stopPropagation();const e=Math.exp(-t.deltaY*(1===t.deltaMode?.04:.002));this.changeZoom(this.zoom*e,t.clientX-this.viewport.getBoundingClientRect().left,"wheel")},this.key=t=>{if(!this.disabled&&!n(t.target)&&(t.metaKey||t.ctrlKey)&&"z"===t.key.toLowerCase()&&!this.drag&&!this.effectDrag&&!this.effectEditing)return void((t.shiftKey?this.redoClips:this.undoClips).length&&(t.preventDefault(),t.stopPropagation(),this.restoreTimelineHistory(t.shiftKey)));if(!(this.disabled||n(t.target)||t.metaKey||t.ctrlKey||t.altKey||"u"!==t.key.toLowerCase())){const e=this._clips.find(t=>t.id===this.selectedClipId&&t.effects?.some(t=>t.properties?.length))||this._clips.find(t=>t.effects?.some(t=>t.properties?.length));if(e){t.preventDefault(),t.stopPropagation();const i=!this.expandedEffects.has(e.id);for(const t of[e.id,...(e.effects||[]).map(t=>`${e.id}/${t.id}`)])i?this.expandedEffects.add(t):this.expandedEffects.delete(t);this.render(),this.viewport?.focus({preventScroll:!0})}return}if("Escape"===t.key&&this.marquee)return t.preventDefault(),void this.finishMarquee(!0);if(!this.disabled&&!n(t.target)&&!t.target?.closest("db-keyframe-editor")&&["Delete","Backspace"].includes(t.key)&&this.selectedEffectKeys.size){t.preventDefault();const e=new Set(Array.from(this.selectedEffectKeys).map(t=>JSON.parse(t)[0]));for(const t of e)this.editSelectedKeys(t);return}if(this.disabled||n(t.target)||t.isComposing||t.target?.closest("db-keyframe-editor"))return;if("Escape"===t.key&&this.effectDrag)return t.preventDefault(),void this.finishEffectDrag(!0);if("Escape"===t.key&&this.drag)return t.preventDefault(),void this.finish(!0);if("Escape"===t.key&&this.resizeDrag)return t.preventDefault(),void this.finishResize(!0);const e=t.target.closest("[data-track-resize],[data-pane-resize]");if(e&&["ArrowUp","ArrowDown","Home"].includes(t.key)){t.preventDefault();const i="ArrowUp"===t.key?-1:1;return void(e.dataset.trackResize?(this._tracks=this._tracks.map(s=>s.id===e.dataset.trackResize?{...s,height:"Home"===t.key?this.rowHeight:d(this.trackHeight(s)+i*(t.shiftKey?8:2),32,120)}:s),this.render(),a(this,"db-track-change",{tracks:this.tracks}),this.querySelector(`[data-track-resize="${CSS.escape(e.dataset.trackResize)}"]`)?.focus()):(this.height="Home"===t.key?0:d((this.height||this.viewport.clientHeight)+i*(t.shiftKey?24:8),120,720),a(this,"db-resize",{height:this.height}),this.paneGrip?.focus()))}if(this.shortcuts&&!t.altKey&&["+","=","-","0"].includes(t.key))return t.preventDefault(),t.stopPropagation(),void("0"===t.key?this.changeZoom((this.viewport.clientWidth-144)/this.duration/.8,void 0,"fit"):this.changeZoom(this.zoom*("-"===t.key?1/1.2:1.2)));if(this.disabled||n(t.target)||t.ctrlKey||t.metaKey||t.altKey)return;const i=t.target.closest("[data-trim],[data-fade]");if(i&&["ArrowLeft","ArrowRight"].includes(t.key)){const e=i.closest("[data-clip]"),s=this._clips.find(t=>t.id===e?.dataset.clip);if(!s||this._tracks.find(t=>t.id===s.trackId)?.locked)return;t.preventDefault();const r="start"===i.dataset.trim?"trim-start":"end"===i.dataset.trim?"trim-end":"in"===i.dataset.fade?"fade-in":"fade-out";this.drag={kind:r,id:-1,clipId:s.id,originX:0,originY:0,scroll:this.viewport.scrollLeft,before:this.clips,x:("ArrowLeft"===t.key?-1:1)*(t.shiftKey?10:1)/this.fps*this.scale,y:0,changed:!1,shift:!0,ripple:t.metaKey||t.ctrlKey},this.updateDrag(),this.finish();const a=i.dataset.focus;return void Array.from(this.querySelectorAll("[data-focus]")).find(t=>t.dataset.focus===a)?.focus({preventScroll:!0})}if(["ArrowLeft","ArrowRight","Home","End"].includes(t.key))t.preventDefault(),this.seek("Home"===t.key?0:"End"===t.key?this.duration:this.currentTime+("ArrowLeft"===t.key?-1:1)*(t.shiftKey?10:1)/this.fps),this.scrollToTime(this.currentTime);else if("s"===t.key.toLowerCase())t.preventDefault(),this.splitSelected();else if("Delete"===t.key||"Backspace"===t.key){const e=this._clips.find(t=>t.id===this.selectedClipId);if(!e||this._tracks.find(t=>t.id===e.trackId)?.locked)return;t.preventDefault(),this._clips=this._clips.filter(t=>t.id!==e.id),this.select(""),this.render(),a(this,"db-change",{clips:this.clips,clipId:e.id,action:"delete"})}else"Enter"===t.key&&t.target instanceof HTMLElement&&t.target.dataset.clip&&(t.preventDefault(),this.select(t.target.dataset.clip))}}static get observedAttributes(){return["duration","fps","current-time","zoom","snap","disabled","selected-clip-id","row-height","height","min-clip-duration","shortcuts"]}get rowHeight(){return d(this.number("row-height",42),32,120)}set rowHeight(t){this.setAttribute("row-height",String(t))}get height(){const t=this.number("height",0);return t>0?d(t,120,720):0}set height(t){t>0?this.setAttribute("height",String(t)):this.removeAttribute("height")}get minClipDuration(){return Math.min(this.duration,Math.max(1/this.fps,Math.ceil(this.number("min-clip-duration",.5)*this.fps)/this.fps))}set minClipDuration(t){this.setAttribute("min-clip-duration",String(t))}get shortcuts(){return"false"!==this.getAttribute("shortcuts")}set shortcuts(t){this.setAttribute("shortcuts",String(t))}trackHeight(t){return d(o(t.height,this.rowHeight),32,120)}number(t,e){const i=this.getAttribute(t);return i?.trim()&&Number.isFinite(Number(i))?Number(i):e}get duration(){return d(this.number("duration",30),1/this.fps,86400)}set duration(t){this.setAttribute("duration",String(t))}get fps(){return d(Math.round(this.number("fps",30)),1,120)}set fps(t){this.setAttribute("fps",String(t))}get zoom(){return d(this.number("zoom",100),10,800)}set zoom(t){this.setAttribute("zoom",String(t))}get currentTime(){return r(d(this.number("current-time",0),0,this.duration),this.fps)}set currentTime(t){this.setAttribute("current-time",String(t))}get snap(){return"false"!==this.getAttribute("snap")}set snap(t){this.setAttribute("snap",String(t))}get disabled(){return this.hasAttribute("disabled")}set disabled(t){this.toggleAttribute("disabled",t)}get selectedClipId(){return this.getAttribute("selected-clip-id")||""}set selectedClipId(t){this.setAttribute("selected-clip-id",t)}get tracks(){return this._tracks.map(t=>({...t}))}set tracks(t){const e=new Set;this._tracks=(Array.isArray(t)?t:[]).filter(t=>t&&"string"==typeof t.id&&!e.has(t.id)&&!!e.add(t.id)).map(t=>({...t,label:String(t.label||t.id),kind:["video","audio","title","mixed"].includes(t.kind)?t.kind:"video"})),this.render()}get clips(){return this._clips.map(t=>({...t,...t.effects?{effects:t.effects.map(t=>structuredClone(t))}:{}}))}set clips(t){if(this.drag||this.effectEditing)return;const e=new Set;this._clips=(Array.isArray(t)?t:[]).filter(t=>t&&"string"==typeof t.id&&!e.has(t.id)&&!!e.add(t.id)).map(t=>{const e=r(d(o(t.start,0),0,this.duration-1/this.fps),this.fps),i=Math.min(this.duration-e,Math.max(1/this.fps,r(o(t.duration,1),this.fps))),s=r(d(o(t.fadeIn,0),0,i),this.fps),a=r(d(o(t.fadeOut,0),0,Math.max(0,i-s)),this.fps);return{...t,label:String(t.label||t.id),start:e,duration:i,sourceOffset:Math.max(0,o(t.sourceOffset,0)),...void 0!==t.fadeIn?{fadeIn:s}:{},...void 0!==t.fadeOut?{fadeOut:a}:{},...t.effects?{effects:Array.isArray(t.effects)?t.effects.filter(t=>t&&"string"==typeof t.id).map(t=>({...t,label:String(t.label||t.id)})):[]}:{}}}),JSON.stringify(this._clips)!==JSON.stringify(this.committedClips)&&(this.committedClips=this.clips,this.undoClips=[],this.redoClips=[]),this.render()}get markers(){return this._markers.map(t=>({...t}))}set markers(t){this._markers=(Array.isArray(t)?t:[]).filter(t=>t&&"string"==typeof t.id&&Number.isFinite(t.time)).map(t=>({...t,label:String(t.label),time:d(t.time,0,this.duration)})),this.render()}get scale(){return.8*this.zoom}connectedCallback(){if(this.classList.add("designbase-wc-video-timeline"),this.setAttribute("role","region"),!this.viewport){this.viewport=document.createElement("div"),this.viewport.className="db-vt__viewport",this.viewport.tabIndex=0,this.canvas=document.createElement("div"),this.canvas.className="db-vt__canvas",this.viewport.append(this.canvas),this.hint=document.createElement("div"),this.hint.className="db-vt__hint";const t=c(i("media.more"),"chevron-right","scroll");this.hint.append(t),this.paneGrip=document.createElement("div"),this.paneGrip.className="db-vt__pane-resize",this.paneGrip.dataset.paneResize="",this.paneGrip.tabIndex=0,this.paneGrip.setAttribute("role","separator"),this.paneGrip.setAttribute("aria-orientation","horizontal"),this.append(this.viewport,this.hint,this.paneGrip),this.viewport.addEventListener("scroll",()=>this.syncHint())}this.viewport.addEventListener("wheel",this.wheel,{passive:!1}),this.addEventListener("dblclick",this.doubleResize),this.addEventListener("db-change",this.recordTimelineHistory,!0),this.addEventListener("contextmenu",this.effectContextMenu),this.addEventListener("pointerdown",this.down),this.addEventListener("pointermove",this.move),this.addEventListener("pointerup",this.up),this.addEventListener("pointercancel",this.cancel),this.addEventListener("lostpointercapture",this.cancel),this.addEventListener("keydown",this.key),this.addEventListener("db-click",this.onActionClick),this.unsubscribe=s(()=>this.render()),this.resize=new ResizeObserver(()=>this.syncHint()),this.resize.observe(this),this.render()}disconnectedCallback(){this.rollback(),this.resize?.disconnect(),this.unsubscribe?.(),this.viewport?.removeEventListener("wheel",this.wheel),this.removeEventListener("dblclick",this.doubleResize),this.finishResize(!0),this.removeEventListener("db-change",this.recordTimelineHistory,!0),this.removeEventListener("contextmenu",this.effectContextMenu),this.keyMenu?.remove(),this.keyMenu=void 0,this.removeEventListener("pointerdown",this.down),this.removeEventListener("pointermove",this.move),this.removeEventListener("pointerup",this.up),this.removeEventListener("pointercancel",this.cancel),this.removeEventListener("lostpointercapture",this.cancel),this.removeEventListener("keydown",this.key),this.removeEventListener("db-click",this.onActionClick)}attributeChangedCallback(t){"disabled"===t&&this.disabled&&(this.finishEffectDrag(!0),this.finish(!0),this.finishResize(!0)),"current-time"===t?this.syncCursor():this.render()}syncHint(){for(const t of this.querySelectorAll(".db-vt__selection-tools"))t.style.width=`${this.viewport?.clientWidth||240}px`;if(this.syncCursor(),this.viewport)for(const t of this.querySelectorAll("[data-marker]"))t.style.visibility=Number(t.dataset.marker)*this.scale<this.viewport.scrollLeft?"hidden":"";this.viewport&&this.hint&&(this.hint.hidden=this.viewport.scrollLeft+this.viewport.clientWidth>=this.viewport.scrollWidth-2)}syncCursor(){for(const t of this.querySelectorAll("[data-effect-property]")){const e=this._clips.find(e=>e.id===t.closest("[data-effect-clip]")?.dataset.effectClip),i=e?.effects?.find(e=>e.id===t.dataset.effectId)?.properties?.find(e=>e.id===t.dataset.effectProperty),s=t.querySelector(".db-vt__property-value");e&&i&&s&&(s.textContent=String(Math.round(100*h(i.keyframes,this.currentTime-e.start))/100))}if(!this.effectEditing)for(const t of this.querySelectorAll("db-keyframe-editor[data-effect-editor]")){const e=this._clips.find(e=>e.id===t.closest("[data-effect-clip]")?.dataset.effectClip);e&&(t.currentTime=d(this.currentTime-e.start,0,e.duration))}this.cursor&&(this.cursor.style.left=144+this.currentTime*this.scale+"px",this.cursor.style.visibility=this.currentTime*this.scale<(this.viewport?.scrollLeft||0)?"hidden":"",this.cursor.setAttribute("aria-valuenow",String(this.currentTime)),this.cursor.setAttribute("aria-valuetext",l(this.currentTime,this.fps)))}seek(t){this.currentTime=r(d(t,0,this.duration),this.fps),a(this,"db-seek",{time:this.currentTime})}scrollToTime(t){this.viewport&&(this.viewport.scrollLeft=Math.max(0,d(t,0,this.duration)*this.scale-(this.viewport.clientWidth-144)/2))}setZoom(t){const e=this.currentTime;this.zoom=t,this.scrollToTime(e)}node(t,e,i){const s=document.createElement(t);return s.className=e,void 0!==i&&(s.textContent=i),s}render(){if(this.effectEditing)return;const t=new Set(this._clips.flatMap(t=>(t.effects||[]).flatMap(e=>(e.properties||[]).flatMap(i=>i.keyframes.map(s=>this.effectToken(t.id,e.id,i.id,s.id))))));if(this.selectedEffectKeys=new Set(Array.from(this.selectedEffectKeys).filter(e=>t.has(e))),!this.canvas||!this.viewport)return;this.querySelector(":scope > .db-vt__readout")?.remove(),this.viewport.style.height=this.height?`${this.height}px`:"",this.viewport.style.maxHeight=this.height?"720px":"360px",this.paneGrip&&(this.paneGrip.setAttribute("aria-label",i("media.resizeTimeline")),this.paneGrip.setAttribute("title",i("media.resizeTimelineHint")),this.paneGrip.setAttribute("aria-valuemin","120"),this.paneGrip.setAttribute("aria-valuemax","720"),this.paneGrip.setAttribute("aria-valuenow",String(this.height||this.viewport.clientHeight)));const e=this.hint?.querySelector("db-button");e?.setAttribute("aria-label",i("media.more")),e?.setAttribute("title",i("media.more"));const s=this.ownerDocument.activeElement?.dataset.focus,r=document.createDocumentFragment(),a=this.node("div","db-vt__ruler"),n=this.node("span","db-vt__corner",i("media.tracks"));a.append(n);const o=[1/this.fps,.1,.25,.5,1,2,5,10,30,60,300,600,3600].find(t=>t*this.scale>=64&&this.duration/t<=200)||3600;for(let t=0;t<=this.duration;t+=o){const e=this.node("span","db-vt__tick",l(t,this.fps));e.style.left=144+t*this.scale+"px",a.append(e)}a.dataset.seek="",r.append(a),this._tracks.forEach((t,e)=>{const s=this.node("div","db-vt__row");s.dataset.track=t.id,s.dataset.kind=t.kind,s.style.setProperty("--db-vt-track-height",`${this.trackHeight(t)}px`),s.dataset.condensed=String(this.trackHeight(t)<44),s.dataset.locked=String(!!t.locked),s.dataset.muted=String(!!t.muted);const a=this.node("div","db-vt__track");a.append(this.node("span","db-vt__track-name",t.label));for(const e of["lock","mute"]){const s=i("lock"===e?t.locked?"media.trackLocked":"media.trackUnlocked":t.muted?"media.soundOff":"media.soundOn"),r=c(`${s} · ${t.label}`,"lock"===e?t.locked?"lock":"unlock":t.muted?"mute":"volume-up",e);r.setAttribute("variant","ghost"),r.dataset.track=t.id,r.setAttribute("aria-pressed",String("lock"===e?!!t.locked:!!t.muted)),r.toggleAttribute("disabled",this.disabled),a.append(r)}s.append(a);const n=this.node("div","db-vt__lane");n.dataset.seek="",n.style.width=this.duration*this.scale+"px",n.style.setProperty("--tick",o*this.scale+"px"),this._clips.filter(e=>e.trackId===t.id).forEach(e=>{const s=this.node("div","db-vt__clip");s.dataset.clip=e.id,s.dataset.focus=e.id,s.dataset.selected=String(this.selectedClipId===e.id),s.setAttribute("role","group"),s.tabIndex=0,s.setAttribute("aria-label",`${e.label}, ${l(e.start,this.fps)}, ${i("media.duration")} ${l(e.duration,this.fps)}`),s.setAttribute("aria-disabled",String(this.disabled||!!t.locked));const r=this.selectedClipId===e.id;s.dataset.appearance=e.appearance||("audio"===(e.kind||t.kind)?"waveform":"standard"),s.dataset.narrow=String(e.duration*this.scale<100),s.dataset.editing=String(this.drag?.clipId===e.id),s.style.left=e.start*this.scale+"px",s.style.width=`${Math.max(24,e.duration*this.scale)}px`,s.dataset.hitProxy=String(e.duration*this.scale<24),s.style.setProperty("--db-vt-actual-width",e.duration*this.scale+"px");for(const a of["start","end"]){const d=this.node("span","db-vt__trim");d.dataset.trim=a,d.dataset.focus=`${e.id}-trim-${a}`,d.tabIndex=!r||this.disabled||t.locked?-1:0,d.setAttribute("role","slider"),d.setAttribute("aria-label",`${i("start"===a?"media.trimStart":"media.trimEnd")} · ${e.label}`),d.setAttribute("aria-valuemin","0"),d.setAttribute("aria-valuemax",String(this.duration)),d.setAttribute("aria-valuenow",String("start"===a?e.start:e.start+e.duration)),d.setAttribute("aria-valuetext",l("start"===a?e.start:e.start+e.duration,this.fps)),d.setAttribute("title",i("start"===a?"media.trimStart":"media.trimEnd")),d.append(this.node("i","db-vt__grip")),s.append(d)}if("filmstrip"===e.appearance&&e.thumbnail){const t=this.node("div","db-vt__filmstrip");t.setAttribute("aria-hidden","true");for(let i=0;i<Math.min(24,Math.ceil(e.duration*this.scale/64));i++){const i=document.createElement("img");i.src=e.thumbnail,i.alt="",i.draggable=!1,t.append(i)}s.append(t)}for(const a of["in","out"]){const n=d("in"===a?e.fadeIn||0:e.fadeOut||0,0,e.duration),o=this.node("span","db-vt__fade");if(o.dataset.fadeArea=a,o.style.width=n*this.scale+"px",o.setAttribute("aria-hidden","true"),s.append(o),r){const r=this.node("span","db-vt__fade-handle");r.dataset.fade=a,r.dataset.focus=`${e.id}-fade-${a}`,r.style["in"===a?"left":"right"]=n*this.scale+"px",r.tabIndex=this.disabled||t.locked?-1:0,r.setAttribute("role","slider"),r.setAttribute("aria-label",`${i("in"===a?"media.fadeIn":"media.fadeOut")} · ${e.label}`),r.setAttribute("aria-valuemin","0"),r.setAttribute("aria-valuemax",String(e.duration)),r.setAttribute("aria-valuenow",String(n)),r.setAttribute("title",i("in"===a?"media.fadeIn":"media.fadeOut")),s.append(r)}}if(s.append(this.node("span","db-vt__clip-label",e.label)),"audio"===(e.kind||t.kind)||"waveform"===e.appearance){const t=this.node("span","db-vt__wave");t.setAttribute("aria-hidden","true");for(let i=0;i<Math.min(160,Math.ceil(e.duration*this.scale/7));i++){const e=this.node("i","");e.style.height=20+75*Math.abs(Math.sin(1.7*i)*Math.cos(.4*i))+"%",t.append(e)}s.append(t)}if(e.effects?.length){const t=this.node("span","db-vt__effects");t.setAttribute("aria-label",i("media.effects"));for(const i of e.effects){const e=this.node("span","db-vt__effect",i.label);e.dataset.enabled=String(!1!==i.enabled),e.dataset.kind=i.kind,e.setAttribute("title",i.label),t.append(e)}s.append(t)}n.append(s)}),s.append(n);const h=this.node("div","db-vt__track-resize");h.dataset.trackResize=t.id,h.tabIndex=0,h.setAttribute("role","separator"),h.setAttribute("aria-orientation","horizontal"),h.setAttribute("aria-label",`${i("media.resizeTrack")} · ${t.label}`),h.setAttribute("aria-valuemin","32"),h.setAttribute("aria-valuemax","120"),h.setAttribute("aria-valuenow",String(this.trackHeight(t))),s.append(h),r.append(s),this.renderEffects(r,t)});for(const t of this._markers){const e=this.node("button","db-vt__marker");e.type="button",e.dataset.marker=String(t.time),e.style.left=144+t.time*this.scale+"px";const s=`${i("media.marker")} · ${t.label} · ${l(t.time,this.fps)}`;e.setAttribute("aria-label",s),e.setAttribute("title",s),r.append(e)}if(this.cursor=this.node("div","db-vt__playhead"),this.cursor.dataset.playhead="",this.cursor.tabIndex=0,this.cursor.setAttribute("role","slider"),this.cursor.dataset.focus="playhead",this.cursor.setAttribute("aria-label",i("media.playhead")),this.cursor.setAttribute("aria-valuemin","0"),this.cursor.setAttribute("aria-valuemax",String(this.duration)),this.cursor.setAttribute("aria-disabled",String(this.disabled)),r.append(this.cursor),this._tracks.length||r.append(this.node("p","db-vt__empty",i("media.emptyTracks"))),this.canvas.style.width=144+this.duration*this.scale+"px",this.drag?.clipId){const t=this._clips.find(t=>t.id===this.drag?.clipId);if(t){const e="fade-in"===this.drag.kind?t.fadeIn||0:"fade-out"===this.drag.kind?t.fadeOut||0:"trim-end"===this.drag.kind?t.start+t.duration:t.start,s=this.node("div","db-vt__readout",`${i("media."+("trim-start"===this.drag.kind?"trimStart":"trim-end"===this.drag.kind?"trimEnd":"fade-in"===this.drag.kind?"fadeIn":"fade-out"===this.drag.kind?"fadeOut":"time"))} ${l(e,this.fps)} / ${i("media.duration")} ${l(t.duration,this.fps)}`);this.append(s)}}this.canvas.replaceChildren(r),this.viewport.setAttribute("aria-label",i("media.timeline")),this.syncCursor(),this.syncHint(),s&&!this.drag&&Array.from(this.querySelectorAll("[data-focus]")).find(t=>t.dataset.focus===s)?.focus({preventScroll:!0})}renderEffects(t,s){for(const r of this._clips.filter(t=>t.trackId===s.id&&t.effects?.some(t=>t.properties?.length))){const n=this.node("section","db-vt__effect-tree");n.dataset.effectClip=r.id;const toggle=(t,e,i,s)=>{const r=c(t,i?"chevron-down":"chevron-right","effect-disclosure");return r.removeAttribute("icon-only"),r.setAttribute("variant","ghost"),r.textContent=t,r.setAttribute("aria-expanded",String(i)),r.setAttribute("aria-controls",e),r.dataset.focus=e,r.addEventListener("db-click",t=>{t.stopPropagation(),s(),this.render()}),r},o=`${this.effectTreeId}-${r.id}`;n.append(toggle(`${r.label} · ${i("media.effects")}`,o,this.expandedEffects.has(r.id),()=>{this.expandedEffects.has(r.id)?this.expandedEffects.delete(r.id):this.expandedEffects.add(r.id)}));const f=this.node("div","db-vt__effect-content");if(f.id=o,f.hidden=!this.expandedEffects.has(r.id),!f.hidden)for(const t of r.effects||[]){if(!t.properties?.length)continue;const i=`${r.id}/${t.id}`,n=`${o}-${t.id}`;f.append(toggle(t.label,n,this.expandedEffects.has(i),()=>{this.expandedEffects.has(i)?this.expandedEffects.delete(i):this.expandedEffects.add(i)}));const p=this.node("div","db-vt__property-group");if(p.id=n,p.hidden=!this.expandedEffects.has(i),!p.hidden)for(const n of t.properties){const o=`${i}/${n.id}`,f=this.node("div","db-vt__property-row");f.dataset.effectProperty=n.id,f.dataset.effectId=t.id;const u=this.node("div","db-vt__property-label"),m=c(n.label,"settings","effect-property");m.removeAttribute("icon-only"),m.setAttribute("variant","ghost"),m.textContent=n.label,m.setAttribute("aria-expanded",String(this.activeProperty.get(r.id)===o)),m.dataset.focus=o,m.addEventListener("db-click",t=>{t.stopPropagation(),this.activeProperty.set(r.id,this.activeProperty.get(r.id)===o?"":o),this.render()}),u.append(m,this.node("span","db-vt__property-value",String(Math.round(100*h(n.keyframes,this.currentTime-r.start))/100))),f.append(u);const k=this.node("div","db-vt__property-lane");k.style.width=this.duration*this.scale+"px";for(const e of n.keyframes){const i=this.node("button","db-vt__property-key");i.dataset.propertyKey=e.id,i.dataset.keyToken=this.effectToken(r.id,t.id,n.id,e.id),i.setAttribute("aria-pressed",String(this.selectedEffectKeys.has(i.dataset.keyToken))),i.addEventListener("pointerdown",s=>{if(i.disabled||0!==s.button)return;s.preventDefault(),s.stopPropagation(),this.viewport?.focus({preventScroll:!0});const a=i.dataset.keyToken;Array.from(this.selectedEffectKeys).some(t=>JSON.parse(t)[0]!==r.id)&&this.selectedEffectKeys.clear(),s.shiftKey?this.selectedEffectKeys.has(a)?this.selectedEffectKeys.delete(a):this.selectedEffectKeys.add(a):this.selectedEffectKeys.has(a)||(this.selectedEffectKeys.clear(),this.selectedEffectKeys.add(a)),this.syncEffectSelection(),this.selectedEffectKeys.has(a)?(this.effectDrag={pointer:s.pointerId,clipId:r.id,effectId:t.id,propertyId:n.id,keyId:e.id,x:s.clientX,time:e.time,before:this.clips,changed:!1,copies:s.altKey?this.makeKeyCopyIds():void 0},this.effectEditing=!0,this.setPointerCapture(s.pointerId)):this.render()}),i.type="button",i.style.left=(r.start+e.time)*this.scale+"px",i.setAttribute("aria-label",`${n.label} · ${l(e.time,this.fps)} · ${e.value}`),i.disabled=this.disabled||!!s.locked||!1===t.enabled,i.addEventListener("click",t=>{t.stopPropagation(),t.shiftKey||(this.selectedEffectKeys.has(i.dataset.keyToken)||(this.selectedEffectKeys.clear(),this.selectedEffectKeys.add(i.dataset.keyToken)),this.activeProperty.set(r.id,o),this.effectKeySelection.set(o,e.id),this.seek(r.start+e.time),this.render())}),k.append(i)}if(f.append(k),p.append(f),this.activeProperty.get(r.id)===o){const i=new e;i.dataset.effectEditor=n.id,i.duration=r.duration,i.fps=this.fps,i.min=n.min,i.max=n.max,i.setAttribute("label",n.label),i.keyframes=n.keyframes,i.selectedKeyframeId=this.effectKeySelection.get(o)||n.keyframes[0]?.id||"",i.currentTime=d(this.currentTime-r.start,0,r.duration),i.disabled=this.disabled||!!s.locked||!1===t.enabled;const edit=e=>{e.stopPropagation();const i=e.detail;this.effectEditing="db-input"===e.type&&!i.cancelled;const s=i.keyframes;this._clips=this._clips.map(e=>e.id!==r.id?e:{...e,effects:e.effects?.map(e=>e.id!==t.id?e:{...e,properties:e.properties?.map(t=>t.id!==n.id?t:{...t,keyframes:s})})}),a(this,e.type,{clips:this.clips,clipId:r.id,action:"keyframe",effectId:t.id,propertyId:n.id,...i.cancelled?{cancelled:!0}:{}}),("db-change"===e.type||i.cancelled)&&(this.effectEditing=!1,this.render())};i.addEventListener("db-input",edit),i.addEventListener("db-change",edit),i.addEventListener("db-selection-change",t=>{t.stopPropagation(),this.effectKeySelection.set(o,t.detail.keyframeId)}),i.addEventListener("db-seek",t=>{t.stopPropagation(),this.seek(r.start+t.detail.time)});const c=this.node("div","db-vt__effect-editor");c.style.width=`${Math.max(240,(this.viewport?.clientWidth||800)-16)}px`,c.append(i),p.append(c)}}f.append(p)}n.addEventListener("pointerdown",t=>{if(0!==t.button||this.disabled||s.locked||t.target.closest("db-button,db-keyframe-editor,db-input,db-select,.db-vt__property-label,[data-property-key]"))return;t.preventDefault(),t.stopPropagation(),this.viewport?.focus({preventScroll:!0});const e=this.node("div","db-vt__marquee");this.append(e),this.marquee={pointer:t.pointerId,x:t.clientX,y:t.clientY,clipId:r.id,before:t.shiftKey?new Set(Array.from(this.selectedEffectKeys).filter(t=>JSON.parse(t)[0]===r.id)):new Set,box:e},t.shiftKey||this.selectedEffectKeys.clear(),this.syncEffectSelection(),this.setPointerCapture(t.pointerId)}),n.append(f),t.append(n)}}select(t){this.selectedClipId!==t&&(this.selectedClipId=t,a(this,"db-selection-change",{clipId:t}))}timeAt(t){return(t-this.viewport.getBoundingClientRect().left+this.viewport.scrollLeft-144)/this.scale}snapped(t,e){let i=r(t,this.fps);if(this.snap&&!this.drag?.shift){const t=[0,this.currentTime,...this._markers.map(t=>t.time),...this._clips.filter(t=>t.id!==e).flatMap(t=>[t.start,t.start+t.duration])].reduce((t,e)=>Math.abs(e-i)<Math.abs(t-i)?e:t,1/0);Math.abs(t-i)*this.scale<=8&&(i=r(t,this.fps))}return i}updateDrag(){const t=this.drag;if(!t)return;if("seek"===t.kind)return void this.seek(this.timeAt(t.x));const e=t.before.find(e=>e.id===t.clipId);if(!e)return;const i=(t.x-t.originX+this.viewport.scrollLeft-t.scroll)/this.scale;let s=e.start,n=e.duration,o=e.sourceOffset||0,c=e.trackId;if("fade-in"===t.kind||"fade-out"===t.kind){const s="fade-in"===t.kind?"fadeIn":"fadeOut",n="fade-in"===t.kind?e.fadeOut||0:e.fadeIn||0,o=r(d((e[s]||0)+("fade-in"===t.kind?i:-i),0,Math.max(0,e.duration-n)),this.fps);return this._clips=t.before.map(t=>t.id===e.id?{...t,[s]:o}:t),t.changed=JSON.stringify(this._clips)!==JSON.stringify(t.before),this.render(),void a(this,"db-input",{clips:this.clips,clipId:e.id,action:t.kind})}if("move"===t.kind){s=this.snapped(s+i,e.id),this.canvas.getBoundingClientRect();const r=this.trackIndexAt(t.y),a=this._tracks[d(r,0,this._tracks.length-1)],o=this._tracks.find(t=>t.id===e.trackId);!a||a.locked||"mixed"!==a.kind&&a.kind!==(e.kind||o?.kind)||(c=a.id),s=d(s,0,this.duration-n)}else if("trim-start"===t.kind)s=d(this.snapped(s+i,e.id),Math.max(0,e.start-o),e.start+n-Math.min(this.minClipDuration,e.start+n-Math.max(0,e.start-o))),n=e.start+e.duration-s,o+=s-e.start;else{const t=e.sourceDuration?Math.min(this.duration-s,e.sourceDuration-o):this.duration-s;n=d(this.snapped(s+n+i,e.id)-s,Math.min(this.minClipDuration,t),t)}const h=t.ripple&&("trim-start"===t.kind||"trim-end"===t.kind),l=t.before.filter(t=>t.id!==e.id&&t.trackId===e.trackId&&t.start>=e.start+e.duration-1e-6);let f=0;if(h){"trim-start"===t.kind&&(s=e.start);const i=Math.max(e.start+e.duration,...l.map(t=>t.start+t.duration));n=Math.min(n,e.duration+this.duration-i),f=r(n-e.duration,this.fps)}let p=t.before.map(t=>l.some(e=>e.id===t.id)&&h?{...t,start:r(t.start+f,this.fps)}:t);if("move"===t.kind&&t.ripple){const i=t.before.filter(i=>i.trackId===c&&(t.duplicate||i.id!==e.id)).sort((t,e)=>t.start-e.start),a=i.find(t=>s>t.start+1e-6&&s<t.start+t.duration-1e-6);a&&(s=a.start);let d=s+n;const o=new Map;for(const t of i){if(t.start<s-1e-6)continue;const e=Math.max(t.start,d);o.set(t.id,e),d=e+t.duration}if(d>this.duration+1e-6)return;p=t.before.map(t=>o.has(t.id)?{...t,start:r(o.get(t.id),this.fps)}:t)}if(p.some(i=>(t.duplicate||i.id!==e.id)&&i.trackId===c&&s<i.start+i.duration-1e-6&&s+n>i.start+1e-6))return;const u=t.duplicate||e.id;t.duplicate&&(p=[...p,{...e,id:u}]),this._clips=p.map(t=>t.id===u?{...t,kind:t.kind||("mixed"===this._tracks.find(t=>t.id===e.trackId)?.kind?"video":this._tracks.find(t=>t.id===e.trackId)?.kind),start:r(s,this.fps),duration:r(n,this.fps),sourceOffset:r(o,this.fps),trackId:c,...void 0!==t.fadeIn?{fadeIn:Math.min(t.fadeIn,n)}:{},...void 0!==t.fadeOut?{fadeOut:Math.min(t.fadeOut,Math.max(0,n-(t.fadeIn||0)))}:{}}:t),t.changed=JSON.stringify(this._clips)!==JSON.stringify(t.before),this.render(),a(this,"db-input",{clips:this.clips,clipId:e.id,action:t.kind})}restoreTimelineHistory(t=!1){const e=t?this.redoClips:this.undoClips,i=t?this.undoClips:this.redoClips,s=e.pop();s&&(i.push(this.clips),this._clips=structuredClone(s),this.committedClips=this.clips,this.selectedEffectKeys.clear(),this.render(),a(this,"db-change",{clips:this.clips,clipId:this.selectedClipId,action:t?"redo":"undo"}))}makeKeyCopyIds(){return new Map(Array.from(this.selectedEffectKeys,t=>[t,`key-${crypto.randomUUID()}`]))}copyKeyData(t,e,i,s){let a=!0,d=0;const n=t.map(t=>t.id!==e?t:{...t,effects:t.effects?.map(e=>({...e,properties:e.properties?.map(n=>{const o=n.keyframes.filter(i=>s.has(this.effectToken(t.id,e.id,n.id,i.id))).map(o=>{const c=r(o.time+i,this.fps);return d++,(c<0||c>t.duration||n.keyframes.some(t=>Math.abs(t.time-c)<.5/this.fps))&&(a=!1),{...structuredClone(o),id:s.get(this.effectToken(t.id,e.id,n.id,o.id)),time:c}});return{...n,keyframes:[...n.keyframes,...o].sort((t,e)=>t.time-e.time)}})}))});return a&&d?n:void 0}duplicateSelectedKeys(t){const e=this.makeKeyCopyIds(),i=this.clips;for(let s=1;s<=Math.min(3e3,Math.ceil(this.duration*this.fps));s++)for(const r of[1,-1]){const d=this.copyKeyData(i,t,r*s/this.fps,e);if(d)return this._clips=d,this.selectedEffectKeys=new Set(Array.from(e,([t,e])=>{const[i,s,r]=JSON.parse(t);return this.effectToken(i,s,r,e)})),a(this,"db-change",{clips:this.clips,clipId:t,action:"keyframe"}),void this.render()}}effectToken(t,e,i,s){return JSON.stringify([t,e,i,s])}syncEffectSelection(){for(const t of this.querySelectorAll("[data-key-token]"))t.setAttribute("aria-pressed",String(this.selectedEffectKeys.has(t.dataset.keyToken)))}finishMarquee(t=!1){const e=this.marquee;e&&(this.marquee=void 0,e.box.remove(),t&&(this.selectedEffectKeys=e.before),this.hasPointerCapture(e.pointer)&&this.releasePointerCapture(e.pointer),this.render())}editSelectedKeys(t,e){const i=this._clips.find(e=>e.id===t);!i||this.disabled||this._tracks.find(t=>t.id===i.trackId)?.locked||(this._clips=this._clips.map(i=>i.id!==t?i:{...i,effects:i.effects?.map(t=>!1===t.enabled?t:{...t,properties:t.properties?.map(s=>{let r=s.keyframes;if(e)for(const a of s.keyframes)this.selectedEffectKeys.has(this.effectToken(i.id,t.id,s.id,a.id))&&(r=f(r,a.id,e));else r=r.filter(e=>!this.selectedEffectKeys.has(this.effectToken(i.id,t.id,s.id,e.id)));return{...s,keyframes:r}})})}),e||this.selectedEffectKeys.clear(),a(this,"db-change",{clips:this.clips,clipId:t,action:"keyframe"}),this.render(),this.viewport?.focus({preventScroll:!0}))}finishEffectDrag(t=!1){const e=this.effectDrag;e&&(this.effectDrag=void 0,this.effectEditing=!1,this.hasPointerCapture(e.pointer)&&this.releasePointerCapture(e.pointer),t?this._clips=e.before:e.copies&&e.changed&&(this.selectedEffectKeys=new Set(Array.from(e.copies,([t,e])=>{const[i,s,r]=JSON.parse(t);return this.effectToken(i,s,r,e)}))),(t||e.changed)&&a(this,t?"db-input":"db-change",{clips:this.clips,clipId:e.clipId,action:"keyframe",effectId:e.effectId,propertyId:e.propertyId,...t?{cancelled:!0}:{}}),this.render())}finish(t=!1){const e=this.drag;e&&(cancelAnimationFrame(this.raf),this.drag=void 0,this.hasPointerCapture(e.id)&&this.releasePointerCapture(e.id),t?(this._clips=e.before,this.render(),a(this,"db-input",{clips:this.clips,clipId:e.clipId,action:e.kind,cancelled:!0})):e.changed&&e.clipId&&"seek"!==e.kind&&a(this,"db-change",{clips:this.clips,clipId:e.clipId,action:e.kind}),this.render(),this.viewport?.focus({preventScroll:!0}))}rollback(){this.finishMarquee(!0),this.finishEffectDrag(!0),this.finish(!0)}splitSelected(){const t=this._clips.find(t=>t.id===this.selectedClipId);if(this.disabled||!t||this._tracks.find(e=>e.id===t.trackId)?.locked||t.duration<2*this.minClipDuration||this.currentTime<t.start+this.minClipDuration||this.currentTime>t.start+t.duration-this.minClipDuration)return;const e=this.currentTime;let i=t.id+"-split";for(;this._clips.some(t=>t.id===i);)i+="-1";this._clips=this._clips.flatMap(s=>s.id===t.id?[{...s,duration:e-s.start,fadeOut:0,fadeIn:Math.min(s.fadeIn||0,e-s.start)},{...s,id:i,fadeIn:0,fadeOut:Math.min(s.fadeOut||0,s.start+s.duration-e),start:e,duration:s.start+s.duration-e,sourceOffset:(s.sourceOffset||0)+e-s.start}]:[s]),this.render(),a(this,"db-change",{clips:this.clips,action:"split",clipId:t.id})}trackIndexAt(t){const e=Array.from(this.canvas.querySelectorAll(".db-vt__row"));let i=0;return e.forEach((e,s)=>{t>=e.getBoundingClientRect().top&&(i=s)}),i}finishResize(t=!1){const e=this.resizeDrag;e&&(this.resizeDrag=void 0,this.hasPointerCapture(e.id)&&this.releasePointerCapture(e.id),t?(this._tracks=e.beforeTracks,this.height=e.beforeHeight,this.render(),a(this,e.trackId?"db-track-input":"db-resize-input",e.trackId?{tracks:this.tracks,cancelled:!0}:{height:this.height,cancelled:!0})):a(this,e.trackId?"db-track-change":"db-resize",e.trackId?{tracks:this.tracks}:{height:this.height}))}changeZoom(t,e,i="keyboard"){if(!this.viewport)return;const s=this.zoom,r=e??Math.min(this.viewport.clientWidth-8,Math.max(152,144+this.currentTime*this.scale-this.viewport.scrollLeft)),n=(this.viewport.scrollLeft+r-144)/this.scale;this.zoom=Math.round(100*d(t,10,800))/100,this.viewport.scrollLeft="fit"===i?0:Math.max(0,144+n*this.scale-r),this.zoom!==s&&a(this,"db-zoom-change",{value:this.zoom,previousValue:s,anchorTime:n,source:i})}}customElements.get("db-video-timeline")||customElements.define("db-video-timeline",DbVideoTimeline);export{DbVideoTimeline};
@@ -55,6 +55,11 @@ declare class DbWorldMap extends HTMLElement {
55
55
  private drawSolid;
56
56
  private line;
57
57
  }
58
+ declare global {
59
+ interface HTMLElementTagNameMap {
60
+ 'db-world-map': DbWorldMap;
61
+ }
62
+ }
58
63
 
59
64
  export { DbWorldMap };
60
65
  export type { WorldMapConnection, WorldMapLocation, WorldMapVariant };
@@ -9,7 +9,16 @@ interface VideoTrack {
9
9
  muted?: boolean;
10
10
  height?: number;
11
11
  }
12
+ interface VideoEffectProperty {
13
+ id: string;
14
+ label: string;
15
+ min: number;
16
+ max: number;
17
+ /** Clip-local seconds, independent of sequence placement. */
18
+ keyframes: EditorKeyframe[];
19
+ }
12
20
  interface VideoClipEffect {
21
+ properties?: VideoEffectProperty[];
13
22
  id: string;
14
23
  label: string;
15
24
  kind: 'color' | 'blur' | 'transform' | 'opacity' | 'speed';
@@ -57,4 +66,4 @@ declare function keyframeEasingCurve(keys: EditorKeyframe[], id: string): [numbe
57
66
  declare function applyKeyframeEasing(keys: EditorKeyframe[], id: string, easing: KeyframeEasing, bezier?: [number, number, number, number]): EditorKeyframe[];
58
67
 
59
68
  export { applyKeyframeEasing, editorTimecode, evaluateKeyframes, frameTime, keyframeEasingCurve };
60
- export type { EditorKeyframe, KeyframeEasing, KeyframeTangent, TimelineMarker, VideoClip, VideoClipEffect, VideoTrack };
69
+ export type { EditorKeyframe, KeyframeEasing, KeyframeTangent, TimelineMarker, VideoClip, VideoClipEffect, VideoEffectProperty, VideoTrack };
@@ -1 +1 @@
1
- const e={"appUpdate.notice":"Update available","appUpdate.changes":"What’s new","appUpdate.automatic":"Restarts automatically when complete.","appUpdate.install":"Update","appUpdate.inProgress":"Updating","appUpdate.dialogTitle":"App update","appUpdate.toastComplete":"Updated to {version}.","appUpdate.checking.title":"Checking for updates","appUpdate.checking.description":"Looking for a new update.","appUpdate.up-to-date.title":"You’re up to date","appUpdate.up-to-date.description":"You’re using the latest available version.","appUpdate.available.title":"An update is available","appUpdate.available.description":"Download the latest improvements and stability updates.","appUpdate.downloading.title":"Downloading update","appUpdate.downloading.description":"We’ll let you know how to apply it when the download finishes.","appUpdate.ready.title":"Ready to update","appUpdate.ready.description":"Save your work, then restart the app to apply the update.","appUpdate.applying.title":"Applying update","appUpdate.applying.description":"Keep the app open and wait a moment.","appUpdate.complete.title":"Update complete","appUpdate.complete.description":"The new version has been applied successfully.","appUpdate.error.title":"Update could not be completed","appUpdate.error.description":"Check your connection and try again.","appUpdate.offline.title":"Internet connection required","appUpdate.offline.description":"Connect to the internet, then try again.","appUpdate.current":"Current","appUpdate.target":"New version","appUpdate.required":"Required update","appUpdate.later":"Later","appUpdate.download":"Download update","appUpdate.restart":"Restart to update","appUpdate.retry":"Try again","appUpdate.done":"Done","media.restoreHandles":"Restore handles (H)","media.tangentHelp":"Drag: symmetric · Cmd/Ctrl: independent · H: restore · dashed handles reveal collapsed controls.","media.resizeTimeline":"Resize timeline height","media.resizeTimelineHint":"Drag vertically to resize. Double-click or Home for automatic height.","media.resizeTrack":"Resize track height","easing.outVelocity":"Out velocity ×","easing.inVelocity":"In velocity ×","easing.label":"Easing & velocity","easing.graphMode":"Graph mode","easing.value":"Value","easing.speed":"Speed","easing.valueGraph":"Normalized progress curve","easing.speedGraph":"Normalized velocity graph","easing.custom":"Custom curve","easing.presets":"Easing presets","easing.linear":"Linear","easing.ease":"Ease","easing.ease-in":"Ease in","easing.ease-out":"Ease out","easing.ease-in-out":"Ease in/out","easing.smooth":"Smooth","easing.snappy":"Snappy","easing.swift":"Swift out","easing.gentle":"Gentle","easing.emphasized":"Emphasized","easing.outgoing":"Outgoing tangent","easing.incoming":"Incoming tangent","easing.preview":"Play easing preview","easing.velocityHint":"1× = constant speed","easing.speedHelp":"Drag tangent controls to change influence and endpoint velocity. Vertical scale is normalized; multiply by value change / duration for units per second.","easing.valueHelp":"Drag handles or enter exact coordinates. Arrow keys nudge .01; Shift nudges .1. Escape cancels dragging.","media.trimStart":"Trim start","media.trimEnd":"Trim end","media.fadeIn":"Fade in","media.fadeOut":"Fade out","media.effects":"Applied effects","media.timeline":"Video timeline","media.tracks":"Tracks","media.more":"Scroll to more tracks and clips","media.lock":"Lock track","media.trackLocked":"Locked","media.trackUnlocked":"Unlocked","media.soundOn":"Sound on","media.soundOff":"Muted","media.marker":"Timeline marker","media.trackMute":"Mute track","media.duration":"Duration","media.playhead":"Playhead","media.emptyTracks":"No tracks. Connect a tracks array to begin.","media.keyframes":"Keyframes","media.curve":"Value graph","media.previousKey":"Previous keyframe","media.nextKey":"Next keyframe","media.addKey":"Add keyframe (K)","media.deleteKey":"Delete keyframe","media.outHandle":"Outgoing Bezier handle","media.inHandle":"Incoming Bezier handle","media.keyframe":"Keyframe","media.emptyKeys":"Add a keyframe or double-click the graph.","media.selectKey":"Select a keyframe to edit its time, value and interpolation.","media.time":"Time (seconds)","media.value":"Value","media.interpolation":"Interpolation","media.linear":"Linear","media.hold":"Hold","media.ease-in":"Ease in","media.ease-out":"Ease out","media.ease-in-out":"Ease in / out","media.bezier":"Custom Bezier","selectionBar.count":"{count} selected","selectionBar.clear":"Clear","selectionBar.label":"Selection actions","zoom.label":"Zoom controls","zoom.out":"Zoom out","zoom.in":"Zoom in","zoom.value":"Zoom level","zoom.fit":"Fit to screen","zoom.fitShort":"Fit","zoom.fullscreen":"Enter fullscreen","zoom.exitFullscreen":"Exit fullscreen","zoom.fullscreenError":"Fullscreen is unavailable.","toolPanel.close":"Close panel","toolPanel.tabs":"Panel tabs","toolLayout.resize.sidebar":"Resize sidebar width","toolLayout.resize.inspector":"Resize inspector width","toolLayout.resize.bottom":"Resize bottom panel height","password.weak":"Weak","password.fair":"Fair","password.good":"Strong","password.veryStrong":"Very strong","rating.high":"Very good","rating.medium":"Average","rating.low":"Low","rating.label":"Rating {value}","rating.review":"{count} review","rating.reviews":"{count} reviews","countdown.days":"Days","countdown.hours":"Hours","countdown.minutes":"Minutes","countdown.seconds":"Seconds","upload.title":"Drag files here or click to choose","upload.choose":"Choose files","upload.allFormats":"All file types supported","upload.maximum":" (up to {size})","upload.files":"Uploaded files ({count})","upload.retry":"Retry","upload.remove":"Remove","upload.illustration":"Upload illustration","creditCard.security":"Security code {code}","creditCard.label":"{issuer} card{lastFour}, flip","creditCard.ending":", ending in {digits}","creditCard.front":"Front","creditCard.back":"Back","creditCard.showing":"Showing {side}","creditCard.expiry":"Expires {expiry}","creditCard.show":"Press to show {side}","flight.scheduled":"Scheduled","flight.boarding":"Boarding","flight.in-flight":"In flight","flight.landed":"Landed","flight.delayed":"Delayed","flight.cancelled":"Cancelled","flight.title":"Flight status","flight.progress":"Journey progress","boardingPass.origin":"From","boardingPass.destination":"To","boardingPass.passenger":"Passenger","boardingPass.service":"Flight","boardingPass.date":"Date","boardingPass.boarding":"Boarding","boardingPass.departure":"Departure","boardingPass.arrival":"Arrival","boardingPass.terminal":"Terminal","boardingPass.gate":"Gate","boardingPass.seat":"Seat","boardingPass.travelClass":"Class","boardingPass.title":"Boarding pass","wallet.title":"My boarding passes","wallet.empty":"No boarding passes yet.","wallet.journeys":"Choose a journey","receipt.quantity":"Qty","receipt.subtotal":"Subtotal","receipt.discount":"Discount","receipt.tax":"Tax","receipt.total":"Total","receipt.payment":"Payment","receipt.empty":"No items to display.","receipt.invalid":"Check the items and amounts.","receipt.title":"Receipt","ticket.title":"Ticket","paper.codeImage":"Code image","aiElement.emptyFolder":"Empty folder","aiElement.noFiles":"No files to display.","attachment.title":"Attachments","attachment.empty":"No attachments yet.","attachment.queued":"Queued","attachment.uploading":"Uploading","attachment.processing":"Processing","attachment.ready":"Ready","attachment.error":"Failed","attachment.cancelled":"Cancelled","attachment.unknown":"Checking progress…","attachment.cancel":"Cancel","attachment.retry":"Retry","attachment.remove":"Remove from list","attachment.open":"View details","attachment.reload":"Reload","workspace.members":"Workspace members","workspace.invite":"Invite members","workspace.email":"Email","workspace.role":"Role","workspace.chooseRole":"Choose a role","workspace.description":"Review the email address and role of the person you want to invite.","workspace.submit":"Send invitation","workspace.emailError":"Enter a valid email address of up to 254 characters.","workspace.roleError":"Choose a role for the invitation.","workspace.retry":"Reload","workspace.reinvite":"Invite again","workspace.active":"Active","workspace.invited":"Invitation pending","workspace.expired":"Invitation expired","workspace.empty":"No members to display yet.","aiElement.pending":"Pending","aiElement.running":"In progress","aiElement.complete":"Complete","aiElement.error":"Failed","aiElement.cancelled":"Cancelled","aiElement.approval":"Awaiting approval","aiElement.researching":"Reviewing sources","aiElement.researched":"View research","aiElement.thinking":"Thinking","aiElement.reasoning":"Response summary","aiElement.plan":"Plan","aiElement.queue":"Queued requests","aiElement.remove":"Remove","aiElement.queueEmpty":"No queued requests.","aiElement.sources":"Sources","aiElement.citation":"Source","aiElement.checkpoint":"Checkpoint saved","aiElement.restore":"Restore from here","aiElement.confirmation":"Proceed with this action?","aiElement.allow":"Allow","aiElement.reject":"Reject","aiElement.accepted":"Action approved.","aiElement.rejected":"Action rejected.","aiElement.context":"Context","aiElement.tokens":"Tokens","aiElement.unknown":"Usage is not available yet.","aiElement.files":"Files","aiElement.task":"Task progress","aiElement.subagent":"Subagent","aiElement.tool":"Tool call","aiElement.terminal":"Terminal","aiElement.stackTrace":"Error details","aiElement.tests":"Test results","aiElement.passed":"Passed","aiElement.failed":"Failed","aiElement.input":"Input","aiElement.output":"Output","aiElement.user":"User","aiElement.assistant":"AI response","aiElement.copy":"Copy","aiElement.regenerate":"Regenerate","aiElement.like":"Like","aiElement.dislike":"Dislike","aiElement.latest":"Latest response","aiElement.conversation":"AI conversation","notification.title":"Notifications","notification.filters":"Notification filters","notification.all":"All","notification.unread":"Unread","notification.readAll":"Mark displayed notifications as read","notification.markRead":"Mark as read","notification.retry":"Reload","notification.empty":"No notifications yet.","notification.caughtUp":"You’re all caught up.","notification.countUnknown":"Needs review","aiModel.label":"Model","aiModel.choose":"Choose a model","aiModel.empty":"No models are available right now.","aiModel.capabilities":"Capabilities","aiModel.usage":"Usage unit","aiQuote.expires":"Quote expires","aiQuote.title":"Execution estimate","aiQuote.reserved":"Maximum credit reservation","aiQuote.providerCost":"Estimated provider cost","aiQuote.pricingVersion":"Pricing version","aiQuote.unknown":"Unconfirmed","aiQuote.idle":"Choose a model and enter a request to get an estimate.","aiQuote.loading":"Checking the estimate…","aiQuote.executing":"Processing your request. Do not submit it again.","aiQuote.stale":"The model or input has changed. Refresh the estimate.","aiQuote.expired":"The estimate has expired. Request a new estimate.","aiQuote.balanceUnknown":"Your balance is unavailable. Refresh to try again.","aiQuote.insufficient":"Additional credits needed:","aiQuote.ready":"Credits are reserved when execution starts and settled against actual usage when it finishes.","aiQuote.refresh":"Refresh estimate","aiQuote.request":"Get estimate","aiQuote.execute":"Run","plan.current":"Current plan","plan.usage":"Usage this period","plan.allowed":"Included","plan.locked":"Not included","plan.manage":"Manage plan","plan.retry":"Reload","history.title":"Credit history","history.empty":"No history yet.","history.unresolved":"Checking","history.grant":"Granted","history.charge":"Used","history.hold":"Reserved · not settled","history.release":"Reservation released","credits.choosePlan":"Credits to add","credits.rechargeDescription":"Choose an amount and review the price and terms.","credits.paymentAmount":"Payment amount","credits.paymentPending":"Checking payment status","credits.remaining":"Remaining credits","credits.available":"Available credits","credits.unit":"credits","credits.held":"Reserved","credits.spent":"Total used","credits.charged":"Charged","credits.low":"Your credit balance is running low.","credits.recharge":"Add credits","credits.checkout":"Continue to payment","credits.noPlans":"No credit packs are available right now.","aiTask.title":"AI task","aiTask.idle":"Not started","aiTask.queued":"Queued","aiTask.running":"Running","aiTask.succeeded":"Complete","aiTask.failed":"Failed","aiTask.cancelled":"Cancelled","aiTask.needs_attention":"Review needed","aiTask.attentionDescription":"We’re checking the result and usage. Reserved credits remain on hold until the review is complete.",close:"Close","modal.close":"Close modal","drawer.close":"Close drawer","dialog.close":"Close dialog","toast.close":"Dismiss notification",clear:"Clear",clearAll:"Clear all",loading:"Loading",required:"Required",optional:"Optional",error:"Error",success:"Success",warning:"Warning",info:"Information",previous:"Previous",next:"Next","select.placeholder":"Select…","select.noResults":"No results","navigationRail.label":"Main navigation","navigationRail.newNotification":"New notification","navigationRail.notifications":"Notifications","tabs.label":"Tabs","pagination.label":"Pagination","search.label":"Search","media.play":"Play","media.pause":"Pause","media.mute":"Mute","media.unmute":"Unmute","media.settings":"Settings","media.shuffle":"Shuffle","media.repeat":"Repeat","media.previous":"Previous track","media.next":"Next track","media.fullscreen":"Full screen","confirm.ok":"Confirm","confirm.cancel":"Cancel","carousel.slide":"slide","carousel.role":"carousel","carousel.previous":"Previous slide","carousel.next":"Next slide","breadcrumbs.expand":"Show hidden path","input.showPassword":"Show password","input.hidePassword":"Hide password","input.clearSearch":"Clear search","verify.done":"Verified","verify.phoneDone":"Your phone number has been verified.","bentoCard.cta":"View details","tweetCard.loading":"Loading post…","tweetCard.error":"Unable to load this post.","tweetCard.viewOnTwitter":"View on X","tweetCard.like":"Like","tweetCard.retweet":"Repost","tweetCard.reply":"Reply","tweetCard.verified":"Verified account","tweetCard.retry":"Try again","chatComposer.placeholder":"Type a message…","chatComposer.send":"Send","chatComposer.stop":"Stop generating","chatComposer.attach":"Attach","voice.start":"Voice input","voice.stop":"Finish dictation","voice.cancel":"Cancel voice input","voice.starting":"Connecting microphone","voice.listening":"Listening","voice.stopping":"Finishing transcription","voice.error":"Voice recognition failed","voice.unsupported":"Voice input not supported"};export{e as ENGLISH_STRINGS};
1
+ const e={"layerList.label":"Layers","layerList.delete":"Delete {name}","layerList.lock":"Lock {name}","layerList.unlock":"Unlock {name}","layerList.show":"Show {name}","layerList.hide":"Hide {name}","layerList.empty":"No layers.","appUpdate.notice":"Update available","appUpdate.changes":"What’s new","appUpdate.automatic":"Restarts automatically when complete.","appUpdate.install":"Update","appUpdate.inProgress":"Updating","appUpdate.dialogTitle":"App update","appUpdate.toastComplete":"Updated to {version}.","appUpdate.checking.title":"Checking for updates","appUpdate.checking.description":"Looking for a new update.","appUpdate.up-to-date.title":"You’re up to date","appUpdate.up-to-date.description":"You’re using the latest available version.","appUpdate.available.title":"An update is available","appUpdate.available.description":"Download the latest improvements and stability updates.","appUpdate.downloading.title":"Downloading update","appUpdate.downloading.description":"We’ll let you know how to apply it when the download finishes.","appUpdate.ready.title":"Ready to update","appUpdate.ready.description":"Save your work, then restart the app to apply the update.","appUpdate.applying.title":"Applying update","appUpdate.applying.description":"Keep the app open and wait a moment.","appUpdate.complete.title":"Update complete","appUpdate.complete.description":"The new version has been applied successfully.","appUpdate.error.title":"Update could not be completed","appUpdate.error.description":"Check your connection and try again.","appUpdate.offline.title":"Internet connection required","appUpdate.offline.description":"Connect to the internet, then try again.","appUpdate.current":"Current","appUpdate.target":"New version","appUpdate.required":"Required update","appUpdate.later":"Later","appUpdate.download":"Download update","appUpdate.restart":"Restart to update","appUpdate.retry":"Try again","appUpdate.done":"Done","media.restoreHandles":"Restore handles (H)","media.tangentHelp":"Drag: symmetric · Cmd/Ctrl: independent · H: restore · dashed handles reveal collapsed controls.","media.resizeTimeline":"Resize timeline height","media.resizeTimelineHint":"Drag vertically to resize. Double-click or Home for automatic height.","media.resizeTrack":"Resize track height","easing.outVelocity":"Out velocity ×","easing.inVelocity":"In velocity ×","easing.label":"Easing & velocity","easing.graphMode":"Graph mode","easing.value":"Value","easing.speed":"Speed","easing.valueGraph":"Normalized progress curve","easing.speedGraph":"Normalized velocity graph","easing.custom":"Custom curve","easing.presets":"Easing presets","easing.linear":"Linear","easing.ease":"Ease","easing.ease-in":"Ease in","easing.ease-out":"Ease out","easing.ease-in-out":"Ease in/out","easing.smooth":"Smooth","easing.snappy":"Snappy","easing.swift":"Swift out","easing.gentle":"Gentle","easing.emphasized":"Emphasized","easing.outgoing":"Outgoing tangent","easing.incoming":"Incoming tangent","easing.preview":"Play easing preview","easing.velocityHint":"1× = constant speed","easing.speedHelp":"Drag tangent controls to change influence and endpoint velocity. Vertical scale is normalized; multiply by value change / duration for units per second.","easing.valueHelp":"Drag handles or enter exact coordinates. Arrow keys nudge .01; Shift nudges .1. Escape cancels dragging.","media.trimStart":"Trim start","media.trimEnd":"Trim end","media.fadeIn":"Fade in","media.fadeOut":"Fade out","media.duplicateKeys":"Duplicate keyframes","media.undo":"Undo","media.selectedKeys":"selected","media.keyEasing":"Selected key easing","media.effects":"Applied effects","media.timeline":"Video timeline","media.tracks":"Tracks","media.more":"Scroll to more tracks and clips","media.lock":"Lock track","media.trackLocked":"Locked","media.trackUnlocked":"Unlocked","media.soundOn":"Sound on","media.soundOff":"Muted","media.marker":"Timeline marker","media.trackMute":"Mute track","media.duration":"Duration","media.playhead":"Playhead","media.emptyTracks":"No tracks. Connect a tracks array to begin.","media.keyframes":"Keyframes","media.curve":"Value graph","media.previousKey":"Previous keyframe","media.nextKey":"Next keyframe","media.addKey":"Add keyframe (K)","media.deleteKey":"Delete keyframe","media.outHandle":"Outgoing Bezier handle","media.inHandle":"Incoming Bezier handle","media.keyframe":"Keyframe","media.emptyKeys":"Add a keyframe or double-click the graph.","media.selectKey":"Select a keyframe to edit its time, value and interpolation.","media.time":"Time (seconds)","media.value":"Value","media.interpolation":"Interpolation","media.linear":"Linear","media.hold":"Hold","media.ease-in":"Ease in","media.ease-out":"Ease out","media.ease-in-out":"Ease in / out","media.bezier":"Custom Bezier","selectionBar.count":"{count} selected","selectionBar.clear":"Clear","selectionBar.label":"Selection actions","zoom.label":"Zoom controls","zoom.out":"Zoom out","zoom.in":"Zoom in","zoom.value":"Zoom level","zoom.fit":"Fit to screen","zoom.fitShort":"Fit","zoom.fullscreen":"Enter fullscreen","zoom.exitFullscreen":"Exit fullscreen","zoom.fullscreenError":"Fullscreen is unavailable.","toolPanel.close":"Close panel","toolPanel.tabs":"Panel tabs","toolLayout.resize.sidebar":"Resize sidebar width","toolLayout.resize.inspector":"Resize inspector width","toolLayout.resize.bottom":"Resize bottom panel height","password.weak":"Weak","password.fair":"Fair","password.good":"Strong","password.veryStrong":"Very strong","rating.high":"Very good","rating.medium":"Average","rating.low":"Low","rating.label":"Rating {value}","rating.review":"{count} review","rating.reviews":"{count} reviews","countdown.days":"Days","countdown.hours":"Hours","countdown.minutes":"Minutes","countdown.seconds":"Seconds","upload.title":"Drag files here or click to choose","upload.choose":"Choose files","upload.allFormats":"All file types supported","upload.maximum":" (up to {size})","upload.files":"Uploaded files ({count})","upload.retry":"Retry","upload.remove":"Remove","upload.illustration":"Upload illustration","creditCard.security":"Security code {code}","creditCard.label":"{issuer} card{lastFour}, flip","creditCard.ending":", ending in {digits}","creditCard.front":"Front","creditCard.back":"Back","creditCard.showing":"Showing {side}","creditCard.expiry":"Expires {expiry}","creditCard.show":"Press to show {side}","flight.scheduled":"Scheduled","flight.boarding":"Boarding","flight.in-flight":"In flight","flight.landed":"Landed","flight.delayed":"Delayed","flight.cancelled":"Cancelled","flight.title":"Flight status","flight.progress":"Journey progress","boardingPass.origin":"From","boardingPass.destination":"To","boardingPass.passenger":"Passenger","boardingPass.service":"Flight","boardingPass.date":"Date","boardingPass.boarding":"Boarding","boardingPass.departure":"Departure","boardingPass.arrival":"Arrival","boardingPass.terminal":"Terminal","boardingPass.gate":"Gate","boardingPass.seat":"Seat","boardingPass.travelClass":"Class","boardingPass.title":"Boarding pass","wallet.title":"My boarding passes","wallet.empty":"No boarding passes yet.","wallet.journeys":"Choose a journey","receipt.quantity":"Qty","receipt.subtotal":"Subtotal","receipt.discount":"Discount","receipt.tax":"Tax","receipt.total":"Total","receipt.payment":"Payment","receipt.empty":"No items to display.","receipt.invalid":"Check the items and amounts.","receipt.title":"Receipt","ticket.title":"Ticket","paper.codeImage":"Code image","aiElement.emptyFolder":"Empty folder","aiElement.noFiles":"No files to display.","attachment.title":"Attachments","attachment.empty":"No attachments yet.","attachment.queued":"Queued","attachment.uploading":"Uploading","attachment.processing":"Processing","attachment.ready":"Ready","attachment.error":"Failed","attachment.cancelled":"Cancelled","attachment.unknown":"Checking progress…","attachment.cancel":"Cancel","attachment.retry":"Retry","attachment.remove":"Remove from list","attachment.open":"View details","attachment.reload":"Reload","workspace.members":"Workspace members","workspace.invite":"Invite members","workspace.email":"Email","workspace.role":"Role","workspace.chooseRole":"Choose a role","workspace.description":"Review the email address and role of the person you want to invite.","workspace.submit":"Send invitation","workspace.emailError":"Enter a valid email address of up to 254 characters.","workspace.roleError":"Choose a role for the invitation.","workspace.retry":"Reload","workspace.reinvite":"Invite again","workspace.active":"Active","workspace.invited":"Invitation pending","workspace.expired":"Invitation expired","workspace.empty":"No members to display yet.","aiElement.pending":"Pending","aiElement.running":"In progress","aiElement.complete":"Complete","aiElement.error":"Failed","aiElement.cancelled":"Cancelled","aiElement.approval":"Awaiting approval","aiElement.researching":"Reviewing sources","aiElement.researched":"View research","aiElement.thinking":"Thinking","aiElement.reasoning":"Response summary","aiElement.plan":"Plan","aiElement.queue":"Queued requests","aiElement.remove":"Remove","aiElement.queueEmpty":"No queued requests.","aiElement.sources":"Sources","aiElement.citation":"Source","aiElement.checkpoint":"Checkpoint saved","aiElement.restore":"Restore from here","aiElement.confirmation":"Proceed with this action?","aiElement.allow":"Allow","aiElement.reject":"Reject","aiElement.accepted":"Action approved.","aiElement.rejected":"Action rejected.","aiElement.context":"Context","aiElement.tokens":"Tokens","aiElement.unknown":"Usage is not available yet.","aiElement.files":"Files","aiElement.task":"Task progress","aiElement.subagent":"Subagent","aiElement.tool":"Tool call","aiElement.terminal":"Terminal","aiElement.stackTrace":"Error details","aiElement.tests":"Test results","aiElement.passed":"Passed","aiElement.failed":"Failed","aiElement.input":"Input","aiElement.output":"Output","aiElement.user":"User","aiElement.assistant":"AI response","aiElement.copy":"Copy","aiElement.regenerate":"Regenerate","aiElement.like":"Like","aiElement.dislike":"Dislike","aiElement.latest":"Latest response","aiElement.conversation":"AI conversation","notification.title":"Notifications","notification.filters":"Notification filters","notification.all":"All","notification.unread":"Unread","notification.readAll":"Mark displayed notifications as read","notification.markRead":"Mark as read","notification.retry":"Reload","notification.empty":"No notifications yet.","notification.caughtUp":"You’re all caught up.","notification.countUnknown":"Needs review","aiModel.label":"Model","aiModel.choose":"Choose a model","aiModel.empty":"No models are available right now.","aiModel.capabilities":"Capabilities","aiModel.usage":"Usage unit","aiQuote.expires":"Quote expires","aiQuote.title":"Execution estimate","aiQuote.reserved":"Maximum credit reservation","aiQuote.providerCost":"Estimated provider cost","aiQuote.pricingVersion":"Pricing version","aiQuote.unknown":"Unconfirmed","aiQuote.idle":"Choose a model and enter a request to get an estimate.","aiQuote.loading":"Checking the estimate…","aiQuote.executing":"Processing your request. Do not submit it again.","aiQuote.stale":"The model or input has changed. Refresh the estimate.","aiQuote.expired":"The estimate has expired. Request a new estimate.","aiQuote.balanceUnknown":"Your balance is unavailable. Refresh to try again.","aiQuote.insufficient":"Additional credits needed:","aiQuote.ready":"Credits are reserved when execution starts and settled against actual usage when it finishes.","aiQuote.refresh":"Refresh estimate","aiQuote.request":"Get estimate","aiQuote.execute":"Run","plan.current":"Current plan","plan.usage":"Usage this period","plan.allowed":"Included","plan.locked":"Not included","plan.manage":"Manage plan","plan.retry":"Reload","history.title":"Credit history","history.empty":"No history yet.","history.unresolved":"Checking","history.grant":"Granted","history.charge":"Used","history.hold":"Reserved · not settled","history.release":"Reservation released","credits.choosePlan":"Credits to add","credits.rechargeDescription":"Choose an amount and review the price and terms.","credits.paymentAmount":"Payment amount","credits.paymentPending":"Checking payment status","credits.remaining":"Remaining credits","credits.available":"Available credits","credits.unit":"credits","credits.held":"Reserved","credits.spent":"Total used","credits.charged":"Charged","credits.low":"Your credit balance is running low.","credits.recharge":"Add credits","credits.checkout":"Continue to payment","credits.noPlans":"No credit packs are available right now.","aiTask.title":"AI task","aiTask.idle":"Not started","aiTask.queued":"Queued","aiTask.running":"Running","aiTask.succeeded":"Complete","aiTask.failed":"Failed","aiTask.cancelled":"Cancelled","aiTask.needs_attention":"Review needed","aiTask.attentionDescription":"We’re checking the result and usage. Reserved credits remain on hold until the review is complete.",close:"Close","modal.close":"Close modal","drawer.close":"Close drawer","dialog.close":"Close dialog","toast.close":"Dismiss notification",clear:"Clear",clearAll:"Clear all",loading:"Loading",required:"Required",optional:"Optional",error:"Error",success:"Success",warning:"Warning",info:"Information",previous:"Previous",next:"Next","select.placeholder":"Select…","select.noResults":"No results","navigationRail.label":"Main navigation","navigationRail.newNotification":"New notification","navigationRail.notifications":"Notifications","tabs.label":"Tabs","pagination.label":"Pagination","search.label":"Search","media.play":"Play","media.pause":"Pause","media.mute":"Mute","media.unmute":"Unmute","media.settings":"Settings","media.shuffle":"Shuffle","media.repeat":"Repeat","media.previous":"Previous track","media.next":"Next track","media.fullscreen":"Full screen","confirm.ok":"Confirm","confirm.cancel":"Cancel","carousel.slide":"slide","carousel.role":"carousel","carousel.previous":"Previous slide","carousel.next":"Next slide","breadcrumbs.expand":"Show hidden path","input.showPassword":"Show password","input.hidePassword":"Hide password","input.clearSearch":"Clear search","verify.done":"Verified","verify.phoneDone":"Your phone number has been verified.","bentoCard.cta":"View details","tweetCard.loading":"Loading post…","tweetCard.error":"Unable to load this post.","tweetCard.viewOnTwitter":"View on X","tweetCard.like":"Like","tweetCard.retweet":"Repost","tweetCard.reply":"Reply","tweetCard.verified":"Verified account","tweetCard.retry":"Try again","chatComposer.placeholder":"Type a message…","chatComposer.send":"Send","chatComposer.stop":"Stop generating","chatComposer.attach":"Attach","voice.start":"Voice input","voice.stop":"Finish dictation","voice.cancel":"Cancel voice input","voice.starting":"Connecting microphone","voice.listening":"Listening","voice.stopping":"Finishing transcription","voice.error":"Voice recognition failed","voice.unsupported":"Voice input not supported"};export{e as ENGLISH_STRINGS};