gsap-timeline-viewer 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,24 @@
1
- var GSAPTimelineViewer=function(o){"use strict";var C=Object.defineProperty;var D=(o,c,h)=>c in o?C(o,c,{enumerable:!0,configurable:!0,writable:!0,value:h}):o[c]=h;var s=(o,c,h)=>D(o,typeof c!="symbol"?c+"":c,h);let c=0;function h(n){if(!n||n.length===0)return"Unknown";const i=n[0];return i.id?`#${i.id}`:i.classList&&i.classList.length>0?`.${i.classList[0]}`:i.tagName?i.tagName.toLowerCase():"element"}function k(n){const i=["ease","duration","delay","onComplete","onStart","onUpdate","onCompleteParams","onStartParams","onUpdateParams","repeat","repeatDelay","yoyo","stagger","overwrite","immediateRender","lazy","autoAlpha","id","paused","reversed","startAt"];return Object.keys(n).filter(t=>!i.includes(t))}function S(n){const i=[];return n.getChildren(!0,!0,!1).forEach((e,a)=>{if(!("targets"in e))return;const r=e,l=r.targets(),d=r.vars||{},p=k(d);let v="";if(d.id&&typeof d.id=="string")v=d.id;else{const f=h(l),x=p.slice(0,2).join(", ");v=x?`${f} (${x})`:f}const b=e.startTime(),y=e.duration();i.push({id:`tween-${++c}`,label:v,startTime:b,endTime:b+y,duration:y,targets:h(l),properties:p,colorIndex:a%6})}),{duration:n.duration(),tweens:i}}function w(){c=0}function g(n,i=!0){const t=Math.abs(n);return i?t.toFixed(2):t.toFixed(0)}const T=":host{--gtv-bg: #1a1a1a;--gtv-bg-secondary: #252525;--gtv-border: #333;--gtv-text: #e0e0e0;--gtv-text-muted: #888;--gtv-accent: #4a9eff;--gtv-playhead: #4a9eff;--gtv-ruler-bg: #1f1f1f;--gtv-track-height: 28px;--gtv-controls-height: 40px;--gtv-ruler-height: 24px;--gtv-track-1: #3b82f6;--gtv-track-2: #f59e0b;--gtv-track-3: #ec4899;--gtv-track-4: #10b981;--gtv-track-5: #8b5cf6;--gtv-track-6: #ef4444}*{box-sizing:border-box;margin:0;padding:0}.gtv-container{position:fixed;bottom:0;left:0;right:0;background:var(--gtv-bg);border-top:1px solid var(--gtv-border);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:12px;color:var(--gtv-text);z-index:999999;display:flex;flex-direction:column}.gtv-container.collapsed{height:auto!important}.gtv-container.collapsed .gtv-timeline-area{display:none}.gtv-controls{display:flex;align-items:center;justify-content:space-between;height:var(--gtv-controls-height);padding:0 12px;background:var(--gtv-bg-secondary);border-bottom:1px solid var(--gtv-border);gap:16px}.gtv-controls-left,.gtv-controls-center,.gtv-controls-right{display:flex;align-items:center;gap:8px}.gtv-controls-center{flex:0 0 auto}.gtv-time-display{font-variant-numeric:tabular-nums;min-width:100px;text-align:center}.gtv-time-current{color:var(--gtv-text)}.gtv-time-total{color:var(--gtv-text-muted)}.gtv-btn{display:flex;align-items:center;justify-content:center;width:28px;height:28px;background:transparent;border:none;border-radius:4px;color:var(--gtv-text);cursor:pointer;transition:background .15s}.gtv-btn:hover{background:#ffffff1a}.gtv-btn:active{background:#ffffff26}.gtv-btn.active{color:var(--gtv-accent)}.gtv-btn svg{width:16px;height:16px;fill:currentColor}.gtv-btn-play svg{width:20px;height:20px}.gtv-speed-btn{width:auto;padding:0 8px;font-size:11px;font-weight:500}.gtv-collapse-btn{margin-left:auto}.gtv-timeline-area{display:flex;flex-direction:column;overflow:hidden;flex:1}.gtv-ruler{position:relative;height:var(--gtv-ruler-height);background:var(--gtv-ruler-bg);border-bottom:1px solid var(--gtv-border);overflow:hidden;flex-shrink:0}.gtv-ruler-inner{position:relative;height:100%}.gtv-ruler-marker{position:absolute;top:0;height:100%;display:flex;flex-direction:column;align-items:center}.gtv-ruler-marker-line{width:1px;height:6px;background:var(--gtv-text-muted)}.gtv-ruler-marker-label{font-size:10px;color:var(--gtv-text-muted);margin-top:2px}.gtv-tracks-container{position:relative;overflow-y:auto;overflow-x:hidden;flex:1}.gtv-tracks-scroll{position:relative;min-height:100%}.gtv-track{position:relative;height:var(--gtv-track-height);border-bottom:1px solid var(--gtv-border)}.gtv-track-bar{position:absolute;top:4px;height:calc(var(--gtv-track-height) - 8px);border-radius:4px;display:flex;align-items:center;padding:0 8px;font-size:11px;font-weight:500;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:default;transition:filter .15s}.gtv-track-bar:hover{filter:brightness(1.1)}.gtv-track-bar.is-active{box-shadow:0 0 0 2px var(--gtv-accent)}.gtv-playhead-container{position:absolute;top:0;bottom:0;width:1px;pointer-events:none;z-index:10}.gtv-playhead-head{position:absolute;top:0;left:-5px;width:11px;height:11px;background:var(--gtv-playhead);clip-path:polygon(50% 100%,0 0,100% 0)}.gtv-playhead-line{position:absolute;top:11px;bottom:0;left:0;width:1px;background:var(--gtv-playhead)}.gtv-scrub-area{position:absolute;top:0;left:0;right:0;bottom:0;cursor:pointer}.gtv-empty{display:flex;align-items:center;justify-content:center;padding:24px;color:var(--gtv-text-muted)}",L=["var(--gtv-track-1)","var(--gtv-track-2)","var(--gtv-track-3)","var(--gtv-track-4)","var(--gtv-track-5)","var(--gtv-track-6)"],u=[.25,.5,1,2,4];class m extends HTMLElement{constructor(){super();s(this,"shadow");s(this,"timeline",null);s(this,"timelineData",null);s(this,"isPlaying",!1);s(this,"isLooping",!1);s(this,"speedIndex",2);s(this,"collapsed",!1);s(this,"height",200);s(this,"isDragging",!1);s(this,"container");s(this,"playBtn");s(this,"loopBtn");s(this,"speedBtn");s(this,"timeDisplay");s(this,"rulerInner");s(this,"tracksScroll");s(this,"playhead");s(this,"scrubArea");this.shadow=this.attachShadow({mode:"open"})}connectedCallback(){this.render(),this.setupEventListeners()}disconnectedCallback(){this.detachTimeline()}setTimeline(t){this.detachTimeline(),this.timeline=t,w(),this.timelineData=S(t),t.eventCallback("onUpdate",()=>this.onTimelineUpdate()),this.renderTracks(),this.updatePlayhead(),this.updateTimeDisplay(),this.updatePlayState()}detachTimeline(){this.timeline&&(this.timeline.eventCallback("onUpdate",null),this.timeline=null,this.timelineData=null)}render(){this.shadow.innerHTML=`
1
+ var GSAPTimelineViewer=function(o){"use strict";var P=Object.defineProperty;var L=(o,c,h)=>c in o?P(o,c,{enumerable:!0,configurable:!0,writable:!0,value:h}):o[c]=h;var n=(o,c,h)=>L(o,typeof c!="symbol"?c+"":c,h);let c=0;function h(l){if(!l||l.length===0)return"Unknown";const s=l[0];return s.id?`#${s.id}`:s.classList&&s.classList.length>0?`.${s.classList[0]}`:s.tagName?s.tagName.toLowerCase():"element"}function x(l){const s=["ease","duration","delay","onComplete","onStart","onUpdate","onCompleteParams","onStartParams","onUpdateParams","repeat","repeatDelay","yoyo","stagger","overwrite","immediateRender","lazy","autoAlpha","id","paused","reversed","startAt"];return Object.keys(l).filter(t=>!s.includes(t))}function w(l){const s=[];return l.getChildren(!0,!0,!1).forEach((e,i)=>{if(!("targets"in e))return;const a=e,r=a.targets(),d=a.vars||{},p=x(d);let v="";if(d.id&&typeof d.id=="string")v=d.id;else{const b=h(r),k=p.slice(0,2).join(", ");v=k?`${b} (${k})`:b}const y=e.startTime(),f=e.duration();s.push({id:`tween-${++c}`,label:v,startTime:y,endTime:y+f,duration:f,targets:h(r),properties:p,colorIndex:i%6})}),{duration:l.duration(),tweens:s}}function S(){c=0}function g(l,s=!0){const t=Math.abs(l);return s?t.toFixed(2):t.toFixed(0)}const T=":host{--gtv-bg: #1a1a1a;--gtv-bg-secondary: #252525;--gtv-border: #333;--gtv-text: #e0e0e0;--gtv-text-muted: #888;--gtv-accent: oklch(65% .15 220);--gtv-playhead: oklch(65% .15 220);--gtv-ruler-bg: #1f1f1f;--gtv-track-height: 28px;--gtv-controls-height: 40px;--gtv-ruler-height: 24px;--gtv-timeline-padding: 16px;--gtv-track-1: oklch(50% .12 220);--gtv-track-1-active: oklch(60% .15 220);--gtv-track-2: oklch(50% .12 70);--gtv-track-2-active: oklch(60% .15 70);--gtv-track-3: oklch(50% .12 350);--gtv-track-3-active: oklch(60% .15 350);--gtv-track-4: oklch(50% .12 160);--gtv-track-4-active: oklch(60% .15 160);--gtv-track-5: oklch(50% .12 290);--gtv-track-5-active: oklch(60% .15 290);--gtv-track-6: oklch(50% .12 25);--gtv-track-6-active: oklch(60% .15 25)}*{box-sizing:border-box;margin:0;padding:0}.gtv-container{position:fixed;bottom:0;left:0;right:0;background:var(--gtv-bg);border-top:1px solid var(--gtv-border);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:12px;color:var(--gtv-text);z-index:999999;display:flex;flex-direction:column;user-select:none;-webkit-user-select:none}.gtv-container.collapsed{height:auto!important}.gtv-container.collapsed .gtv-timeline-area{display:none}.gtv-controls{position:relative;display:flex;align-items:center;justify-content:center;height:var(--gtv-controls-height);padding:0 12px;background:var(--gtv-bg-secondary);border-bottom:1px solid var(--gtv-border);gap:8px}.gtv-controls-left{position:absolute;left:12px;display:flex;align-items:center;gap:8px}.gtv-controls-center{display:flex;align-items:center;gap:8px}.gtv-controls-right{position:absolute;right:12px;display:flex;align-items:center;gap:8px}.gtv-time-display{font-variant-numeric:tabular-nums;min-width:100px;text-align:center}.gtv-time-current{color:var(--gtv-text)}.gtv-time-total{color:var(--gtv-text-muted)}.gtv-btn{display:flex;align-items:center;justify-content:center;width:28px;height:28px;background:transparent;border:none;border-radius:4px;color:var(--gtv-text);cursor:pointer;transition:background .15s}.gtv-btn:hover{background:#ffffff1a}.gtv-btn:active{background:#ffffff26}.gtv-btn.active{color:var(--gtv-accent)}.gtv-btn svg{width:16px;height:16px;fill:currentColor}.gtv-btn-play svg{width:20px;height:20px}.gtv-speed-btn{width:auto;padding:0 8px;font-size:11px;font-weight:500}.gtv-collapse-btn{margin-left:auto}.gtv-timeline-area{position:relative;display:flex;flex-direction:column;overflow:hidden;flex:1}.gtv-resize-handle{position:absolute;top:0;left:0;right:0;height:6px;cursor:ns-resize;z-index:20}.gtv-resize-handle:hover,.gtv-resize-handle:active{background:#ffffff1a}.gtv-ruler{position:relative;height:var(--gtv-ruler-height);background:var(--gtv-ruler-bg);border-bottom:1px solid var(--gtv-border);overflow:visible;flex-shrink:0;padding:0 var(--gtv-timeline-padding)}.gtv-ruler-inner{position:relative;height:100%;width:100%}.gtv-ruler-marker{position:absolute;top:0;height:100%;display:flex;flex-direction:column;align-items:center}.gtv-ruler-marker-line{width:1px;height:6px;background:var(--gtv-text-muted)}.gtv-ruler-marker-label{font-size:10px;color:var(--gtv-text-muted);margin-top:2px}.gtv-tracks-container{position:relative;overflow-y:auto;overflow-x:hidden;flex:1;padding:0 var(--gtv-timeline-padding)}.gtv-tracks-scroll{position:relative;min-height:100%;width:100%}.gtv-track{position:relative;height:var(--gtv-track-height)}.gtv-track-bar{position:absolute;top:4px;height:calc(var(--gtv-track-height) - 8px);border-radius:4px;display:flex;align-items:center;padding:0 8px;font-size:11px;font-weight:500;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:default;transition:filter .15s}.gtv-track-bar:hover{filter:brightness(1.1)}.gtv-playhead-wrapper{position:absolute;top:0;bottom:0;left:var(--gtv-timeline-padding);right:var(--gtv-timeline-padding);pointer-events:none;z-index:15}.gtv-playhead{position:absolute;top:0;bottom:0;width:0;left:0}.gtv-playhead-head{position:absolute;top:6px;left:-5px;width:11px;height:11px;background:var(--gtv-playhead);clip-path:polygon(50% 100%,0 0,100% 0)}.gtv-playhead-line{position:absolute;top:6px;bottom:0;left:0;width:1px;background:var(--gtv-playhead)}.gtv-scrub-area{position:absolute;top:0;left:0;right:0;bottom:0;cursor:ew-resize}.gtv-empty{display:flex;align-items:center;justify-content:center;padding:24px;color:var(--gtv-text-muted)}",u=[.25,.5,1,2,4];class m extends HTMLElement{constructor(){super();n(this,"shadow");n(this,"timeline",null);n(this,"timelineData",null);n(this,"isPlaying",!1);n(this,"isLooping",!1);n(this,"speedIndex",2);n(this,"collapsed",!1);n(this,"height",200);n(this,"isDragging",!1);n(this,"container");n(this,"playBtn");n(this,"loopBtn");n(this,"speedBtn");n(this,"timeDisplay");n(this,"rulerInner");n(this,"tracksScroll");n(this,"playhead");n(this,"scrubArea");n(this,"resizeHandle");n(this,"isResizing",!1);this.shadow=this.attachShadow({mode:"open"})}connectedCallback(){this.render(),this.setupEventListeners()}disconnectedCallback(){this.detachTimeline()}setTimeline(t){this.detachTimeline(),this.timeline=t,S(),this.timelineData=w(t),t.eventCallback("onUpdate",()=>this.onTimelineUpdate()),this.renderTracks(),this.updatePlayhead(),this.updateTimeDisplay(),this.updatePlayState()}detachTimeline(){this.timeline&&(this.timeline.eventCallback("onUpdate",null),this.timeline=null,this.timelineData=null)}render(){this.shadow.innerHTML=`
2
2
  <style>${T}</style>
3
3
  <div class="gtv-container ${this.collapsed?"collapsed":""}" style="height: ${this.height}px;">
4
4
  <!-- Controls Bar -->
5
5
  <div class="gtv-controls">
6
6
  <div class="gtv-controls-left">
7
+ <button class="gtv-btn" data-action="loop" title="Loop (L)">
8
+ <svg viewBox="0 0 24 24"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></svg>
9
+ </button>
10
+ <button class="gtv-btn gtv-speed-btn" data-action="speed" title="Playback speed">1x</button>
11
+ </div>
12
+
13
+ <div class="gtv-controls-center">
14
+ <span class="gtv-time-display">
15
+ <span class="gtv-time-current">0.00</span>
16
+ <span class="gtv-time-total"> / 0.00</span>
17
+ </span>
7
18
  <button class="gtv-btn" data-action="skip-start" title="Skip to start">
8
19
  <svg viewBox="0 0 24 24"><path d="M6 6h2v12H6V6zm3.5 6l8.5 6V6l-8.5 6z"/></svg>
9
20
  </button>
10
- <button class="gtv-btn gtv-btn-play" data-action="play" title="Play/Pause">
21
+ <button class="gtv-btn gtv-btn-play" data-action="play" title="Play/Pause (Space)">
11
22
  <svg class="play-icon" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>
12
23
  <svg class="pause-icon" viewBox="0 0 24 24" style="display: none;"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
13
24
  </button>
@@ -16,17 +27,6 @@ var GSAPTimelineViewer=function(o){"use strict";var C=Object.defineProperty;var
16
27
  </button>
17
28
  </div>
18
29
 
19
- <div class="gtv-controls-center">
20
- <button class="gtv-btn" data-action="loop" title="Loop">
21
- <svg viewBox="0 0 24 24"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></svg>
22
- </button>
23
- <span class="gtv-time-display">
24
- <span class="gtv-time-current">0.00</span>
25
- <span class="gtv-time-total"> / 0.00</span>
26
- </span>
27
- <button class="gtv-btn gtv-speed-btn" data-action="speed" title="Playback speed">1x</button>
28
- </div>
29
-
30
30
  <div class="gtv-controls-right">
31
31
  <button class="gtv-btn gtv-collapse-btn" data-action="collapse" title="Collapse/Expand">
32
32
  <svg viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"/></svg>
@@ -36,37 +36,43 @@ var GSAPTimelineViewer=function(o){"use strict";var C=Object.defineProperty;var
36
36
 
37
37
  <!-- Timeline Area -->
38
38
  <div class="gtv-timeline-area">
39
+ <!-- Resize Handle -->
40
+ <div class="gtv-resize-handle"></div>
41
+
39
42
  <!-- Ruler -->
40
43
  <div class="gtv-ruler">
41
44
  <div class="gtv-ruler-inner"></div>
42
- <div class="gtv-playhead-container">
43
- <div class="gtv-playhead-head"></div>
44
- </div>
45
45
  </div>
46
46
 
47
47
  <!-- Tracks -->
48
48
  <div class="gtv-tracks-container">
49
49
  <div class="gtv-tracks-scroll">
50
50
  <div class="gtv-scrub-area"></div>
51
- <div class="gtv-playhead-container">
52
- <div class="gtv-playhead-line"></div>
53
- </div>
54
51
  </div>
55
52
  <div class="gtv-empty">No timeline attached. Call setTimeline() to visualize a GSAP timeline.</div>
56
53
  </div>
54
+
55
+ <!-- Playhead spans entire timeline area -->
56
+ <div class="gtv-playhead-wrapper">
57
+ <div class="gtv-playhead">
58
+ <div class="gtv-playhead-head"></div>
59
+ <div class="gtv-playhead-line"></div>
60
+ </div>
61
+ </div>
57
62
  </div>
58
63
  </div>
59
- `,this.container=this.shadow.querySelector(".gtv-container"),this.playBtn=this.shadow.querySelector('[data-action="play"]'),this.loopBtn=this.shadow.querySelector('[data-action="loop"]'),this.speedBtn=this.shadow.querySelector('[data-action="speed"]'),this.timeDisplay=this.shadow.querySelector(".gtv-time-display"),this.rulerInner=this.shadow.querySelector(".gtv-ruler-inner"),this.tracksScroll=this.shadow.querySelector(".gtv-tracks-scroll"),this.playhead=this.shadow.querySelector(".gtv-ruler .gtv-playhead-container"),this.scrubArea=this.shadow.querySelector(".gtv-scrub-area")}setupEventListeners(){this.shadow.addEventListener("click",t=>{const a=t.target.closest("[data-action]");if(!a)return;switch(a.dataset.action){case"play":this.togglePlay();break;case"skip-start":this.skipToStart();break;case"skip-end":this.skipToEnd();break;case"loop":this.toggleLoop();break;case"speed":this.cycleSpeed();break;case"collapse":this.toggleCollapse();break}}),this.scrubArea.addEventListener("mousedown",t=>this.startScrub(t)),this.shadow.querySelector(".gtv-ruler").addEventListener("mousedown",t=>this.startScrub(t)),document.addEventListener("mousemove",t=>this.onScrub(t)),document.addEventListener("mouseup",()=>this.endScrub()),document.addEventListener("keydown",t=>{t.target===document.body&&t.code==="Space"&&(t.preventDefault(),this.togglePlay())})}startScrub(t){this.timeline&&(this.isDragging=!0,this.scrubToPosition(t))}onScrub(t){!this.isDragging||!this.timeline||this.scrubToPosition(t)}endScrub(){this.isDragging=!1}scrubToPosition(t){if(!this.timeline||!this.timelineData)return;const e=this.rulerInner.getBoundingClientRect(),r=Math.max(0,Math.min(t.clientX-e.left,e.width))/e.width;this.timeline.progress(r),this.timeline.pause(),this.updatePlayState()}togglePlay(){this.timeline&&(this.timeline.paused()||this.timeline.progress()===1?this.timeline.progress()===1?this.timeline.restart():this.timeline.play():this.timeline.pause(),this.updatePlayState())}skipToStart(){this.timeline&&(this.timeline.progress(0),this.timeline.pause(),this.updatePlayState())}skipToEnd(){this.timeline&&(this.timeline.progress(1),this.timeline.pause(),this.updatePlayState())}toggleLoop(){this.timeline&&(this.isLooping=!this.isLooping,this.timeline.repeat(this.isLooping?-1:0),this.loopBtn.classList.toggle("active",this.isLooping))}cycleSpeed(){if(!this.timeline)return;this.speedIndex=(this.speedIndex+1)%u.length;const t=u[this.speedIndex];this.timeline.timeScale(t),this.speedBtn.textContent=`${t}x`}toggleCollapse(){this.collapsed=!this.collapsed,this.container.classList.toggle("collapsed",this.collapsed);const t=this.shadow.querySelector('[data-action="collapse"]');t.innerHTML=this.collapsed?'<svg viewBox="0 0 24 24"><path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6z"/></svg>':'<svg viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"/></svg>'}updatePlayState(){if(!this.timeline)return;this.isPlaying=!this.timeline.paused()&&this.timeline.progress()<1;const t=this.playBtn.querySelector(".play-icon"),e=this.playBtn.querySelector(".pause-icon");t.style.display=this.isPlaying?"none":"block",e.style.display=this.isPlaying?"block":"none"}onTimelineUpdate(){this.updatePlayhead(),this.updateTimeDisplay(),this.updateActiveTracks(),this.updatePlayState()}updatePlayhead(){if(!this.timeline||!this.timelineData)return;const e=`${this.timeline.progress()*100}%`;this.playhead.style.left=e;const a=this.tracksScroll.querySelector(".gtv-playhead-container");a&&(a.style.left=e)}updateTimeDisplay(){if(!this.timeline||!this.timelineData)return;const t=this.timeline.time(),e=this.timelineData.duration,a=this.timeDisplay.querySelector(".gtv-time-current"),r=this.timeDisplay.querySelector(".gtv-time-total");a.textContent=g(t),r.textContent=` / ${g(e)}`}updateActiveTracks(){if(!this.timeline||!this.timelineData)return;const t=this.timeline.time();this.tracksScroll.querySelectorAll(".gtv-track-bar").forEach((a,r)=>{const l=this.timelineData.tweens[r],d=t>=l.startTime&&t<=l.endTime;a.classList.toggle("is-active",d)})}renderTracks(){if(!this.timelineData)return;const{duration:t,tweens:e}=this.timelineData,a=this.shadow.querySelector(".gtv-empty");a.style.display=e.length>0?"none":"flex",this.renderRuler(t);const r=e.map(p=>this.renderTrack(p,t)).join(""),l=this.tracksScroll.querySelector(".gtv-scrub-area"),d=this.tracksScroll.querySelector(".gtv-playhead-container");this.tracksScroll.innerHTML=r,this.tracksScroll.prepend(l),this.tracksScroll.appendChild(d),this.scrubArea=l}renderRuler(t){const e=[],a=this.calculateInterval(t);for(let r=0;r<=t;r+=a){const l=r/t*100;e.push(`
60
- <div class="gtv-ruler-marker" style="left: ${l}%;">
64
+ `,this.container=this.shadow.querySelector(".gtv-container"),this.playBtn=this.shadow.querySelector('[data-action="play"]'),this.loopBtn=this.shadow.querySelector('[data-action="loop"]'),this.speedBtn=this.shadow.querySelector('[data-action="speed"]'),this.timeDisplay=this.shadow.querySelector(".gtv-time-display"),this.rulerInner=this.shadow.querySelector(".gtv-ruler-inner"),this.tracksScroll=this.shadow.querySelector(".gtv-tracks-scroll"),this.playhead=this.shadow.querySelector(".gtv-playhead"),this.scrubArea=this.shadow.querySelector(".gtv-scrub-area"),this.resizeHandle=this.shadow.querySelector(".gtv-resize-handle")}setupEventListeners(){this.shadow.addEventListener("click",t=>{const i=t.target.closest("[data-action]");if(!i)return;switch(i.dataset.action){case"play":this.togglePlay();break;case"skip-start":this.skipToStart();break;case"skip-end":this.skipToEnd();break;case"loop":this.toggleLoop();break;case"speed":this.cycleSpeed();break;case"collapse":this.toggleCollapse();break}}),this.scrubArea.addEventListener("mousedown",t=>this.startScrub(t)),this.shadow.querySelector(".gtv-ruler").addEventListener("mousedown",t=>this.startScrub(t)),document.addEventListener("mousemove",t=>{this.onScrub(t),this.onResize(t)}),document.addEventListener("mouseup",()=>{this.endScrub(),this.endResize()}),this.resizeHandle.addEventListener("mousedown",t=>this.startResize(t)),document.addEventListener("keydown",t=>{if(t.target===document.body)switch(t.code){case"Space":t.preventDefault(),this.togglePlay();break;case"KeyJ":t.preventDefault(),this.jumpToPrevPoint();break;case"KeyK":t.preventDefault(),this.jumpToNextPoint();break;case"KeyL":t.preventDefault(),this.toggleLoop();break}})}startScrub(t){this.timeline&&(t.preventDefault(),this.isDragging=!0,document.body.style.cursor="ew-resize",document.body.style.userSelect="none",this.scrubToPosition(t))}onScrub(t){!this.isDragging||!this.timeline||this.scrubToPosition(t)}endScrub(){this.isDragging=!1,document.body.style.cursor="",document.body.style.userSelect=""}startResize(t){t.preventDefault(),this.isResizing=!0,document.body.style.cursor="ns-resize",document.body.style.userSelect="none"}onResize(t){if(!this.isResizing)return;const e=window.innerHeight,i=e-t.clientY;this.height=Math.max(100,Math.min(i,e-100)),this.container.style.height=`${this.height}px`}endResize(){this.isResizing&&(this.isResizing=!1,document.body.style.cursor="",document.body.style.userSelect="")}scrubToPosition(t){if(!this.timeline||!this.timelineData)return;const e=this.rulerInner.getBoundingClientRect(),a=Math.max(0,Math.min(t.clientX-e.left,e.width))/e.width;this.timeline.progress(a),this.timeline.pause(),this.updatePlayState()}togglePlay(){this.timeline&&(this.timeline.paused()||this.timeline.progress()===1?this.timeline.progress()===1?this.timeline.restart():this.timeline.play():this.timeline.pause(),this.updatePlayState())}skipToStart(){this.timeline&&(this.timeline.progress(0),this.timeline.pause(),this.updatePlayState())}skipToEnd(){this.timeline&&(this.timeline.progress(1),this.timeline.pause(),this.updatePlayState())}getTimePoints(){if(!this.timelineData)return[0];const t=new Set;return t.add(0),t.add(Math.round(this.timelineData.duration*1e3)/1e3),this.timelineData.tweens.forEach(e=>{t.add(Math.round(e.startTime*1e3)/1e3),t.add(Math.round(e.endTime*1e3)/1e3)}),Array.from(t).sort((e,i)=>e-i)}jumpToPrevPoint(){if(!this.timeline||!this.timelineData)return;const t=Math.round(this.timeline.time()*1e3)/1e3,e=this.getTimePoints();let i=0;for(const a of e)if(a<t-.001)i=a;else break;this.timeline.time(i),this.timeline.pause(),this.updatePlayState()}jumpToNextPoint(){if(!this.timeline||!this.timelineData)return;const t=Math.round(this.timeline.time()*1e3)/1e3,e=this.getTimePoints();let i=this.timelineData.duration;for(const a of e)if(a>t+.001){i=a;break}this.timeline.time(i),this.timeline.pause(),this.updatePlayState()}toggleLoop(){this.timeline&&(this.isLooping=!this.isLooping,this.timeline.repeat(this.isLooping?-1:0),this.loopBtn.classList.toggle("active",this.isLooping))}cycleSpeed(){if(!this.timeline)return;this.speedIndex=(this.speedIndex+1)%u.length;const t=u[this.speedIndex];this.timeline.timeScale(t),this.speedBtn.textContent=`${t}x`}toggleCollapse(){this.collapsed=!this.collapsed,this.container.classList.toggle("collapsed",this.collapsed);const t=this.shadow.querySelector('[data-action="collapse"]');t.innerHTML=this.collapsed?'<svg viewBox="0 0 24 24"><path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6z"/></svg>':'<svg viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"/></svg>'}updatePlayState(){if(!this.timeline)return;this.isPlaying=!this.timeline.paused()&&this.timeline.progress()<1;const t=this.playBtn.querySelector(".play-icon"),e=this.playBtn.querySelector(".pause-icon");t.style.display=this.isPlaying?"none":"block",e.style.display=this.isPlaying?"block":"none"}onTimelineUpdate(){this.updatePlayhead(),this.updateTimeDisplay(),this.updateActiveTracks(),this.updatePlayState()}updatePlayhead(){if(!this.timeline||!this.timelineData)return;const t=this.timeline.progress();this.playhead.style.left=`${t*100}%`}updateTimeDisplay(){if(!this.timeline||!this.timelineData)return;const t=this.timeline.time(),e=this.timelineData.duration,i=this.timeDisplay.querySelector(".gtv-time-current"),a=this.timeDisplay.querySelector(".gtv-time-total");i.textContent=g(t),a.textContent=` / ${g(e)}`}updateActiveTracks(){if(!this.timeline||!this.timelineData)return;const t=this.timeline.time();this.tracksScroll.querySelectorAll(".gtv-track-bar").forEach((i,a)=>{const r=this.timelineData.tweens[a],d=t>=r.startTime&&t<=r.endTime,p=i.dataset.color;d?i.style.background=`var(--gtv-track-${p}-active)`:i.style.background=`var(--gtv-track-${p})`})}renderTracks(){if(!this.timelineData)return;const{duration:t,tweens:e}=this.timelineData,i=this.shadow.querySelector(".gtv-empty");i.style.display=e.length>0?"none":"flex",this.renderRuler(t);const a=e.map(d=>this.renderTrack(d,t)).join(""),r=this.tracksScroll.querySelector(".gtv-scrub-area");this.tracksScroll.innerHTML=a,this.tracksScroll.prepend(r),this.scrubArea=r}renderRuler(t){const e=[],i=this.calculateInterval(t);for(let a=0;a<=t;a+=i){const r=a/t*100;e.push(`
65
+ <div class="gtv-ruler-marker" style="left: ${r}%;">
61
66
  <div class="gtv-ruler-marker-line"></div>
62
- <span class="gtv-ruler-marker-label">${g(r,!1)}s</span>
67
+ <span class="gtv-ruler-marker-label">${g(a,!1)}s</span>
63
68
  </div>
64
- `)}this.rulerInner.innerHTML=e.join("")}calculateInterval(t){return t<=1?.25:t<=3?.5:t<=10?1:t<=30?5:10}renderTrack(t,e){const a=t.startTime/e*100,r=t.duration/e*100,l=L[t.colorIndex];return`
69
+ `)}this.rulerInner.innerHTML=e.join("")}calculateInterval(t){return t<=1?.25:t<=3?.5:t<=10?1:t<=30?5:10}renderTrack(t,e){const i=t.startTime/e*100,a=t.duration/e*100,r=t.colorIndex+1;return`
65
70
  <div class="gtv-track">
66
71
  <div class="gtv-track-bar"
67
- style="left: ${a}%; width: ${r}%; background: ${l};"
72
+ data-color="${r}"
73
+ style="left: ${i}%; width: ${a}%; background: var(--gtv-track-${r});"
68
74
  title="${t.label} (${g(t.startTime)}s - ${g(t.endTime)}s)">
69
75
  ${t.label}
70
76
  </div>
71
77
  </div>
72
- `}}customElements.define("gsap-timeline-viewer",m);class P{constructor(i){s(this,"element");this.element=document.createElement("gsap-timeline-viewer"),i.height&&this.element.style.setProperty("--viewer-height",`${i.height}px`),i.timeline&&setTimeout(()=>{this.element.setTimeline(i.timeline)},0)}attach(i=document.body){i.appendChild(this.element)}detach(){this.element.remove()}setTimeline(i){this.element.setTimeline(i)}get htmlElement(){return this.element}}return o.TimelineViewer=P,o.TimelineViewerElement=m,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"}),o}({});
78
+ `}}customElements.define("gsap-timeline-viewer",m);class z{constructor(s){n(this,"element");this.element=document.createElement("gsap-timeline-viewer"),s.height&&this.element.style.setProperty("--viewer-height",`${s.height}px`),s.timeline&&setTimeout(()=>{this.element.setTimeline(s.timeline)},0)}attach(s=document.body){s.appendChild(this.element)}detach(){this.element.remove()}setTimeline(s){this.element.setTimeline(s)}get htmlElement(){return this.element}}return o.TimelineViewer=z,o.TimelineViewerElement=m,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"}),o}({});
@@ -1,14 +1,14 @@
1
- var f = Object.defineProperty;
2
- var x = (a, e, t) => e in a ? f(a, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[e] = t;
3
- var s = (a, e, t) => x(a, typeof e != "symbol" ? e + "" : e, t);
4
- let y = 0;
5
- function m(a) {
6
- if (!a || a.length === 0) return "Unknown";
7
- const e = a[0];
8
- return e.id ? `#${e.id}` : e.classList && e.classList.length > 0 ? `.${e.classList[0]}` : e.tagName ? e.tagName.toLowerCase() : "element";
1
+ var b = Object.defineProperty;
2
+ var k = (r, i, t) => i in r ? b(r, i, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[i] = t;
3
+ var n = (r, i, t) => k(r, typeof i != "symbol" ? i + "" : i, t);
4
+ let f = 0;
5
+ function m(r) {
6
+ if (!r || r.length === 0) return "Unknown";
7
+ const i = r[0];
8
+ return i.id ? `#${i.id}` : i.classList && i.classList.length > 0 ? `.${i.classList[0]}` : i.tagName ? i.tagName.toLowerCase() : "element";
9
9
  }
10
- function k(a) {
11
- const e = [
10
+ function x(r) {
11
+ const i = [
12
12
  "ease",
13
13
  "duration",
14
14
  "delay",
@@ -31,74 +31,69 @@ function k(a) {
31
31
  "reversed",
32
32
  "startAt"
33
33
  ];
34
- return Object.keys(a).filter((t) => !e.includes(t));
34
+ return Object.keys(r).filter((t) => !i.includes(t));
35
35
  }
36
- function S(a) {
37
- const e = [];
38
- return a.getChildren(!0, !0, !1).forEach((i, r) => {
39
- if (!("targets" in i)) return;
40
- const n = i, l = n.targets(), o = n.vars || {}, d = k(o);
36
+ function w(r) {
37
+ const i = [];
38
+ return r.getChildren(!0, !0, !1).forEach((e, s) => {
39
+ if (!("targets" in e)) return;
40
+ const a = e, l = a.targets(), o = a.vars || {}, c = x(o);
41
41
  let h = "";
42
42
  if (o.id && typeof o.id == "string")
43
43
  h = o.id;
44
44
  else {
45
- const v = m(l), u = d.slice(0, 2).join(", ");
45
+ const v = m(l), u = c.slice(0, 2).join(", ");
46
46
  h = u ? `${v} (${u})` : v;
47
47
  }
48
- const g = i.startTime(), p = i.duration();
49
- e.push({
50
- id: `tween-${++y}`,
48
+ const p = e.startTime(), g = e.duration();
49
+ i.push({
50
+ id: `tween-${++f}`,
51
51
  label: h,
52
- startTime: g,
53
- endTime: g + p,
54
- duration: p,
52
+ startTime: p,
53
+ endTime: p + g,
54
+ duration: g,
55
55
  targets: m(l),
56
- properties: d,
57
- colorIndex: r % 6
56
+ properties: c,
57
+ colorIndex: s % 6
58
58
  });
59
59
  }), {
60
- duration: a.duration(),
61
- tweens: e
60
+ duration: r.duration(),
61
+ tweens: i
62
62
  };
63
63
  }
64
- function w() {
65
- y = 0;
64
+ function S() {
65
+ f = 0;
66
66
  }
67
- function c(a, e = !0) {
68
- const t = Math.abs(a);
69
- return e ? t.toFixed(2) : t.toFixed(0);
67
+ function d(r, i = !0) {
68
+ const t = Math.abs(r);
69
+ return i ? t.toFixed(2) : t.toFixed(0);
70
70
  }
71
- const T = ":host{--gtv-bg: #1a1a1a;--gtv-bg-secondary: #252525;--gtv-border: #333;--gtv-text: #e0e0e0;--gtv-text-muted: #888;--gtv-accent: #4a9eff;--gtv-playhead: #4a9eff;--gtv-ruler-bg: #1f1f1f;--gtv-track-height: 28px;--gtv-controls-height: 40px;--gtv-ruler-height: 24px;--gtv-track-1: #3b82f6;--gtv-track-2: #f59e0b;--gtv-track-3: #ec4899;--gtv-track-4: #10b981;--gtv-track-5: #8b5cf6;--gtv-track-6: #ef4444}*{box-sizing:border-box;margin:0;padding:0}.gtv-container{position:fixed;bottom:0;left:0;right:0;background:var(--gtv-bg);border-top:1px solid var(--gtv-border);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:12px;color:var(--gtv-text);z-index:999999;display:flex;flex-direction:column}.gtv-container.collapsed{height:auto!important}.gtv-container.collapsed .gtv-timeline-area{display:none}.gtv-controls{display:flex;align-items:center;justify-content:space-between;height:var(--gtv-controls-height);padding:0 12px;background:var(--gtv-bg-secondary);border-bottom:1px solid var(--gtv-border);gap:16px}.gtv-controls-left,.gtv-controls-center,.gtv-controls-right{display:flex;align-items:center;gap:8px}.gtv-controls-center{flex:0 0 auto}.gtv-time-display{font-variant-numeric:tabular-nums;min-width:100px;text-align:center}.gtv-time-current{color:var(--gtv-text)}.gtv-time-total{color:var(--gtv-text-muted)}.gtv-btn{display:flex;align-items:center;justify-content:center;width:28px;height:28px;background:transparent;border:none;border-radius:4px;color:var(--gtv-text);cursor:pointer;transition:background .15s}.gtv-btn:hover{background:#ffffff1a}.gtv-btn:active{background:#ffffff26}.gtv-btn.active{color:var(--gtv-accent)}.gtv-btn svg{width:16px;height:16px;fill:currentColor}.gtv-btn-play svg{width:20px;height:20px}.gtv-speed-btn{width:auto;padding:0 8px;font-size:11px;font-weight:500}.gtv-collapse-btn{margin-left:auto}.gtv-timeline-area{display:flex;flex-direction:column;overflow:hidden;flex:1}.gtv-ruler{position:relative;height:var(--gtv-ruler-height);background:var(--gtv-ruler-bg);border-bottom:1px solid var(--gtv-border);overflow:hidden;flex-shrink:0}.gtv-ruler-inner{position:relative;height:100%}.gtv-ruler-marker{position:absolute;top:0;height:100%;display:flex;flex-direction:column;align-items:center}.gtv-ruler-marker-line{width:1px;height:6px;background:var(--gtv-text-muted)}.gtv-ruler-marker-label{font-size:10px;color:var(--gtv-text-muted);margin-top:2px}.gtv-tracks-container{position:relative;overflow-y:auto;overflow-x:hidden;flex:1}.gtv-tracks-scroll{position:relative;min-height:100%}.gtv-track{position:relative;height:var(--gtv-track-height);border-bottom:1px solid var(--gtv-border)}.gtv-track-bar{position:absolute;top:4px;height:calc(var(--gtv-track-height) - 8px);border-radius:4px;display:flex;align-items:center;padding:0 8px;font-size:11px;font-weight:500;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:default;transition:filter .15s}.gtv-track-bar:hover{filter:brightness(1.1)}.gtv-track-bar.is-active{box-shadow:0 0 0 2px var(--gtv-accent)}.gtv-playhead-container{position:absolute;top:0;bottom:0;width:1px;pointer-events:none;z-index:10}.gtv-playhead-head{position:absolute;top:0;left:-5px;width:11px;height:11px;background:var(--gtv-playhead);clip-path:polygon(50% 100%,0 0,100% 0)}.gtv-playhead-line{position:absolute;top:11px;bottom:0;left:0;width:1px;background:var(--gtv-playhead)}.gtv-scrub-area{position:absolute;top:0;left:0;right:0;bottom:0;cursor:pointer}.gtv-empty{display:flex;align-items:center;justify-content:center;padding:24px;color:var(--gtv-text-muted)}", L = [
72
- "var(--gtv-track-1)",
73
- "var(--gtv-track-2)",
74
- "var(--gtv-track-3)",
75
- "var(--gtv-track-4)",
76
- "var(--gtv-track-5)",
77
- "var(--gtv-track-6)"
78
- ], b = [0.25, 0.5, 1, 2, 4];
79
- class P extends HTMLElement {
71
+ const T = ":host{--gtv-bg: #1a1a1a;--gtv-bg-secondary: #252525;--gtv-border: #333;--gtv-text: #e0e0e0;--gtv-text-muted: #888;--gtv-accent: oklch(65% .15 220);--gtv-playhead: oklch(65% .15 220);--gtv-ruler-bg: #1f1f1f;--gtv-track-height: 28px;--gtv-controls-height: 40px;--gtv-ruler-height: 24px;--gtv-timeline-padding: 16px;--gtv-track-1: oklch(50% .12 220);--gtv-track-1-active: oklch(60% .15 220);--gtv-track-2: oklch(50% .12 70);--gtv-track-2-active: oklch(60% .15 70);--gtv-track-3: oklch(50% .12 350);--gtv-track-3-active: oklch(60% .15 350);--gtv-track-4: oklch(50% .12 160);--gtv-track-4-active: oklch(60% .15 160);--gtv-track-5: oklch(50% .12 290);--gtv-track-5-active: oklch(60% .15 290);--gtv-track-6: oklch(50% .12 25);--gtv-track-6-active: oklch(60% .15 25)}*{box-sizing:border-box;margin:0;padding:0}.gtv-container{position:fixed;bottom:0;left:0;right:0;background:var(--gtv-bg);border-top:1px solid var(--gtv-border);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:12px;color:var(--gtv-text);z-index:999999;display:flex;flex-direction:column;user-select:none;-webkit-user-select:none}.gtv-container.collapsed{height:auto!important}.gtv-container.collapsed .gtv-timeline-area{display:none}.gtv-controls{position:relative;display:flex;align-items:center;justify-content:center;height:var(--gtv-controls-height);padding:0 12px;background:var(--gtv-bg-secondary);border-bottom:1px solid var(--gtv-border);gap:8px}.gtv-controls-left{position:absolute;left:12px;display:flex;align-items:center;gap:8px}.gtv-controls-center{display:flex;align-items:center;gap:8px}.gtv-controls-right{position:absolute;right:12px;display:flex;align-items:center;gap:8px}.gtv-time-display{font-variant-numeric:tabular-nums;min-width:100px;text-align:center}.gtv-time-current{color:var(--gtv-text)}.gtv-time-total{color:var(--gtv-text-muted)}.gtv-btn{display:flex;align-items:center;justify-content:center;width:28px;height:28px;background:transparent;border:none;border-radius:4px;color:var(--gtv-text);cursor:pointer;transition:background .15s}.gtv-btn:hover{background:#ffffff1a}.gtv-btn:active{background:#ffffff26}.gtv-btn.active{color:var(--gtv-accent)}.gtv-btn svg{width:16px;height:16px;fill:currentColor}.gtv-btn-play svg{width:20px;height:20px}.gtv-speed-btn{width:auto;padding:0 8px;font-size:11px;font-weight:500}.gtv-collapse-btn{margin-left:auto}.gtv-timeline-area{position:relative;display:flex;flex-direction:column;overflow:hidden;flex:1}.gtv-resize-handle{position:absolute;top:0;left:0;right:0;height:6px;cursor:ns-resize;z-index:20}.gtv-resize-handle:hover,.gtv-resize-handle:active{background:#ffffff1a}.gtv-ruler{position:relative;height:var(--gtv-ruler-height);background:var(--gtv-ruler-bg);border-bottom:1px solid var(--gtv-border);overflow:visible;flex-shrink:0;padding:0 var(--gtv-timeline-padding)}.gtv-ruler-inner{position:relative;height:100%;width:100%}.gtv-ruler-marker{position:absolute;top:0;height:100%;display:flex;flex-direction:column;align-items:center}.gtv-ruler-marker-line{width:1px;height:6px;background:var(--gtv-text-muted)}.gtv-ruler-marker-label{font-size:10px;color:var(--gtv-text-muted);margin-top:2px}.gtv-tracks-container{position:relative;overflow-y:auto;overflow-x:hidden;flex:1;padding:0 var(--gtv-timeline-padding)}.gtv-tracks-scroll{position:relative;min-height:100%;width:100%}.gtv-track{position:relative;height:var(--gtv-track-height)}.gtv-track-bar{position:absolute;top:4px;height:calc(var(--gtv-track-height) - 8px);border-radius:4px;display:flex;align-items:center;padding:0 8px;font-size:11px;font-weight:500;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:default;transition:filter .15s}.gtv-track-bar:hover{filter:brightness(1.1)}.gtv-playhead-wrapper{position:absolute;top:0;bottom:0;left:var(--gtv-timeline-padding);right:var(--gtv-timeline-padding);pointer-events:none;z-index:15}.gtv-playhead{position:absolute;top:0;bottom:0;width:0;left:0}.gtv-playhead-head{position:absolute;top:6px;left:-5px;width:11px;height:11px;background:var(--gtv-playhead);clip-path:polygon(50% 100%,0 0,100% 0)}.gtv-playhead-line{position:absolute;top:6px;bottom:0;left:0;width:1px;background:var(--gtv-playhead)}.gtv-scrub-area{position:absolute;top:0;left:0;right:0;bottom:0;cursor:ew-resize}.gtv-empty{display:flex;align-items:center;justify-content:center;padding:24px;color:var(--gtv-text-muted)}", y = [0.25, 0.5, 1, 2, 4];
72
+ class z extends HTMLElement {
80
73
  constructor() {
81
74
  super();
82
- s(this, "shadow");
83
- s(this, "timeline", null);
84
- s(this, "timelineData", null);
85
- s(this, "isPlaying", !1);
86
- s(this, "isLooping", !1);
87
- s(this, "speedIndex", 2);
75
+ n(this, "shadow");
76
+ n(this, "timeline", null);
77
+ n(this, "timelineData", null);
78
+ n(this, "isPlaying", !1);
79
+ n(this, "isLooping", !1);
80
+ n(this, "speedIndex", 2);
88
81
  // 1x
89
- s(this, "collapsed", !1);
90
- s(this, "height", 200);
91
- s(this, "isDragging", !1);
82
+ n(this, "collapsed", !1);
83
+ n(this, "height", 200);
84
+ n(this, "isDragging", !1);
92
85
  // DOM references
93
- s(this, "container");
94
- s(this, "playBtn");
95
- s(this, "loopBtn");
96
- s(this, "speedBtn");
97
- s(this, "timeDisplay");
98
- s(this, "rulerInner");
99
- s(this, "tracksScroll");
100
- s(this, "playhead");
101
- s(this, "scrubArea");
86
+ n(this, "container");
87
+ n(this, "playBtn");
88
+ n(this, "loopBtn");
89
+ n(this, "speedBtn");
90
+ n(this, "timeDisplay");
91
+ n(this, "rulerInner");
92
+ n(this, "tracksScroll");
93
+ n(this, "playhead");
94
+ n(this, "scrubArea");
95
+ n(this, "resizeHandle");
96
+ n(this, "isResizing", !1);
102
97
  this.shadow = this.attachShadow({ mode: "open" });
103
98
  }
104
99
  connectedCallback() {
@@ -108,7 +103,7 @@ class P extends HTMLElement {
108
103
  this.detachTimeline();
109
104
  }
110
105
  setTimeline(t) {
111
- this.detachTimeline(), this.timeline = t, w(), this.timelineData = S(t), t.eventCallback("onUpdate", () => this.onTimelineUpdate()), this.renderTracks(), this.updatePlayhead(), this.updateTimeDisplay(), this.updatePlayState();
106
+ this.detachTimeline(), this.timeline = t, S(), this.timelineData = w(t), t.eventCallback("onUpdate", () => this.onTimelineUpdate()), this.renderTracks(), this.updatePlayhead(), this.updateTimeDisplay(), this.updatePlayState();
112
107
  }
113
108
  detachTimeline() {
114
109
  this.timeline && (this.timeline.eventCallback("onUpdate", null), this.timeline = null, this.timelineData = null);
@@ -120,10 +115,21 @@ class P extends HTMLElement {
120
115
  <!-- Controls Bar -->
121
116
  <div class="gtv-controls">
122
117
  <div class="gtv-controls-left">
118
+ <button class="gtv-btn" data-action="loop" title="Loop (L)">
119
+ <svg viewBox="0 0 24 24"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></svg>
120
+ </button>
121
+ <button class="gtv-btn gtv-speed-btn" data-action="speed" title="Playback speed">1x</button>
122
+ </div>
123
+
124
+ <div class="gtv-controls-center">
125
+ <span class="gtv-time-display">
126
+ <span class="gtv-time-current">0.00</span>
127
+ <span class="gtv-time-total"> / 0.00</span>
128
+ </span>
123
129
  <button class="gtv-btn" data-action="skip-start" title="Skip to start">
124
130
  <svg viewBox="0 0 24 24"><path d="M6 6h2v12H6V6zm3.5 6l8.5 6V6l-8.5 6z"/></svg>
125
131
  </button>
126
- <button class="gtv-btn gtv-btn-play" data-action="play" title="Play/Pause">
132
+ <button class="gtv-btn gtv-btn-play" data-action="play" title="Play/Pause (Space)">
127
133
  <svg class="play-icon" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>
128
134
  <svg class="pause-icon" viewBox="0 0 24 24" style="display: none;"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
129
135
  </button>
@@ -132,17 +138,6 @@ class P extends HTMLElement {
132
138
  </button>
133
139
  </div>
134
140
 
135
- <div class="gtv-controls-center">
136
- <button class="gtv-btn" data-action="loop" title="Loop">
137
- <svg viewBox="0 0 24 24"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></svg>
138
- </button>
139
- <span class="gtv-time-display">
140
- <span class="gtv-time-current">0.00</span>
141
- <span class="gtv-time-total"> / 0.00</span>
142
- </span>
143
- <button class="gtv-btn gtv-speed-btn" data-action="speed" title="Playback speed">1x</button>
144
- </div>
145
-
146
141
  <div class="gtv-controls-right">
147
142
  <button class="gtv-btn gtv-collapse-btn" data-action="collapse" title="Collapse/Expand">
148
143
  <svg viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"/></svg>
@@ -152,33 +147,38 @@ class P extends HTMLElement {
152
147
 
153
148
  <!-- Timeline Area -->
154
149
  <div class="gtv-timeline-area">
150
+ <!-- Resize Handle -->
151
+ <div class="gtv-resize-handle"></div>
152
+
155
153
  <!-- Ruler -->
156
154
  <div class="gtv-ruler">
157
155
  <div class="gtv-ruler-inner"></div>
158
- <div class="gtv-playhead-container">
159
- <div class="gtv-playhead-head"></div>
160
- </div>
161
156
  </div>
162
157
 
163
158
  <!-- Tracks -->
164
159
  <div class="gtv-tracks-container">
165
160
  <div class="gtv-tracks-scroll">
166
161
  <div class="gtv-scrub-area"></div>
167
- <div class="gtv-playhead-container">
168
- <div class="gtv-playhead-line"></div>
169
- </div>
170
162
  </div>
171
163
  <div class="gtv-empty">No timeline attached. Call setTimeline() to visualize a GSAP timeline.</div>
172
164
  </div>
165
+
166
+ <!-- Playhead spans entire timeline area -->
167
+ <div class="gtv-playhead-wrapper">
168
+ <div class="gtv-playhead">
169
+ <div class="gtv-playhead-head"></div>
170
+ <div class="gtv-playhead-line"></div>
171
+ </div>
172
+ </div>
173
173
  </div>
174
174
  </div>
175
- `, this.container = this.shadow.querySelector(".gtv-container"), this.playBtn = this.shadow.querySelector('[data-action="play"]'), this.loopBtn = this.shadow.querySelector('[data-action="loop"]'), this.speedBtn = this.shadow.querySelector('[data-action="speed"]'), this.timeDisplay = this.shadow.querySelector(".gtv-time-display"), this.rulerInner = this.shadow.querySelector(".gtv-ruler-inner"), this.tracksScroll = this.shadow.querySelector(".gtv-tracks-scroll"), this.playhead = this.shadow.querySelector(".gtv-ruler .gtv-playhead-container"), this.scrubArea = this.shadow.querySelector(".gtv-scrub-area");
175
+ `, this.container = this.shadow.querySelector(".gtv-container"), this.playBtn = this.shadow.querySelector('[data-action="play"]'), this.loopBtn = this.shadow.querySelector('[data-action="loop"]'), this.speedBtn = this.shadow.querySelector('[data-action="speed"]'), this.timeDisplay = this.shadow.querySelector(".gtv-time-display"), this.rulerInner = this.shadow.querySelector(".gtv-ruler-inner"), this.tracksScroll = this.shadow.querySelector(".gtv-tracks-scroll"), this.playhead = this.shadow.querySelector(".gtv-playhead"), this.scrubArea = this.shadow.querySelector(".gtv-scrub-area"), this.resizeHandle = this.shadow.querySelector(".gtv-resize-handle");
176
176
  }
177
177
  setupEventListeners() {
178
178
  this.shadow.addEventListener("click", (t) => {
179
- const r = t.target.closest("[data-action]");
180
- if (!r) return;
181
- switch (r.dataset.action) {
179
+ const s = t.target.closest("[data-action]");
180
+ if (!s) return;
181
+ switch (s.dataset.action) {
182
182
  case "play":
183
183
  this.togglePlay();
184
184
  break;
@@ -198,23 +198,52 @@ class P extends HTMLElement {
198
198
  this.toggleCollapse();
199
199
  break;
200
200
  }
201
- }), this.scrubArea.addEventListener("mousedown", (t) => this.startScrub(t)), this.shadow.querySelector(".gtv-ruler").addEventListener("mousedown", (t) => this.startScrub(t)), document.addEventListener("mousemove", (t) => this.onScrub(t)), document.addEventListener("mouseup", () => this.endScrub()), document.addEventListener("keydown", (t) => {
202
- t.target === document.body && t.code === "Space" && (t.preventDefault(), this.togglePlay());
201
+ }), this.scrubArea.addEventListener("mousedown", (t) => this.startScrub(t)), this.shadow.querySelector(".gtv-ruler").addEventListener("mousedown", (t) => this.startScrub(t)), document.addEventListener("mousemove", (t) => {
202
+ this.onScrub(t), this.onResize(t);
203
+ }), document.addEventListener("mouseup", () => {
204
+ this.endScrub(), this.endResize();
205
+ }), this.resizeHandle.addEventListener("mousedown", (t) => this.startResize(t)), document.addEventListener("keydown", (t) => {
206
+ if (t.target === document.body)
207
+ switch (t.code) {
208
+ case "Space":
209
+ t.preventDefault(), this.togglePlay();
210
+ break;
211
+ case "KeyJ":
212
+ t.preventDefault(), this.jumpToPrevPoint();
213
+ break;
214
+ case "KeyK":
215
+ t.preventDefault(), this.jumpToNextPoint();
216
+ break;
217
+ case "KeyL":
218
+ t.preventDefault(), this.toggleLoop();
219
+ break;
220
+ }
203
221
  });
204
222
  }
205
223
  startScrub(t) {
206
- this.timeline && (this.isDragging = !0, this.scrubToPosition(t));
224
+ this.timeline && (t.preventDefault(), this.isDragging = !0, document.body.style.cursor = "ew-resize", document.body.style.userSelect = "none", this.scrubToPosition(t));
207
225
  }
208
226
  onScrub(t) {
209
227
  !this.isDragging || !this.timeline || this.scrubToPosition(t);
210
228
  }
211
229
  endScrub() {
212
- this.isDragging = !1;
230
+ this.isDragging = !1, document.body.style.cursor = "", document.body.style.userSelect = "";
231
+ }
232
+ startResize(t) {
233
+ t.preventDefault(), this.isResizing = !0, document.body.style.cursor = "ns-resize", document.body.style.userSelect = "none";
234
+ }
235
+ onResize(t) {
236
+ if (!this.isResizing) return;
237
+ const e = window.innerHeight, s = e - t.clientY;
238
+ this.height = Math.max(100, Math.min(s, e - 100)), this.container.style.height = `${this.height}px`;
239
+ }
240
+ endResize() {
241
+ this.isResizing && (this.isResizing = !1, document.body.style.cursor = "", document.body.style.userSelect = "");
213
242
  }
214
243
  scrubToPosition(t) {
215
244
  if (!this.timeline || !this.timelineData) return;
216
- const i = this.rulerInner.getBoundingClientRect(), n = Math.max(0, Math.min(t.clientX - i.left, i.width)) / i.width;
217
- this.timeline.progress(n), this.timeline.pause(), this.updatePlayState();
245
+ const e = this.rulerInner.getBoundingClientRect(), a = Math.max(0, Math.min(t.clientX - e.left, e.width)) / e.width;
246
+ this.timeline.progress(a), this.timeline.pause(), this.updatePlayState();
218
247
  }
219
248
  togglePlay() {
220
249
  this.timeline && (this.timeline.paused() || this.timeline.progress() === 1 ? this.timeline.progress() === 1 ? this.timeline.restart() : this.timeline.play() : this.timeline.pause(), this.updatePlayState());
@@ -225,13 +254,42 @@ class P extends HTMLElement {
225
254
  skipToEnd() {
226
255
  this.timeline && (this.timeline.progress(1), this.timeline.pause(), this.updatePlayState());
227
256
  }
257
+ getTimePoints() {
258
+ if (!this.timelineData) return [0];
259
+ const t = /* @__PURE__ */ new Set();
260
+ return t.add(0), t.add(Math.round(this.timelineData.duration * 1e3) / 1e3), this.timelineData.tweens.forEach((e) => {
261
+ t.add(Math.round(e.startTime * 1e3) / 1e3), t.add(Math.round(e.endTime * 1e3) / 1e3);
262
+ }), Array.from(t).sort((e, s) => e - s);
263
+ }
264
+ jumpToPrevPoint() {
265
+ if (!this.timeline || !this.timelineData) return;
266
+ const t = Math.round(this.timeline.time() * 1e3) / 1e3, e = this.getTimePoints();
267
+ let s = 0;
268
+ for (const a of e)
269
+ if (a < t - 1e-3)
270
+ s = a;
271
+ else
272
+ break;
273
+ this.timeline.time(s), this.timeline.pause(), this.updatePlayState();
274
+ }
275
+ jumpToNextPoint() {
276
+ if (!this.timeline || !this.timelineData) return;
277
+ const t = Math.round(this.timeline.time() * 1e3) / 1e3, e = this.getTimePoints();
278
+ let s = this.timelineData.duration;
279
+ for (const a of e)
280
+ if (a > t + 1e-3) {
281
+ s = a;
282
+ break;
283
+ }
284
+ this.timeline.time(s), this.timeline.pause(), this.updatePlayState();
285
+ }
228
286
  toggleLoop() {
229
287
  this.timeline && (this.isLooping = !this.isLooping, this.timeline.repeat(this.isLooping ? -1 : 0), this.loopBtn.classList.toggle("active", this.isLooping));
230
288
  }
231
289
  cycleSpeed() {
232
290
  if (!this.timeline) return;
233
- this.speedIndex = (this.speedIndex + 1) % b.length;
234
- const t = b[this.speedIndex];
291
+ this.speedIndex = (this.speedIndex + 1) % y.length;
292
+ const t = y[this.speedIndex];
235
293
  this.timeline.timeScale(t), this.speedBtn.textContent = `${t}x`;
236
294
  }
237
295
  toggleCollapse() {
@@ -242,90 +300,89 @@ class P extends HTMLElement {
242
300
  updatePlayState() {
243
301
  if (!this.timeline) return;
244
302
  this.isPlaying = !this.timeline.paused() && this.timeline.progress() < 1;
245
- const t = this.playBtn.querySelector(".play-icon"), i = this.playBtn.querySelector(".pause-icon");
246
- t.style.display = this.isPlaying ? "none" : "block", i.style.display = this.isPlaying ? "block" : "none";
303
+ const t = this.playBtn.querySelector(".play-icon"), e = this.playBtn.querySelector(".pause-icon");
304
+ t.style.display = this.isPlaying ? "none" : "block", e.style.display = this.isPlaying ? "block" : "none";
247
305
  }
248
306
  onTimelineUpdate() {
249
307
  this.updatePlayhead(), this.updateTimeDisplay(), this.updateActiveTracks(), this.updatePlayState();
250
308
  }
251
309
  updatePlayhead() {
252
310
  if (!this.timeline || !this.timelineData) return;
253
- const i = `${this.timeline.progress() * 100}%`;
254
- this.playhead.style.left = i;
255
- const r = this.tracksScroll.querySelector(".gtv-playhead-container");
256
- r && (r.style.left = i);
311
+ const t = this.timeline.progress();
312
+ this.playhead.style.left = `${t * 100}%`;
257
313
  }
258
314
  updateTimeDisplay() {
259
315
  if (!this.timeline || !this.timelineData) return;
260
- const t = this.timeline.time(), i = this.timelineData.duration, r = this.timeDisplay.querySelector(".gtv-time-current"), n = this.timeDisplay.querySelector(".gtv-time-total");
261
- r.textContent = c(t), n.textContent = ` / ${c(i)}`;
316
+ const t = this.timeline.time(), e = this.timelineData.duration, s = this.timeDisplay.querySelector(".gtv-time-current"), a = this.timeDisplay.querySelector(".gtv-time-total");
317
+ s.textContent = d(t), a.textContent = ` / ${d(e)}`;
262
318
  }
263
319
  updateActiveTracks() {
264
320
  if (!this.timeline || !this.timelineData) return;
265
321
  const t = this.timeline.time();
266
- this.tracksScroll.querySelectorAll(".gtv-track-bar").forEach((r, n) => {
267
- const l = this.timelineData.tweens[n], o = t >= l.startTime && t <= l.endTime;
268
- r.classList.toggle("is-active", o);
322
+ this.tracksScroll.querySelectorAll(".gtv-track-bar").forEach((s, a) => {
323
+ const l = this.timelineData.tweens[a], o = t >= l.startTime && t <= l.endTime, c = s.dataset.color;
324
+ o ? s.style.background = `var(--gtv-track-${c}-active)` : s.style.background = `var(--gtv-track-${c})`;
269
325
  });
270
326
  }
271
327
  renderTracks() {
272
328
  if (!this.timelineData) return;
273
- const { duration: t, tweens: i } = this.timelineData, r = this.shadow.querySelector(".gtv-empty");
274
- r.style.display = i.length > 0 ? "none" : "flex", this.renderRuler(t);
275
- const n = i.map((d) => this.renderTrack(d, t)).join(""), l = this.tracksScroll.querySelector(".gtv-scrub-area"), o = this.tracksScroll.querySelector(".gtv-playhead-container");
276
- this.tracksScroll.innerHTML = n, this.tracksScroll.prepend(l), this.tracksScroll.appendChild(o), this.scrubArea = l;
329
+ const { duration: t, tweens: e } = this.timelineData, s = this.shadow.querySelector(".gtv-empty");
330
+ s.style.display = e.length > 0 ? "none" : "flex", this.renderRuler(t);
331
+ const a = e.map((o) => this.renderTrack(o, t)).join(""), l = this.tracksScroll.querySelector(".gtv-scrub-area");
332
+ this.tracksScroll.innerHTML = a, this.tracksScroll.prepend(l), this.scrubArea = l;
277
333
  }
278
334
  renderRuler(t) {
279
- const i = [], r = this.calculateInterval(t);
280
- for (let n = 0; n <= t; n += r) {
281
- const l = n / t * 100;
282
- i.push(`
335
+ const e = [], s = this.calculateInterval(t);
336
+ for (let a = 0; a <= t; a += s) {
337
+ const l = a / t * 100;
338
+ e.push(`
283
339
  <div class="gtv-ruler-marker" style="left: ${l}%;">
284
340
  <div class="gtv-ruler-marker-line"></div>
285
- <span class="gtv-ruler-marker-label">${c(n, !1)}s</span>
341
+ <span class="gtv-ruler-marker-label">${d(a, !1)}s</span>
286
342
  </div>
287
343
  `);
288
344
  }
289
- this.rulerInner.innerHTML = i.join("");
345
+ this.rulerInner.innerHTML = e.join("");
290
346
  }
291
347
  calculateInterval(t) {
292
348
  return t <= 1 ? 0.25 : t <= 3 ? 0.5 : t <= 10 ? 1 : t <= 30 ? 5 : 10;
293
349
  }
294
- renderTrack(t, i) {
295
- const r = t.startTime / i * 100, n = t.duration / i * 100, l = L[t.colorIndex];
350
+ renderTrack(t, e) {
351
+ const s = t.startTime / e * 100, a = t.duration / e * 100, l = t.colorIndex + 1;
296
352
  return `
297
353
  <div class="gtv-track">
298
354
  <div class="gtv-track-bar"
299
- style="left: ${r}%; width: ${n}%; background: ${l};"
300
- title="${t.label} (${c(t.startTime)}s - ${c(t.endTime)}s)">
355
+ data-color="${l}"
356
+ style="left: ${s}%; width: ${a}%; background: var(--gtv-track-${l});"
357
+ title="${t.label} (${d(t.startTime)}s - ${d(t.endTime)}s)">
301
358
  ${t.label}
302
359
  </div>
303
360
  </div>
304
361
  `;
305
362
  }
306
363
  }
307
- customElements.define("gsap-timeline-viewer", P);
308
- class D {
309
- constructor(e) {
310
- s(this, "element");
311
- this.element = document.createElement("gsap-timeline-viewer"), e.height && this.element.style.setProperty("--viewer-height", `${e.height}px`), e.timeline && setTimeout(() => {
312
- this.element.setTimeline(e.timeline);
364
+ customElements.define("gsap-timeline-viewer", z);
365
+ class L {
366
+ constructor(i) {
367
+ n(this, "element");
368
+ this.element = document.createElement("gsap-timeline-viewer"), i.height && this.element.style.setProperty("--viewer-height", `${i.height}px`), i.timeline && setTimeout(() => {
369
+ this.element.setTimeline(i.timeline);
313
370
  }, 0);
314
371
  }
315
- attach(e = document.body) {
316
- e.appendChild(this.element);
372
+ attach(i = document.body) {
373
+ i.appendChild(this.element);
317
374
  }
318
375
  detach() {
319
376
  this.element.remove();
320
377
  }
321
- setTimeline(e) {
322
- this.element.setTimeline(e);
378
+ setTimeline(i) {
379
+ this.element.setTimeline(i);
323
380
  }
324
381
  get htmlElement() {
325
382
  return this.element;
326
383
  }
327
384
  }
328
385
  export {
329
- D as TimelineViewer,
330
- P as TimelineViewerElement
386
+ L as TimelineViewer,
387
+ z as TimelineViewerElement
331
388
  };
@@ -1,13 +1,24 @@
1
- (function(l,n){typeof exports=="object"&&typeof module<"u"?n(exports):typeof define=="function"&&define.amd?define(["exports"],n):(l=typeof globalThis<"u"?globalThis:l||self,n(l.GSAPTimelineViewer={}))})(this,function(l){"use strict";var C=Object.defineProperty;var D=(l,n,h)=>n in l?C(l,n,{enumerable:!0,configurable:!0,writable:!0,value:h}):l[n]=h;var s=(l,n,h)=>D(l,typeof n!="symbol"?n+"":n,h);let n=0;function h(o){if(!o||o.length===0)return"Unknown";const i=o[0];return i.id?`#${i.id}`:i.classList&&i.classList.length>0?`.${i.classList[0]}`:i.tagName?i.tagName.toLowerCase():"element"}function k(o){const i=["ease","duration","delay","onComplete","onStart","onUpdate","onCompleteParams","onStartParams","onUpdateParams","repeat","repeatDelay","yoyo","stagger","overwrite","immediateRender","lazy","autoAlpha","id","paused","reversed","startAt"];return Object.keys(o).filter(t=>!i.includes(t))}function S(o){const i=[];return o.getChildren(!0,!0,!1).forEach((e,a)=>{if(!("targets"in e))return;const r=e,c=r.targets(),d=r.vars||{},p=k(d);let v="";if(d.id&&typeof d.id=="string")v=d.id;else{const y=h(c),x=p.slice(0,2).join(", ");v=x?`${y} (${x})`:y}const f=e.startTime(),b=e.duration();i.push({id:`tween-${++n}`,label:v,startTime:f,endTime:f+b,duration:b,targets:h(c),properties:p,colorIndex:a%6})}),{duration:o.duration(),tweens:i}}function w(){n=0}function g(o,i=!0){const t=Math.abs(o);return i?t.toFixed(2):t.toFixed(0)}const T=":host{--gtv-bg: #1a1a1a;--gtv-bg-secondary: #252525;--gtv-border: #333;--gtv-text: #e0e0e0;--gtv-text-muted: #888;--gtv-accent: #4a9eff;--gtv-playhead: #4a9eff;--gtv-ruler-bg: #1f1f1f;--gtv-track-height: 28px;--gtv-controls-height: 40px;--gtv-ruler-height: 24px;--gtv-track-1: #3b82f6;--gtv-track-2: #f59e0b;--gtv-track-3: #ec4899;--gtv-track-4: #10b981;--gtv-track-5: #8b5cf6;--gtv-track-6: #ef4444}*{box-sizing:border-box;margin:0;padding:0}.gtv-container{position:fixed;bottom:0;left:0;right:0;background:var(--gtv-bg);border-top:1px solid var(--gtv-border);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:12px;color:var(--gtv-text);z-index:999999;display:flex;flex-direction:column}.gtv-container.collapsed{height:auto!important}.gtv-container.collapsed .gtv-timeline-area{display:none}.gtv-controls{display:flex;align-items:center;justify-content:space-between;height:var(--gtv-controls-height);padding:0 12px;background:var(--gtv-bg-secondary);border-bottom:1px solid var(--gtv-border);gap:16px}.gtv-controls-left,.gtv-controls-center,.gtv-controls-right{display:flex;align-items:center;gap:8px}.gtv-controls-center{flex:0 0 auto}.gtv-time-display{font-variant-numeric:tabular-nums;min-width:100px;text-align:center}.gtv-time-current{color:var(--gtv-text)}.gtv-time-total{color:var(--gtv-text-muted)}.gtv-btn{display:flex;align-items:center;justify-content:center;width:28px;height:28px;background:transparent;border:none;border-radius:4px;color:var(--gtv-text);cursor:pointer;transition:background .15s}.gtv-btn:hover{background:#ffffff1a}.gtv-btn:active{background:#ffffff26}.gtv-btn.active{color:var(--gtv-accent)}.gtv-btn svg{width:16px;height:16px;fill:currentColor}.gtv-btn-play svg{width:20px;height:20px}.gtv-speed-btn{width:auto;padding:0 8px;font-size:11px;font-weight:500}.gtv-collapse-btn{margin-left:auto}.gtv-timeline-area{display:flex;flex-direction:column;overflow:hidden;flex:1}.gtv-ruler{position:relative;height:var(--gtv-ruler-height);background:var(--gtv-ruler-bg);border-bottom:1px solid var(--gtv-border);overflow:hidden;flex-shrink:0}.gtv-ruler-inner{position:relative;height:100%}.gtv-ruler-marker{position:absolute;top:0;height:100%;display:flex;flex-direction:column;align-items:center}.gtv-ruler-marker-line{width:1px;height:6px;background:var(--gtv-text-muted)}.gtv-ruler-marker-label{font-size:10px;color:var(--gtv-text-muted);margin-top:2px}.gtv-tracks-container{position:relative;overflow-y:auto;overflow-x:hidden;flex:1}.gtv-tracks-scroll{position:relative;min-height:100%}.gtv-track{position:relative;height:var(--gtv-track-height);border-bottom:1px solid var(--gtv-border)}.gtv-track-bar{position:absolute;top:4px;height:calc(var(--gtv-track-height) - 8px);border-radius:4px;display:flex;align-items:center;padding:0 8px;font-size:11px;font-weight:500;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:default;transition:filter .15s}.gtv-track-bar:hover{filter:brightness(1.1)}.gtv-track-bar.is-active{box-shadow:0 0 0 2px var(--gtv-accent)}.gtv-playhead-container{position:absolute;top:0;bottom:0;width:1px;pointer-events:none;z-index:10}.gtv-playhead-head{position:absolute;top:0;left:-5px;width:11px;height:11px;background:var(--gtv-playhead);clip-path:polygon(50% 100%,0 0,100% 0)}.gtv-playhead-line{position:absolute;top:11px;bottom:0;left:0;width:1px;background:var(--gtv-playhead)}.gtv-scrub-area{position:absolute;top:0;left:0;right:0;bottom:0;cursor:pointer}.gtv-empty{display:flex;align-items:center;justify-content:center;padding:24px;color:var(--gtv-text-muted)}",L=["var(--gtv-track-1)","var(--gtv-track-2)","var(--gtv-track-3)","var(--gtv-track-4)","var(--gtv-track-5)","var(--gtv-track-6)"],u=[.25,.5,1,2,4];class m extends HTMLElement{constructor(){super();s(this,"shadow");s(this,"timeline",null);s(this,"timelineData",null);s(this,"isPlaying",!1);s(this,"isLooping",!1);s(this,"speedIndex",2);s(this,"collapsed",!1);s(this,"height",200);s(this,"isDragging",!1);s(this,"container");s(this,"playBtn");s(this,"loopBtn");s(this,"speedBtn");s(this,"timeDisplay");s(this,"rulerInner");s(this,"tracksScroll");s(this,"playhead");s(this,"scrubArea");this.shadow=this.attachShadow({mode:"open"})}connectedCallback(){this.render(),this.setupEventListeners()}disconnectedCallback(){this.detachTimeline()}setTimeline(t){this.detachTimeline(),this.timeline=t,w(),this.timelineData=S(t),t.eventCallback("onUpdate",()=>this.onTimelineUpdate()),this.renderTracks(),this.updatePlayhead(),this.updateTimeDisplay(),this.updatePlayState()}detachTimeline(){this.timeline&&(this.timeline.eventCallback("onUpdate",null),this.timeline=null,this.timelineData=null)}render(){this.shadow.innerHTML=`
1
+ (function(l,r){typeof exports=="object"&&typeof module<"u"?r(exports):typeof define=="function"&&define.amd?define(["exports"],r):(l=typeof globalThis<"u"?globalThis:l||self,r(l.GSAPTimelineViewer={}))})(this,function(l){"use strict";var P=Object.defineProperty;var L=(l,r,h)=>r in l?P(l,r,{enumerable:!0,configurable:!0,writable:!0,value:h}):l[r]=h;var n=(l,r,h)=>L(l,typeof r!="symbol"?r+"":r,h);let r=0;function h(c){if(!c||c.length===0)return"Unknown";const s=c[0];return s.id?`#${s.id}`:s.classList&&s.classList.length>0?`.${s.classList[0]}`:s.tagName?s.tagName.toLowerCase():"element"}function x(c){const s=["ease","duration","delay","onComplete","onStart","onUpdate","onCompleteParams","onStartParams","onUpdateParams","repeat","repeatDelay","yoyo","stagger","overwrite","immediateRender","lazy","autoAlpha","id","paused","reversed","startAt"];return Object.keys(c).filter(t=>!s.includes(t))}function w(c){const s=[];return c.getChildren(!0,!0,!1).forEach((e,i)=>{if(!("targets"in e))return;const a=e,o=a.targets(),d=a.vars||{},g=x(d);let v="";if(d.id&&typeof d.id=="string")v=d.id;else{const b=h(o),k=g.slice(0,2).join(", ");v=k?`${b} (${k})`:b}const f=e.startTime(),y=e.duration();s.push({id:`tween-${++r}`,label:v,startTime:f,endTime:f+y,duration:y,targets:h(o),properties:g,colorIndex:i%6})}),{duration:c.duration(),tweens:s}}function S(){r=0}function p(c,s=!0){const t=Math.abs(c);return s?t.toFixed(2):t.toFixed(0)}const T=":host{--gtv-bg: #1a1a1a;--gtv-bg-secondary: #252525;--gtv-border: #333;--gtv-text: #e0e0e0;--gtv-text-muted: #888;--gtv-accent: oklch(65% .15 220);--gtv-playhead: oklch(65% .15 220);--gtv-ruler-bg: #1f1f1f;--gtv-track-height: 28px;--gtv-controls-height: 40px;--gtv-ruler-height: 24px;--gtv-timeline-padding: 16px;--gtv-track-1: oklch(50% .12 220);--gtv-track-1-active: oklch(60% .15 220);--gtv-track-2: oklch(50% .12 70);--gtv-track-2-active: oklch(60% .15 70);--gtv-track-3: oklch(50% .12 350);--gtv-track-3-active: oklch(60% .15 350);--gtv-track-4: oklch(50% .12 160);--gtv-track-4-active: oklch(60% .15 160);--gtv-track-5: oklch(50% .12 290);--gtv-track-5-active: oklch(60% .15 290);--gtv-track-6: oklch(50% .12 25);--gtv-track-6-active: oklch(60% .15 25)}*{box-sizing:border-box;margin:0;padding:0}.gtv-container{position:fixed;bottom:0;left:0;right:0;background:var(--gtv-bg);border-top:1px solid var(--gtv-border);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:12px;color:var(--gtv-text);z-index:999999;display:flex;flex-direction:column;user-select:none;-webkit-user-select:none}.gtv-container.collapsed{height:auto!important}.gtv-container.collapsed .gtv-timeline-area{display:none}.gtv-controls{position:relative;display:flex;align-items:center;justify-content:center;height:var(--gtv-controls-height);padding:0 12px;background:var(--gtv-bg-secondary);border-bottom:1px solid var(--gtv-border);gap:8px}.gtv-controls-left{position:absolute;left:12px;display:flex;align-items:center;gap:8px}.gtv-controls-center{display:flex;align-items:center;gap:8px}.gtv-controls-right{position:absolute;right:12px;display:flex;align-items:center;gap:8px}.gtv-time-display{font-variant-numeric:tabular-nums;min-width:100px;text-align:center}.gtv-time-current{color:var(--gtv-text)}.gtv-time-total{color:var(--gtv-text-muted)}.gtv-btn{display:flex;align-items:center;justify-content:center;width:28px;height:28px;background:transparent;border:none;border-radius:4px;color:var(--gtv-text);cursor:pointer;transition:background .15s}.gtv-btn:hover{background:#ffffff1a}.gtv-btn:active{background:#ffffff26}.gtv-btn.active{color:var(--gtv-accent)}.gtv-btn svg{width:16px;height:16px;fill:currentColor}.gtv-btn-play svg{width:20px;height:20px}.gtv-speed-btn{width:auto;padding:0 8px;font-size:11px;font-weight:500}.gtv-collapse-btn{margin-left:auto}.gtv-timeline-area{position:relative;display:flex;flex-direction:column;overflow:hidden;flex:1}.gtv-resize-handle{position:absolute;top:0;left:0;right:0;height:6px;cursor:ns-resize;z-index:20}.gtv-resize-handle:hover,.gtv-resize-handle:active{background:#ffffff1a}.gtv-ruler{position:relative;height:var(--gtv-ruler-height);background:var(--gtv-ruler-bg);border-bottom:1px solid var(--gtv-border);overflow:visible;flex-shrink:0;padding:0 var(--gtv-timeline-padding)}.gtv-ruler-inner{position:relative;height:100%;width:100%}.gtv-ruler-marker{position:absolute;top:0;height:100%;display:flex;flex-direction:column;align-items:center}.gtv-ruler-marker-line{width:1px;height:6px;background:var(--gtv-text-muted)}.gtv-ruler-marker-label{font-size:10px;color:var(--gtv-text-muted);margin-top:2px}.gtv-tracks-container{position:relative;overflow-y:auto;overflow-x:hidden;flex:1;padding:0 var(--gtv-timeline-padding)}.gtv-tracks-scroll{position:relative;min-height:100%;width:100%}.gtv-track{position:relative;height:var(--gtv-track-height)}.gtv-track-bar{position:absolute;top:4px;height:calc(var(--gtv-track-height) - 8px);border-radius:4px;display:flex;align-items:center;padding:0 8px;font-size:11px;font-weight:500;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:default;transition:filter .15s}.gtv-track-bar:hover{filter:brightness(1.1)}.gtv-playhead-wrapper{position:absolute;top:0;bottom:0;left:var(--gtv-timeline-padding);right:var(--gtv-timeline-padding);pointer-events:none;z-index:15}.gtv-playhead{position:absolute;top:0;bottom:0;width:0;left:0}.gtv-playhead-head{position:absolute;top:6px;left:-5px;width:11px;height:11px;background:var(--gtv-playhead);clip-path:polygon(50% 100%,0 0,100% 0)}.gtv-playhead-line{position:absolute;top:6px;bottom:0;left:0;width:1px;background:var(--gtv-playhead)}.gtv-scrub-area{position:absolute;top:0;left:0;right:0;bottom:0;cursor:ew-resize}.gtv-empty{display:flex;align-items:center;justify-content:center;padding:24px;color:var(--gtv-text-muted)}",u=[.25,.5,1,2,4];class m extends HTMLElement{constructor(){super();n(this,"shadow");n(this,"timeline",null);n(this,"timelineData",null);n(this,"isPlaying",!1);n(this,"isLooping",!1);n(this,"speedIndex",2);n(this,"collapsed",!1);n(this,"height",200);n(this,"isDragging",!1);n(this,"container");n(this,"playBtn");n(this,"loopBtn");n(this,"speedBtn");n(this,"timeDisplay");n(this,"rulerInner");n(this,"tracksScroll");n(this,"playhead");n(this,"scrubArea");n(this,"resizeHandle");n(this,"isResizing",!1);this.shadow=this.attachShadow({mode:"open"})}connectedCallback(){this.render(),this.setupEventListeners()}disconnectedCallback(){this.detachTimeline()}setTimeline(t){this.detachTimeline(),this.timeline=t,S(),this.timelineData=w(t),t.eventCallback("onUpdate",()=>this.onTimelineUpdate()),this.renderTracks(),this.updatePlayhead(),this.updateTimeDisplay(),this.updatePlayState()}detachTimeline(){this.timeline&&(this.timeline.eventCallback("onUpdate",null),this.timeline=null,this.timelineData=null)}render(){this.shadow.innerHTML=`
2
2
  <style>${T}</style>
3
3
  <div class="gtv-container ${this.collapsed?"collapsed":""}" style="height: ${this.height}px;">
4
4
  <!-- Controls Bar -->
5
5
  <div class="gtv-controls">
6
6
  <div class="gtv-controls-left">
7
+ <button class="gtv-btn" data-action="loop" title="Loop (L)">
8
+ <svg viewBox="0 0 24 24"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></svg>
9
+ </button>
10
+ <button class="gtv-btn gtv-speed-btn" data-action="speed" title="Playback speed">1x</button>
11
+ </div>
12
+
13
+ <div class="gtv-controls-center">
14
+ <span class="gtv-time-display">
15
+ <span class="gtv-time-current">0.00</span>
16
+ <span class="gtv-time-total"> / 0.00</span>
17
+ </span>
7
18
  <button class="gtv-btn" data-action="skip-start" title="Skip to start">
8
19
  <svg viewBox="0 0 24 24"><path d="M6 6h2v12H6V6zm3.5 6l8.5 6V6l-8.5 6z"/></svg>
9
20
  </button>
10
- <button class="gtv-btn gtv-btn-play" data-action="play" title="Play/Pause">
21
+ <button class="gtv-btn gtv-btn-play" data-action="play" title="Play/Pause (Space)">
11
22
  <svg class="play-icon" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>
12
23
  <svg class="pause-icon" viewBox="0 0 24 24" style="display: none;"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
13
24
  </button>
@@ -16,17 +27,6 @@
16
27
  </button>
17
28
  </div>
18
29
 
19
- <div class="gtv-controls-center">
20
- <button class="gtv-btn" data-action="loop" title="Loop">
21
- <svg viewBox="0 0 24 24"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></svg>
22
- </button>
23
- <span class="gtv-time-display">
24
- <span class="gtv-time-current">0.00</span>
25
- <span class="gtv-time-total"> / 0.00</span>
26
- </span>
27
- <button class="gtv-btn gtv-speed-btn" data-action="speed" title="Playback speed">1x</button>
28
- </div>
29
-
30
30
  <div class="gtv-controls-right">
31
31
  <button class="gtv-btn gtv-collapse-btn" data-action="collapse" title="Collapse/Expand">
32
32
  <svg viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"/></svg>
@@ -36,37 +36,43 @@
36
36
 
37
37
  <!-- Timeline Area -->
38
38
  <div class="gtv-timeline-area">
39
+ <!-- Resize Handle -->
40
+ <div class="gtv-resize-handle"></div>
41
+
39
42
  <!-- Ruler -->
40
43
  <div class="gtv-ruler">
41
44
  <div class="gtv-ruler-inner"></div>
42
- <div class="gtv-playhead-container">
43
- <div class="gtv-playhead-head"></div>
44
- </div>
45
45
  </div>
46
46
 
47
47
  <!-- Tracks -->
48
48
  <div class="gtv-tracks-container">
49
49
  <div class="gtv-tracks-scroll">
50
50
  <div class="gtv-scrub-area"></div>
51
- <div class="gtv-playhead-container">
52
- <div class="gtv-playhead-line"></div>
53
- </div>
54
51
  </div>
55
52
  <div class="gtv-empty">No timeline attached. Call setTimeline() to visualize a GSAP timeline.</div>
56
53
  </div>
54
+
55
+ <!-- Playhead spans entire timeline area -->
56
+ <div class="gtv-playhead-wrapper">
57
+ <div class="gtv-playhead">
58
+ <div class="gtv-playhead-head"></div>
59
+ <div class="gtv-playhead-line"></div>
60
+ </div>
61
+ </div>
57
62
  </div>
58
63
  </div>
59
- `,this.container=this.shadow.querySelector(".gtv-container"),this.playBtn=this.shadow.querySelector('[data-action="play"]'),this.loopBtn=this.shadow.querySelector('[data-action="loop"]'),this.speedBtn=this.shadow.querySelector('[data-action="speed"]'),this.timeDisplay=this.shadow.querySelector(".gtv-time-display"),this.rulerInner=this.shadow.querySelector(".gtv-ruler-inner"),this.tracksScroll=this.shadow.querySelector(".gtv-tracks-scroll"),this.playhead=this.shadow.querySelector(".gtv-ruler .gtv-playhead-container"),this.scrubArea=this.shadow.querySelector(".gtv-scrub-area")}setupEventListeners(){this.shadow.addEventListener("click",t=>{const a=t.target.closest("[data-action]");if(!a)return;switch(a.dataset.action){case"play":this.togglePlay();break;case"skip-start":this.skipToStart();break;case"skip-end":this.skipToEnd();break;case"loop":this.toggleLoop();break;case"speed":this.cycleSpeed();break;case"collapse":this.toggleCollapse();break}}),this.scrubArea.addEventListener("mousedown",t=>this.startScrub(t)),this.shadow.querySelector(".gtv-ruler").addEventListener("mousedown",t=>this.startScrub(t)),document.addEventListener("mousemove",t=>this.onScrub(t)),document.addEventListener("mouseup",()=>this.endScrub()),document.addEventListener("keydown",t=>{t.target===document.body&&t.code==="Space"&&(t.preventDefault(),this.togglePlay())})}startScrub(t){this.timeline&&(this.isDragging=!0,this.scrubToPosition(t))}onScrub(t){!this.isDragging||!this.timeline||this.scrubToPosition(t)}endScrub(){this.isDragging=!1}scrubToPosition(t){if(!this.timeline||!this.timelineData)return;const e=this.rulerInner.getBoundingClientRect(),r=Math.max(0,Math.min(t.clientX-e.left,e.width))/e.width;this.timeline.progress(r),this.timeline.pause(),this.updatePlayState()}togglePlay(){this.timeline&&(this.timeline.paused()||this.timeline.progress()===1?this.timeline.progress()===1?this.timeline.restart():this.timeline.play():this.timeline.pause(),this.updatePlayState())}skipToStart(){this.timeline&&(this.timeline.progress(0),this.timeline.pause(),this.updatePlayState())}skipToEnd(){this.timeline&&(this.timeline.progress(1),this.timeline.pause(),this.updatePlayState())}toggleLoop(){this.timeline&&(this.isLooping=!this.isLooping,this.timeline.repeat(this.isLooping?-1:0),this.loopBtn.classList.toggle("active",this.isLooping))}cycleSpeed(){if(!this.timeline)return;this.speedIndex=(this.speedIndex+1)%u.length;const t=u[this.speedIndex];this.timeline.timeScale(t),this.speedBtn.textContent=`${t}x`}toggleCollapse(){this.collapsed=!this.collapsed,this.container.classList.toggle("collapsed",this.collapsed);const t=this.shadow.querySelector('[data-action="collapse"]');t.innerHTML=this.collapsed?'<svg viewBox="0 0 24 24"><path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6z"/></svg>':'<svg viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"/></svg>'}updatePlayState(){if(!this.timeline)return;this.isPlaying=!this.timeline.paused()&&this.timeline.progress()<1;const t=this.playBtn.querySelector(".play-icon"),e=this.playBtn.querySelector(".pause-icon");t.style.display=this.isPlaying?"none":"block",e.style.display=this.isPlaying?"block":"none"}onTimelineUpdate(){this.updatePlayhead(),this.updateTimeDisplay(),this.updateActiveTracks(),this.updatePlayState()}updatePlayhead(){if(!this.timeline||!this.timelineData)return;const e=`${this.timeline.progress()*100}%`;this.playhead.style.left=e;const a=this.tracksScroll.querySelector(".gtv-playhead-container");a&&(a.style.left=e)}updateTimeDisplay(){if(!this.timeline||!this.timelineData)return;const t=this.timeline.time(),e=this.timelineData.duration,a=this.timeDisplay.querySelector(".gtv-time-current"),r=this.timeDisplay.querySelector(".gtv-time-total");a.textContent=g(t),r.textContent=` / ${g(e)}`}updateActiveTracks(){if(!this.timeline||!this.timelineData)return;const t=this.timeline.time();this.tracksScroll.querySelectorAll(".gtv-track-bar").forEach((a,r)=>{const c=this.timelineData.tweens[r],d=t>=c.startTime&&t<=c.endTime;a.classList.toggle("is-active",d)})}renderTracks(){if(!this.timelineData)return;const{duration:t,tweens:e}=this.timelineData,a=this.shadow.querySelector(".gtv-empty");a.style.display=e.length>0?"none":"flex",this.renderRuler(t);const r=e.map(p=>this.renderTrack(p,t)).join(""),c=this.tracksScroll.querySelector(".gtv-scrub-area"),d=this.tracksScroll.querySelector(".gtv-playhead-container");this.tracksScroll.innerHTML=r,this.tracksScroll.prepend(c),this.tracksScroll.appendChild(d),this.scrubArea=c}renderRuler(t){const e=[],a=this.calculateInterval(t);for(let r=0;r<=t;r+=a){const c=r/t*100;e.push(`
60
- <div class="gtv-ruler-marker" style="left: ${c}%;">
64
+ `,this.container=this.shadow.querySelector(".gtv-container"),this.playBtn=this.shadow.querySelector('[data-action="play"]'),this.loopBtn=this.shadow.querySelector('[data-action="loop"]'),this.speedBtn=this.shadow.querySelector('[data-action="speed"]'),this.timeDisplay=this.shadow.querySelector(".gtv-time-display"),this.rulerInner=this.shadow.querySelector(".gtv-ruler-inner"),this.tracksScroll=this.shadow.querySelector(".gtv-tracks-scroll"),this.playhead=this.shadow.querySelector(".gtv-playhead"),this.scrubArea=this.shadow.querySelector(".gtv-scrub-area"),this.resizeHandle=this.shadow.querySelector(".gtv-resize-handle")}setupEventListeners(){this.shadow.addEventListener("click",t=>{const i=t.target.closest("[data-action]");if(!i)return;switch(i.dataset.action){case"play":this.togglePlay();break;case"skip-start":this.skipToStart();break;case"skip-end":this.skipToEnd();break;case"loop":this.toggleLoop();break;case"speed":this.cycleSpeed();break;case"collapse":this.toggleCollapse();break}}),this.scrubArea.addEventListener("mousedown",t=>this.startScrub(t)),this.shadow.querySelector(".gtv-ruler").addEventListener("mousedown",t=>this.startScrub(t)),document.addEventListener("mousemove",t=>{this.onScrub(t),this.onResize(t)}),document.addEventListener("mouseup",()=>{this.endScrub(),this.endResize()}),this.resizeHandle.addEventListener("mousedown",t=>this.startResize(t)),document.addEventListener("keydown",t=>{if(t.target===document.body)switch(t.code){case"Space":t.preventDefault(),this.togglePlay();break;case"KeyJ":t.preventDefault(),this.jumpToPrevPoint();break;case"KeyK":t.preventDefault(),this.jumpToNextPoint();break;case"KeyL":t.preventDefault(),this.toggleLoop();break}})}startScrub(t){this.timeline&&(t.preventDefault(),this.isDragging=!0,document.body.style.cursor="ew-resize",document.body.style.userSelect="none",this.scrubToPosition(t))}onScrub(t){!this.isDragging||!this.timeline||this.scrubToPosition(t)}endScrub(){this.isDragging=!1,document.body.style.cursor="",document.body.style.userSelect=""}startResize(t){t.preventDefault(),this.isResizing=!0,document.body.style.cursor="ns-resize",document.body.style.userSelect="none"}onResize(t){if(!this.isResizing)return;const e=window.innerHeight,i=e-t.clientY;this.height=Math.max(100,Math.min(i,e-100)),this.container.style.height=`${this.height}px`}endResize(){this.isResizing&&(this.isResizing=!1,document.body.style.cursor="",document.body.style.userSelect="")}scrubToPosition(t){if(!this.timeline||!this.timelineData)return;const e=this.rulerInner.getBoundingClientRect(),a=Math.max(0,Math.min(t.clientX-e.left,e.width))/e.width;this.timeline.progress(a),this.timeline.pause(),this.updatePlayState()}togglePlay(){this.timeline&&(this.timeline.paused()||this.timeline.progress()===1?this.timeline.progress()===1?this.timeline.restart():this.timeline.play():this.timeline.pause(),this.updatePlayState())}skipToStart(){this.timeline&&(this.timeline.progress(0),this.timeline.pause(),this.updatePlayState())}skipToEnd(){this.timeline&&(this.timeline.progress(1),this.timeline.pause(),this.updatePlayState())}getTimePoints(){if(!this.timelineData)return[0];const t=new Set;return t.add(0),t.add(Math.round(this.timelineData.duration*1e3)/1e3),this.timelineData.tweens.forEach(e=>{t.add(Math.round(e.startTime*1e3)/1e3),t.add(Math.round(e.endTime*1e3)/1e3)}),Array.from(t).sort((e,i)=>e-i)}jumpToPrevPoint(){if(!this.timeline||!this.timelineData)return;const t=Math.round(this.timeline.time()*1e3)/1e3,e=this.getTimePoints();let i=0;for(const a of e)if(a<t-.001)i=a;else break;this.timeline.time(i),this.timeline.pause(),this.updatePlayState()}jumpToNextPoint(){if(!this.timeline||!this.timelineData)return;const t=Math.round(this.timeline.time()*1e3)/1e3,e=this.getTimePoints();let i=this.timelineData.duration;for(const a of e)if(a>t+.001){i=a;break}this.timeline.time(i),this.timeline.pause(),this.updatePlayState()}toggleLoop(){this.timeline&&(this.isLooping=!this.isLooping,this.timeline.repeat(this.isLooping?-1:0),this.loopBtn.classList.toggle("active",this.isLooping))}cycleSpeed(){if(!this.timeline)return;this.speedIndex=(this.speedIndex+1)%u.length;const t=u[this.speedIndex];this.timeline.timeScale(t),this.speedBtn.textContent=`${t}x`}toggleCollapse(){this.collapsed=!this.collapsed,this.container.classList.toggle("collapsed",this.collapsed);const t=this.shadow.querySelector('[data-action="collapse"]');t.innerHTML=this.collapsed?'<svg viewBox="0 0 24 24"><path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6z"/></svg>':'<svg viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"/></svg>'}updatePlayState(){if(!this.timeline)return;this.isPlaying=!this.timeline.paused()&&this.timeline.progress()<1;const t=this.playBtn.querySelector(".play-icon"),e=this.playBtn.querySelector(".pause-icon");t.style.display=this.isPlaying?"none":"block",e.style.display=this.isPlaying?"block":"none"}onTimelineUpdate(){this.updatePlayhead(),this.updateTimeDisplay(),this.updateActiveTracks(),this.updatePlayState()}updatePlayhead(){if(!this.timeline||!this.timelineData)return;const t=this.timeline.progress();this.playhead.style.left=`${t*100}%`}updateTimeDisplay(){if(!this.timeline||!this.timelineData)return;const t=this.timeline.time(),e=this.timelineData.duration,i=this.timeDisplay.querySelector(".gtv-time-current"),a=this.timeDisplay.querySelector(".gtv-time-total");i.textContent=p(t),a.textContent=` / ${p(e)}`}updateActiveTracks(){if(!this.timeline||!this.timelineData)return;const t=this.timeline.time();this.tracksScroll.querySelectorAll(".gtv-track-bar").forEach((i,a)=>{const o=this.timelineData.tweens[a],d=t>=o.startTime&&t<=o.endTime,g=i.dataset.color;d?i.style.background=`var(--gtv-track-${g}-active)`:i.style.background=`var(--gtv-track-${g})`})}renderTracks(){if(!this.timelineData)return;const{duration:t,tweens:e}=this.timelineData,i=this.shadow.querySelector(".gtv-empty");i.style.display=e.length>0?"none":"flex",this.renderRuler(t);const a=e.map(d=>this.renderTrack(d,t)).join(""),o=this.tracksScroll.querySelector(".gtv-scrub-area");this.tracksScroll.innerHTML=a,this.tracksScroll.prepend(o),this.scrubArea=o}renderRuler(t){const e=[],i=this.calculateInterval(t);for(let a=0;a<=t;a+=i){const o=a/t*100;e.push(`
65
+ <div class="gtv-ruler-marker" style="left: ${o}%;">
61
66
  <div class="gtv-ruler-marker-line"></div>
62
- <span class="gtv-ruler-marker-label">${g(r,!1)}s</span>
67
+ <span class="gtv-ruler-marker-label">${p(a,!1)}s</span>
63
68
  </div>
64
- `)}this.rulerInner.innerHTML=e.join("")}calculateInterval(t){return t<=1?.25:t<=3?.5:t<=10?1:t<=30?5:10}renderTrack(t,e){const a=t.startTime/e*100,r=t.duration/e*100,c=L[t.colorIndex];return`
69
+ `)}this.rulerInner.innerHTML=e.join("")}calculateInterval(t){return t<=1?.25:t<=3?.5:t<=10?1:t<=30?5:10}renderTrack(t,e){const i=t.startTime/e*100,a=t.duration/e*100,o=t.colorIndex+1;return`
65
70
  <div class="gtv-track">
66
71
  <div class="gtv-track-bar"
67
- style="left: ${a}%; width: ${r}%; background: ${c};"
68
- title="${t.label} (${g(t.startTime)}s - ${g(t.endTime)}s)">
72
+ data-color="${o}"
73
+ style="left: ${i}%; width: ${a}%; background: var(--gtv-track-${o});"
74
+ title="${t.label} (${p(t.startTime)}s - ${p(t.endTime)}s)">
69
75
  ${t.label}
70
76
  </div>
71
77
  </div>
72
- `}}customElements.define("gsap-timeline-viewer",m);class P{constructor(i){s(this,"element");this.element=document.createElement("gsap-timeline-viewer"),i.height&&this.element.style.setProperty("--viewer-height",`${i.height}px`),i.timeline&&setTimeout(()=>{this.element.setTimeline(i.timeline)},0)}attach(i=document.body){i.appendChild(this.element)}detach(){this.element.remove()}setTimeline(i){this.element.setTimeline(i)}get htmlElement(){return this.element}}l.TimelineViewer=P,l.TimelineViewerElement=m,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});
78
+ `}}customElements.define("gsap-timeline-viewer",m);class z{constructor(s){n(this,"element");this.element=document.createElement("gsap-timeline-viewer"),s.height&&this.element.style.setProperty("--viewer-height",`${s.height}px`),s.timeline&&setTimeout(()=>{this.element.setTimeline(s.timeline)},0)}attach(s=document.body){s.appendChild(this.element)}detach(){this.element.remove()}setTimeline(s){this.element.setTimeline(s)}get htmlElement(){return this.element}}l.TimelineViewer=z,l.TimelineViewerElement=m,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});
package/dist/index.d.ts CHANGED
@@ -32,6 +32,8 @@ export declare class TimelineViewerElement extends HTMLElement {
32
32
  private tracksScroll;
33
33
  private playhead;
34
34
  private scrubArea;
35
+ private resizeHandle;
36
+ private isResizing;
35
37
  constructor();
36
38
  connectedCallback(): void;
37
39
  disconnectedCallback(): void;
@@ -42,10 +44,16 @@ export declare class TimelineViewerElement extends HTMLElement {
42
44
  private startScrub;
43
45
  private onScrub;
44
46
  private endScrub;
47
+ private startResize;
48
+ private onResize;
49
+ private endResize;
45
50
  private scrubToPosition;
46
51
  private togglePlay;
47
52
  private skipToStart;
48
53
  private skipToEnd;
54
+ private getTimePoints;
55
+ private jumpToPrevPoint;
56
+ private jumpToNextPoint;
49
57
  private toggleLoop;
50
58
  private cycleSpeed;
51
59
  private toggleCollapse;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsap-timeline-viewer",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A lightweight, framework-agnostic timeline viewer for GSAP animations",
5
5
  "type": "module",
6
6
  "main": "./dist/gsap-timeline-viewer.umd.cjs",