gsap-timeline-viewer 0.1.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.
- package/README.md +110 -0
- package/dist/gsap-timeline-viewer.iife.js +72 -0
- package/dist/gsap-timeline-viewer.js +331 -0
- package/dist/gsap-timeline-viewer.umd.cjs +72 -0
- package/dist/index.d.ts +63 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# GSAP Timeline Viewer
|
|
2
|
+
|
|
3
|
+
A lightweight, framework-agnostic development tool for visualizing GSAP timelines. Debug and scrub through your animations with a visual timeline panel.
|
|
4
|
+
|
|
5
|
+
**4.8 KB gzipped** | Works with React, Vue, Angular, Svelte, or vanilla JS
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install gsap-timeline-viewer
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Note: GSAP is a peer dependency. Make sure you have it installed:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install gsap
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
import { TimelineViewer } from 'gsap-timeline-viewer';
|
|
23
|
+
import gsap from 'gsap';
|
|
24
|
+
|
|
25
|
+
// Create your GSAP timeline
|
|
26
|
+
const tl = gsap.timeline();
|
|
27
|
+
tl.to('.box', { x: 100, duration: 1 })
|
|
28
|
+
.to('.box', { y: 50, duration: 0.5 });
|
|
29
|
+
|
|
30
|
+
// Attach the viewer
|
|
31
|
+
const viewer = new TimelineViewer({ timeline: tl });
|
|
32
|
+
viewer.attach();
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- Visual timeline with colored tracks for each tween
|
|
38
|
+
- Scrubber/playhead synced with your timeline
|
|
39
|
+
- Playback controls (play/pause, skip to start/end)
|
|
40
|
+
- Speed control (0.25x, 0.5x, 1x, 2x, 4x)
|
|
41
|
+
- Loop toggle
|
|
42
|
+
- Auto-scaling time ruler
|
|
43
|
+
- Keyboard shortcut: `Space` to play/pause
|
|
44
|
+
- Collapsible panel
|
|
45
|
+
- Dark theme
|
|
46
|
+
|
|
47
|
+
## API
|
|
48
|
+
|
|
49
|
+
### TimelineViewer
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { TimelineViewer } from 'gsap-timeline-viewer';
|
|
53
|
+
|
|
54
|
+
const viewer = new TimelineViewer({
|
|
55
|
+
timeline: myTimeline, // Required: GSAP timeline instance
|
|
56
|
+
height: 200, // Optional: Panel height in pixels (default: 200)
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
viewer.attach(); // Add viewer to document.body
|
|
60
|
+
viewer.attach(containerElement); // Add to specific container
|
|
61
|
+
viewer.detach(); // Remove from DOM
|
|
62
|
+
viewer.setTimeline(newTimeline); // Switch to a different timeline
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Named Tweens
|
|
66
|
+
|
|
67
|
+
For better labels in the viewer, add an `id` to your tweens:
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
tl.to('.hero', {
|
|
71
|
+
opacity: 1,
|
|
72
|
+
duration: 1,
|
|
73
|
+
id: 'Hero Fade In' // This label appears in the viewer
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Web Component
|
|
78
|
+
|
|
79
|
+
The viewer is also available as a custom element:
|
|
80
|
+
|
|
81
|
+
```html
|
|
82
|
+
<gsap-timeline-viewer></gsap-timeline-viewer>
|
|
83
|
+
|
|
84
|
+
<script type="module">
|
|
85
|
+
import 'gsap-timeline-viewer';
|
|
86
|
+
|
|
87
|
+
const viewer = document.querySelector('gsap-timeline-viewer');
|
|
88
|
+
viewer.setTimeline(myTimeline);
|
|
89
|
+
</script>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## UMD / Script Tag
|
|
93
|
+
|
|
94
|
+
```html
|
|
95
|
+
<script src="https://unpkg.com/gsap"></script>
|
|
96
|
+
<script src="https://unpkg.com/gsap-timeline-viewer"></script>
|
|
97
|
+
|
|
98
|
+
<script>
|
|
99
|
+
const viewer = new GSAPTimelineViewer.TimelineViewer({ timeline: tl });
|
|
100
|
+
viewer.attach();
|
|
101
|
+
</script>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Browser Support
|
|
105
|
+
|
|
106
|
+
Works in all modern browsers that support Web Components (Chrome, Firefox, Safari, Edge).
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT
|
|
@@ -0,0 +1,72 @@
|
|
|
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=`
|
|
2
|
+
<style>${T}</style>
|
|
3
|
+
<div class="gtv-container ${this.collapsed?"collapsed":""}" style="height: ${this.height}px;">
|
|
4
|
+
<!-- Controls Bar -->
|
|
5
|
+
<div class="gtv-controls">
|
|
6
|
+
<div class="gtv-controls-left">
|
|
7
|
+
<button class="gtv-btn" data-action="skip-start" title="Skip to start">
|
|
8
|
+
<svg viewBox="0 0 24 24"><path d="M6 6h2v12H6V6zm3.5 6l8.5 6V6l-8.5 6z"/></svg>
|
|
9
|
+
</button>
|
|
10
|
+
<button class="gtv-btn gtv-btn-play" data-action="play" title="Play/Pause">
|
|
11
|
+
<svg class="play-icon" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>
|
|
12
|
+
<svg class="pause-icon" viewBox="0 0 24 24" style="display: none;"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
|
|
13
|
+
</button>
|
|
14
|
+
<button class="gtv-btn" data-action="skip-end" title="Skip to end">
|
|
15
|
+
<svg viewBox="0 0 24 24"><path d="M6 18l8.5-6L6 6v12zm2 0V6l6.5 6L8 18zm8-12v12h2V6h-2z"/></svg>
|
|
16
|
+
</button>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
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
|
+
<div class="gtv-controls-right">
|
|
31
|
+
<button class="gtv-btn gtv-collapse-btn" data-action="collapse" title="Collapse/Expand">
|
|
32
|
+
<svg viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"/></svg>
|
|
33
|
+
</button>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<!-- Timeline Area -->
|
|
38
|
+
<div class="gtv-timeline-area">
|
|
39
|
+
<!-- Ruler -->
|
|
40
|
+
<div class="gtv-ruler">
|
|
41
|
+
<div class="gtv-ruler-inner"></div>
|
|
42
|
+
<div class="gtv-playhead-container">
|
|
43
|
+
<div class="gtv-playhead-head"></div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<!-- Tracks -->
|
|
48
|
+
<div class="gtv-tracks-container">
|
|
49
|
+
<div class="gtv-tracks-scroll">
|
|
50
|
+
<div class="gtv-scrub-area"></div>
|
|
51
|
+
<div class="gtv-playhead-container">
|
|
52
|
+
<div class="gtv-playhead-line"></div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
<div class="gtv-empty">No timeline attached. Call setTimeline() to visualize a GSAP timeline.</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</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}%;">
|
|
61
|
+
<div class="gtv-ruler-marker-line"></div>
|
|
62
|
+
<span class="gtv-ruler-marker-label">${g(r,!1)}s</span>
|
|
63
|
+
</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`
|
|
65
|
+
<div class="gtv-track">
|
|
66
|
+
<div class="gtv-track-bar"
|
|
67
|
+
style="left: ${a}%; width: ${r}%; background: ${l};"
|
|
68
|
+
title="${t.label} (${g(t.startTime)}s - ${g(t.endTime)}s)">
|
|
69
|
+
${t.label}
|
|
70
|
+
</div>
|
|
71
|
+
</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}({});
|
|
@@ -0,0 +1,331 @@
|
|
|
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";
|
|
9
|
+
}
|
|
10
|
+
function k(a) {
|
|
11
|
+
const e = [
|
|
12
|
+
"ease",
|
|
13
|
+
"duration",
|
|
14
|
+
"delay",
|
|
15
|
+
"onComplete",
|
|
16
|
+
"onStart",
|
|
17
|
+
"onUpdate",
|
|
18
|
+
"onCompleteParams",
|
|
19
|
+
"onStartParams",
|
|
20
|
+
"onUpdateParams",
|
|
21
|
+
"repeat",
|
|
22
|
+
"repeatDelay",
|
|
23
|
+
"yoyo",
|
|
24
|
+
"stagger",
|
|
25
|
+
"overwrite",
|
|
26
|
+
"immediateRender",
|
|
27
|
+
"lazy",
|
|
28
|
+
"autoAlpha",
|
|
29
|
+
"id",
|
|
30
|
+
"paused",
|
|
31
|
+
"reversed",
|
|
32
|
+
"startAt"
|
|
33
|
+
];
|
|
34
|
+
return Object.keys(a).filter((t) => !e.includes(t));
|
|
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);
|
|
41
|
+
let h = "";
|
|
42
|
+
if (o.id && typeof o.id == "string")
|
|
43
|
+
h = o.id;
|
|
44
|
+
else {
|
|
45
|
+
const v = m(l), u = d.slice(0, 2).join(", ");
|
|
46
|
+
h = u ? `${v} (${u})` : v;
|
|
47
|
+
}
|
|
48
|
+
const g = i.startTime(), p = i.duration();
|
|
49
|
+
e.push({
|
|
50
|
+
id: `tween-${++y}`,
|
|
51
|
+
label: h,
|
|
52
|
+
startTime: g,
|
|
53
|
+
endTime: g + p,
|
|
54
|
+
duration: p,
|
|
55
|
+
targets: m(l),
|
|
56
|
+
properties: d,
|
|
57
|
+
colorIndex: r % 6
|
|
58
|
+
});
|
|
59
|
+
}), {
|
|
60
|
+
duration: a.duration(),
|
|
61
|
+
tweens: e
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function w() {
|
|
65
|
+
y = 0;
|
|
66
|
+
}
|
|
67
|
+
function c(a, e = !0) {
|
|
68
|
+
const t = Math.abs(a);
|
|
69
|
+
return e ? t.toFixed(2) : t.toFixed(0);
|
|
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 {
|
|
80
|
+
constructor() {
|
|
81
|
+
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);
|
|
88
|
+
// 1x
|
|
89
|
+
s(this, "collapsed", !1);
|
|
90
|
+
s(this, "height", 200);
|
|
91
|
+
s(this, "isDragging", !1);
|
|
92
|
+
// 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");
|
|
102
|
+
this.shadow = this.attachShadow({ mode: "open" });
|
|
103
|
+
}
|
|
104
|
+
connectedCallback() {
|
|
105
|
+
this.render(), this.setupEventListeners();
|
|
106
|
+
}
|
|
107
|
+
disconnectedCallback() {
|
|
108
|
+
this.detachTimeline();
|
|
109
|
+
}
|
|
110
|
+
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();
|
|
112
|
+
}
|
|
113
|
+
detachTimeline() {
|
|
114
|
+
this.timeline && (this.timeline.eventCallback("onUpdate", null), this.timeline = null, this.timelineData = null);
|
|
115
|
+
}
|
|
116
|
+
render() {
|
|
117
|
+
this.shadow.innerHTML = `
|
|
118
|
+
<style>${T}</style>
|
|
119
|
+
<div class="gtv-container ${this.collapsed ? "collapsed" : ""}" style="height: ${this.height}px;">
|
|
120
|
+
<!-- Controls Bar -->
|
|
121
|
+
<div class="gtv-controls">
|
|
122
|
+
<div class="gtv-controls-left">
|
|
123
|
+
<button class="gtv-btn" data-action="skip-start" title="Skip to start">
|
|
124
|
+
<svg viewBox="0 0 24 24"><path d="M6 6h2v12H6V6zm3.5 6l8.5 6V6l-8.5 6z"/></svg>
|
|
125
|
+
</button>
|
|
126
|
+
<button class="gtv-btn gtv-btn-play" data-action="play" title="Play/Pause">
|
|
127
|
+
<svg class="play-icon" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>
|
|
128
|
+
<svg class="pause-icon" viewBox="0 0 24 24" style="display: none;"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
|
|
129
|
+
</button>
|
|
130
|
+
<button class="gtv-btn" data-action="skip-end" title="Skip to end">
|
|
131
|
+
<svg viewBox="0 0 24 24"><path d="M6 18l8.5-6L6 6v12zm2 0V6l6.5 6L8 18zm8-12v12h2V6h-2z"/></svg>
|
|
132
|
+
</button>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
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
|
+
<div class="gtv-controls-right">
|
|
147
|
+
<button class="gtv-btn gtv-collapse-btn" data-action="collapse" title="Collapse/Expand">
|
|
148
|
+
<svg viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"/></svg>
|
|
149
|
+
</button>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<!-- Timeline Area -->
|
|
154
|
+
<div class="gtv-timeline-area">
|
|
155
|
+
<!-- Ruler -->
|
|
156
|
+
<div class="gtv-ruler">
|
|
157
|
+
<div class="gtv-ruler-inner"></div>
|
|
158
|
+
<div class="gtv-playhead-container">
|
|
159
|
+
<div class="gtv-playhead-head"></div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<!-- Tracks -->
|
|
164
|
+
<div class="gtv-tracks-container">
|
|
165
|
+
<div class="gtv-tracks-scroll">
|
|
166
|
+
<div class="gtv-scrub-area"></div>
|
|
167
|
+
<div class="gtv-playhead-container">
|
|
168
|
+
<div class="gtv-playhead-line"></div>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
<div class="gtv-empty">No timeline attached. Call setTimeline() to visualize a GSAP timeline.</div>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
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");
|
|
176
|
+
}
|
|
177
|
+
setupEventListeners() {
|
|
178
|
+
this.shadow.addEventListener("click", (t) => {
|
|
179
|
+
const r = t.target.closest("[data-action]");
|
|
180
|
+
if (!r) return;
|
|
181
|
+
switch (r.dataset.action) {
|
|
182
|
+
case "play":
|
|
183
|
+
this.togglePlay();
|
|
184
|
+
break;
|
|
185
|
+
case "skip-start":
|
|
186
|
+
this.skipToStart();
|
|
187
|
+
break;
|
|
188
|
+
case "skip-end":
|
|
189
|
+
this.skipToEnd();
|
|
190
|
+
break;
|
|
191
|
+
case "loop":
|
|
192
|
+
this.toggleLoop();
|
|
193
|
+
break;
|
|
194
|
+
case "speed":
|
|
195
|
+
this.cycleSpeed();
|
|
196
|
+
break;
|
|
197
|
+
case "collapse":
|
|
198
|
+
this.toggleCollapse();
|
|
199
|
+
break;
|
|
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());
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
startScrub(t) {
|
|
206
|
+
this.timeline && (this.isDragging = !0, this.scrubToPosition(t));
|
|
207
|
+
}
|
|
208
|
+
onScrub(t) {
|
|
209
|
+
!this.isDragging || !this.timeline || this.scrubToPosition(t);
|
|
210
|
+
}
|
|
211
|
+
endScrub() {
|
|
212
|
+
this.isDragging = !1;
|
|
213
|
+
}
|
|
214
|
+
scrubToPosition(t) {
|
|
215
|
+
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();
|
|
218
|
+
}
|
|
219
|
+
togglePlay() {
|
|
220
|
+
this.timeline && (this.timeline.paused() || this.timeline.progress() === 1 ? this.timeline.progress() === 1 ? this.timeline.restart() : this.timeline.play() : this.timeline.pause(), this.updatePlayState());
|
|
221
|
+
}
|
|
222
|
+
skipToStart() {
|
|
223
|
+
this.timeline && (this.timeline.progress(0), this.timeline.pause(), this.updatePlayState());
|
|
224
|
+
}
|
|
225
|
+
skipToEnd() {
|
|
226
|
+
this.timeline && (this.timeline.progress(1), this.timeline.pause(), this.updatePlayState());
|
|
227
|
+
}
|
|
228
|
+
toggleLoop() {
|
|
229
|
+
this.timeline && (this.isLooping = !this.isLooping, this.timeline.repeat(this.isLooping ? -1 : 0), this.loopBtn.classList.toggle("active", this.isLooping));
|
|
230
|
+
}
|
|
231
|
+
cycleSpeed() {
|
|
232
|
+
if (!this.timeline) return;
|
|
233
|
+
this.speedIndex = (this.speedIndex + 1) % b.length;
|
|
234
|
+
const t = b[this.speedIndex];
|
|
235
|
+
this.timeline.timeScale(t), this.speedBtn.textContent = `${t}x`;
|
|
236
|
+
}
|
|
237
|
+
toggleCollapse() {
|
|
238
|
+
this.collapsed = !this.collapsed, this.container.classList.toggle("collapsed", this.collapsed);
|
|
239
|
+
const t = this.shadow.querySelector('[data-action="collapse"]');
|
|
240
|
+
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>';
|
|
241
|
+
}
|
|
242
|
+
updatePlayState() {
|
|
243
|
+
if (!this.timeline) return;
|
|
244
|
+
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";
|
|
247
|
+
}
|
|
248
|
+
onTimelineUpdate() {
|
|
249
|
+
this.updatePlayhead(), this.updateTimeDisplay(), this.updateActiveTracks(), this.updatePlayState();
|
|
250
|
+
}
|
|
251
|
+
updatePlayhead() {
|
|
252
|
+
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);
|
|
257
|
+
}
|
|
258
|
+
updateTimeDisplay() {
|
|
259
|
+
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)}`;
|
|
262
|
+
}
|
|
263
|
+
updateActiveTracks() {
|
|
264
|
+
if (!this.timeline || !this.timelineData) return;
|
|
265
|
+
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);
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
renderTracks() {
|
|
272
|
+
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;
|
|
277
|
+
}
|
|
278
|
+
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(`
|
|
283
|
+
<div class="gtv-ruler-marker" style="left: ${l}%;">
|
|
284
|
+
<div class="gtv-ruler-marker-line"></div>
|
|
285
|
+
<span class="gtv-ruler-marker-label">${c(n, !1)}s</span>
|
|
286
|
+
</div>
|
|
287
|
+
`);
|
|
288
|
+
}
|
|
289
|
+
this.rulerInner.innerHTML = i.join("");
|
|
290
|
+
}
|
|
291
|
+
calculateInterval(t) {
|
|
292
|
+
return t <= 1 ? 0.25 : t <= 3 ? 0.5 : t <= 10 ? 1 : t <= 30 ? 5 : 10;
|
|
293
|
+
}
|
|
294
|
+
renderTrack(t, i) {
|
|
295
|
+
const r = t.startTime / i * 100, n = t.duration / i * 100, l = L[t.colorIndex];
|
|
296
|
+
return `
|
|
297
|
+
<div class="gtv-track">
|
|
298
|
+
<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)">
|
|
301
|
+
${t.label}
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
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);
|
|
313
|
+
}, 0);
|
|
314
|
+
}
|
|
315
|
+
attach(e = document.body) {
|
|
316
|
+
e.appendChild(this.element);
|
|
317
|
+
}
|
|
318
|
+
detach() {
|
|
319
|
+
this.element.remove();
|
|
320
|
+
}
|
|
321
|
+
setTimeline(e) {
|
|
322
|
+
this.element.setTimeline(e);
|
|
323
|
+
}
|
|
324
|
+
get htmlElement() {
|
|
325
|
+
return this.element;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
export {
|
|
329
|
+
D as TimelineViewer,
|
|
330
|
+
P as TimelineViewerElement
|
|
331
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
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=`
|
|
2
|
+
<style>${T}</style>
|
|
3
|
+
<div class="gtv-container ${this.collapsed?"collapsed":""}" style="height: ${this.height}px;">
|
|
4
|
+
<!-- Controls Bar -->
|
|
5
|
+
<div class="gtv-controls">
|
|
6
|
+
<div class="gtv-controls-left">
|
|
7
|
+
<button class="gtv-btn" data-action="skip-start" title="Skip to start">
|
|
8
|
+
<svg viewBox="0 0 24 24"><path d="M6 6h2v12H6V6zm3.5 6l8.5 6V6l-8.5 6z"/></svg>
|
|
9
|
+
</button>
|
|
10
|
+
<button class="gtv-btn gtv-btn-play" data-action="play" title="Play/Pause">
|
|
11
|
+
<svg class="play-icon" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>
|
|
12
|
+
<svg class="pause-icon" viewBox="0 0 24 24" style="display: none;"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
|
|
13
|
+
</button>
|
|
14
|
+
<button class="gtv-btn" data-action="skip-end" title="Skip to end">
|
|
15
|
+
<svg viewBox="0 0 24 24"><path d="M6 18l8.5-6L6 6v12zm2 0V6l6.5 6L8 18zm8-12v12h2V6h-2z"/></svg>
|
|
16
|
+
</button>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
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
|
+
<div class="gtv-controls-right">
|
|
31
|
+
<button class="gtv-btn gtv-collapse-btn" data-action="collapse" title="Collapse/Expand">
|
|
32
|
+
<svg viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"/></svg>
|
|
33
|
+
</button>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<!-- Timeline Area -->
|
|
38
|
+
<div class="gtv-timeline-area">
|
|
39
|
+
<!-- Ruler -->
|
|
40
|
+
<div class="gtv-ruler">
|
|
41
|
+
<div class="gtv-ruler-inner"></div>
|
|
42
|
+
<div class="gtv-playhead-container">
|
|
43
|
+
<div class="gtv-playhead-head"></div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<!-- Tracks -->
|
|
48
|
+
<div class="gtv-tracks-container">
|
|
49
|
+
<div class="gtv-tracks-scroll">
|
|
50
|
+
<div class="gtv-scrub-area"></div>
|
|
51
|
+
<div class="gtv-playhead-container">
|
|
52
|
+
<div class="gtv-playhead-line"></div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
<div class="gtv-empty">No timeline attached. Call setTimeline() to visualize a GSAP timeline.</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</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}%;">
|
|
61
|
+
<div class="gtv-ruler-marker-line"></div>
|
|
62
|
+
<span class="gtv-ruler-marker-label">${g(r,!1)}s</span>
|
|
63
|
+
</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`
|
|
65
|
+
<div class="gtv-track">
|
|
66
|
+
<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)">
|
|
69
|
+
${t.label}
|
|
70
|
+
</div>
|
|
71
|
+
</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"})});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export declare class TimelineViewer {
|
|
2
|
+
private element;
|
|
3
|
+
constructor(config: TimelineViewerConfig);
|
|
4
|
+
attach(container?: HTMLElement): void;
|
|
5
|
+
detach(): void;
|
|
6
|
+
setTimeline(timeline: gsap.core.Timeline): void;
|
|
7
|
+
get htmlElement(): TimelineViewerElement;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export declare interface TimelineViewerConfig {
|
|
11
|
+
timeline: gsap.core.Timeline;
|
|
12
|
+
height?: number;
|
|
13
|
+
collapsed?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export declare class TimelineViewerElement extends HTMLElement {
|
|
17
|
+
private shadow;
|
|
18
|
+
private timeline;
|
|
19
|
+
private timelineData;
|
|
20
|
+
private isPlaying;
|
|
21
|
+
private isLooping;
|
|
22
|
+
private speedIndex;
|
|
23
|
+
private collapsed;
|
|
24
|
+
private height;
|
|
25
|
+
private isDragging;
|
|
26
|
+
private container;
|
|
27
|
+
private playBtn;
|
|
28
|
+
private loopBtn;
|
|
29
|
+
private speedBtn;
|
|
30
|
+
private timeDisplay;
|
|
31
|
+
private rulerInner;
|
|
32
|
+
private tracksScroll;
|
|
33
|
+
private playhead;
|
|
34
|
+
private scrubArea;
|
|
35
|
+
constructor();
|
|
36
|
+
connectedCallback(): void;
|
|
37
|
+
disconnectedCallback(): void;
|
|
38
|
+
setTimeline(timeline: gsap.core.Timeline): void;
|
|
39
|
+
private detachTimeline;
|
|
40
|
+
private render;
|
|
41
|
+
private setupEventListeners;
|
|
42
|
+
private startScrub;
|
|
43
|
+
private onScrub;
|
|
44
|
+
private endScrub;
|
|
45
|
+
private scrubToPosition;
|
|
46
|
+
private togglePlay;
|
|
47
|
+
private skipToStart;
|
|
48
|
+
private skipToEnd;
|
|
49
|
+
private toggleLoop;
|
|
50
|
+
private cycleSpeed;
|
|
51
|
+
private toggleCollapse;
|
|
52
|
+
private updatePlayState;
|
|
53
|
+
private onTimelineUpdate;
|
|
54
|
+
private updatePlayhead;
|
|
55
|
+
private updateTimeDisplay;
|
|
56
|
+
private updateActiveTracks;
|
|
57
|
+
private renderTracks;
|
|
58
|
+
private renderRuler;
|
|
59
|
+
private calculateInterval;
|
|
60
|
+
private renderTrack;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { }
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gsap-timeline-viewer",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A lightweight, framework-agnostic timeline viewer for GSAP animations",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/gsap-timeline-viewer.umd.cjs",
|
|
7
|
+
"module": "./dist/gsap-timeline-viewer.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/gsap-timeline-viewer.js",
|
|
12
|
+
"require": "./dist/gsap-timeline-viewer.umd.cjs",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "vite",
|
|
21
|
+
"build": "tsc && vite build",
|
|
22
|
+
"preview": "vite preview"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"gsap",
|
|
26
|
+
"timeline",
|
|
27
|
+
"animation",
|
|
28
|
+
"devtools",
|
|
29
|
+
"viewer",
|
|
30
|
+
"debug"
|
|
31
|
+
],
|
|
32
|
+
"author": "reboiedo",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/reboiedo/gsap-timeline-viewer.git"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/reboiedo/gsap-timeline-viewer#readme",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/reboiedo/gsap-timeline-viewer/issues"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"gsap": "^3.0.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"gsap": "^3.12.5",
|
|
47
|
+
"typescript": "^5.3.3",
|
|
48
|
+
"vite": "^5.0.10",
|
|
49
|
+
"vite-plugin-dts": "^3.7.0"
|
|
50
|
+
}
|
|
51
|
+
}
|