├── .gitmodules ├── LICENSE ├── gsui.css ├── gsui0ne └── gsui0ne.js ├── gsuiActionMenu ├── gsuiActionMenu.css ├── gsuiActionMenu.js └── index.html ├── gsuiAnalyserHist ├── gsuiAnalyserHist.css ├── gsuiAnalyserHist.js └── index.html ├── gsuiAnalyserHz ├── gsuiAnalyserHz.js └── index.html ├── gsuiAnalyserTd ├── gsuiAnalyserTd.js └── index.html ├── gsuiAnalyserVu ├── gsuiAnalyserVu.css ├── gsuiAnalyserVu.html.js ├── gsuiAnalyserVu.js └── index.html ├── gsuiBeatlines ├── gsuiBeatlines.css ├── gsuiBeatlines.html.js ├── gsuiBeatlines.js └── index.html ├── gsuiBlocksManager ├── gsuiBlocksManager.css └── gsuiBlocksManager.js ├── gsuiChannel ├── gsuiChannel.css ├── gsuiChannel.html.js ├── gsuiChannel.js └── index.html ├── gsuiChannels ├── gsuiChannels.css ├── gsuiChannels.html.js └── gsuiChannels.js ├── gsuiClock ├── gsuiClock.css ├── gsuiClock.html.js ├── gsuiClock.js └── index.html ├── gsuiComAvatar ├── gsuiComAvatar.css ├── gsuiComAvatar.js └── index.html ├── gsuiComButton ├── gsuiComButton.css ├── gsuiComButton.js └── index.html ├── gsuiComPlayer ├── gsuiComPlayer.css ├── gsuiComPlayer.html.js ├── gsuiComPlayer.js └── index.html ├── gsuiComPlaylist ├── gsuiComPlaylist.css ├── gsuiComPlaylist.html.js ├── gsuiComPlaylist.js └── index.html ├── gsuiComProfile ├── gsuiComProfile.css ├── gsuiComProfile.html.js ├── gsuiComProfile.js └── index.html ├── gsuiCurves ├── gsuiCurves.css ├── gsuiCurves.html.js └── gsuiCurves.js ├── gsuiDAW ├── gsuiDAW-btn.css ├── gsuiDAW-head.css ├── gsuiDAW-popup-about.css ├── gsuiDAW-popup-about.html.js ├── gsuiDAW-popup-export.css ├── gsuiDAW-popup-export.html.js ├── gsuiDAW-popup-settings.css ├── gsuiDAW-popup-settings.html.js ├── gsuiDAW-windows.css ├── gsuiDAW-windows.html.js ├── gsuiDAW.css ├── gsuiDAW.html.js ├── gsuiDAW.js └── index.html ├── gsuiDotline ├── gsuiDotline.css ├── gsuiDotline.html.js ├── gsuiDotline.js └── index.html ├── gsuiDotlineSVG ├── gsuiDotlineSVG.html.js └── gsuiDotlineSVG.js ├── gsuiDragline ├── gsuiDragline.css ├── gsuiDragline.html.js └── gsuiDragline.js ├── gsuiDropdown ├── getAbsPos.js ├── gsuiDropdown.css └── gsuiDropdown.js ├── gsuiDrum ├── gsuiDrum.css ├── gsuiDrum.html.js ├── gsuiDrum.js └── gsuiDrumcut.js ├── gsuiDrumrow ├── gsuiDrumrow.css ├── gsuiDrumrow.html.js └── gsuiDrumrow.js ├── gsuiDrumrows ├── gsuiDrumrows.css ├── gsuiDrumrows.js └── index.html ├── gsuiDrums ├── gsuiDrums.css ├── gsuiDrums.html.js ├── gsuiDrums.js └── index.html ├── gsuiEffect ├── gsuiEffect.css ├── gsuiEffect.html.js └── gsuiEffect.js ├── gsuiEffects ├── gsuiEffects.css ├── gsuiEffects.html.js └── gsuiEffects.js ├── gsuiEnvelope ├── gsuiEnvelope.css ├── gsuiEnvelope.html.js ├── gsuiEnvelope.js └── index.html ├── gsuiEnvelopeGraph ├── gsuiEnvelopeGraph.css ├── gsuiEnvelopeGraph.html.js └── gsuiEnvelopeGraph.js ├── gsuiFxDelay ├── gsuiFxDelay.css ├── gsuiFxDelay.html.js ├── gsuiFxDelay.js └── index.html ├── gsuiFxFilter ├── gsuiFxFilter.css ├── gsuiFxFilter.html.js ├── gsuiFxFilter.js └── index.html ├── gsuiFxReverb ├── gsuiFxReverb.css ├── gsuiFxReverb.html.js ├── gsuiFxReverb.js └── index.html ├── gsuiFxWaveShaper ├── gsuiFxWaveShaper.css ├── gsuiFxWaveShaper.html.js ├── gsuiFxWaveShaper.js └── index.html ├── gsuiGlitchText ├── gsuiGlitchText.css ├── gsuiGlitchText.html.js ├── gsuiGlitchText.js └── index.html ├── gsuiHelpLink ├── gsuiHelpLink.css └── gsuiHelpLink.js ├── gsuiIcon └── gsuiIcon.css ├── gsuiKeys ├── gsuiKeys.css ├── gsuiKeys.html.js ├── gsuiKeys.js └── index.html ├── gsuiLFO ├── gsuiLFO.css ├── gsuiLFO.html.js ├── gsuiLFO.js └── index.html ├── gsuiLibraries ├── gsuiLibraries.css ├── gsuiLibraries.html.js ├── gsuiLibraries.js └── index.html ├── gsuiLibrary ├── gsuiLibrary.css ├── gsuiLibrary.html.js ├── gsuiLibrary.js └── index.html ├── gsuiMixer ├── gsuiMixer.css ├── gsuiMixer.html.js ├── gsuiMixer.js └── index.html ├── gsuiNoise ├── gsuiNoise.css ├── gsuiNoise.html.js ├── gsuiNoise.js └── index.html ├── gsuiOscillator ├── gsuiOscillator.css ├── gsuiOscillator.html.js ├── gsuiOscillator.js └── index.html ├── gsuiPanels ├── gsuiPanels.css ├── gsuiPanels.js └── index.html ├── gsuiPatternroll ├── gsuiPatternroll.css ├── gsuiPatternroll.html.js └── gsuiPatternroll.js ├── gsuiPatterns ├── gsuiPatterns-infoPopup.html.js ├── gsuiPatterns-pattern.css ├── gsuiPatterns-pattern.html.js ├── gsuiPatterns-synth.css ├── gsuiPatterns-synth.html.js ├── gsuiPatterns.css ├── gsuiPatterns.html.js └── gsuiPatterns.js ├── gsuiPeriodicWave ├── gsuiPeriodicWave.css └── gsuiPeriodicWave.js ├── gsuiPianoroll ├── gsuiPianoroll-block.css ├── gsuiPianoroll.css ├── gsuiPianoroll.html.js ├── gsuiPianoroll.js └── index.html ├── gsuiPopup ├── gsuiPopup.css ├── gsuiPopup.html.js ├── gsuiPopup.js └── index.html ├── gsuiPropSelect ├── gsuiPropSelect.css ├── gsuiPropSelect.js └── index.html ├── gsuiReorder ├── gsuiReorder.css ├── gsuiReorder.js └── index.html ├── gsuiRipple ├── gsuiRipple.css └── gsuiRipple.js ├── gsuiSVGPatterns ├── gsuiSVGPatterns.js ├── gsuiSVGPatternsAutomation.js ├── gsuiSVGPatternsDrums.js ├── gsuiSVGPatternsKeys.js ├── gsuiSVGPatternsSlices.js └── index.html ├── gsuiScrollShadow ├── gsuiScrollShadow.css ├── gsuiScrollShadow.js └── index.html ├── gsuiSkins └── gsuiSkins-light.css ├── gsuiSlicer ├── gsuiSlicer.css ├── gsuiSlicer.html.js ├── gsuiSlicer.js └── index.html ├── gsuiSlider ├── gsuiSlider.css ├── gsuiSlider.html.js ├── gsuiSlider.js └── index.html ├── gsuiSliderGroup ├── gsuiSliderGroup.css ├── gsuiSliderGroup.html.js ├── gsuiSliderGroup.js └── index.html ├── gsuiStepSelect ├── gsuiStepSelect.css ├── gsuiStepSelect.html.js ├── gsuiStepSelect.js └── index.html ├── gsuiSynthesizer ├── gsuiSynthesizer.css ├── gsuiSynthesizer.html.js ├── gsuiSynthesizer.js └── index.html ├── gsuiTempo ├── gsuiTempo.css ├── gsuiTempo.html.js ├── gsuiTempo.js └── index.html ├── gsuiTimeline ├── gsuiTimeline.css ├── gsuiTimeline.html.js ├── gsuiTimeline.js └── index.html ├── gsuiTimewindow ├── gsuiTimewindow.css ├── gsuiTimewindow.html.js ├── gsuiTimewindow.js └── index.html ├── gsuiTitleUser ├── gsuiTitleUser.css ├── gsuiTitleUser.html.js ├── gsuiTitleUser.js └── index.html ├── gsuiToggle ├── gsuiToggle.css ├── gsuiToggle.js └── index.html ├── gsuiTrack ├── gsuiTrack.css ├── gsuiTrack.html.js ├── gsuiTrack.js └── index.html ├── gsuiTracklist └── gsuiTracklist.js ├── gsuiWaveform └── gsuiWaveform.js ├── gsuiWavetable ├── gsuiWavetable.css ├── gsuiWavetable.html.js ├── gsuiWavetable.js └── index.html ├── gsuiWavetableGraph ├── gsuiWavetableGraph.css ├── gsuiWavetableGraph.js └── index.html ├── gsuiWindows ├── gsuiWindow.css ├── gsuiWindow.html.js ├── gsuiWindow.js ├── gsuiWindows.css ├── gsuiWindows.js └── index.html ├── index.html └── test-assets ├── 440-2.wav ├── 440-simple.wav ├── 440.wav ├── 808kick.wav ├── Cymatics-IcedOut.wav ├── Too Long - Steam Machine.wav ├── avatar.jpg ├── basssine.wav ├── clap-013.wav ├── hat-024.wav ├── kick-017.wav ├── okay.wav ├── openhat-012.wav ├── snare-018.wav ├── test.css └── test.js /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "gs-utils"] 2 | path = gs-utils 3 | url = https://github.com/gridsound/gs-utils.git 4 | [submodule "assets"] 5 | path = assets 6 | url = https://github.com/gridsound/assets.git 7 | -------------------------------------------------------------------------------- /gsuiActionMenu/gsuiActionMenu.css: -------------------------------------------------------------------------------- 1 | .gsuiActionMenu-actions { 2 | overflow: auto; 3 | } 4 | 5 | /* .......................................................................... */ 6 | .gsuiActionMenu-action { 7 | display: flex; 8 | width: 100%; 9 | align-items: center; 10 | text-align: start; 11 | padding: 4px; 12 | border: 0; 13 | border-radius: 2px; 14 | gap: 6px; 15 | font-size: 14px; 16 | cursor: pointer; 17 | background: none; 18 | transition: .1s; 19 | transition-property: color, background-color; 20 | } 21 | .gsuiActionMenu-action:hover { 22 | /* color: #111;*/ 23 | background-color: #fff2; 24 | } 25 | .gsuiActionMenu-action-body { 26 | display: flex; 27 | flex-direction: column; 28 | gap: 2px; 29 | } 30 | .gsuiActionMenu-action .gsuiIcon { 31 | width: 16px; 32 | min-width: 16px; 33 | } 34 | .gsuiActionMenu-action-name { 35 | font-weight: bold; 36 | } 37 | .gsuiActionMenu-action-desc { 38 | font-size: 12px; 39 | opacity: .7; 40 | } 41 | -------------------------------------------------------------------------------- /gsuiActionMenu/gsuiActionMenu.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiActionMenu { 4 | #dropdown = new gsuiDropdown(); 5 | #minw = "100px"; 6 | #minh = "32px"; 7 | #maxw = "400px"; 8 | #maxh = "400px"; 9 | #actions = []; 10 | #onclickFn = GSUnoop; 11 | #closeAfterClick = true; 12 | 13 | constructor() { 14 | this.#dropdown.$onopenCreateElement( this.#createOptions.bind( this ) ); 15 | this.#dropdown.$onbeforeOpening( () => { 16 | return this.#actions.length > 0; 17 | } ); 18 | } 19 | 20 | // ......................................................................... 21 | $open() { this.#dropdown.$open(); } 22 | $close() { this.#dropdown.$close(); } 23 | $bindTargetElement( btn ) { this.#dropdown.$bindTargetElement( btn ); } 24 | $setTarget( tar ) { this.#dropdown.$setTarget( tar ); } 25 | $setDirection( dir ) { this.#dropdown.$setDirection( dir ); } 26 | $setMenuParent( el ) { this.#dropdown.$setParent( el ); } 27 | $setMinSize( w, h ) { 28 | this.#minw = w; 29 | this.#minh = h; 30 | } 31 | $setMaxSize( w, h ) { 32 | this.#maxw = w; 33 | this.#maxh = h; 34 | } 35 | $setCallback( fn ) { 36 | this.#onclickFn = fn; 37 | } 38 | $closeAfterClick( b ) { 39 | this.#closeAfterClick = b; 40 | } 41 | $setActions( arr ) { 42 | this.#actions = arr.filter( Boolean ).map( act => ( { ...act } ) ); 43 | } 44 | $changeAction( id, prop, val ) { 45 | const act = this.#actions.find( act => act.id === id ); 46 | const elActions = this.#dropdown.$getContent(); 47 | 48 | if ( act ) { 49 | act[ prop ] = val; 50 | if ( elActions ) { 51 | switch ( prop ) { 52 | case "icon": 53 | elActions.querySelector( `.gsuiActionMenu-action[data-id='${ act.id }'] .gsuiIcon` ).dataset.icon = val; 54 | break; 55 | } 56 | } 57 | } 58 | } 59 | 60 | // ......................................................................... 61 | #onclickActions( e ) { 62 | const act = e.target.dataset.id; 63 | 64 | if ( act ) { 65 | this.#onclickFn( act ); 66 | if ( this.#closeAfterClick ) { 67 | this.#dropdown.$close(); 68 | } 69 | } 70 | } 71 | #createOptions() { 72 | const style = { 73 | minWidth: this.#minw, 74 | minHeight: this.#minh, 75 | maxWidth: this.#maxw, 76 | maxHeight: this.#maxh, 77 | }; 78 | const elem = GSUcreateDiv( { class: "gsuiActionMenu-actions", style }, this.#actions.map( act => 79 | !act.hidden && GSUcreateButton( { class: "gsuiActionMenu-action", "data-id": act.id }, 80 | act.icon && GSUcreateIcon( { icon: act.icon } ), 81 | GSUcreateDiv( { class: "gsuiActionMenu-action-body", inert: true }, 82 | GSUcreateSpan( { class: "gsuiActionMenu-action-name" }, act.name ), 83 | act.desc && GSUcreateSpan( { class: "gsuiActionMenu-action-desc" }, act.desc ), 84 | ), 85 | ) 86 | ) ); 87 | 88 | elem.onclick = this.#onclickActions.bind( this ); 89 | return elem; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /gsuiAnalyserHist/gsuiAnalyserHist.css: -------------------------------------------------------------------------------- 1 | gsui-analyser-hz, 2 | gsui-analyser-td, 3 | gsui-analyser-hist { 4 | display: flex; 5 | position: relative; 6 | overflow: hidden; 7 | height: 100%; 8 | background-color: var( --gsui-screen-spectrum ); 9 | filter: var( --gsui-screen-spectrum-filter ); 10 | } 11 | 12 | gsui-analyser-hz canvas, 13 | gsui-analyser-td canvas, 14 | gsui-analyser-hist canvas { 15 | position: absolute; 16 | width: 100%; 17 | height: 100%; 18 | } 19 | 20 | gsui-analyser-hist[ type="td" ]::after { 21 | position: absolute; 22 | content: ""; 23 | width: 1px; 24 | height: 100%; 25 | left: 50%; 26 | background-color: #0004; 27 | } 28 | -------------------------------------------------------------------------------- /gsuiAnalyserHz/gsuiAnalyserHz.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiAnalyserHz extends gsui0ne { 4 | static #colors = [ 5 | // datum R G B 6 | [ .99, 255, 255, 255 ], 7 | [ .98, 255, 255, 100 ], 8 | [ .97, 200, 200, 100 ], 9 | [ .95, 200, 200, 80 ], 10 | [ .9, 60, 90, 80 ], 11 | [ .8, 45, 45, 80 ], 12 | [ .7, 40, 40, 80 ], 13 | [ .6, 35, 35, 80 ], 14 | [ .4, 30, 30, 70 ], 15 | [ .3, 20, 20, 50 ], 16 | [ .25, 10, 10, 35 ], 17 | [ .17, 5, 5, 25 ], 18 | [ .1, 0, 0, 10 ], 19 | [ .01, 0, 0, 5 ], 20 | [ 0, 0, 0, 0 ], 21 | ]; 22 | #ctx = null; 23 | 24 | constructor() { 25 | super( { 26 | $cmpName: "gsuiAnalyserHz", 27 | $tagName: "gsui-analyser-hz", 28 | $template: GSUcreateElement( "canvas", { inert: true } ), 29 | $attributes: { 30 | resolution: 256, 31 | }, 32 | } ); 33 | Object.seal( this ); 34 | this.#ctx = this.$element.getContext( "2d" ); 35 | } 36 | 37 | // ......................................................................... 38 | static get observedAttributes() { 39 | return [ "resolution" ]; 40 | } 41 | $attributeChanged( prop, val ) { 42 | switch ( prop ) { 43 | case "resolution": 44 | this.$element.width = +val; 45 | this.$element.height = 1; 46 | break; 47 | } 48 | } 49 | 50 | // ......................................................................... 51 | $clear() { 52 | this.#ctx.clearRect( 0, 0, this.$element.width, 1 ); 53 | } 54 | $draw( data ) { 55 | this.#ctx.putImageData( gsuiAnalyserHz.$draw( this.#ctx, data, this.$element.width ), 0, 0 ); 56 | } 57 | 58 | // ......................................................................... 59 | static $draw( ctx, data, width = data.length ) { 60 | const img = ctx.createImageData( width, 1 ); 61 | const imgData = img.data; 62 | 63 | for ( let i = 0; i < width; ++i ) { 64 | const x = i * 4; 65 | const i2 = Math.round( GSUXtoHz( i / width ) * data.length ); 66 | const datum = GSUmathEaseOutCirc( 1 - ( data[ i2 ] / -200 ) ) || 0; 67 | const [ , r, g, b ] = gsuiAnalyserHz.#colors.find( arr => arr[ 0 ] <= datum ) || gsuiAnalyserHz.#colors.at( -1 ); 68 | 69 | imgData[ x ] = Math.min( r * datum, 255 ) | 0; 70 | imgData[ x + 1 ] = Math.min( g * datum, 255 ) | 0; 71 | imgData[ x + 2 ] = Math.min( b * datum, 255 ) | 0; 72 | imgData[ x + 3 ] = datum * 255; 73 | } 74 | return img; 75 | } 76 | } 77 | 78 | GSUdefineElement( "gsui-analyser-hz", gsuiAnalyserHz ); 79 | -------------------------------------------------------------------------------- /gsuiAnalyserVu/gsuiAnalyserVu.css: -------------------------------------------------------------------------------- 1 | gsui-analyser-vu { 2 | display: flex; 3 | gap: min( max( 5%, 1px ), 8px ); 4 | height: 100%; 5 | } 6 | 7 | .gsuiAnalyserVu-meter { 8 | position: relative; 9 | flex: 1; 10 | height: 100%; 11 | background-color: var( --gsui-screen-spectrum ); 12 | filter: var( --gsui-screen-spectrum-filter ); 13 | } 14 | 15 | .gsuiAnalyserVu-meter-val { 16 | position: absolute; 17 | inset: auto 0 0; 18 | background-color: #ff9; 19 | } 20 | .gsuiAnalyserVu-meter-val-max { 21 | position: absolute; 22 | inset: 0 0 auto 0; 23 | background-color: #e04c4c; 24 | } 25 | 26 | .gsuiAnalyserVu-meter-tick, 27 | .gsuiAnalyserVu-meter-0dB { 28 | position: absolute; 29 | width: 100%; 30 | height: 2px; 31 | margin-bottom: -2px; 32 | background-color: #fff; 33 | } 34 | .gsuiAnalyserVu-meter-tick { 35 | bottom: 0; 36 | opacity: 0; 37 | transition: opacity .4s; 38 | } 39 | .gsuiAnalyserVu-meter-0dB { 40 | opacity: .1; 41 | } 42 | -------------------------------------------------------------------------------- /gsuiAnalyserVu/gsuiAnalyserVu.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-analyser-vu", () => [ 4 | GSUgetTemplate( "gsui-analyser-vu-meter", { chan: "L" } ), 5 | GSUgetTemplate( "gsui-analyser-vu-meter", { chan: "R" } ), 6 | ] ); 7 | 8 | GSUsetTemplate( "gsui-analyser-vu-meter", p => 9 | GSUcreateDiv( { class: "gsuiAnalyserVu-meter", "data-chan": p.chan, inert: true }, 10 | GSUcreateDiv( { class: "gsuiAnalyserVu-meter-val" }, 11 | GSUcreateDiv( { class: "gsuiAnalyserVu-meter-val-max" } ), 12 | ), 13 | GSUcreateDiv( { class: "gsuiAnalyserVu-meter-tick" } ), 14 | GSUcreateDiv( { class: "gsuiAnalyserVu-meter-0dB" } ), 15 | ), 16 | ); 17 | -------------------------------------------------------------------------------- /gsuiBeatlines/gsuiBeatlines.css: -------------------------------------------------------------------------------- 1 | gsui-beatlines { 2 | --gsui-deg: 90deg; 3 | --gsui-bPM: 4em; 4 | --gsui-sPB: .25em; 5 | position: absolute; 6 | inset: 0; 7 | pointer-events: none; 8 | color: var( --gsuiBeatlines-lines, #000 ); 9 | } 10 | gsui-beatlines[ vertical ] { 11 | --gsui-deg: 180deg; 12 | } 13 | 14 | /* .......................................................................... */ 15 | .gsuiBeatlines-bg { 16 | position: absolute; 17 | inset: 0; 18 | } 19 | .gsuiBeatlines-measures { 20 | background-image: repeating-linear-gradient( 21 | var( --gsui-deg ), 22 | currentColor, 23 | currentColor 1px, 24 | transparent 1px, 25 | transparent calc( var( --gsui-bPM ) - 1px ), 26 | currentColor calc( var( --gsui-bPM ) - 1px ), 27 | currentColor var( --gsui-bPM ) ); 28 | } 29 | .gsuiBeatlines-steps { 30 | opacity: .2; 31 | background-image: repeating-linear-gradient( 32 | var( --gsui-deg ), 33 | currentColor, 34 | currentColor .5px, 35 | transparent .5px, 36 | transparent calc( var( --gsui-sPB ) - .5px ), 37 | currentColor calc( var( --gsui-sPB ) - .5px ), 38 | currentColor var( --gsui-sPB ) ); 39 | } 40 | .gsuiBeatlines-beats { 41 | opacity: .75; 42 | background-image: repeating-linear-gradient( 43 | var( --gsui-deg ), 44 | currentColor, 45 | currentColor .5px, 46 | transparent .5px, 47 | transparent calc( 1em - .5px ), 48 | currentColor calc( 1em - .5px ), 49 | currentColor 1em ); 50 | } 51 | gsui-beatlines[ coloredbeats ] .gsuiBeatlines-beatsBG { 52 | background-image: repeating-linear-gradient( 53 | var( --gsui-deg ), 54 | #0001, 55 | #0001 1em, 56 | transparent 1em, 57 | transparent 2em ); 58 | } 59 | -------------------------------------------------------------------------------- /gsuiBeatlines/gsuiBeatlines.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-beatlines", () => [ 4 | GSUcreateDiv( { class: "gsuiBeatlines-bg gsuiBeatlines-measures" } ), 5 | GSUcreateDiv( { class: "gsuiBeatlines-bg gsuiBeatlines-beats" } ), 6 | GSUcreateDiv( { class: "gsuiBeatlines-bg gsuiBeatlines-steps" } ), 7 | GSUcreateDiv( { class: "gsuiBeatlines-bg gsuiBeatlines-beatsBG" } ), 8 | ] ); 9 | -------------------------------------------------------------------------------- /gsuiBeatlines/gsuiBeatlines.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiBeatlines extends gsui0ne { 4 | constructor() { 5 | super( { 6 | $cmpName: "gsuiBeatlines", 7 | $tagName: "gsui-beatlines", 8 | $attributes: { 9 | timedivision: "4/4", 10 | coloredbeats: true, 11 | pxperbeat: 10, 12 | }, 13 | } ); 14 | } 15 | static get observedAttributes() { 16 | return [ "timedivision", "pxperbeat" ]; 17 | } 18 | $attributeChanged( prop, val ) { 19 | switch ( prop ) { 20 | case "timedivision": 21 | GSUsetStyle( this, { 22 | "--gsui-bPM": `${ val.split( "/" )[ 0 ] }em`, 23 | "--gsui-sPB": `${ 1 / val.split( "/" )[ 1 ] }em`, 24 | } ); 25 | break; 26 | case "pxperbeat": 27 | GSUsetStyle( this, { 28 | fontSize: `${ val }px`, 29 | opacity: Math.min( val / 48, 1 ), 30 | } ); 31 | break; 32 | } 33 | } 34 | } 35 | 36 | GSUdefineElement( "gsui-beatlines", gsuiBeatlines ); 37 | -------------------------------------------------------------------------------- /gsuiBlocksManager/gsuiBlocksManager.css: -------------------------------------------------------------------------------- 1 | .gsuiBlocksManager { 2 | --gsuiBlocksManager-timeline-height: 32px; 3 | --gsuiBlocksManager-selection-bgColor: #b226; 4 | --gsuiBlocksManager-selection-borderColor: #f44; 5 | --gsuiBlocksManager-blockColor: var( --gsui-col-pattern8 ); 6 | --gsuiBlocksManager-blockSelectedColor: var( --gsui-col-patternSelected8 ); 7 | } 8 | 9 | /* .......................................................................... */ 10 | .gsuiBlocksManager-selection { 11 | position: absolute; 12 | box-sizing: border-box; 13 | top: 0; 14 | z-index: 2; 15 | border: 2px solid var( --gsuiBlocksManager-selection-borderColor ); 16 | border-radius: 4px; 17 | background-color: var( --gsuiBlocksManager-selection-bgColor ); 18 | transition: .2s; 19 | transition-property: opacity, visibility; 20 | } 21 | .gsuiBlocksManager-selection-hidden { 22 | opacity: 0; 23 | visibility: hidden; 24 | } 25 | 26 | /* .......................................................................... */ 27 | .gsuiBlocksManager-block { 28 | position: absolute; 29 | z-index: 1; 30 | top: 0; 31 | bottom: 1px; 32 | background-color: var( --gsuiBlocksManager-blockColor ); 33 | transition: .1s; 34 | transition-property: opacity, background-color; 35 | } 36 | .gsuiBlocksManager-block-selected { 37 | --gsuiBlocksManager-blockColor: var( --gsuiBlocksManager-blockSelectedColor ); 38 | } 39 | .gsuiBlocksManager-block-hidden.gsuiBlocksManager-block-hidden.gsuiBlocksManager-block-hidden { 40 | z-index: 0; 41 | opacity: .3; 42 | } 43 | .gsuiBlocksManager-block-crop { 44 | position: absolute; 45 | z-index: 2; 46 | width: 50%; 47 | height: 100%; 48 | max-width: 8px; 49 | border-radius: inherit; 50 | background-color: #000; 51 | opacity: 0; 52 | transition: .1s opacity; 53 | } 54 | .gsuiBlocksManager-block-cropA { 55 | left: 0; 56 | border-top-right-radius: 0; 57 | border-bottom-right-radius: 0; 58 | } 59 | .gsuiBlocksManager-block-cropB { 60 | right: 0; 61 | border-top-left-radius: 0; 62 | border-bottom-left-radius: 0; 63 | } 64 | .gsuiBlocksManager-block:hover .gsuiBlocksManager-block-crop, 65 | .gsuiBlocksManager-block.gsui-hover .gsuiBlocksManager-block-crop { 66 | opacity: .12; 67 | } 68 | .gsuiBlocksManager-block .gsuiBlocksManager-block-crop:hover, 69 | .gsuiBlocksManager-block .gsuiBlocksManager-block-crop.gsui-hover { 70 | opacity: .25; 71 | } 72 | -------------------------------------------------------------------------------- /gsuiChannel/gsuiChannel.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-channel", () => [ 4 | GSUcreateButton( { class: "gsuiChannel-nameWrap" }, 5 | GSUcreateSpan( { class: "gsuiChannel-name" } ), 6 | ), 7 | GSUcreateButton( { class: "gsuiChannel-headBtn gsuiChannel-rename", icon: "pen", title: "Rename the channel" } ), 8 | GSUcreateButton( { class: "gsuiChannel-headBtn gsuiChannel-delete", icon: "close", title: "Remove the channel" } ), 9 | GSUcreateDiv( { class: "gsuiChannel-analyser" }, 10 | GSUcreateElement( "gsui-analyser-hist" ), 11 | GSUcreateDiv( { class: "gsuiChannel-effects" } ), 12 | ), 13 | GSUcreateElement( "gsui-toggle" ), 14 | GSUcreateDiv( { class: "gsuiChannel-pan" }, 15 | GSUcreateElement( "gsui-slider", { type: "circular", min: -1, max: 1, step: .02, "mousemove-size": 800, "stroke-width": 3, "data-prop": "pan", defaultValue: 0 } ), 16 | ), 17 | GSUcreateDiv( { class: "gsuiChannel-gain" }, 18 | GSUcreateElement( "gsui-slider", { type: "linear-y", min: 0, max: 1, step: .01, "mousemove-size": 400, "data-prop": "gain", defaultValue: 1 } ), 19 | ), 20 | GSUcreateButton( { class: "gsuiChannel-connect" }, 21 | GSUcreateIcon( { class: "gsuiChannel-connectA", icon: "caret-up" } ), 22 | GSUcreateIcon( { class: "gsuiChannel-connectB", icon: "caret-up" } ), 23 | ), 24 | GSUcreateDiv( { class: "gsuiChannel-grip gsuiIcon", "data-icon": "grip-h" } ), 25 | ] ); 26 | 27 | GSUsetTemplate( "gsui-channel-effect", ( id, name ) => 28 | GSUcreateButton( { class: "gsuiChannel-effect gsuiChannel-effect-enable", "data-id": id }, 29 | GSUcreateSpan( { class: "gsuiChannel-effect-name" }, name ), 30 | ), 31 | ); 32 | -------------------------------------------------------------------------------- /gsuiChannel/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 |
37 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /gsuiChannels/gsuiChannels.css: -------------------------------------------------------------------------------- 1 | gsui-channels { 2 | --vuW: 11px; 3 | display: flex; 4 | z-index: 0; 5 | height: 100%; 6 | position: relative; 7 | background-color: var( --gsui-items-bg ); 8 | } 9 | 10 | gsui-channels gsui-analyser-vu { 11 | width: var( --vuW ); 12 | } 13 | 14 | /* .......................................................................... */ 15 | .gsuiChannels-panMain { 16 | position: absolute; 17 | inset: 0 auto 0 0; 18 | display: flex; 19 | gap: 3px; 20 | } 21 | .gsuiChannels-panChannels { 22 | position: absolute; 23 | overflow: scroll; 24 | inset: 0 0 0 calc( var( --vuW ) + 3px + 50px ); 25 | display: flex; 26 | scrollbar-width: none; 27 | } 28 | .gsuiChannels-panChannels::-webkit-scrollbar { 29 | display: none; 30 | } 31 | .gsuiChannels-panChannels::after { 32 | order: 2147483647; 33 | content: "."; 34 | opacity: 0; 35 | margin-left: 16px; 36 | } 37 | 38 | /* .......................................................................... */ 39 | .gsuiChannels-addChan { 40 | position: relative; 41 | order: 2147483646; 42 | display: flex; 43 | flex-direction: column; 44 | gap: 1ch; 45 | align-items: center; 46 | justify-content: center; 47 | min-width: 52px; 48 | border: 0; 49 | padding: 0; 50 | outline: 0; 51 | font-size: 18px; 52 | color: inherit; 53 | cursor: pointer; 54 | border-radius: 2px; 55 | background: none; 56 | opacity: .4; 57 | transition: .2s opacity; 58 | } 59 | .gsuiChannels-addChan:focus, 60 | .gsuiChannels-addChan:hover { 61 | opacity: .6; 62 | } 63 | .gsuiChannels-addChan::before { 64 | content: ""; 65 | inset: 6px; 66 | opacity: .4; 67 | border: 2px dashed; 68 | position: absolute; 69 | } 70 | .gsuiChannels-addChan .gsuiIcon:first-child { 71 | font-size: 24px; 72 | } 73 | .gsuiChannels-addChan .gsuiIcon:last-child { 74 | font-size: 14px; 75 | } 76 | -------------------------------------------------------------------------------- /gsuiChannels/gsuiChannels.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-channels", () => [ 4 | GSUcreateDiv( { class: "gsuiChannels-panMain" }, 5 | GSUcreateElement( "gsui-analyser-vu", { max: 120, title: "current channel's VU (max 120%)" } ), 6 | ), 7 | GSUcreateDiv( { class: "gsuiChannels-panChannels" }, 8 | GSUcreateButton( { class: "gsuiChannels-addChan", title: "Add a channel" }, 9 | GSUcreateIcon( { icon: "channels" } ), 10 | GSUcreateIcon( { icon: "plus" } ), 11 | ), 12 | ), 13 | ] ); 14 | 15 | GSUsetTemplate( "gsui-channels-selectPopup", () => 16 | GSUcreateDiv( null, 17 | GSUcreateElement( "fieldset", null, 18 | GSUcreateElement( "legend", null, "Select a channel" ), 19 | GSUcreateSelect( { name: "channel", size: 8 } ), 20 | ), 21 | ) 22 | ); 23 | -------------------------------------------------------------------------------- /gsuiClock/gsuiClock.css: -------------------------------------------------------------------------------- 1 | gsui-clock { 2 | display: flex; 3 | width: min-content; 4 | height: 28px; 5 | min-height: 28px; 6 | border-radius: 4px; 7 | font-size: 18px; 8 | background-color: var( --gsui-clock-bg ); 9 | } 10 | 11 | .gsuiClock-relative { 12 | position: relative; 13 | height: 100%; 14 | padding-left: 8px; 15 | border-top-left-radius: inherit; 16 | border-bottom-left-radius: inherit; 17 | } 18 | .gsuiClock-absolute { 19 | display: flex; 20 | position: absolute; 21 | white-space: nowrap; 22 | align-items: baseline; 23 | padding-top: 4px; 24 | font-family: var( --gsui-font-number ); 25 | } 26 | 27 | .gsuiClock-modes { 28 | position: relative; 29 | display: flex; 30 | margin: 0; 31 | border: 0; 32 | outline: 0; 33 | padding: 7px 6px; 34 | border-top-right-radius: inherit; 35 | border-bottom-right-radius: inherit; 36 | cursor: pointer; 37 | color: inherit; 38 | flex-direction: column; 39 | justify-content: space-between; 40 | background: none; 41 | } 42 | .gsuiClock-modes:focus-visible { 43 | box-shadow: 0 0 1px 2px #69b; 44 | } 45 | .gsuiClock-mode { 46 | width: 5px; 47 | height: 5px; 48 | border-radius: 1px; 49 | background-color: currentColor; 50 | opacity: .2; 51 | } 52 | gsui-clock[ mode="beat" ] .gsuiClock-beat, 53 | gsui-clock[ mode="second" ] .gsuiClock-second { 54 | opacity: .8; 55 | } 56 | 57 | .gsuiClock-b::before { 58 | content: ":"; 59 | margin: 0 .2ch; 60 | } 61 | .gsuiClock-c { 62 | font-size: 12px; 63 | opacity: .5; 64 | } 65 | .gsuiClock-c::before { 66 | content: "."; 67 | margin: 0 .4ch; 68 | } 69 | 70 | .gsuiClock-modeText { 71 | position: absolute; 72 | top: 3px; 73 | right: 0; 74 | font-size: 8px; 75 | font-family: var( --gsui-font ); 76 | opacity: .25; 77 | } 78 | gsui-clock[ mode="beat" ] .gsuiClock-modeText::before { content: "beat"; } 79 | gsui-clock[ mode="second" ] .gsuiClock-modeText::before { content: "sec"; } 80 | -------------------------------------------------------------------------------- /gsuiClock/gsuiClock.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-clock", () => [ 4 | GSUcreateDiv( { class: "gsuiClock-relative", inert: true }, 5 | GSUcreateDiv( { class: "gsuiClock-absolute" }, 6 | GSUcreateDiv( { class: "gsuiClock-a" }, "0" ), 7 | GSUcreateDiv( { class: "gsuiClock-b" }, "00" ), 8 | GSUcreateDiv( { class: "gsuiClock-c" }, "000" ), 9 | GSUcreateSpan( { class: "gsuiClock-modeText" } ), 10 | ), 11 | ), 12 | GSUcreateButton( { class: "gsuiClock-modes" }, 13 | GSUcreateDiv( { class: "gsuiClock-mode gsuiClock-beat" } ), 14 | GSUcreateDiv( { class: "gsuiClock-mode gsuiClock-second" } ), 15 | ) 16 | ] ); 17 | -------------------------------------------------------------------------------- /gsuiClock/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | 23 | 24 | 25 | 26 |
27 | 28 |
29 |
30 | 31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /gsuiComAvatar/gsuiComAvatar.css: -------------------------------------------------------------------------------- 1 | gsui-com-avatar { 2 | container-type: inline-size; 3 | overflow: hidden; 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | min-width: 24px; 8 | min-height: 24px; 9 | background-color: #0001; 10 | } 11 | 12 | gsui-com-avatar img { 13 | width: 100%; 14 | height: 100%; 15 | object-fit: cover; 16 | } 17 | gsui-com-avatar .gsuiIcon { 18 | position: absolute; 19 | font-size: 60cqw; 20 | color: #2e355b; 21 | } 22 | 23 | gsui-com-avatar:not( [ loaded ] ) img, 24 | gsui-com-avatar[ loaded ] .gsuiIcon { 25 | display: none; 26 | } 27 | -------------------------------------------------------------------------------- /gsuiComAvatar/gsuiComAvatar.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiComAvatar extends gsui0ne { 4 | constructor() { 5 | super( { 6 | $cmpName: "gsuiComAvatar", 7 | $tagName: "gsui-com-avatar", 8 | $template: [ 9 | GSUcreateElement( "img", { src: "" } ), 10 | GSUcreateIcon( { icon: "musician" } ), 11 | ], 12 | } ); 13 | Object.seal( this ); 14 | this.$element.onload = () => GSUsetAttribute( this, "loaded", true ); 15 | } 16 | 17 | // ......................................................................... 18 | static get observedAttributes() { 19 | return [ "src" ]; 20 | } 21 | $attributeChanged( prop, val ) { 22 | switch ( prop ) { 23 | case "src": 24 | GSUsetAttribute( this, "loaded", false ); 25 | this.$element.src = val || ""; 26 | break; 27 | } 28 | } 29 | } 30 | 31 | GSUdefineElement( "gsui-com-avatar", gsuiComAvatar ); 32 | -------------------------------------------------------------------------------- /gsuiComAvatar/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 34 | 35 | 36 | 37 | 38 |
39 | 40 |
41 |
42 |
43 |
44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /gsuiComButton/gsuiComButton.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiComButton extends gsui0ne { 4 | #href = null; 5 | 6 | constructor() { 7 | super( { 8 | $cmpName: "gsuiComButton", 9 | $tagName: "gsui-com-button", 10 | $template: GSUcreateButton( { class: "gsuiComButton-btn" }, 11 | GSUcreateSpan( { class: "gsuiComButton-text" } ), 12 | ), 13 | } ); 14 | Object.seal( this ); 15 | gsuiRipple.$init( this.$element ); 16 | this.$element.addEventListener( "click", () => { 17 | if ( GSUhasAttribute( this, "href" ) ) { 18 | window.location = this.#href; 19 | } 20 | } ); 21 | } 22 | 23 | // ......................................................................... 24 | static get observedAttributes() { 25 | return [ "text", "loading", "disabled", "type", "href" ]; 26 | } 27 | $attributeChanged( prop, val ) { 28 | switch ( prop ) { 29 | case "disabled": 30 | case "loading": this.#updateDisabled(); break; 31 | case "text": this.$element.firstChild.textContent = val; break; 32 | case "type": this.$element.type = val === "submit" ? val : "button"; break; 33 | case "href": this.#href = val; break; 34 | } 35 | } 36 | 37 | // ......................................................................... 38 | #updateDisabled() { 39 | this.$element.disabled = GSUhasAttribute( this, "disabled" ) || GSUhasAttribute( this, "loading" ); 40 | } 41 | } 42 | 43 | GSUdefineElement( "gsui-com-button", gsuiComButton ); 44 | -------------------------------------------------------------------------------- /gsuiComButton/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | 40 | 41 | 42 | 43 |
44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /gsuiComPlayer/gsuiComPlayer.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-com-player", () => [ 4 | GSUcreateButton( { class: "gsuiComPlayer-btn gsuiComPlayer-play", icon: "play" } ), 5 | GSUcreateDiv( { class: "gsuiComPlayer-body" }, 6 | GSUcreateDiv( { class: "gsuiComPlayer-text" }, 7 | GSUcreateDiv( { class: "gsuiComPlayer-name" }, 8 | GSUcreateA( { class: "gsuiComPlayer-nameLink", href: false } ), 9 | ), 10 | GSUcreateDiv( { class: "gsuiComPlayer-info-wrap" }, 11 | GSUcreateDiv( { class: "gsuiComPlayer-info" }, 12 | GSUcreateDiv( { class: "gsuiComPlayer-time" }, 13 | GSUcreateIcon( { icon: "clock" } ), 14 | GSUcreateSpan( { class: "gsuiComPlayer-currentTime" } ), 15 | GSUcreateSpan( { class: "gsuiComPlayer-duration" } ), 16 | ), 17 | GSUcreateDiv( { class: "gsuiComPlayer-tempo" }, 18 | GSUcreateIcon( { icon: "speed" } ), 19 | GSUcreateSpan( { class: "gsuiComPlayer-bpm" } ), 20 | ), 21 | ), 22 | ), 23 | ), 24 | GSUcreateDiv( { class: "gsuiComPlayer-slider" }, 25 | GSUcreateDiv( { class: "gsuiComPlayer-sliderValue" } ), 26 | GSUcreateDiv( { class: "gsuiComPlayer-sliderInput" } ), 27 | ), 28 | ), 29 | GSUcreateAExt( { class: "gsuiComPlayer-btn gsuiComPlayer-dawlink", title: "Open in the DAW", href: false }, 30 | GSUcreateIcon( { icon: "music" } ), 31 | GSUcreateIcon( { icon: "pen" } ), 32 | GSUcreateIcon( { icon: "eye" } ), 33 | ), 34 | GSUcreateButton( { class: "gsuiComPlayer-btn gsuiComPlayer-actions", icon: "ellipsis-v" } ), 35 | ] ); 36 | -------------------------------------------------------------------------------- /gsuiComPlaylist/gsuiComPlaylist.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-com-playlist", () => [ 4 | GSUcreateDiv( { class: "gsuiComPlaylist-head" }, 5 | GSUcreateButton( { class: "gsuiComPlaylist-head-btn" }, 6 | GSUcreateSpan(), 7 | GSUcreateSpan( null, "composition·s" ), 8 | ), 9 | GSUcreateButton( { class: "gsuiComPlaylist-head-btn" }, 10 | GSUcreateSpan(), 11 | GSUcreateSpan( null, "in the bin" ), 12 | ), 13 | ), 14 | GSUcreateDiv( { class: "gsuiComPlaylist-list", "data-list": "cmps" } ), 15 | GSUcreateDiv( { class: "gsuiComPlaylist-list", "data-list": "bin" } ), 16 | GSUcreateDiv( { class: "gsuiComPlaylist-placeh", "data-list": "cmps" }, 17 | GSUcreateSpan( { class: "gsuiComPlaylist-placeh-text" }, 18 | GSUcreateSpan( null, "No saved composition yet" ), 19 | GSUcreateSpan( null, ", you should create a new one right now!" ), 20 | ), 21 | GSUcreateDiv( { class: "gsuiComPlaylist-placeh-draw" }, 22 | GSUcreateIcon( { icon: "music" } ), 23 | ), 24 | ), 25 | GSUcreateDiv( { class: "gsuiComPlaylist-placeh", "data-list": "bin" }, 26 | GSUcreateSpan( { class: "gsuiComPlaylist-placeh-text" }, "There is no composition inside your bin." ), 27 | GSUcreateDiv( { class: "gsuiComPlaylist-placeh-draw" }, 28 | GSUcreateIcon( { icon: "trash" } ), 29 | ), 30 | ), 31 | ] ); 32 | -------------------------------------------------------------------------------- /gsuiCurves/gsuiCurves.css: -------------------------------------------------------------------------------- 1 | gsui-curves { 2 | display: flex; 3 | flex-direction: column; 4 | gap: 4px; 5 | height: 100%; 6 | } 7 | 8 | /* .......................................................................... */ 9 | .gsuiCurves-marks { 10 | display: flex; 11 | align-items: center; 12 | height: 10px; 13 | } 14 | .gsuiCurves-marks div { 15 | flex: 1; 16 | font-size: 10px; 17 | } 18 | .gsuiCurves-marks div::before { 19 | content: attr( data-hz ); 20 | display: flex; 21 | padding-left: 3px; 22 | margin-right: 3px; 23 | opacity: .2; 24 | background-color: #000; 25 | } 26 | .gsuiCurves-marks div:last-child::before { 27 | margin-right: 0; 28 | } 29 | 30 | /* .......................................................................... */ 31 | .gsuiCurves-graph { 32 | flex: 1; 33 | position: relative; 34 | overflow: hidden; 35 | border-radius: 4px; 36 | } 37 | 38 | /* .......................................................................... */ 39 | gsui-curves gsui-analyser-hz { 40 | position: absolute; 41 | inset: 0; 42 | background-color: var( --gsui-screen-graph ); 43 | } 44 | 45 | /* .......................................................................... */ 46 | .gsuiCurves-svg { 47 | position: absolute; 48 | width: 100%; 49 | height: 100%; 50 | } 51 | .gsuiCurves-line { 52 | stroke: currentColor; 53 | stroke-width: 2; 54 | stroke-dasharray: 4; 55 | opacity: .5; 56 | } 57 | .gsuiCurves-curve { 58 | fill: none; 59 | stroke: currentColor; 60 | stroke-width: 3; 61 | filter: drop-shadow( 0 -1px 0 #000a ) drop-shadow( 0 1px 0 #000a ); 62 | } 63 | -------------------------------------------------------------------------------- /gsuiCurves/gsuiCurves.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-curves", () => [ 4 | GSUcreateDiv( { class: "gsuiCurves-marks" } ), 5 | GSUcreateDiv( { class: "gsuiCurves-graph" }, 6 | GSUcreateElement( "gsui-analyser-hz" ), 7 | GSUcreateElementSVG( "svg", { class: "gsuiCurves-svg", preserveAspectRatio: "none" }, 8 | GSUcreateElementSVG( "line", { class: "gsuiCurves-line", "shape-rendering": "crispEdges" } ), 9 | GSUcreateElementSVG( "g", { class: "gsuiCurves-curves" } ), 10 | ) 11 | ) 12 | ] ); 13 | -------------------------------------------------------------------------------- /gsuiDAW/gsuiDAW-btn.css: -------------------------------------------------------------------------------- 1 | .gsuiDAW-btn { 2 | display: inline-flex; 3 | align-items: center; 4 | justify-content: center; 5 | border: 0; 6 | padding: 0; 7 | outline: 0; 8 | color: inherit; 9 | cursor: pointer; 10 | font-size: 14px; 11 | line-height: 28px; 12 | text-decoration: none; 13 | border-radius: var( --radius ); 14 | background: none; 15 | transition: .2s; 16 | transition-property: color, opacity, background-color; 17 | } 18 | .gsuiDAW-btn.gsuiIcon { 19 | width: 28px; 20 | } 21 | .gsuiDAW-btn:not( .gsuiDAW-btnColor ):hover { 22 | color: #fff; 23 | opacity: 1; 24 | } 25 | .gsuiDAW-btn:active { 26 | padding-top: 4px; 27 | } 28 | 29 | .gsuiDAW-btnBg { 30 | background-color: var( --gsui-btn-default-bg ); 31 | } 32 | .gsuiDAW-btnBg:hover { 33 | background-color: #fff3; 34 | } 35 | 36 | .gsuiDAW-btnColor { 37 | color: var( --gsui-color ); 38 | } 39 | .gsuiDAW-btnColor:hover { 40 | color: var( --gsui-head1-bg ); 41 | background-color: var( --gsui-color ); 42 | } 43 | 44 | .gsuiDAW-btns { 45 | display: flex; 46 | border-radius: var( --radius ); 47 | } 48 | .gsuiDAW-btns .gsuiDAW-btn:first-child { 49 | border-top-right-radius: 0; 50 | border-bottom-right-radius: 0; 51 | } 52 | .gsuiDAW-btns .gsuiDAW-btn:last-child { 53 | border-top-left-radius: 0; 54 | border-bottom-left-radius: 0; 55 | } 56 | .gsuiDAW-btns .gsuiDAW-btn:not( :first-child ):not( :last-child ) { 57 | border-radius: 0; 58 | } 59 | -------------------------------------------------------------------------------- /gsuiDAW/gsuiDAW-popup-about.css: -------------------------------------------------------------------------------- 1 | .gsuiDAW-popup-about { 2 | width: 450px; 3 | text-align: center; 4 | } 5 | 6 | /* .......................................................................... */ 7 | .gsuiDAW-popup-about > div { 8 | margin-bottom: 16px; 9 | } 10 | .gsuiDAW-popup-about a { 11 | color: #ccf; 12 | font-weight: bold; 13 | } 14 | 15 | /* .......................................................................... */ 16 | .gsuiDAW-popup-about-head { 17 | display: flex; 18 | align-items: baseline; 19 | justify-content: center; 20 | font-size: 18px; 21 | gap: 4px; 22 | } 23 | .gsuiDAW-popup-about-title { 24 | font: 32px var( --gsui-font-title ); 25 | } 26 | .gsuiDAW-popup-about-versionNum { 27 | opacity: .8; 28 | font-family: var( --gsui-font-number ); 29 | } 30 | .gsuiDAW-popup-about-versionCheck { 31 | font-size: 11px; 32 | font-weight: bold; 33 | opacity: .4; 34 | cursor: pointer; 35 | transition: .2s opacity; 36 | } 37 | .gsuiDAW-popup-about-versionCheck:hover { 38 | opacity: 1; 39 | } 40 | 41 | /* .......................................................................... */ 42 | .gsuiDAW-popup-about-links { 43 | display: flex; 44 | } 45 | .gsuiDAW-popup-about-links a { 46 | flex: 1; 47 | padding: 20px 0; 48 | color: #fff; 49 | font-size: 36px; 50 | font-weight: normal; 51 | opacity: .5; 52 | transition: .4s; 53 | transition-property: opacity; 54 | } 55 | .gsuiDAW-popup-about-links a:hover { 56 | opacity: 1; 57 | } 58 | .gsuiDAW-popup-about-links a:hover::before { 59 | top: -4px; 60 | left: 4px; 61 | text-shadow: -4px 4px 2px rgba( 0, 0, 0, .2 ); 62 | } 63 | .gsuiDAW-popup-about-links a::before { 64 | position: relative; 65 | top: 0; 66 | left: 0; 67 | transition: .2s; 68 | transition-property: top, left, text-shadow; 69 | } 70 | .gsuiDAW-popup-about-links:hover a:not( :hover ) { 71 | opacity: .3; 72 | } 73 | -------------------------------------------------------------------------------- /gsuiDAW/gsuiDAW-popup-about.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-daw-popup-about", () => 4 | GSUcreateDiv( { class: "gsuiDAW-popup-about" }, 5 | GSUcreateDiv( { class: "gsuiDAW-popup-about-head" }, 6 | GSUcreateSpan( { class: "gsuiDAW-popup-about-title" }, "GridSound" ), 7 | GSUcreateSpan( { class: "gsuiDAW-popup-about-versionNum" } ), 8 | GSUcreateIcon( null ), 9 | GSUcreateButton( { class: "gsuiDAW-popup-about-versionCheck" }, "check the version" ), 10 | ), 11 | GSUcreateDiv( null, 12 | "GridSound is a ", 13 | GSUcreateElement( "b", null, "work-in-progress" ), 14 | " free browser-based digital audio workstation following the ", 15 | GSUcreateAExt( { href: "https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API" }, "Web Audio API" ), 16 | ".", 17 | ), 18 | GSUcreateDiv( null, 19 | "You can create an account (by clicking ", GSUcreateIcon( { icon: "profile" } ), 20 | ") and start uploading your compositions online ", GSUcreateIcon( { icon: "cloud" } ), 21 | ), 22 | GSUcreateDiv( { class: "gsuiDAW-popup-about-links" }, 23 | GSUcreateAExt( { title: "GitHub", class: "gsuiIcon", "data-icon": "br-github", href: "https://github.com/gridsound" } ), 24 | GSUcreateAExt( { title: "Twitter", class: "gsuiIcon", "data-icon": "br-twitter", href: "https://twitter.com/gridsound" } ), 25 | GSUcreateAExt( { title: "YouTube", class: "gsuiIcon", "data-icon": "br-youtube", href: "https://youtube.com/@gridsound" } ), 26 | GSUcreateAExt( { title: "Facebook", class: "gsuiIcon", "data-icon": "br-facebook", href: "https://facebook.com/gridsound" } ), 27 | GSUcreateAExt( { title: "CodePen", class: "gsuiIcon", "data-icon": "br-codepen", href: "https://codepen.io/gridsound" } ), 28 | GSUcreateAExt( { title: "Discord", class: "gsuiIcon", "data-icon": "br-discord", href: "https://discord.gg/NUYxHAg" } ), 29 | ), 30 | ) 31 | ); 32 | -------------------------------------------------------------------------------- /gsuiDAW/gsuiDAW-popup-export.css: -------------------------------------------------------------------------------- 1 | .gsuiDAW-popup-export { 2 | width: 500px; 3 | } 4 | .gsuiDAW-popup-export-wrap { 5 | display: flex; 6 | padding: 10px 0; 7 | } 8 | .gsuiDAW-popup-export-progress { 9 | flex: 1; 10 | height: 28px; 11 | } 12 | .gsuiDAW-popup-export-btn { 13 | padding: 0 .8em; 14 | border-radius: 2px; 15 | margin-right: 10px; 16 | font-size: 12px; 17 | line-height: 28px; 18 | font-weight: bold; 19 | text-decoration: none; 20 | background-color: #ff9; 21 | } 22 | .gsuiDAW-popup-export-btn > span { 23 | display: none; 24 | color: #111; 25 | } 26 | .gsuiDAW-popup-export-btn[ data-status="0" ] .gsuiDAW-popup-export-btn0, 27 | .gsuiDAW-popup-export-btn[ data-status="1" ] .gsuiDAW-popup-export-btn1, 28 | .gsuiDAW-popup-export-btn[ data-status="2" ] .gsuiDAW-popup-export-btn2 { 29 | display: inline; 30 | } 31 | .gsuiDAW-popup-export-btn .gsuiIcon { 32 | margin-left: .3em; 33 | } 34 | -------------------------------------------------------------------------------- /gsuiDAW/gsuiDAW-popup-export.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-daw-popup-export", () => 4 | GSUcreateDiv( { class: "gsuiDAW-popup-export" }, 5 | GSUcreateElement( "fieldset", null, 6 | GSUcreateElement( "legend", null, "Render the current composition" ), 7 | GSUcreateDiv( { class: "gsuiDAW-popup-export-wrap" }, 8 | GSUcreateA( { class: "gsuiDAW-popup-export-btn", "data-status": 0 }, 9 | GSUcreateSpan( { class: "gsuiDAW-popup-export-btn0" }, 10 | GSUcreateSpan( null, "Render" ), 11 | GSUcreateIcon( { icon: "render" } ), 12 | ), 13 | GSUcreateSpan( { class: "gsuiDAW-popup-export-btn1" }, 14 | GSUcreateSpan( null, "Rendering..." ), 15 | GSUcreateIcon( { spin: true } ), 16 | ), 17 | GSUcreateSpan( { class: "gsuiDAW-popup-export-btn2" }, 18 | GSUcreateSpan( null, "Download WAV file" ), 19 | GSUcreateIcon( { icon: "export" } ), 20 | ), 21 | ), 22 | GSUcreateElement( "progress", { class: "gsuiDAW-popup-export-progress", value: "", max: 1 } ), 23 | ), 24 | ), 25 | ) 26 | ); 27 | -------------------------------------------------------------------------------- /gsuiDAW/gsuiDAW-popup-settings.css: -------------------------------------------------------------------------------- 1 | .gsuiDAW-popup-settings { 2 | width: 480px; 3 | } 4 | .gsuiDAW-popup-settings .gsuiPopup-row-values { 5 | width: 200px; 6 | } 7 | .gsuiDAW-uiRateFps { 8 | font-size: 16px; 9 | font-family: var( --gsui-font-number ); 10 | white-space: pre; 11 | } 12 | .gsuiDAW-uiRateFps::after { 13 | content: " fps"; 14 | font-family: var( --gsui-font ); 15 | font-size: 12px; 16 | opacity: .6; 17 | } 18 | .gsuiDAW-popup-settings input[ name="uiRateFPS" ] { 19 | min-width: 0; 20 | } 21 | -------------------------------------------------------------------------------- /gsuiDAW/gsuiDAW-windows.css: -------------------------------------------------------------------------------- 1 | .gsuiDAW-winBtn { 2 | height: 16px; 3 | padding: 0 6px; 4 | max-width: 110px; 5 | border: 0; 6 | outline: 0; 7 | cursor: pointer; 8 | overflow: hidden; 9 | border-radius: 3px; 10 | line-height: 1; 11 | color: inherit; 12 | font-size: 10px; 13 | font-weight: bold; 14 | font-family: inherit; 15 | white-space: nowrap; 16 | text-overflow: ellipsis; 17 | background-color: var( --gsuiDAW-winBtn-bg ); 18 | transition: filter .2s; 19 | } 20 | .gsuiDAW-winBtn:focus, 21 | .gsuiDAW-winBtn:hover { 22 | filter: brightness( 1.1 ); 23 | } 24 | .gsuiDAW-winBtn:empty::before { 25 | content: "Untitled"; 26 | font-style: italic; 27 | } 28 | .gsuiDAW-winBtn .gsuiIcon, 29 | .gsuiDAW-winBtn span { 30 | pointer-events: none; 31 | } 32 | .gsuiDAW-winBtn .gsuiIcon + span { 33 | margin-left: 4px; 34 | } 35 | 36 | /* .......................................................................... */ 37 | .gsuiDAW-winBtn + .gsuiIcon { 38 | font-size: 12px; 39 | } 40 | 41 | /* .......................................................................... */ 42 | .gsuiDAW-winBtn[ data-target="channel" ], 43 | .gsuiDAW-winBtn[ data-target="synthChannel" ] { color: var( --gsuiDAW-winBtn-chan-color ); } 44 | .gsuiDAW-winBtn[ data-target="drums" ], 45 | .gsuiDAW-winBtn[ data-target="pianoroll" ] { color: var( --gsuiDAW-winBtn-pattern-color ); } 46 | .gsuiDAW-winBtn[ data-target="synth" ] { color: var( --gsuiDAW-winBtn-synth-color ); } 47 | .gsuiDAW-winBtn[ data-target="slices" ] { color: var( --gsuiDAW-winBtn-slices-color ); } 48 | 49 | /* .......................................................................... */ 50 | .gsuiDAW-window-ctrl { 51 | display: flex; 52 | align-items: center; 53 | gap: inherit; 54 | } 55 | .gsuiDAW-window-playPause { 56 | overflow: hidden; 57 | display: flex; 58 | border-radius: 4px; 59 | } 60 | .gsuiDAW-window-ctrl button { 61 | width: 18px; 62 | height: 16px; 63 | padding: 0; 64 | border: 0; 65 | outline: 0; 66 | color: #fff; 67 | font-size: 8px; 68 | cursor: pointer; 69 | background-color: var( --gsui-btn-playPause-bg ); 70 | } 71 | .gsuiDAW-window-playPause button:hover { 72 | color: #fffa; 73 | } 74 | .gsuiDAW-window-ctrl button:hover { 75 | filter: brightness( 1.2 ); 76 | } 77 | .gsuiDAW-window-ctrl button:active { 78 | padding-top: 2px; 79 | } 80 | .gsuiDAW-window-ctrl button[ data-icon="play" ] { 81 | padding-left: 2px; 82 | } 83 | .gsuiDAW-window-ctrl button[ data-action="autoscroll" ] { 84 | width: 16px; 85 | border: 2px solid #67a; 86 | border-radius: 999px; 87 | background: none; 88 | } 89 | gsui-window:has( gsui-timewindow[ autoscroll ] ) .gsuiDAW-window-ctrl button[ data-action="autoscroll" ] { 90 | background-color: #67a; 91 | } 92 | -------------------------------------------------------------------------------- /gsuiDAW/gsuiDAW-windows.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-daw-windowHeads", () => [ 4 | GSUcreateDiv( { "data-window": "composition" }, 5 | GSUgetTemplate( "gsui-daw-window-playPause" ), 6 | ), 7 | GSUcreateDiv( { "data-window": "keys" }, 8 | GSUcreateButton( { class: "gsuiDAW-winBtn", "data-target": "pianoroll" } ), 9 | GSUgetTemplate( "gsui-daw-window-playPause" ), 10 | ), 11 | GSUcreateDiv( { "data-window": "synth" }, 12 | GSUcreateButton( { class: "gsuiDAW-winBtn", "data-target": "synth" } ), 13 | GSUcreateIcon( { icon: "arrow-right" } ), 14 | GSUcreateButton( { class: "gsuiDAW-winBtn", "data-target": "synthChannel" }, 15 | GSUcreateIcon( { icon: "mixer" } ), 16 | GSUcreateSpan(), 17 | ), 18 | ), 19 | GSUcreateDiv( { "data-window": "mixer" } ), 20 | GSUcreateDiv( { "data-window": "effects" }, 21 | GSUcreateButton( { class: "gsuiDAW-winBtn", "data-target": "channel" } ), 22 | ), 23 | GSUcreateDiv( { "data-window": "drums" }, 24 | GSUcreateButton( { class: "gsuiDAW-winBtn", "data-target": "drums" } ), 25 | GSUgetTemplate( "gsui-daw-window-playPause" ), 26 | ), 27 | GSUcreateDiv( { "data-window": "slices" }, 28 | GSUcreateButton( { class: "gsuiDAW-winBtn", "data-target": "slices" } ), 29 | GSUgetTemplate( "gsui-daw-window-playPause" ), 30 | ), 31 | ] ); 32 | 33 | GSUsetTemplate( "gsui-daw-window-playPause", () => [ 34 | GSUcreateDiv( { "class": "gsuiDAW-window-ctrl" }, 35 | GSUcreateDiv( { "class": "gsuiDAW-window-playPause" }, 36 | GSUcreateButton( { "data-action": "play", icon: "play" } ), 37 | GSUcreateButton( { "data-action": "stop", icon: "stop" } ), 38 | ), 39 | GSUcreateButton( { "data-action": "autoscroll", icon: "chevron-double-right", title: "Auto scrolling" } ), 40 | ), 41 | ] ); 42 | -------------------------------------------------------------------------------- /gsuiDAW/gsuiDAW.css: -------------------------------------------------------------------------------- 1 | gsui-daw { 2 | --radius: 4px; 3 | --gap: 4px; 4 | display: flex; 5 | flex-direction: column; 6 | position: relative; 7 | box-sizing: border-box; 8 | width: 100%; 9 | height: 100%; 10 | font-size: 16px; 11 | background-color: var( --gsui-app-bg ); 12 | --gsuiDAW-winBtn-bg: #0004; 13 | --gsuiDAW-winBtn-chan-color: #ce93d8; 14 | --gsuiDAW-winBtn-synth-color: #a5d6a7; 15 | --gsuiDAW-winBtn-slices-color: #e29d8a; 16 | --gsuiDAW-winBtn-pattern-color: #81d4fa; 17 | } 18 | 19 | /* .......................................................................... */ 20 | .gsuiDAW-head { 21 | display: grid; 22 | padding: var( --gap ); 23 | gap: var( --gap ); 24 | grid-template: 25 | "areaUser areaCtrl areaVisu areaHelp" 28px 26 | "areaUser areaTime areaWins areaVers" 20px 27 | /min-content min-content min-content 1fr; 28 | border-bottom: 2px solid var( --gsui-win-brd ); 29 | background-color: var( --gsui-head1-bg ); 30 | } 31 | .gsuiDAW-area { 32 | display: flex; 33 | gap: inherit; 34 | } 35 | .gsuiDAW-areaUser { 36 | grid-area: areaUser; 37 | } 38 | .gsuiDAW-body[ resources-hidden ] .gsuiDAW-resources { 39 | display: none; 40 | } 41 | .gsuiDAW-body[ resources-hidden ] .gsuiDAW-windows { 42 | left: 0 !important; 43 | width: 100% !important; 44 | } 45 | .gsuiDAW-resources { 46 | display: flex; 47 | width: 300px; 48 | min-width: 330px; 49 | max-width: 700px; 50 | border-right: 3px solid var( --gsui-win-brd ); 51 | background-color: var( --gsui-items-bg ); 52 | } 53 | .gsuiDAW-libraries { 54 | flex: 1; 55 | border: inherit; 56 | border-right-width: 2px; 57 | } 58 | .gsuiDAW-patterns { 59 | flex: 1; 60 | } 61 | .gsuiDAW-windows { 62 | flex: 1; 63 | } 64 | gsui-daw gsui-windows { 65 | z-index: 0; 66 | background-image: url( "data:image/svg+xml,%3Csvg width='6' height='6' viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23000000' fill-opacity='0.2' fill-rule='evenodd'%3E%3Cpath d='M5 0h1L0 6V5zM6 5v1H5z'/%3E%3C/g%3E%3C/svg%3E" ); 67 | background-attachment: local; 68 | } 69 | -------------------------------------------------------------------------------- /gsuiDotline/gsuiDotline.css: -------------------------------------------------------------------------------- 1 | gsui-dotline { 2 | --gsuiDotline-dotSize: 4px; 3 | --gsuiDotline-cdotSize: 8px; 4 | display: block; 5 | width: 100%; 6 | height: 100%; 7 | position: relative; 8 | stroke: currentColor; 9 | stroke-width: 2px; 10 | touch-action: none; 11 | } 12 | 13 | /* .......................................................................... */ 14 | .gsuiDotline-padding { 15 | position: absolute; 16 | z-index: 0; 17 | inset: calc( var( --gsuiDotline-dotSize ) / 2 ); 18 | } 19 | 20 | /* .......................................................................... */ 21 | gsui-dotline svg { 22 | position: absolute; 23 | left: 0; 24 | top: 0; 25 | width: 100%; 26 | height: 100%; 27 | } 28 | gsui-dotline path { 29 | fill: none; 30 | } 31 | gsui-dotline gsui-beatlines + gsui-beatlines { 32 | --gsui-deg: 0deg; 33 | } 34 | 35 | /* .......................................................................... */ 36 | gsui-dotline gsui-slider { 37 | width: 10px; 38 | height: 32px; 39 | opacity: 0; 40 | pointer-events: none; 41 | } 42 | 43 | /* .......................................................................... */ 44 | .gsuiDotline-dot, 45 | .gsuiDotline-cdot { 46 | position: absolute; 47 | box-sizing: border-box; 48 | border-radius: 50%; 49 | width: var( --w ); 50 | height: var( --w ); 51 | margin: 52 | calc( var( --w ) / -2 ) 0 0 53 | calc( var( --w ) / -2 ); 54 | } 55 | .gsuiDotline-dot { 56 | --w: var( --gsuiDotline-dotSize ); 57 | z-index: 2; 58 | background-color: currentColor; 59 | } 60 | .gsuiDotline-cdot { 61 | --w: var( --gsuiDotline-cdotSize ); 62 | z-index: 1; 63 | border: 1px solid; 64 | transition: 65 | .2s width, 66 | .2s height, 67 | .2s margin; 68 | } 69 | .gsuiDotline-dot::before, 70 | .gsuiDotline-cdot::before { 71 | content: ""; 72 | position: absolute; 73 | inset: -4px; 74 | border-radius: 50%; 75 | } 76 | .gsuiDotline-dot:hover::before, 77 | .gsuiDotline-cdot:hover::before, 78 | .gsuiDotline-dotSelected::before { 79 | background-color: #fff3; 80 | } 81 | .gsuiDotline-cdot[ data-type="hold" ], 82 | .gsuiDotline-cdot[ data-type="line" ] { 83 | display: none; 84 | } 85 | -------------------------------------------------------------------------------- /gsuiDotline/gsuiDotline.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-dotline", () => ( 4 | GSUcreateDiv( { class: "gsuiDotline-padding" }, 5 | GSUcreateElement( "gsui-slider", { type: "linear-y", min: -32, max: 32, step: .01, "mousemove-size": 2000 } ), 6 | GSUcreateElement( "gsui-dotlinesvg" ), 7 | ) 8 | ) ); 9 | -------------------------------------------------------------------------------- /gsuiDotlineSVG/gsuiDotlineSVG.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-dotlinesvg", () => 4 | GSUcreateElementSVG( "svg", { preserveAspectRatio: "none", inert: true }, 5 | GSUcreateElementSVG( "path" ), 6 | ) 7 | ); 8 | -------------------------------------------------------------------------------- /gsuiDotlineSVG/gsuiDotlineSVG.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiDotlineSVG extends gsui0ne { 4 | #w = 1; 5 | #h = 1; 6 | #xmin = 0; 7 | #ymin = 0; 8 | #xmax = 0; 9 | #ymax = 0; 10 | #svgW = 0; 11 | #svgH = 0; 12 | #resW = 500; 13 | 14 | constructor() { 15 | super( { 16 | $cmpName: "gsuiDotlineSVG", 17 | $tagName: "gsui-dotlinesvg", 18 | $elements: { 19 | $svg: "svg", 20 | $path: "path", 21 | }, 22 | } ); 23 | Object.seal( this ); 24 | } 25 | 26 | // ......................................................................... 27 | $setSVGSize( w, h ) { 28 | this.#svgW = w; 29 | this.#svgH = h; 30 | this.#resW = w * 2; 31 | GSUsetViewBoxWH( this.$elements.$svg, w, h ); 32 | } 33 | $setDataBox( box ) { 34 | const n = box.split( " " ); 35 | 36 | this.#xmin = +n[ 0 ]; 37 | this.#ymin = +n[ 1 ]; 38 | this.#xmax = +n[ 2 ]; 39 | this.#ymax = +n[ 3 ]; 40 | this.#w = this.#xmax - this.#xmin; 41 | this.#h = this.#ymax - this.#ymin; 42 | } 43 | $setCurve( data ) { 44 | const xy = GSUmathSampleDotLine( data, this.#resW ); 45 | const curveDots = []; 46 | 47 | if ( xy.length > 1 ) { 48 | const xy0 = xy.shift(); 49 | 50 | curveDots.push( "M", this.#calcX( xy0[ 0 ] ), this.#calcY( xy0[ 1 ] ) ); 51 | xy.forEach( dot => curveDots.push( "L", this.#calcX( dot[ 0 ] ), this.#calcY( dot[ 1 ] ) ) ); 52 | } 53 | GSUsetAttribute( this.$elements.$path, "d", curveDots.join( " " ) ); 54 | } 55 | 56 | // ......................................................................... 57 | #calcX( x ) { 58 | return GSUmathFix( ( x - this.#xmin ) / this.#w * this.#svgW, 5 ); 59 | } 60 | #calcY( y ) { 61 | return GSUmathFix( this.#svgH - ( y - this.#ymin ) / this.#h * this.#svgH, 5 ); 62 | } 63 | } 64 | 65 | GSUdefineElement( "gsui-dotlinesvg", gsuiDotlineSVG ); 66 | -------------------------------------------------------------------------------- /gsuiDragline/gsuiDragline.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --gsuiDragline-color: #fff; 3 | --gsuiDragline-dotSize: 8px; 4 | --gsuiDragline-dotRad: 50%; 5 | --gsuiDragline-lineSize: 4px; 6 | } 7 | 8 | /* ................................................................ */ 9 | .gsuiDragline { 10 | position: absolute; 11 | max-width: 0; 12 | max-height: 0; 13 | color: var( --gsuiDragline-color ); 14 | } 15 | .gsuiDragline-dragging, 16 | .gsuiDragline-dragging .gsuiDragline-to { 17 | pointer-events: none; 18 | } 19 | 20 | /* ................................................................ */ 21 | .gsuiDragline-main { 22 | position: relative; 23 | pointer-events: none; 24 | } 25 | 26 | /* ................................................................ */ 27 | .gsuiDragline-line { 28 | position: absolute; 29 | bottom: 0; 30 | right: 0; 31 | width: 0; 32 | height: 0; 33 | fill: none; 34 | stroke: currentColor; 35 | stroke-width: var( --gsuiDragline-lineSize ); 36 | stroke-linecap: round; 37 | } 38 | .gsuiDragline-down .gsuiDragline-line { 39 | top: 0; 40 | bottom: auto; 41 | } 42 | .gsuiDragline-right .gsuiDragline-line { 43 | left: 0; 44 | right: auto; 45 | } 46 | 47 | /* ................................................................ */ 48 | .gsuiDragline-to { 49 | position: absolute; 50 | width: var( --gsuiDragline-dotSize ); 51 | height: var( --gsuiDragline-dotSize ); 52 | margin: calc( var( --gsuiDragline-dotSize ) / -2 ); 53 | cursor: pointer; 54 | border-radius: var( --gsuiDragline-dotRad ); 55 | pointer-events: all; 56 | background-color: var( --gsuiDragline-color ); 57 | transition: .2s background-color; 58 | } 59 | .gsuiDragline-down .gsuiDragline-to { bottom: 0; } 60 | .gsuiDragline-right .gsuiDragline-to { right: 0; } 61 | .gsuiDragline-main:not( .gsuiDragline-down ) .gsuiDragline-to { top: 0; } 62 | .gsuiDragline-main:not( .gsuiDragline-right ) .gsuiDragline-to { left: 0; } 63 | 64 | /* ................................................................ */ 65 | .gsuiDragline-drop { 66 | position: relative; 67 | max-width: 0; 68 | max-height: 0; 69 | } 70 | .gsuiDragline-drop::before { 71 | content: ""; 72 | position: absolute; 73 | width: 0; 74 | height: 0; 75 | margin: 0; 76 | background-color: var( --gsuiDragline-color ); 77 | transition: .2s all; 78 | } 79 | .gsuiDragline-dropActive::before { 80 | width: var( --gsuiDragline-dotSize ); 81 | height: var( --gsuiDragline-dotSize ); 82 | margin: calc( var( --gsuiDragline-dotSize ) / -2 ); 83 | cursor: pointer; 84 | border-radius: var( --gsuiDragline-dotRad ); 85 | } 86 | .gsuiDragline-dropActive:hover::before { 87 | transform: scale( 2 ); 88 | } 89 | -------------------------------------------------------------------------------- /gsuiDragline/gsuiDragline.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-dragline", () => 4 | GSUcreateDiv( { class: "gsuiDragline" }, 5 | GSUcreateDiv( { class: "gsuiDragline-main" }, 6 | GSUcreateElementSVG( "svg", { class: "gsuiDragline-line" }, 7 | GSUcreateElementSVG( "polyline" ), 8 | ), 9 | GSUcreateDiv( { class: "gsuiDragline-to" } ), 10 | ), 11 | ) 12 | ); 13 | -------------------------------------------------------------------------------- /gsuiDropdown/gsuiDropdown.css: -------------------------------------------------------------------------------- 1 | .gsuiDropdown { 2 | --bg-color: #555; 3 | --brd-color: #333; 4 | --brd: 2px solid var( --brd-color ); 5 | z-index: 2; 6 | position: absolute; 7 | box-sizing: border-box; 8 | display: flex; 9 | border-radius: 6px; 10 | opacity: 0; 11 | visibility: hidden; 12 | box-shadow: 0 4px 10px 5px #0003; 13 | transition: .2s; 14 | transition-property: opacity, visibility; 15 | } 16 | .gsuiDropdown[ data-open ] { 17 | opacity: 1; 18 | visibility: visible; 19 | } 20 | 21 | /* .......................................................................... */ 22 | .gsuiDropdown-content { 23 | position: relative; 24 | width: 100%; 25 | padding: 4px; 26 | border: var( --brd ); 27 | border-radius: inherit; 28 | background-color: var( --bg-color ); 29 | } 30 | 31 | /* .......................................................................... */ 32 | .gsuiDropdown-arrow { 33 | content: ""; 34 | position: absolute; 35 | box-sizing: border-box; 36 | top: -3px; 37 | left: 50%; 38 | width: 8px; 39 | height: 8px; 40 | margin-top: -4px; 41 | margin-left: -4px; 42 | border: var( --brd ); 43 | border-right: 0; 44 | border-bottom: 0; 45 | border-radius: 2px 0 0 0; 46 | background-color: var( --bg-color ); 47 | background-color: var( --brd-color ); 48 | transform: rotate( 45deg ); 49 | } 50 | .gsuiDropdown[ data-dir^="top" ] .gsuiDropdown-arrow { 51 | transform: rotate( 225deg ); 52 | } 53 | -------------------------------------------------------------------------------- /gsuiDrum/gsuiDrum.css: -------------------------------------------------------------------------------- 1 | gsui-drum, 2 | gsui-drumcut { 3 | position: absolute; 4 | width: 1em; 5 | } 6 | gsui-drum { 7 | height: 66%; 8 | } 9 | gsui-drumcut { 10 | container: gsuiDrumcut-query / inline-size; 11 | bottom: 1px; 12 | height: calc( 34% - 2px ); 13 | } 14 | 15 | /* .......................................................................... */ 16 | .gsuiDrum-in, 17 | .gsuiDrumcut-in { 18 | display: flex; 19 | box-sizing: border-box; 20 | align-items: center; 21 | flex-direction: column; 22 | justify-content: center; 23 | height: 100%; 24 | border-radius: 3px; 25 | border-top: 2px solid #ffffff4f; 26 | border-bottom: 2px solid #0000001f; 27 | border-right: 1px solid #00000033; 28 | background-color: var( --gsuiDrums-drum-bg ); 29 | } 30 | .gsuiDrumcut-in { 31 | font-size: 10px; 32 | background-color: var( --gsuiDrums-drumcut-bg ); 33 | } 34 | .gsuiDrumcut-in .gsuiIcon { 35 | opacity: .6; 36 | } 37 | 38 | /* .......................................................................... */ 39 | .gsuiDrum-prop { 40 | position: relative; 41 | width: 100%; 42 | height: 3px; 43 | background-color: var( --gsuiDrums-drumprop-bg ); 44 | transition: .1s height; 45 | } 46 | .gsuiDrums-lineOpen[ data-prop="pan" ] .gsuiDrum-prop[ data-value="pan" ], 47 | .gsuiDrums-lineOpen[ data-prop="gain" ] .gsuiDrum-prop[ data-value="gain" ], 48 | .gsuiDrums-lineOpen[ data-prop="detune" ] .gsuiDrum-prop[ data-value="detune" ] { 49 | height: 6px; 50 | } 51 | .gsuiDrum-prop + .gsuiDrum-prop { 52 | margin-top: 2px; 53 | } 54 | .gsuiDrum-propValue { 55 | position: absolute; 56 | height: 100%; 57 | background-color: var( --gsuiDrums-drumprop-color ); 58 | } 59 | 60 | /* .......................................................................... */ 61 | @container gsuiDrumcut-query ( max-width: 14px ) { 62 | .gsuiDrumcut-in .gsuiIcon { 63 | display: none; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /gsuiDrum/gsuiDrum.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-drum", () => 4 | GSUcreateDiv( { class: "gsuiDrum-in", inert: true }, 5 | [ "detune", "pan", "gain" ].map( p => 6 | GSUcreateDiv( { class: "gsuiDrum-prop", "data-value": p }, 7 | GSUcreateDiv( { class: "gsuiDrum-propValue" } ), 8 | ) 9 | ), 10 | ) 11 | ); 12 | 13 | GSUsetTemplate( "gsui-drumcut", () => 14 | GSUcreateDiv( { class: "gsuiDrumcut-in", inert: true }, 15 | GSUcreateIcon( { icon: "drumcut" } ), 16 | ) 17 | ); 18 | -------------------------------------------------------------------------------- /gsuiDrum/gsuiDrum.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiDrum extends gsui0ne { 4 | constructor() { 5 | super( { 6 | $cmpName: "gsuiDrum", 7 | $tagName: "gsui-drum", 8 | } ); 9 | Object.seal( this ); 10 | } 11 | 12 | static get observedAttributes() { 13 | return [ "when", "duration", "gain", "pan", "detune" ]; 14 | } 15 | $attributeChanged( prop, val ) { 16 | switch ( prop ) { 17 | case "when": 18 | this.style.left = `${ val }em`; 19 | break; 20 | case "duration": 21 | this.style.width = `${ val }em`; 22 | break; 23 | case "pan": 24 | case "gain": 25 | case "detune": 26 | this.#changeProp( prop, +val ); 27 | break; 28 | } 29 | } 30 | 31 | // ......................................................................... 32 | #changeProp( prop, val ) { 33 | const st = {}; 34 | 35 | switch ( prop ) { 36 | case "detune": 37 | st.left = val > 0 ? "50%" : `${ ( 1 + val / 12 ) * 50 }%`; 38 | st.width = `${ Math.abs( val / 12 ) * 50 }%`; 39 | break; 40 | case "pan": 41 | st.left = val > 0 ? "50%" : `${ ( 1 + val ) * 50 }%`; 42 | st.width = `${ Math.abs( val ) * 50 }%`; 43 | break; 44 | case "gain": 45 | st.left = 0; 46 | st.width = `${ val * 100 }%`; 47 | break; 48 | } 49 | GSUsetStyle( this.querySelector( `.gsuiDrum-prop[data-value="${ prop }"]` ).firstChild, st ); 50 | } 51 | } 52 | 53 | GSUdefineElement( "gsui-drum", gsuiDrum ); 54 | -------------------------------------------------------------------------------- /gsuiDrum/gsuiDrumcut.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiDrumcut extends gsui0ne { 4 | constructor() { 5 | super( { 6 | $cmpName: "gsuiDrumcut", 7 | $tagName: "gsui-drumcut", 8 | } ); 9 | Object.seal( this ); 10 | } 11 | 12 | static get observedAttributes() { 13 | return [ "when", "duration" ]; 14 | } 15 | $attributeChanged( prop, val ) { 16 | switch ( prop ) { 17 | case "when": 18 | this.style.left = `${ val }em`; 19 | break; 20 | case "duration": 21 | this.style.width = `${ val }em`; 22 | break; 23 | } 24 | } 25 | } 26 | 27 | GSUdefineElement( "gsui-drumcut", gsuiDrumcut ); 28 | -------------------------------------------------------------------------------- /gsuiDrumrow/gsuiDrumrow.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-drumrow", () => [ 4 | GSUcreateDiv( { class: "gsuiDrumrow-grip" }, 5 | GSUcreateIcon( { icon: "grip-v" } ), 6 | ), 7 | GSUcreateDiv( { class: "gsuiDrumrow-main" }, 8 | GSUcreateElement( "gsui-toggle", { title: "Toggle the drumrow (right click for solo)" } ), 9 | GSUcreateButton( { class: "gsuiDrumrow-btnProps", "data-action": "props", icon: "drumprops", title: "Expand props panel" } ), 10 | GSUcreateButton( { class: "gsuiDrumrow-btnDelete", "data-action": "delete", icon: "close", title: "Remove the drumrow" } ), 11 | GSUcreateSpan( { class: "gsuiDrumrow-name" } ), 12 | GSUcreateDiv( { class: "gsuiDrumrow-waveWrap" } ), 13 | GSUcreateDiv( { class: "gsuiDrumrow-detune", title: "pitch" }, 14 | GSUcreateElement( "gsui-slider", { type: "linear-y", min: -12, max: 12, step: 1, "mousemove-size": 400, "data-prop": "detune", defaultValue: 0 } ), 15 | ), 16 | GSUcreateDiv( { class: "gsuiDrumrow-pan", title: "pan" }, 17 | GSUcreateElement( "gsui-slider", { type: "linear-y", min: -1, max: 1, step: .02, "mousemove-size": 400, "data-prop": "pan", defaultValue: 0 } ), 18 | ), 19 | GSUcreateDiv( { class: "gsuiDrumrow-gain", title: "gain" }, 20 | GSUcreateElement( "gsui-slider", { type: "linear-y", min: 0, max: 1, step: .01, "mousemove-size": 400, "data-prop": "gain", defaultValue: 1 } ), 21 | ), 22 | ), 23 | GSUcreateElement( "gsui-prop-select", { props: "gain pan detune:pitch" } ), 24 | ] ); 25 | -------------------------------------------------------------------------------- /gsuiDrumrows/gsuiDrumrows.css: -------------------------------------------------------------------------------- 1 | gsui-drumrows { 2 | display: flex; 3 | flex-direction: column; 4 | font-size: var( --gsuiTimewindow-lineH ); 5 | } 6 | gsui-drumrows::-webkit-scrollbar { 7 | display: none; 8 | } 9 | 10 | .gsuiDrumrows-dropNew { 11 | display: flex; 12 | align-items: center; 13 | justify-content: center; 14 | order: 2147483647; 15 | height: 1em; 16 | } 17 | -------------------------------------------------------------------------------- /gsuiDrums/gsuiDrums.css: -------------------------------------------------------------------------------- 1 | gsui-drums { 2 | display: block; 3 | height: 100%; 4 | } 5 | 6 | /* .......................................................................... */ 7 | gsui-drums .gsuiTimewindow-currentTime { 8 | margin-left: 0; 9 | width: var( --gsuiDrums-pxperstep, 1px ); 10 | opacity: .1; 11 | } 12 | gsui-drums gsui-timewindow[ currenttime="0" ] .gsuiTimewindow-currentTime { 13 | display: block; 14 | } 15 | gsui-drums gsui-step-select, 16 | gsui-drums gsui-slider[ data-zoom="y" ] { 17 | display: none; 18 | } 19 | gsui-drums .gsuiTimewindow-rows { 20 | display: flex; 21 | flex-direction: column; 22 | } 23 | 24 | /* .......................................................................... */ 25 | .gsuiDrums-line { 26 | position: relative; 27 | width: 1000000px; 28 | font-size: var( --gsuiTimewindow-lineH ); 29 | height: 1em; 30 | box-sizing: border-box; 31 | border-bottom: 1px solid var( --gsuiDrums-line-border ); 32 | transition: .2s height, .1s filter, .1s background-color; 33 | } 34 | .gsuiDrums-lineOpen { 35 | height: calc( 1em + 1.5em ); 36 | } 37 | .gsuiDrums-line.gsuiDrumrow-mute { 38 | filter: brightness( .8 ) contrast( .8 ); 39 | background-color: var( --gsuiDrums-line-mute-bg ); 40 | } 41 | .gsuiDrums-lineDrums { 42 | position: absolute; 43 | inset: 0; 44 | height: 1em; 45 | } 46 | .gsuiDrums-lineIn { 47 | height: 100%; 48 | box-sizing: border-box; 49 | border-bottom: 1px dashed var( --gsuiDrums-line-border ); 50 | font-size: var( --gsuiTimewindow-pxperbeat ); 51 | cursor: pointer; 52 | } 53 | .gsuiDrums-lineProps { 54 | position: absolute; 55 | inset: 1em 0 0; 56 | } 57 | 58 | /* .......................................................................... */ 59 | .gsuiDrums-drumHover, 60 | .gsuiDrums-drumcutHover { 61 | position: absolute; 62 | z-index: 1; 63 | background-color: #0002; 64 | } 65 | .gsuiDrums-drumHover { 66 | height: 66%; 67 | } 68 | .gsuiDrums-drumcutHover { 69 | bottom: 0; 70 | height: 34%; 71 | } 72 | .gsuiDrums-drumcutHoverIn, 73 | .gsuiDrums-drumHoverIn { 74 | box-sizing: border-box; 75 | height: 100%; 76 | margin: 0 1px 1px 0; 77 | border: 2px solid var( --gsuiDrums-drumHover-border ); 78 | border-radius: 4px; 79 | background-color: var( --gsuiDrums-drumHover-bg ); 80 | pointer-events: none; 81 | } 82 | 83 | /* .......................................................................... */ 84 | .gsuiDrums-previewDeleted, 85 | .gsuiDrums-preview { 86 | opacity: .4; 87 | } 88 | -------------------------------------------------------------------------------- /gsuiDrums/gsuiDrums.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-drums-line", () => 4 | GSUcreateDiv( { class: "gsuiDrums-line" }, 5 | GSUcreateDiv( { class: "gsuiDrums-lineDrums" }, 6 | GSUcreateDiv( { class: "gsuiDrums-lineIn" } ), 7 | ), 8 | GSUcreateDiv( { class: "gsuiDrums-lineProps" }, 9 | GSUcreateElement( "gsui-slidergroup" ), 10 | ), 11 | ) 12 | ); 13 | -------------------------------------------------------------------------------- /gsuiEffect/gsuiEffect.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-effect", () => [ 4 | GSUcreateDiv( { class: "gsuiEffect-head" }, 5 | GSUcreateDiv( { class: "gsuiEffect-grip gsuiIcon", "data-icon": "grip-v" } ), 6 | GSUcreateButton( { class: "gsuiEffect-expand", icon: "caret-right" } ), 7 | GSUcreateElement( "gsui-toggle", { off: true, title: "Toggle this effect" } ), 8 | GSUcreateSpan( { class: "gsuiEffect-name" } ), 9 | GSUcreateElement( "gsui-help-link" ), 10 | GSUcreateButton( { class: "gsuiEffect-remove", icon: "close", title: "Delete this effect" } ), 11 | ), 12 | GSUcreateDiv( { class: "gsuiEffect-content" } ), 13 | ] ); 14 | -------------------------------------------------------------------------------- /gsuiEffect/gsuiEffect.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiEffect extends gsui0ne { 4 | constructor() { 5 | super( { 6 | $cmpName: "gsuiEffect", 7 | $tagName: "gsui-effect", 8 | $elements: { 9 | $toggle: "gsui-toggle", 10 | $name: ".gsuiEffect-name", 11 | $help: "gsui-help-link", 12 | $expand: ".gsuiEffect-expand", 13 | $remove: ".gsuiEffect-remove", 14 | $content: ".gsuiEffect-content", 15 | }, 16 | } ); 17 | Object.seal( this ); 18 | 19 | this.$elements.$expand.onclick = () => { 20 | GSUtoggleAttribute( this, "expanded" ); 21 | this.$dispatch( "expand" ); 22 | }; 23 | this.$elements.$remove.onclick = () => this.$dispatch( "remove" ); 24 | GSUlistenEvents( this, { 25 | gsuiToggle: { 26 | toggle: ( d, t ) => { 27 | GSUtoggleAttribute( this, "enable" ); 28 | this.$dispatch( "toggle" ); 29 | }, 30 | }, 31 | } ); 32 | } 33 | 34 | // ......................................................................... 35 | static get observedAttributes() { 36 | return [ "order", "enable", "name" ]; 37 | } 38 | $attributeChanged( prop, val ) { 39 | switch ( prop ) { 40 | case "order": 41 | this.style.order = val; 42 | break; 43 | case "enable": 44 | GSUsetAttribute( this.$elements.$toggle, "off", val !== "" ); 45 | GSUsetAttribute( this.$elements.$content.firstChild, "off", val !== "" ); 46 | break; 47 | case "name": 48 | this.$elements.$name.textContent = val; 49 | GSUsetAttribute( this.$elements.$help, "page", `mixer-effects-${ val }` ); 50 | break; 51 | } 52 | } 53 | 54 | // ......................................................................... 55 | $setFxElement( elFx ) { 56 | this.$elements.$content.append( elFx ); 57 | } 58 | $getFxElement() { 59 | return this.$elements.$content?.firstChild; 60 | } 61 | } 62 | 63 | GSUdefineElement( "gsui-effect", gsuiEffect ); 64 | -------------------------------------------------------------------------------- /gsuiEffects/gsuiEffects.css: -------------------------------------------------------------------------------- 1 | gsui-effects { 2 | display: flex; 3 | height: 100%; 4 | overflow: auto; 5 | flex-direction: column; 6 | background-color: var( --gsui-items-bg ); 7 | } 8 | 9 | /* .......................................................................... */ 10 | .gsuiEffects-list { 11 | display: flex; 12 | overflow: auto; 13 | flex-direction: column; 14 | } 15 | 16 | /* .......................................................................... */ 17 | .gsuiEffects-addBtn { 18 | position: relative; 19 | order: 2147483646; 20 | border: 0; 21 | outline: 0; 22 | width: 100%; 23 | min-height: 52px; 24 | color: inherit; 25 | font-size: 18px; 26 | cursor: pointer; 27 | background: none; 28 | opacity: .4; 29 | transition: .2s opacity; 30 | } 31 | .gsuiEffects-addBtn:hover { 32 | opacity: .7; 33 | } 34 | .gsuiEffects-addBtn::before { 35 | content: ""; 36 | position: absolute; 37 | inset: 6px; 38 | border: 2px dashed; 39 | border-radius: 4px; 40 | opacity: .4; 41 | } 42 | .gsuiEffects-addBtn:active, 43 | .gsuiEffects-addBtn:active .gsuiEffects-addBtn::before { 44 | margin-top: 2px; 45 | } 46 | -------------------------------------------------------------------------------- /gsuiEffects/gsuiEffects.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-effects", () => [ 4 | GSUcreateButton( { class: "gsuiEffects-addBtn", title: "Add an effect" }, 5 | GSUcreateIcon( { icon: "add-effect" } ), 6 | ), 7 | ] ); 8 | -------------------------------------------------------------------------------- /gsuiEnvelope/gsuiEnvelope.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-envelope", () => 4 | GSUcreateDiv( { class: "gsuiEnvelope-in" }, 5 | GSUcreateDiv( { class: "gsuiEnvelope-props" }, 6 | [ 7 | [ "attack", "attack", "att", 0, 1, .01 ], 8 | [ "hold", "hold", "hold", 0, 1, .01 ], 9 | [ "decay", "decay", "dec", 0, 1, .01 ], 10 | [ "sustain", "sustain", "sus", 0, 1, .01 ], 11 | [ "release", "release", "rel", 0, 4, .01 ], 12 | [ "amp", "amplification", "pitch", -24, 24, 1 ], 13 | [ "q", "Q", "Q", 0, 25, .01 ], 14 | ].map( ( [ prop, title, text, min, max, step ] ) => 15 | GSUcreateDiv( { class: "gsuiEnvelope-prop", title, "data-prop": prop }, 16 | GSUcreateDiv( { class: "gsuiEnvelope-propLabel" }, text ), 17 | GSUcreateDiv( { class: "gsuiEnvelope-propValue" } ), 18 | GSUcreateElement( "gsui-slider", { type: "linear-x", disabled: true, min, max, step, "mousemove-size": "800", "data-prop": prop } ), 19 | ) 20 | ), 21 | ), 22 | GSUcreateDiv( { class: "gsuiEnvelope-graph", inert: true }, 23 | GSUcreateElement( "gsui-beatlines", { coloredbeats: "" } ), 24 | GSUcreateElement( "gsui-envelope-graph" ), 25 | GSUcreateDiv( { class: "gsuiEnvelope-keyPreviews" } ), 26 | ), 27 | ) 28 | ); 29 | -------------------------------------------------------------------------------- /gsuiEnvelopeGraph/gsuiEnvelopeGraph.css: -------------------------------------------------------------------------------- 1 | gsui-envelope-graph { 2 | transition: color .2s; 3 | } 4 | gsui-envelope-graph svg { 5 | width: 100%; 6 | height: 100%; 7 | stroke: currentColor; 8 | stroke-width: 2px; 9 | } 10 | .gsuiEnvelopeGraph-line { 11 | fill: transparent; 12 | } 13 | .gsuiEnvelopeGraph-mainLine { 14 | stroke-dasharray: 12 4; 15 | stroke-opacity: .5; 16 | fill: currentColor; 17 | fill-opacity: .05; 18 | } 19 | -------------------------------------------------------------------------------- /gsuiEnvelopeGraph/gsuiEnvelopeGraph.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-envelope-graph", () => 4 | GSUcreateElementSVG( "svg", { preserveAspectRatio: "none" }, 5 | GSUcreateElementSVG( "polyline", { class: "gsuiEnvelopeGraph-mainLine" } ), 6 | GSUcreateElementSVG( "polyline", { class: "gsuiEnvelopeGraph-line" } ), 7 | GSUcreateElementSVG( "polyline", { class: "gsuiEnvelopeGraph-line" } ), 8 | ) 9 | ); 10 | -------------------------------------------------------------------------------- /gsuiEnvelopeGraph/gsuiEnvelopeGraph.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiEnvelopeGraph extends gsui0ne { 4 | $amp = 1; 5 | $attack = .25; 6 | $hold = .25; 7 | $decay = .25; 8 | $sustain = .8; 9 | $release = 1; 10 | $duration = 4; 11 | 12 | constructor() { 13 | super( { 14 | $cmpName: "gsuiEnvelopeGraph", 15 | $tagName: "gsui-envelope-graph", 16 | $elements: { 17 | $svg: "svg", 18 | $mainLine: ".gsuiEnvelopeGraph-mainLine", 19 | $attLine: ".gsuiEnvelopeGraph-line", 20 | $relLine: ".gsuiEnvelopeGraph-line + .gsuiEnvelopeGraph-line", 21 | }, 22 | } ); 23 | Object.seal( this ); 24 | } 25 | 26 | // ......................................................................... 27 | $firstTimeConnected() { 28 | this.$resized(); 29 | } 30 | 31 | // ......................................................................... 32 | $resized() { 33 | GSUsetViewBoxWH( this.$elements.$svg, this.clientWidth, this.clientHeight ); 34 | this.$draw(); 35 | } 36 | $draw() { 37 | if ( this.firstChild ) { 38 | const pts = gsuiEnvelopeGraph.#getPoints( 39 | this.clientWidth, this.clientHeight, this.$duration, 40 | Math.abs( this.$amp ), this.$attack, this.$hold, this.$decay, this.$sustain, this.$release ); 41 | 42 | GSUsetAttribute( this.$elements.$attLine, "points", pts.slice( 0, 8 ).join( " " ) ); 43 | GSUsetAttribute( this.$elements.$relLine, "points", pts.slice( -4 ).join( " " ) ); 44 | GSUsetAttribute( this.$elements.$mainLine, "points", pts.join( " " ) ); 45 | } 46 | } 47 | static #getPoints( w, h, dur, amp, att, hold, dec, sus, rel ) { 48 | const dur2 = dur !== "auto" ? dur : att + hold + dec + 1 + rel; 49 | const bpp = w / dur2; 50 | const attX = bpp * att; 51 | const holX = attX + bpp * hold; 52 | const decX = holX + bpp * dec; 53 | const susX = decX + bpp * ( dur === "auto" ? 1 : dur - att - hold - dec - rel ); 54 | const relX = susX + bpp * rel; 55 | const holY = h - ( h * amp ); 56 | const susY = h - ( h * amp * sus ); 57 | 58 | return [ 59 | 0, h, 60 | attX, holY, 61 | holX, holY, 62 | decX, susY, 63 | susX, susY, 64 | relX, h, 65 | ]; 66 | } 67 | } 68 | 69 | GSUdefineElement( "gsui-envelope-graph", gsuiEnvelopeGraph ); 70 | -------------------------------------------------------------------------------- /gsuiFxDelay/gsuiFxDelay.css: -------------------------------------------------------------------------------- 1 | gsui-fx-delay { 2 | display: flex; 3 | flex-direction: column; 4 | box-sizing: border-box; 5 | gap: 8px; 6 | height: 100%; 7 | min-width: 160px; 8 | min-height: 110px; 9 | padding: 8px; 10 | } 11 | 12 | /* .......................................................................... */ 13 | .gsuiEffect-param-row gsui-slider { 14 | flex: 1; 15 | height: 10px; 16 | } 17 | .gsuiEffect-param-row[ data-prop="time" ] gsui-slider { --gsuiSlider-lineColor: var( --gsui-col-time ) } 18 | .gsuiEffect-param-row[ data-prop="gain" ] gsui-slider { --gsuiSlider-lineColor: var( --gsui-col-gain ) } 19 | .gsuiEffect-param-row[ data-prop="pan" ] gsui-slider { --gsuiSlider-lineColor: var( --gsui-col-pan ) } 20 | .gsuiEffect-param-row .gsuiSlider-eventCatcher { 21 | inset: -4px 0; 22 | } 23 | 24 | /* .......................................................................... */ 25 | gsui-fx-delay .gsuiEffect-param-label { 26 | width: 30px; 27 | } 28 | gsui-fx-delay .gsuiEffect-param-value { 29 | width: 52px; 30 | } 31 | .gsuiEffect-param-row[ data-prop="time" ] .gsuiEffect-param-value::after { 32 | content: " b"; 33 | } 34 | .gsuiEffect-param-row[ data-prop="pan" ] .gsuiEffect-param-value::after, 35 | .gsuiEffect-param-row[ data-prop="gain" ] .gsuiEffect-param-value::after { 36 | content: " %"; 37 | } 38 | 39 | /* .......................................................................... */ 40 | .gsuiFxDelay-graph { 41 | position: relative; 42 | overflow: hidden; 43 | flex: 1; 44 | border-radius: 6px; 45 | background-color: var( --gsui-screen-graph ); 46 | } 47 | .gsuiFxDelay-graph-lines { 48 | position: absolute; 49 | inset: 0; 50 | } 51 | .gsuiFxDelay-graph-line { 52 | position: absolute; 53 | top: calc( 50% - 1px ); 54 | width: 100%; 55 | height: 2px; 56 | background-color: currentColor; 57 | opacity: .2; 58 | } 59 | .gsuiFxDelay-graph-source, 60 | .gsuiFxDelay-graph-echo { 61 | position: absolute; 62 | top: 50%; 63 | width: 10px; 64 | height: 10px; 65 | margin-top: -5px; 66 | margin-left: -5px; 67 | border-radius: 50%; 68 | background-color: currentColor; 69 | } 70 | -------------------------------------------------------------------------------- /gsuiFxDelay/gsuiFxDelay.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-fx-delay", () => [ 4 | GSUcreateDiv( { class: "gsuiEffect-param-row", "data-prop": "time" }, 5 | GSUcreateSpan( { class: "gsuiEffect-param-label" }, "time" ), 6 | GSUcreateElement( "gsui-slider", { type: "linear-x", step: .01, min: 0, max: 2 } ), 7 | GSUcreateSpan( { class: "gsuiEffect-param-value" } ), 8 | ), 9 | GSUcreateDiv( { class: "gsuiEffect-param-row", "data-prop": "gain" }, 10 | GSUcreateSpan( { class: "gsuiEffect-param-label" }, "gain" ), 11 | GSUcreateElement( "gsui-slider", { type: "linear-x", step: .01, min: 0, max: .95 } ), 12 | GSUcreateSpan( { class: "gsuiEffect-param-value" } ), 13 | ), 14 | GSUcreateDiv( { class: "gsuiEffect-param-row", "data-prop": "pan" }, 15 | GSUcreateSpan( { class: "gsuiEffect-param-label" }, "pan" ), 16 | GSUcreateElement( "gsui-slider", { type: "linear-x", step: .01, min: -1, max: 1 } ), 17 | GSUcreateSpan( { class: "gsuiEffect-param-value" } ), 18 | ), 19 | GSUcreateDiv( { class: "gsuiFxDelay-graph" }, 20 | GSUcreateDiv( { class: "gsuiFxDelay-graph-lines" }, 21 | GSUcreateElement( "gsui-beatlines", { timedivision: "4/4" } ), 22 | GSUcreateDiv( { class: "gsuiFxDelay-graph-line" } ), 23 | GSUcreateDiv( { class: "gsuiFxDelay-graph-source" } ), 24 | ), 25 | Array.from( { length: 20 }, ( _, n ) => GSUcreateDiv( { class: "gsuiFxDelay-graph-echo" } ) ) 26 | ), 27 | ] ); 28 | -------------------------------------------------------------------------------- /gsuiFxDelay/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 | 31 |
32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /gsuiFxFilter/gsuiFxFilter.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-fx-filter", () => [ 4 | GSUcreateDiv( { class: "gsuiFxFilter-area gsuiFxFilter-areaType" }, 5 | GSUcreateSpan( { class: "gsuiEffect-param-label" }, "type" ), 6 | GSUcreateDiv( { class: "gsuiFxFilter-area-content" }, 7 | GSUgetTemplate( "gsui-fx-filter-type", "lowpass", "M 1 4 L 12 4 L 15 8" ), 8 | GSUgetTemplate( "gsui-fx-filter-type", "highpass", "M 1 8 L 4 4 L 15 4" ), 9 | GSUgetTemplate( "gsui-fx-filter-type", "bandpass", "M 1 8 L 8 4 L 15 8" ), 10 | GSUgetTemplate( "gsui-fx-filter-type", "lowshelf", "M 1 8 L 4 8 L 6 4 L 15 4" ), 11 | GSUgetTemplate( "gsui-fx-filter-type", "highshelf", "M 1 4 L 10 4 L 12 8 L 15 8" ), 12 | GSUgetTemplate( "gsui-fx-filter-type", "peaking", "M 1 8 L 7 8 L 8 4 L 9 8 L 15 8" ), 13 | GSUgetTemplate( "gsui-fx-filter-type", "notch", "M 1 4 L 7 4 L 8 8 L 9 4 L 15 4" ), 14 | GSUgetTemplate( "gsui-fx-filter-type", "allpass", "M 1 6 L 15 6" ), 15 | ), 16 | ), 17 | GSUcreateDiv( { class: "gsuiFxFilter-area gsuiFxFilter-areaGraph" }, 18 | GSUcreateSpan( { class: "gsuiEffect-param-label" }, "frequency" ), 19 | GSUcreateDiv( { class: "gsuiFxFilter-area-content" }, 20 | GSUcreateElement( "gsui-curves" ), 21 | ), 22 | ), 23 | GSUcreateDiv( { class: "gsuiFxFilter-area gsuiFxFilter-areaFrequency" }, 24 | GSUcreateDiv( { class: "gsuiFxFilter-area-content" }, 25 | GSUcreateElement( "gsui-slider", { type: "linear-x", min: 0, max: 1, step: .001, "data-prop": "frequency" } ), 26 | ), 27 | ), 28 | GSUcreateDiv( { class: "gsuiFxFilter-area gsuiFxFilter-areaGain" }, 29 | GSUcreateSpan( { class: "gsuiEffect-param-label" }, "gain" ), 30 | GSUcreateDiv( { class: "gsuiFxFilter-area-content" }, 31 | GSUcreateElement( "gsui-slider", { type: "linear-y", min: -50, max: 50, step: .1, "mousemove-size": 400, "data-prop": "gain" } ), 32 | ), 33 | ), 34 | GSUcreateDiv( { class: "gsuiFxFilter-area gsuiFxFilter-areaQ" }, 35 | GSUcreateSpan( { class: "gsuiEffect-param-label" }, "Q" ), 36 | GSUcreateDiv( { class: "gsuiFxFilter-area-content" }, 37 | GSUcreateElement( "gsui-slider", { type: "circular", min: .001, max: 25, step: .001, "mousemove-size": 400, "data-prop": "Q" } ), 38 | ), 39 | ), 40 | GSUcreateDiv( { class: "gsuiFxFilter-area gsuiFxFilter-areaDetune" }, 41 | GSUcreateSpan( { class: "gsuiEffect-param-label" }, "detune" ), 42 | GSUcreateDiv( { class: "gsuiFxFilter-area-content" }, 43 | GSUcreateElement( "gsui-slider", { type: "circular", min: -1200, max: 1200, step: 10, "mousemove-size": 400, "data-prop": "detune" } ), 44 | ), 45 | ), 46 | ] ); 47 | 48 | GSUsetTemplate( "gsui-fx-filter-type", ( type, d ) => 49 | GSUcreateButton( { class: "gsuiFxFilter-areaType-btn", "data-type": type, title: type }, 50 | GSUcreateElementSVG( "svg", { viewBox: "0 0 16 12" }, 51 | GSUcreateElementSVG( "path", { d } ), 52 | ), 53 | ) 54 | ); 55 | -------------------------------------------------------------------------------- /gsuiFxFilter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 28 | 29 | 30 | 31 | 32 |
33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /gsuiFxReverb/gsuiFxReverb.css: -------------------------------------------------------------------------------- 1 | gsui-fx-reverb { 2 | box-sizing: border-box; 3 | display: flex; 4 | flex-direction: column; 5 | height: 100%; 6 | min-width: 140px; 7 | min-height: 100px; 8 | gap: 8px; 9 | padding: 8px; 10 | --gsui-fadein-p: 0%; 11 | } 12 | 13 | /* .......................................................................... */ 14 | gsui-fx-reverb .gsuiEffect-param-label { 15 | min-width: 40px; 16 | } 17 | gsui-fx-reverb gsui-slider { 18 | height: 10px; 19 | --gsuiSlider-lineColor: var( --gsui-col-gain ); 20 | } 21 | gsui-fx-reverb .gsuiEffect-param-value { 22 | min-width: 52px; 23 | align-self: stretch; 24 | } 25 | gsui-fx-reverb .gsuiEffect-param-value::after { 26 | content: " %"; 27 | } 28 | gsui-fx-reverb .gsuiEffect-param-row[ data-prop="delay" ] gsui-slider, 29 | gsui-fx-reverb .gsuiEffect-param-row[ data-prop="decay" ] gsui-slider, 30 | gsui-fx-reverb .gsuiEffect-param-row[ data-prop="fadein" ] gsui-slider { 31 | --gsuiSlider-lineColor: var( --gsui-col-time ); 32 | } 33 | gsui-fx-reverb .gsuiEffect-param-row[ data-prop="delay" ] .gsuiEffect-param-value::after, 34 | gsui-fx-reverb .gsuiEffect-param-row[ data-prop="decay" ] .gsuiEffect-param-value::after, 35 | gsui-fx-reverb .gsuiEffect-param-row[ data-prop="fadein" ] .gsuiEffect-param-value::after { 36 | content: " b"; 37 | } 38 | 39 | /* .......................................................................... */ 40 | .gsuiFxReverb-graph { 41 | position: relative; 42 | overflow: hidden; 43 | flex: 1; 44 | min-height: 32px; 45 | border-radius: 6px; 46 | background-color: var( --gsui-screen-graph ); 47 | } 48 | .gsuiFxReverb-graph-dry { 49 | position: absolute; 50 | inset: 20% auto 20% 0; 51 | width: 8px; 52 | border-radius: 2px; 53 | background-color: #ff9; 54 | } 55 | .gsuiFxReverb-graph-wet { 56 | position: absolute; 57 | inset: 15% auto 15% 0; 58 | width: 50%; 59 | border-radius: 2px; 60 | background-image: linear-gradient( 90deg, transparent, currentColor var( --gsui-fadein-p ), transparent ); 61 | } 62 | -------------------------------------------------------------------------------- /gsuiFxReverb/gsuiFxReverb.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-fx-reverb", () => [ 4 | GSUcreateDiv( { class: "gsuiEffect-param-row", "data-prop": "dry" }, 5 | GSUcreateSpan( { class: "gsuiEffect-param-label" }, "dry" ), 6 | GSUcreateElement( "gsui-slider", { type: "linear-x", min: 0, max: 1, step: .01 } ), 7 | GSUcreateSpan( { class: "gsuiEffect-param-value" } ), 8 | ), 9 | GSUcreateDiv( { class: "gsuiEffect-param-row", "data-prop": "wet" }, 10 | GSUcreateSpan( { class: "gsuiEffect-param-label" }, "wet" ), 11 | GSUcreateElement( "gsui-slider", { type: "linear-x", min: 0, max: 10, step: .01 } ), 12 | GSUcreateSpan( { class: "gsuiEffect-param-value" } ), 13 | ), 14 | GSUcreateDiv( { class: "gsuiEffect-param-row", "data-prop": "delay" }, 15 | GSUcreateSpan( { class: "gsuiEffect-param-label" }, "delay" ), 16 | GSUcreateElement( "gsui-slider", { type: "linear-x", min: 0, max: 2, step: .01 } ), 17 | GSUcreateSpan( { class: "gsuiEffect-param-value" } ), 18 | ), 19 | GSUcreateDiv( { class: "gsuiEffect-param-row", "data-prop": "fadein" }, 20 | GSUcreateSpan( { class: "gsuiEffect-param-label" }, "fadeIn" ), 21 | GSUcreateElement( "gsui-slider", { type: "linear-x", min: 0, max: 2, step: .01 } ), 22 | GSUcreateSpan( { class: "gsuiEffect-param-value" } ), 23 | ), 24 | GSUcreateDiv( { class: "gsuiEffect-param-row", "data-prop": "decay" }, 25 | GSUcreateSpan( { class: "gsuiEffect-param-label" }, "decay" ), 26 | GSUcreateElement( "gsui-slider", { type: "linear-x", min: 0, max: 2, step: .01 } ), 27 | GSUcreateSpan( { class: "gsuiEffect-param-value" } ), 28 | ), 29 | GSUcreateDiv( { class: "gsuiFxReverb-graph" }, 30 | GSUcreateElement( "gsui-beatlines", { timedivision: "4/4" } ), 31 | GSUcreateDiv( { class: "gsuiFxReverb-graph-wet" } ), 32 | GSUcreateDiv( { class: "gsuiFxReverb-graph-dry" } ), 33 | ), 34 | ] ); 35 | -------------------------------------------------------------------------------- /gsuiFxWaveShaper/gsuiFxWaveShaper.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-fx-waveshaper", () => [ 4 | GSUcreateDiv( { class: "gsuiFxWaveShaper-params" }, 5 | GSUcreateDiv( { class: "gsuiFxWaveShaper-symmetry" }, 6 | GSUcreateElement( "gsui-toggle", { off: true, "data-prop": "symmetry" } ), 7 | GSUcreateSpan( { class: "gsuiEffect-param-label" }, "symm" ), 8 | ), 9 | GSUcreateDiv( { class: "gsuiFxWaveShaper-oversample" }, 10 | GSUcreateElement( "gsui-toggle", { off: true, "data-prop": "oversample" } ), 11 | GSUcreateSpan( { class: "gsuiEffect-param-label" }, "oversmp" ), 12 | GSUcreateSelect( { class: "gsuiEffect-param-value" }, 13 | GSUcreateOption( { value: "2x" } ), 14 | GSUcreateOption( { value: "4x" } ), 15 | ), 16 | ), 17 | GSUcreateButton( { class: "gsuiFxWaveShaper-reset gsuiEffect-param-value" }, "reset" ), 18 | ), 19 | GSUcreateDiv( { class: "gsuiFxWaveShaper-in" }, 20 | GSUcreateDiv( { class: "gsuiFxWaveShaper-side-graph" }, 21 | GSUcreateDiv( { class: "gsuiFxWaveShaper-graph" }, 22 | GSUcreateElementSVG( "svg", { class: "gsuiFxWaveShaper-graph-diag", preserveAspectRatio: "none", inert: true }, 23 | GSUcreateElementSVG( "line" ), 24 | ), 25 | GSUcreateDiv( { class: "gsuiFxWaveShaper-graph-x", inert: true } ), 26 | GSUcreateDiv( { class: "gsuiFxWaveShaper-graph-y", inert: true } ), 27 | GSUcreateDiv( { class: "gsuiFxWaveShaper-graph-unit gsuiFxWaveShaper-graph-x-plus", inert: true } ), 28 | GSUcreateDiv( { class: "gsuiFxWaveShaper-graph-unit gsuiFxWaveShaper-graph-x-minus", inert: true } ), 29 | GSUcreateDiv( { class: "gsuiFxWaveShaper-graph-unit gsuiFxWaveShaper-graph-y-plus", inert: true } ), 30 | GSUcreateDiv( { class: "gsuiFxWaveShaper-graph-unit gsuiFxWaveShaper-graph-y-minus", inert: true } ), 31 | GSUcreateElement( "gsui-dotline", { viewbox: "-1 -1 1 1", xstep: .01, ystep: .01 } ), 32 | ), 33 | ), 34 | GSUcreateDiv( { class: "gsuiFxWaveShaper-side-waves" }, 35 | GSUcreateElementSVG( "svg", { class: "gsuiFxWaveShaper-waves", preserveAspectRatio: "none", inert: true }, 36 | GSUcreateElementSVG( "polyline", { class: "gsuiFxWaveShaper-waveA" } ), 37 | GSUcreateElementSVG( "polyline", { class: "gsuiFxWaveShaper-waveB" } ), 38 | ), 39 | ), 40 | ), 41 | ] ); 42 | -------------------------------------------------------------------------------- /gsuiGlitchText/gsuiGlitchText.css: -------------------------------------------------------------------------------- 1 | gsui-glitchtext { 2 | position: relative; 3 | color: #ccc; 4 | line-height: 1em; 5 | font-size: 68px; 6 | font-family: "oswald", monospace; 7 | } 8 | .gsuiGlitchText-blended { 9 | color: #fff; 10 | } 11 | 12 | /* .......................................................................... */ 13 | .gsuiGlitchText-clip { 14 | position: relative; 15 | } 16 | .gsuiGlitchText-clip + .gsuiGlitchText-clip { 17 | position: absolute; 18 | top: 0; 19 | } 20 | .gsuiGlitchText:not( .gsuiGlitchText-blended ) .gsuiGlitchText-clip + .gsuiGlitchText-clip { 21 | display: none; 22 | } 23 | 24 | /* .......................................................................... */ 25 | .gsuiGlitchText-word { 26 | margin: 0; 27 | white-space: nowrap; 28 | } 29 | 30 | /* .......................................................................... */ 31 | .gsuiGlitchText-blend { 32 | position: absolute; 33 | top: 0; 34 | opacity: 0; 35 | transition: .1s; 36 | transition-property: opacity; 37 | } 38 | .gsuiGlitchText-blendA { 39 | color: #2af; 40 | margin: -.03em 0 0 .03em; 41 | mix-blend-mode: darken; 42 | } 43 | .gsuiGlitchText-blendB { 44 | color: #f64; 45 | margin: .03em 0 0 -.03em; 46 | mix-blend-mode: color-burn; 47 | } 48 | .gsuiGlitchText-blended .gsuiGlitchText-blend { 49 | opacity: .4; 50 | } 51 | -------------------------------------------------------------------------------- /gsuiGlitchText/gsuiGlitchText.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-glitchtext", () => [ 4 | GSUgetTemplate( "gsui-glitchtext-layer" ), 5 | GSUgetTemplate( "gsui-glitchtext-layer" ), 6 | GSUgetTemplate( "gsui-glitchtext-layer" ), 7 | ] ); 8 | 9 | GSUsetTemplate( "gsui-glitchtext-layer", () => 10 | GSUcreateDiv( { class: "gsuiGlitchText-clip" }, 11 | GSUcreateDiv( { class: "gsuiGlitchText-word" } ), 12 | GSUcreateDiv( { class: "gsuiGlitchText-word gsuiGlitchText-blend gsuiGlitchText-blendA" } ), 13 | GSUcreateDiv( { class: "gsuiGlitchText-word gsuiGlitchText-blend gsuiGlitchText-blendB" } ), 14 | ) 15 | ); 16 | -------------------------------------------------------------------------------- /gsuiGlitchText/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 29 | 30 | 31 | 32 | 33 |
34 | GridSound 35 |
36 |
37 | 41 |
42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /gsuiHelpLink/gsuiHelpLink.css: -------------------------------------------------------------------------------- 1 | [ gsuihelplink-hide ] gsui-help-link { 2 | display: none; 3 | } 4 | gsui-help-link { 5 | font-size: 14px; 6 | } 7 | gsui-help-link a { 8 | color: inherit; 9 | } 10 | gsui-help-link a::before, 11 | gsui-help-link a::after { 12 | color: var( --gsuiHelpLink-txt ); 13 | } 14 | gsui-help-link a:hover::before { 15 | color: currentColor; 16 | } 17 | -------------------------------------------------------------------------------- /gsuiHelpLink/gsuiHelpLink.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiHelpLink extends gsui0ne { 4 | constructor() { 5 | super( { 6 | $cmpName: "gsuiHelpLink", 7 | $tagName: "gsui-help-link", 8 | $template: GSUcreateAExt( { class: "gsuiIcon", "data-icon": "info" } ), 9 | } ); 10 | Object.seal( this ); 11 | } 12 | 13 | // ......................................................................... 14 | static get observedAttributes() { 15 | return [ "page" ]; 16 | } 17 | $attributeChanged( prop, val ) { 18 | if ( prop === "page" ) { 19 | GSUsetAttribute( this.$element, { 20 | href: `https://github.com/gridsound/daw/wiki/help-${ val }`, 21 | title: `Open the ${ val } help page`, 22 | } ); 23 | } 24 | } 25 | } 26 | 27 | GSUdefineElement( "gsui-help-link", gsuiHelpLink ); 28 | -------------------------------------------------------------------------------- /gsuiKeys/gsuiKeys.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-keys-octave", () => [ 4 | GSUcreateDiv( { class: "gsuiKeys-key", "data-key": 11 }, GSUcreateDiv( { class: "gsui-row gsuiKeys-row", "data-key": 11 }, GSUcreateDiv() ) ), 5 | GSUcreateDiv( { class: "gsuiKeys-key gsuiKeys-keyBlack", "data-key": 10 }, GSUcreateDiv( { class: "gsui-row gsuiKeys-row gsuiKeys-rowBlack", "data-key": 10 }, GSUcreateDiv() ) ), 6 | GSUcreateDiv( { class: "gsuiKeys-key", "data-key": 9 }, GSUcreateDiv( { class: "gsui-row gsuiKeys-row", "data-key": 9 }, GSUcreateDiv() ) ), 7 | GSUcreateDiv( { class: "gsuiKeys-key gsuiKeys-keyBlack", "data-key": 8 }, GSUcreateDiv( { class: "gsui-row gsuiKeys-row gsuiKeys-rowBlack", "data-key": 8 }, GSUcreateDiv() ) ), 8 | GSUcreateDiv( { class: "gsuiKeys-key", "data-key": 7 }, GSUcreateDiv( { class: "gsui-row gsuiKeys-row", "data-key": 7 }, GSUcreateDiv() ) ), 9 | GSUcreateDiv( { class: "gsuiKeys-key gsuiKeys-keyBlack", "data-key": 6 }, GSUcreateDiv( { class: "gsui-row gsuiKeys-row gsuiKeys-rowBlack", "data-key": 6 }, GSUcreateDiv() ) ), 10 | GSUcreateDiv( { class: "gsuiKeys-key", "data-key": 5 }, GSUcreateDiv( { class: "gsui-row gsuiKeys-row", "data-key": 5 }, GSUcreateDiv() ) ), 11 | GSUcreateDiv( { class: "gsuiKeys-key", "data-key": 4 }, GSUcreateDiv( { class: "gsui-row gsuiKeys-row", "data-key": 4 }, GSUcreateDiv() ) ), 12 | GSUcreateDiv( { class: "gsuiKeys-key gsuiKeys-keyBlack", "data-key": 3 }, GSUcreateDiv( { class: "gsui-row gsuiKeys-row gsuiKeys-rowBlack", "data-key": 3 }, GSUcreateDiv() ) ), 13 | GSUcreateDiv( { class: "gsuiKeys-key", "data-key": 2 }, GSUcreateDiv( { class: "gsui-row gsuiKeys-row", "data-key": 2 }, GSUcreateDiv() ) ), 14 | GSUcreateDiv( { class: "gsuiKeys-key gsuiKeys-keyBlack", "data-key": 1 }, GSUcreateDiv( { class: "gsui-row gsuiKeys-row gsuiKeys-rowBlack", "data-key": 1 }, GSUcreateDiv() ) ), 15 | GSUcreateDiv( { class: "gsuiKeys-key", "data-key": 0 }, GSUcreateDiv( { class: "gsui-row gsuiKeys-row", "data-key": 0 }, GSUcreateDiv() ) ), 16 | ] ); 17 | -------------------------------------------------------------------------------- /gsuiKeys/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 |
31 | 32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /gsuiLFO/gsuiLFO.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-lfo", () => 4 | GSUcreateDiv( { class: "gsuiLFO-in" }, 5 | GSUcreateDiv( { class: "gsuiLFO-props" }, 6 | [ 7 | [ "delay", "delay", "del", 0, 4, .03125 ], 8 | [ "attack", "attack", "att", 0, 4, .03125 ], 9 | [ "speed", "speed", "spd", .25, 18, .125 ], 10 | [ "amp", "amplitude", "amp", .001, 1, .001 ], 11 | ].map( ( [ prop, title, text, min, max, step ] ) => 12 | GSUcreateDiv( { class: "gsuiLFO-prop", title, "data-prop": prop }, 13 | GSUcreateSpan( { class: "gsuiLFO-propLabel" }, text ), 14 | GSUcreateSpan( { class: "gsuiLFO-propValue" } ), 15 | GSUcreateElement( "gsui-slider", { type: "linear-x", disabled: true, min, max, step, "mousemove-size": "800", "data-prop": prop } ), 16 | ) 17 | ), 18 | ), 19 | GSUcreateDiv( { class: "gsuiLFO-type" }, 20 | [ 21 | [ "sine", "M 1 5 C 1 4 1 1 4 1 C 7 1 7 4 7 5 C 7 6 7 9 10 9 C 13 9 13 6 13 5" ], 22 | [ "triangle", "M 1 5 L 4 1 L 10 9 L 13 5" ], 23 | [ "sawtooth", "M 1 5 L 7 1 L 7 9 L 13 5" ], 24 | [ "square", "M 1 5 L 1 1 L 7 1 L 7 9 L 13 9 L 13 5" ], 25 | ].map( ( [ w, dots ] ) => 26 | GSUcreateLabel( { class: "gsuiLFO-btn gsuiLFO-typeBtn", title: w }, 27 | GSUcreateInput( { class: "gsuiLFO-btnInput gsuiLFO-typeRadio", name: "gsuiLFO-type", type: "radio", value: w } ), 28 | GSUcreateElementSVG( "svg", { class: "gsuiLFO-btnIcon gsuiLFO-typeSVG", viewBox: "0 0 14 10" }, GSUcreateElementSVG( "path", { d: dots } ) ), 29 | ) 30 | ), 31 | ), 32 | GSUcreateDiv( { class: "gsuiLFO-graph" }, 33 | GSUcreateDiv( { class: "gsuiLFO-wave" }, 34 | GSUcreateElement( "gsui-beatlines", { coloredbeats: "" } ), 35 | GSUcreateElement( "gsui-periodicwave" ), 36 | GSUcreateDiv( { class: "gsuiLFO-keyPreviews" } ), 37 | ), 38 | GSUcreateDiv( { class: "gsuiLFO-ampSigns" }, 39 | GSUcreateLabel( { class: "gsuiLFO-btn gsuiLFO-ampSign" }, 40 | GSUcreateInput( { class: "gsuiLFO-btnInput gsuiLFO-ampSignRadio", name: "gsuiLFO-ampSign", type: "radio", value: "1" } ), 41 | GSUcreateIcon( { class: "gsuiLFO-btnIcon gsuiLFO-ampSignIcon", icon: "caret-up" } ), 42 | ), 43 | GSUcreateLabel( { class: "gsuiLFO-btn gsuiLFO-ampSign" }, 44 | GSUcreateInput( { class: "gsuiLFO-btnInput gsuiLFO-ampSignRadio", name: "gsuiLFO-ampSign", type: "radio", value: "-1" } ), 45 | GSUcreateIcon( { class: "gsuiLFO-btnIcon gsuiLFO-ampSignIcon", icon: "caret-down" } ), 46 | ), 47 | ), 48 | ), 49 | ) 50 | ); 51 | -------------------------------------------------------------------------------- /gsuiLibraries/gsuiLibraries.css: -------------------------------------------------------------------------------- 1 | gsui-libraries { 2 | display: flex; 3 | flex-direction: column; 4 | height: 100%; 5 | } 6 | 7 | /* .......................................................................... */ 8 | .gsuiLibraries-head { 9 | display: flex; 10 | align-items: center; 11 | gap: 4px; 12 | height: 24px; 13 | min-height: 24px; 14 | line-height: 1; 15 | padding: 0 6px; 16 | font-size: 11px; 17 | background-color: var( --gsui-head2-bg ); 18 | } 19 | .gsuiLibraries-head-icon, 20 | .gsuiLibraries-head-title { 21 | font-weight: bold; 22 | opacity: .7; 23 | } 24 | 25 | /* .......................................................................... */ 26 | .gsuiLibraries-libBtns { 27 | display: flex; 28 | margin-left: 8px; 29 | border-radius: 4px; 30 | background-color: #0004; 31 | overflow: hidden; 32 | } 33 | .gsuiLibraries-libBtn { 34 | display: flex; 35 | align-items: center; 36 | height: 18px; 37 | border: 0; 38 | outline: 0; 39 | padding: 1px 3px 0 6px; 40 | font: inherit; 41 | font-size: 9px; 42 | font-weight: bold; 43 | cursor: pointer; 44 | background-color: transparent; 45 | } 46 | .gsuiLibraries-libBtn + .gsuiLibraries-libBtn { 47 | padding-left: 3px; 48 | } 49 | .gsuiLibraries-libBtn:last-child { 50 | padding-right: 6px; 51 | } 52 | gsui-libraries:not( [ lib="default" ] ) .gsuiLibraries-libBtn[ data-lib="default" ], 53 | gsui-libraries:not( [ lib="local" ] ) .gsuiLibraries-libBtn[ data-lib="local" ] { 54 | opacity: .3; 55 | } 56 | 57 | /* .......................................................................... */ 58 | .gsuiLibraries-body { 59 | position: relative; 60 | flex: 1; 61 | } 62 | .gsuiLibraries-body gsui-library { 63 | position: absolute; 64 | inset: 0; 65 | } 66 | gsui-libraries:not( [ lib="default" ] ) .gsuiLibrary-default, 67 | gsui-libraries:not( [ lib="local" ] ) .gsuiLibrary-local { 68 | display: none; 69 | } 70 | -------------------------------------------------------------------------------- /gsuiLibraries/gsuiLibraries.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-libraries", () => [ 4 | GSUcreateDiv( { class: "gsuiLibraries-head" }, 5 | GSUcreateIcon( { class: "gsuiLibraries-head-icon", icon: "waveform" } ), 6 | GSUcreateSpan( { class: "gsuiLibraries-head-title" }, "library" ), 7 | GSUcreateDiv( { class: "gsuiLibraries-libBtns" }, 8 | GSUcreateButton( { class: "gsuiLibraries-libBtn", "data-lib": "default" }, "default" ), 9 | GSUcreateButton( { class: "gsuiLibraries-libBtn", "data-lib": "local" }, "local" ), 10 | ), 11 | ), 12 | GSUcreateDiv( { class: "gsuiLibraries-body" }, 13 | GSUcreateElement( "gsui-library", { class: "gsuiLibrary-default" } ), 14 | GSUcreateElement( "gsui-library", { class: "gsuiLibrary-local" } ), 15 | ), 16 | ] ); 17 | -------------------------------------------------------------------------------- /gsuiLibraries/gsuiLibraries.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiLibraries extends gsui0ne { 4 | constructor() { 5 | super( { 6 | $cmpName: "gsuiLibraries", 7 | $tagName: "gsui-libraries", 8 | $elements: { 9 | $libBtns: ".gsuiLibraries-libBtns", 10 | $libDef: ".gsuiLibrary-default", 11 | $libLoc: ".gsuiLibrary-local", 12 | }, 13 | $attributes: { 14 | lib: "default", 15 | }, 16 | } ); 17 | Object.seal( this ); 18 | this.$elements.$libBtns.onclick = gsuiLibraries.#onclickBtns.bind( null, this ); 19 | this.$elements.$libDef.$setPlaceholder( "loading..." ); 20 | this.$elements.$libLoc.$setPlaceholder( "drag'n drop your own samples in the app, they will appear here" ); 21 | } 22 | 23 | // ......................................................................... 24 | $getLibrary( lib ) { 25 | return lib === "local" ? this.$elements.$libLoc : this.$elements.$libDef; 26 | } 27 | 28 | // ......................................................................... 29 | static #onclickBtns( root, e ) { 30 | if ( e.target.dataset.lib ) { 31 | GSUsetAttribute( root, "lib", e.target.dataset.lib ); 32 | } 33 | } 34 | } 35 | 36 | GSUdefineElement( "gsui-libraries", gsuiLibraries ); 37 | -------------------------------------------------------------------------------- /gsuiLibrary/gsuiLibrary.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-library", () => [ 4 | GSUcreateDiv( { class: "gsuiLibrary-head" } ), 5 | GSUcreateDiv( { class: "gsuiLibrary-body" }, 6 | GSUcreateDiv( { class: "gsuiLibrary-placeholder" }, "no sample here..." ), 7 | ), 8 | ] ); 9 | 10 | GSUsetTemplate( "gsui-library-sep", id => 11 | GSUcreateDiv( { class: "gsuiLibrary-sep gsuiLibrary-sep-expanded", "data-id": id, title: id }, 12 | GSUcreateButton( { class: "gsuiLibrary-sep-btn", tabindex: -1 }, 13 | GSUcreateIcon( { icon: "caret-right" } ), 14 | GSUcreateSpan( null, id ), 15 | ), 16 | ) 17 | ); 18 | 19 | GSUsetTemplate( "gsui-library-sample", obj => 20 | GSUcreateDiv( { class: "gsuiLibrary-sample gsuiLibrary-sample-expanded", "data-id": obj.id, "data-name": obj.name, title: obj.name }, 21 | GSUcreateDiv( { class: "gsuiLibrary-sample-wave", inert: true }, 22 | GSUcreateElementSVG( "svg", { class: "gsuiLibrary-sample-svg", viewBox: "0 0 40 10", preserveAspectRatio: "none" }, 23 | GSUcreateElementSVG( "polygon", { class: "gsuiLibrary-sample-poly", points: obj.points } ), 24 | ), 25 | ), 26 | ) 27 | ); 28 | -------------------------------------------------------------------------------- /gsuiLibrary/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 | 31 |
32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /gsuiMixer/gsuiMixer.css: -------------------------------------------------------------------------------- 1 | gsui-mixer { 2 | position: relative; 3 | z-index: 0; 4 | display: flex; 5 | height: 100%; 6 | min-width: 400px; 7 | min-height: 200px; 8 | } 9 | 10 | /* .......................................................................... */ 11 | .gsuiMixer-channels { 12 | display: flex; 13 | flex-direction: column; 14 | flex: 1; 15 | min-width: 140px; 16 | } 17 | .gsuiMixer-effects { 18 | display: flex; 19 | flex-direction: column; 20 | width: 50%; 21 | min-width: 250px; 22 | border-left: 2px solid var( --gsui-head3-brd ); 23 | } 24 | 25 | /* .......................................................................... */ 26 | .gsuiMixer-head { 27 | box-sizing: border-box; 28 | display: flex; 29 | align-items: center; 30 | gap: 6px; 31 | height: 26px; 32 | min-height: 26px; 33 | padding: 2px 6px 0; 34 | font-size: 10px; 35 | border-bottom: 2px solid var( --gsui-head3-brd ); 36 | background-color: var( --gsui-head3-bg ); 37 | } 38 | .gsuiMixer-head-title { 39 | font-weight: bold; 40 | } 41 | 42 | /* .......................................................................... */ 43 | .gsuiMixer-bottomShadow { 44 | position: absolute; 45 | top: 100%; 46 | width: 100%; 47 | height: 50px; 48 | } 49 | 50 | /* .......................................................................... */ 51 | .gsuiMixer-analyserTypes { 52 | margin-left: 10px; 53 | display: flex; 54 | gap: 4px; 55 | opacity: .8; 56 | } 57 | .gsuiMixer-analyserTypes > .gsuiIcon { 58 | font-size: 13px; 59 | } 60 | .gsuiMixer-analyserTypes-labels { 61 | display: flex; 62 | flex-direction: column; 63 | cursor: pointer; 64 | } 65 | 66 | /* .......................................................................... */ 67 | .gsuiMixer-analyserTypes-label { 68 | display: flex; 69 | align-items: center; 70 | height: 9px; 71 | gap: .5ch; 72 | font-size: 8px; 73 | } 74 | .gsuiMixer-analyserTypes-label span { opacity: .7 } 75 | .gsuiMixer-analyserTypes-label .gsuiIcon::after { opacity: .3 } 76 | .gsuiMixer-analyserTypes-label .gsuiIcon::before { opacity: 0 } 77 | gsui-mixer[ analyser="td" ] .gsuiMixer-analyserTypes-label:first-child .gsuiIcon::before, 78 | gsui-mixer[ analyser="hz" ] .gsuiMixer-analyserTypes-label:last-child .gsuiIcon::before, 79 | gsui-mixer[ analyser="td" ] .gsuiMixer-analyserTypes-label:first-child span, 80 | gsui-mixer[ analyser="hz" ] .gsuiMixer-analyserTypes-label:last-child span { 81 | opacity: 1; 82 | } 83 | -------------------------------------------------------------------------------- /gsuiMixer/gsuiMixer.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-mixer", () => 4 | GSUcreateElement( "gsui-panels", { dir: "x" }, 5 | GSUcreateDiv( { class: "gsuiMixer-channels" }, 6 | GSUcreateDiv( { class: "gsuiMixer-head" }, 7 | GSUcreateIcon( { class: "gsuiMixer-head-icon", icon: "channels" } ), 8 | GSUcreateSpan( { class: "gsuiMixer-head-title", inert: true }, "channels" ), 9 | GSUcreateElement( "gsui-help-link", { page: "mixer-channels" } ), 10 | GSUcreateDiv( { class: "gsuiMixer-analyserTypes" }, 11 | GSUcreateIcon( { icon: "waveform" } ), 12 | GSUcreateDiv( { class: "gsuiMixer-analyserTypes-labels" }, 13 | GSUgetTemplate( "gsui-mixer-analyser-label", "timeDomain" ), 14 | GSUgetTemplate( "gsui-mixer-analyser-label", "frequency" ), 15 | ), 16 | ), 17 | ), 18 | GSUcreateElement( "gsui-channels" ), 19 | ), 20 | GSUcreateDiv( { class: "gsuiMixer-effects" }, 21 | GSUcreateDiv( { class: "gsuiMixer-head" }, 22 | GSUcreateIcon( { class: "gsuiMixer-head-icon", icon: "effects" } ), 23 | GSUcreateSpan( { class: "gsuiMixer-head-title", inert: true }, "effects" ), 24 | GSUcreateElement( "gsui-help-link", { page: "mixer-effects" } ), 25 | ), 26 | GSUcreateElement( "gsui-effects" ), 27 | GSUcreateDiv( { class: "gsuiMixer-bottomShadow", inert: true } ), 28 | ), 29 | ), 30 | ); 31 | 32 | GSUsetTemplate( "gsui-mixer-analyser-label", txt => 33 | GSUcreateDiv( { class: "gsuiMixer-analyserTypes-label", inert: true }, 34 | GSUcreateIcon( { icon: "radio-btn-checked" } ), 35 | GSUcreateSpan( null, txt ), 36 | ) 37 | ); 38 | -------------------------------------------------------------------------------- /gsuiMixer/gsuiMixer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiMixer extends gsui0ne { 4 | #shadowChans = null; 5 | #shadowEffects = null; 6 | 7 | constructor() { 8 | super( { 9 | $cmpName: "gsuiMixer", 10 | $tagName: "gsui-mixer", 11 | $elements: { 12 | $channels: "gsui-channels", 13 | $effects: "gsui-effects", 14 | $analyserType: ".gsuiMixer-analyserTypes-labels", 15 | }, 16 | $attributes: { 17 | analyser: "hz", 18 | }, 19 | } ); 20 | Object.seal( this ); 21 | this.$elements.$analyserType.onclick = () => { 22 | const type = GSUgetAttribute( this, "analyser" ) === "hz" ? "td" : "hz"; 23 | 24 | GSUsetAttribute( this, "analyser", type ); 25 | this.$dispatch( "changeAnalyser", type ); 26 | }; 27 | GSUlistenEvents( this, { 28 | gsuiChannels: { 29 | nbChannelsChange: () => { 30 | this.#shadowChans.$update(); 31 | }, 32 | }, 33 | gsuiEffect: { 34 | expand: () => { 35 | GSUsetTimeout( () => this.#shadowEffects.$update(), .1 ); 36 | }, 37 | }, 38 | } ); 39 | } 40 | 41 | // ......................................................................... 42 | $connected() { 43 | this.#shadowChans = new gsuiScrollShadow( { 44 | scrolledElem: this.querySelector( ".gsuiChannels-panChannels" ), 45 | leftShadow: this.querySelector( ".gsuiChannels-panMain" ), 46 | rightShadow: this.querySelector( ".gsuiMixer-effects" ), 47 | } ); 48 | this.#shadowEffects = new gsuiScrollShadow( { 49 | scrolledElem: this.$elements.$effects, 50 | topShadow: this.querySelector( ".gsuiMixer-effects .gsuiMixer-head" ), 51 | bottomShadow: this.querySelector( ".gsuiMixer-effects .gsuiMixer-bottomShadow" ), 52 | } ); 53 | } 54 | $disconnected() { 55 | this.#shadowChans.$disconnected(); 56 | this.#shadowEffects.$disconnected(); 57 | } 58 | static get observedAttributes() { 59 | return [ "analyser" ]; 60 | } 61 | $attributeChanged( prop, val ) { 62 | switch ( prop ) { 63 | case "analyser": 64 | this.$elements.$channels.$setAnalyserType( val ); 65 | break; 66 | } 67 | } 68 | 69 | // ......................................................................... 70 | $getChannels() { return this.$elements.$channels; } 71 | $getEffects() { return this.$elements.$effects; } 72 | } 73 | 74 | GSUdefineElement( "gsui-mixer", gsuiMixer ); 75 | -------------------------------------------------------------------------------- /gsuiNoise/gsuiNoise.css: -------------------------------------------------------------------------------- 1 | gsui-noise { 2 | box-sizing: border-box; 3 | display: flex; 4 | align-items: center; 5 | min-width: 380px; 6 | gap: 8px; 7 | padding: 6px 10px; 8 | font-size: 12px; 9 | border-top: var( --gsui-item-brdtop ); 10 | border-bottom: var( --gsui-item-brdbottom ); 11 | background-color: var( --gsui-item-bg ); 12 | } 13 | 14 | /* .......................................................................... */ 15 | .gsuiNoise-type-txt, 16 | gsui-noise > span { 17 | opacity: .5; 18 | } 19 | 20 | /* .......................................................................... */ 21 | .gsuiNoise-type { 22 | position: relative; 23 | display: flex; 24 | align-items: center; 25 | width: 92px; 26 | gap: 4px; 27 | } 28 | .gsuiNoise-type-color { 29 | width: 10px; 30 | height: 10px; 31 | border: 1px solid #000c; 32 | border-radius: 3px; 33 | } 34 | gsui-noise:not( [ toggle ] ) .gsuiNoise-type-color { 35 | opacity: .3; 36 | } 37 | gsui-noise[ color="white" ] .gsuiNoise-type-color { background-color: #fff } 38 | gsui-noise[ color="pink" ] .gsuiNoise-type-color { background-color: #b04565 } 39 | gsui-noise[ color="brown" ] .gsuiNoise-type-color { background-color: #bd6c40 } 40 | .gsuiNoise-type-txt::after { 41 | content: " noise"; 42 | } 43 | .gsuiNoise-type-select { 44 | position: absolute; 45 | width: 100%; 46 | opacity: 0; 47 | cursor: pointer; 48 | } 49 | gsui-noise:not( [ toggle ] ) .gsuiNoise-type-select { 50 | cursor: not-allowed; 51 | } 52 | 53 | /* .......................................................................... */ 54 | .gsuiNoise-value { 55 | width: 27px; 56 | font-family: var( --gsui-font-number ); 57 | } 58 | 59 | /* .......................................................................... */ 60 | gsui-noise gsui-slider { 61 | height: 8px; 62 | } 63 | gsui-noise .gsuiSlider-eventCatcher { 64 | inset: -10px -8px; 65 | } 66 | gsui-noise gsui-slider[ data-prop="gain" ] { 67 | width: 80px; 68 | --gsuiSlider-lineColor: var( --gsui-col-gain ); 69 | } 70 | gsui-noise gsui-slider[ data-prop="pan" ] { 71 | width: 50px; 72 | --gsuiSlider-lineColor: var( --gsui-col-pan ); 73 | } -------------------------------------------------------------------------------- /gsuiNoise/gsuiNoise.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-noise", txt => [ 4 | GSUcreateDiv( { class: "gsuiNoise-type" }, 5 | GSUcreateDiv( { class: "gsuiNoise-type-color" } ), 6 | GSUcreateSpan( { class: "gsuiNoise-type-txt" }, "white" ), 7 | GSUcreateSelect( { class: "gsuiNoise-type-select" }, 8 | GSUcreateOption( { value: "white" } ), 9 | GSUcreateOption( { value: "pink" } ), 10 | GSUcreateOption( { value: "brown" } ), 11 | ), 12 | ), 13 | GSUcreateElement( "gsui-slider", { "data-prop": "gain", type: "linear-x", min: 0, max: 1, step: .005, "mousemove-size": 400 } ), 14 | GSUcreateSpan( { class: "gsuiNoise-value", "data-prop": "gain" } ), 15 | GSUcreateSpan( null, "pan" ), 16 | GSUcreateElement( "gsui-slider", { "data-prop": "pan", type: "linear-x", min: -1, max: 1, step: .01, "mousemove-size": 400, defaultValue: 0 } ), 17 | GSUcreateSpan( { class: "gsuiNoise-value", "data-prop": "pan" } ), 18 | ] ); 19 | -------------------------------------------------------------------------------- /gsuiNoise/gsuiNoise.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiNoise extends gsui0ne { 4 | constructor() { 5 | super( { 6 | $cmpName: "gsuiNoise", 7 | $tagName: "gsui-noise", 8 | $elements: { 9 | $values: { 10 | gain: ".gsuiNoise-value[data-prop=gain]", 11 | pan: ".gsuiNoise-value[data-prop=pan]", 12 | }, 13 | $sliders: { 14 | gain: "gsui-slider[data-prop=gain]", 15 | pan: "gsui-slider[data-prop=pan]", 16 | }, 17 | $colorTxt: ".gsuiNoise-type-txt", 18 | $colorSelect: ".gsuiNoise-type select", 19 | }, 20 | $attributes: { 21 | toggle: false, 22 | color: "white", 23 | gain: 0, 24 | pan: 0, 25 | }, 26 | } ); 27 | Object.seal( this ); 28 | this.$elements.$colorSelect.onkeydown = GSUnoopFalse; 29 | this.$elements.$colorSelect.onchange = () => { 30 | const col = this.$elements.$colorSelect.value; 31 | 32 | GSUsetAttribute( this, "color", col ); 33 | this.$dispatch( "change", "color", col ); 34 | }; 35 | GSUlistenEvents( this, { 36 | gsuiSlider: { 37 | inputStart: GSUnoop, 38 | inputEnd: GSUnoop, 39 | input: ( d, t ) => { 40 | this.#setValue( t.dataset.prop, d.args[ 0 ] ); 41 | this.$dispatch( "input", t.dataset.prop, d.args[ 0 ] ); 42 | }, 43 | change: ( d, t ) => { 44 | this.$dispatch( "change", t.dataset.prop, d.args[ 0 ] ); 45 | }, 46 | }, 47 | } ); 48 | } 49 | 50 | // ......................................................................... 51 | static get observedAttributes() { 52 | return [ "toggle", "color", "gain", "pan" ]; 53 | } 54 | $attributeChanged( prop, val ) { 55 | switch ( prop ) { 56 | case "toggle": 57 | GSUsetAttribute( this.$elements.$sliders.gain, "disabled", val === null ); 58 | GSUsetAttribute( this.$elements.$sliders.pan, "disabled", val === null ); 59 | GSUsetAttribute( this.$elements.$colorSelect, "disabled", val === null ); 60 | break; 61 | case "color": 62 | this.$elements.$colorTxt.textContent = val; 63 | this.$elements.$colorSelect.value = val; 64 | break; 65 | case "gain": 66 | case "pan": 67 | this.#setValue( prop, val ); 68 | GSUsetAttribute( this.$elements.$sliders[ prop ], "value", val ); 69 | break; 70 | } 71 | } 72 | 73 | // ......................................................................... 74 | #setValue( prop, val ) { 75 | const val2 = Math.round( val * 100 ); 76 | const val3 = prop === "pan" 77 | ? GSUmathSign( val2 ) 78 | : val2; 79 | 80 | this.$elements.$values[ prop ].textContent = `${ val3 }%`; 81 | } 82 | } 83 | 84 | GSUdefineElement( "gsui-noise", gsuiNoise ); 85 | -------------------------------------------------------------------------------- /gsuiNoise/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 26 | 27 | 28 | 29 | 30 |
31 | 32 |
33 |
34 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /gsuiPanels/gsuiPanels.css: -------------------------------------------------------------------------------- 1 | gsui-panels { 2 | position: relative; 3 | overflow: hidden; 4 | display: flex; 5 | width: 100%; 6 | height: 100%; 7 | } 8 | gsui-panels[ dir="y" ] { 9 | flex-direction: column; 10 | } 11 | 12 | /* .......................................................................... */ 13 | .gsuiPanels-panel { 14 | position: absolute; 15 | box-sizing: border-box; 16 | width: 100%; 17 | height: 100%; 18 | } 19 | 20 | /* .......................................................................... */ 21 | .gsuiPanels-extend { 22 | position: absolute; 23 | width: 100%; 24 | height: 100%; 25 | transition: background-color .2s; 26 | } 27 | gsui-panels[ dir="x" ] > .gsuiPanels-panel > .gsuiPanels-extend { 28 | top: 0; 29 | left: -2px; 30 | width: 4px; 31 | cursor: col-resize; 32 | } 33 | gsui-panels[ dir="y" ] > .gsuiPanels-panel > .gsuiPanels-extend { 34 | left: 0; 35 | top: -2px; 36 | height: 4px; 37 | cursor: row-resize; 38 | } 39 | -------------------------------------------------------------------------------- /gsuiPanels/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 39 | 40 | 41 | 42 | 43 |
44 | 45 |
46 |
47 |
48 |
49 |
50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /gsuiPatternroll/gsuiPatternroll.css: -------------------------------------------------------------------------------- 1 | gsui-patternroll { 2 | display: flex; 3 | height: 100%; 4 | } 5 | 6 | gsui-patternroll gsui-track { 7 | height: var( --gsuiTimewindow-lineH ); 8 | } 9 | 10 | .gsuiPatternroll-block { 11 | container: gsuiPatternroll-block-query / inline-size; 12 | box-sizing: border-box; 13 | white-space: nowrap; 14 | color: var( --gsuiPatterns-pattern-color ); 15 | border-radius: 2px; 16 | border-top: 2px solid #ffffff4f; 17 | border-bottom: 2px solid #0000001f; 18 | border-left: 2px solid #ffffff69; 19 | border-right: 1px solid #00000033; 20 | box-shadow: 0 0 0px 1px #00000047; 21 | --gsuiPatterns-pattern-color: #223; 22 | } 23 | .gsui-mute .gsuiPatternroll-block { 24 | opacity: .6; 25 | } 26 | .gsuiPatternroll-block-header { 27 | position: absolute; 28 | inset: 3px 3px auto; 29 | box-sizing: border-box; 30 | z-index: 1; 31 | display: flex; 32 | border-radius: inherit; 33 | flex-direction: column; 34 | justify-content: center; 35 | padding: 0 4px; 36 | font-size: 10px; 37 | font-weight: bold; 38 | background-color: #fff3; 39 | pointer-events: none; 40 | } 41 | .gsuiPatternroll-block-name { 42 | overflow: hidden; 43 | text-overflow: ellipsis; 44 | } 45 | .gsuiPatternroll-block-content { 46 | position: absolute; 47 | inset: 18px 0 0; 48 | pointer-events: none; 49 | } 50 | .gsuiPatternroll-block[ data-missing ] .gsuiPatternroll-block-content { 51 | display: none; 52 | } 53 | .gsuiPatternroll-block-content svg { 54 | position: absolute; 55 | top: 0; 56 | left: 0; 57 | width: 100%; 58 | height: 100%; 59 | opacity: .8; 60 | fill: currentColor; 61 | } 62 | 63 | .gsui-row-small .gsuiPatternroll-block-header { 64 | background-color: inherit; 65 | } 66 | .gsui-row-small .gsuiPatternroll-block-header, 67 | .gsui-row-small .gsuiPatternroll-block-content { 68 | position: absolute; 69 | inset: 0; 70 | } 71 | 72 | .gsuiPatternroll-block-placeholder { 73 | flex: 1; 74 | display: none; 75 | align-items: center; 76 | pointer-events: none; 77 | font-size: 14px; 78 | opacity: .5; 79 | } 80 | .gsuiPatternroll-block[ data-missing ] .gsuiPatternroll-block-placeholder { 81 | display: flex; 82 | } 83 | .gsuiPatternroll-block-placeholderIcon { 84 | margin: 0 8px; 85 | } 86 | .gsuiPatternroll-block-placeholderText { 87 | overflow: hidden; 88 | text-overflow: ellipsis; 89 | } 90 | 91 | /* .......................................................................... */ 92 | @container gsuiPatternroll-block-query ( max-width: 32px ) { 93 | .gsuiPatternroll-block-name { 94 | display: none; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /gsuiPatternroll/gsuiPatternroll.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-patternroll-block", () => 4 | GSUcreateDiv( { class: "gsuiBlocksManager-block gsuiPatternroll-block", "data-action": "move" }, 5 | GSUcreateDiv( { class: "gsuiBlocksManager-block-crop gsuiBlocksManager-block-cropA", "data-action": "cropA" } ), 6 | GSUcreateDiv( { class: "gsuiBlocksManager-block-crop gsuiBlocksManager-block-cropB", "data-action": "cropB" } ), 7 | GSUcreateDiv( { class: "gsuiPatternroll-block-header" }, 8 | GSUcreateSpan( { class: "gsuiPatternroll-block-name" } ), 9 | ), 10 | GSUcreateDiv( { class: "gsuiPatternroll-block-content" } ), 11 | GSUcreateDiv( { class: "gsuiPatternroll-block-placeholder" }, 12 | GSUcreateIcon( { class: "gsuiPatternroll-block-placeholderIcon", icon: "file-corrupt" } ), 13 | GSUcreateSpan( { class: "gsuiPatternroll-block-placeholderText" }, "missing data" ), 14 | ), 15 | ) 16 | ); 17 | -------------------------------------------------------------------------------- /gsuiPatterns/gsuiPatterns-infoPopup.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-patterns-infoPopup", () => 4 | GSUcreateDiv( { id: "gsuiPatterns-infoPopupContent" }, 5 | GSUcreateElement( "fieldset", null, 6 | GSUcreateElement( "legend", null, "Type" ), 7 | GSUcreateLabel( null, 8 | GSUcreateInput( { type: "radio", name: "type", value: "drum" } ), 9 | GSUcreateIcon( { icon: "buf-drum" } ), 10 | GSUcreateSpan( null, "drum" ), 11 | ), 12 | GSUcreateLabel( null, 13 | GSUcreateInput( { type: "radio", name: "type", value: "fx" } ), 14 | GSUcreateIcon( { icon: "buf-fx" } ), 15 | GSUcreateSpan( null, "fx" ), 16 | ), 17 | GSUcreateLabel( null, 18 | GSUcreateInput( { type: "radio", name: "type", value: "vocal" } ), 19 | GSUcreateIcon( { icon: "buf-vocal" } ), 20 | GSUcreateSpan( null, "vocal" ), 21 | ), 22 | GSUcreateLabel( null, 23 | GSUcreateInput( { type: "radio", name: "type", value: "loop" } ), 24 | GSUcreateIcon( { icon: "buf-loop" } ), 25 | GSUcreateSpan( null, "loop" ), 26 | GSUcreateSpan( null, "bpm" ), 27 | GSUcreateInput( { class: "gsuiPopup-inputText", type: "number", name: "bpm", min: 1, max: 999.99, step: .01 } ), 28 | ), 29 | ), 30 | GSUcreateElement( "fieldset", null, 31 | GSUcreateElement( "legend", null, "Edit" ), 32 | GSUcreateLabel( null, 33 | GSUcreateInput( { type: "checkbox", name: "reverse" } ), 34 | GSUcreateIcon( { icon: "reverse" } ), 35 | GSUcreateSpan( null, "reverse" ), 36 | ), 37 | ), 38 | GSUcreateElement( "fieldset", null, 39 | GSUcreateElement( "legend", null, "Name" ), 40 | GSUcreateLabel( null, 41 | GSUcreateInput( { class: "gsuiPopup-inputText", type: "text", name: "name" } ), 42 | ), 43 | ), 44 | ) 45 | ); 46 | -------------------------------------------------------------------------------- /gsuiPatterns/gsuiPatterns-pattern.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-patterns-pattern", () => 4 | GSUcreateDiv( { class: "gsuiPatterns-pattern" }, 5 | GSUcreateDiv( { class: "gsuiPatterns-pattern-grip gsuiIcon", "data-icon": "grip-v" } ), 6 | GSUcreateDiv( { class: "gsuiPatterns-pattern-head" }, 7 | GSUcreateDiv( { class: "gsuiPatterns-pattern-info" }, 8 | GSUcreateButton( { class: "gsuiPatterns-pattern-btn gsuiPatterns-pattern-btnInfo", "data-action": "editInfo", icon: "buf-undefined", title: "Edit buffer's info" } ), 9 | GSUcreateDiv( { class: "gsuiPatterns-pattern-name" } ), 10 | GSUcreateButton( { class: "gsuiPatterns-btnSolid gsuiPatterns-pattern-dest", "data-action": "redirect", title: "Redirect this pattern" }, 11 | GSUcreateIcon( { class: "gsuiPatterns-btnIcon", icon: "mixer" } ), 12 | GSUcreateSpan( { class: "gsuiPatterns-btnText" } ), 13 | ), 14 | ), 15 | GSUcreateButton( { class: "gsuiPatterns-pattern-btn", "data-action": "clone", icon: "clone", title: "Clone this pattern" } ), 16 | GSUcreateButton( { class: "gsuiPatterns-pattern-btn", "data-action": "remove", icon: "close", title: "Delete this pattern" } ), 17 | ), 18 | GSUcreateDiv( { class: "gsuiPatterns-pattern-content" } ), 19 | GSUcreateDiv( { class: "gsuiPatterns-pattern-placeholder" }, 20 | GSUcreateIcon( { class: "gsuiPatterns-pattern-placeholderIcon", icon: "file-corrupt" } ), 21 | GSUcreateSpan( { class: "gsuiPatterns-pattern-placeholderText" }, "missing data" ), 22 | ), 23 | ) 24 | ); 25 | -------------------------------------------------------------------------------- /gsuiPatterns/gsuiPatterns-synth.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-patterns-synth", () => 4 | GSUcreateDiv( { class: "gsuiPatterns-synth" }, 5 | GSUcreateDiv( { class: "gsuiPatterns-synth-head" }, 6 | GSUcreateButton( { class: "gsuiPatterns-synth-btn gsuiPatterns-synth-expand", "data-action": "expand", icon: "caret-right" } ), 7 | GSUcreateDiv( { class: "gsuiPatterns-synth-info" }, 8 | GSUcreateDiv( { class: "gsuiPatterns-synth-name" } ), 9 | GSUcreateButton( { class: "gsuiPatterns-btnSolid gsuiPatterns-synth-dest", "data-action": "redirect", title: "Redirect this synthesizer" }, 10 | GSUcreateIcon( { class: "gsuiPatterns-btnIcon", icon: "mixer" } ), 11 | GSUcreateSpan( { class: "gsuiPatterns-btnText" } ), 12 | ), 13 | ), 14 | GSUcreateButton( { class: "gsuiPatterns-synth-btn", "data-action": "newPattern", icon: "plus", title: "Create a new pattern with this synthesizer" } ), 15 | GSUcreateButton( { class: "gsuiPatterns-synth-btn", "data-action": "delete", icon: "close", title: "Delete the synthesizer and its patterns" } ), 16 | ), 17 | GSUcreateDiv( { class: "gsuiPatterns-placeholderToCheck gsuiPatterns-synth-patterns" }, 18 | GSUcreateDiv( { class: "gsuiPatterns-placeholder", inert: true }, 19 | GSUcreateSpan( null, "this synthesizer has no related pattern" ), 20 | ), 21 | ), 22 | ) 23 | ); 24 | -------------------------------------------------------------------------------- /gsuiPatterns/gsuiPatterns.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-patterns", () => 4 | GSUcreateElement( "gsui-panels", { dir: "y" }, 5 | GSUgetTemplate( "gsui-patterns-panel", { 6 | type: "buffers", 7 | title: "buffers", 8 | icon: "waveform", 9 | placeholder: "drag 'n drop raw files here (mp3, ogg, wav)", 10 | } ), 11 | GSUgetTemplate( "gsui-patterns-panel", { 12 | type: "slices", 13 | title: "slices", 14 | icon: "slices", 15 | placeholder: "no slices yet", 16 | button: { action: "newSlices", title: "Create a new slices pattern" }, 17 | } ), 18 | GSUgetTemplate( "gsui-patterns-panel", { 19 | type: "drums", 20 | title: "drums", 21 | icon: "drums", 22 | placeholder: "no drums yet", 23 | button: { action: "newDrums", title: "Create a new drums pattern" }, 24 | } ), 25 | GSUgetTemplate( "gsui-patterns-panel", { 26 | type: "keys", 27 | title: "keys", 28 | icon: "oscillator", 29 | placeholder: "no synth yet", 30 | button: { action: "newSynth", title: "Create a new synthesizer" }, 31 | } ), 32 | ) 33 | ); 34 | 35 | GSUsetTemplate( "gsui-patterns-panel", obj => 36 | GSUcreateDiv( { class: "gsuiPatterns-panel", "data-type": obj.type }, 37 | GSUcreateDiv( { class: "gsuiPatterns-panel-menu" }, 38 | GSUcreateIcon( { class: "gsuiPatterns-panel-icon", icon: obj.icon } ), 39 | GSUcreateSpan( { class: "gsuiPatterns-panel-title" }, obj.title ), 40 | obj.button && GSUcreateButton( { class: "gsuiPatterns-btnSolid", "data-action": obj.button.action, title: obj.button.title }, 41 | GSUcreateIcon( { class: "gsuiPatterns-btnIcon", icon: "plus" } ), 42 | ), 43 | ), 44 | GSUcreateDiv( { class: "gsuiPatterns-panel-list-wrap" }, 45 | GSUcreateDiv( { class: "gsuiPatterns-placeholderToCheck gsuiPatterns-panel-list" } ), 46 | GSUcreateDiv( { class: "gsuiPatterns-placeholder", inert: true }, 47 | GSUcreateSpan( null, obj.placeholder ), 48 | ), 49 | ), 50 | ) 51 | ); 52 | -------------------------------------------------------------------------------- /gsuiPeriodicWave/gsuiPeriodicWave.css: -------------------------------------------------------------------------------- 1 | gsui-periodicwave, 2 | gsui-periodicwave svg { 3 | width: 100%; 4 | height: 100%; 5 | } 6 | gsui-periodicwave polyline { 7 | stroke: currentColor; 8 | fill: none; 9 | } 10 | -------------------------------------------------------------------------------- /gsuiPianoroll/gsuiPianoroll-block.css: -------------------------------------------------------------------------------- 1 | .gsuiPianoroll-block { 2 | container: gsuiPianoroll-block-query / size; 3 | --gsuiDragline-color: var( --gsuiBlocksManager-blockColor ); 4 | } 5 | 6 | /* .......................................................................... */ 7 | .gsuiPianoroll-block-key { 8 | position: absolute; 9 | display: flex; 10 | align-items: center; 11 | left: 4px; 12 | height: 100%; 13 | color: #000; 14 | font-size: 11px; 15 | font-weight: bold; 16 | opacity: .4; 17 | pointer-events: none; 18 | } 19 | 20 | /* .......................................................................... */ 21 | .gsuiPianoroll-block .gsuiDragline { 22 | top: 50%; 23 | right: 0; 24 | } 25 | 26 | /* .......................................................................... */ 27 | .gsuiPianoroll-block .gsuiDragline-drop { 28 | top: 50%; 29 | left: 0; 30 | } 31 | .gsuiPianoroll-block .gsuiDragline-drop::before { 32 | left: 0; 33 | } 34 | .gsuiPianoroll-block .gsuiDragline-dropActive::before { 35 | left: -4px; 36 | } 37 | 38 | /* .......................................................................... */ 39 | .gsuiPianoroll-block:not( :hover ):not( .gsui-hover ) .gsuiDragline:not( .gsuiDragline-dragging ) .gsuiDragline-to { 40 | background-color: transparent; 41 | } 42 | .gsuiPianoroll-block:hover .gsuiDragline:not( .gsuiDragline-linked ) .gsuiDragline-to, 43 | .gsuiPianoroll-block.gsui-hover .gsuiDragline:not( .gsuiDragline-linked ) .gsuiDragline-to { 44 | transform: translateX( 2px ); 45 | } 46 | .gsuiPianoroll-block:hover .gsuiDragline-linked .gsuiDragline-to, 47 | .gsuiPianoroll-block.gsui-hover .gsuiDragline-linked .gsuiDragline-to { 48 | transform: translateX( -2px ); 49 | } 50 | 51 | /* .......................................................................... */ 52 | @container gsuiPianoroll-block-query ( max-width: 24px ) { 53 | .gsuiPianoroll-block-key { 54 | display: none; 55 | } 56 | } 57 | @container gsuiPianoroll-block-query ( max-height: 14px ) { 58 | .gsuiPianoroll-block-key { 59 | font-size: 10px; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /gsuiPianoroll/gsuiPianoroll.css: -------------------------------------------------------------------------------- 1 | gsui-pianoroll { 2 | display: flex; 3 | height: 100%; 4 | --gsuiDragline-dotSize: 10px; 5 | --gsuiDragline-dotRad: 2px; 6 | --gsuiDragline-lineSize: 3px; 7 | } 8 | gsui-pianoroll:focus { 9 | outline: 1px solid var( --gsuiBlocksManager-blockColor ); 10 | } 11 | 12 | /* .......................................................................... */ 13 | gsui-pianoroll gsui-keys { 14 | font-size: var( --gsuiTimewindow-lineH ); 15 | } 16 | gsui-pianoroll .gsuiTimewindow-rows { 17 | font-size: var( --gsuiTimewindow-lineH ); 18 | } 19 | gsui-pianoroll .gsuiKeys-row > div { 20 | font-size: var( --gsuiTimewindow-pxperbeat ); 21 | } 22 | gsui-pianoroll gsui-prop-select { 23 | position: absolute; 24 | inset: 0; 25 | padding: 6px 0; 26 | } 27 | 28 | /* .......................................................................... */ 29 | gsui-pianoroll .gsuiBlocksManager-block { 30 | border-radius: 2px; 31 | } 32 | 33 | /* .......................................................................... */ 34 | gsui-pianoroll .gsuiTimewindow-panelContentDown { 35 | display: flex; 36 | align-items: center; 37 | justify-content: center; 38 | } 39 | .gsuiPianoroll-slidersSelect { 40 | border: 0; 41 | outline: 0; 42 | font: inherit; 43 | color: inherit; 44 | cursor: pointer; 45 | font-size: 11px; 46 | border-radius: 4px; 47 | background-color: #fff2; 48 | scrollbar-width: none; 49 | opacity: .5; 50 | transition: .1s opacity; 51 | } 52 | .gsuiPianoroll-slidersSelect::-webkit-scrollbar { 53 | display: none; 54 | } 55 | .gsuiPianoroll-slidersSelect:focus, 56 | .gsuiPianoroll-slidersSelect:hover { 57 | opacity: 1; 58 | } 59 | .gsuiPianoroll-slidersSelect option { 60 | padding: 1px 4px; 61 | } 62 | .gsuiPianoroll-slidersSelect option::after { 63 | content: attr( data-number ); 64 | margin-left: 1ch; 65 | font-size: 14px; 66 | font-weight: bold; 67 | font-family: var( --gsui-font-number ); 68 | } 69 | .gsuiPianoroll-slidersSelect option[ value^="gainLFO" ]::after { 70 | content: "x" attr( data-number ); 71 | } 72 | .gsuiPianoroll-slidersSelect option:not( [ data-number ] )::after { 73 | content: ""; 74 | } 75 | -------------------------------------------------------------------------------- /gsuiPianoroll/gsuiPianoroll.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-pianoroll-block", () => 4 | GSUcreateDiv( { class: "gsuiBlocksManager-block gsuiPianoroll-block", "data-action": "move" }, 5 | GSUcreateDiv( { class: "gsuiPianoroll-block-key" } ), 6 | GSUcreateDiv( { class: "gsuiDragline-drop" } ), 7 | GSUcreateDiv( { class: "gsuiBlocksManager-block-crop gsuiBlocksManager-block-cropB", "data-action": "cropB" } ), 8 | ) 9 | ); 10 | -------------------------------------------------------------------------------- /gsuiPopup/gsuiPopup.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-popup", () => [ 4 | GSUcreateDiv( { class: "gsuiPopup-overlay" } ), 5 | GSUcreateDiv( { class: "gsuiPopup-window", tabindex: 0 }, 6 | GSUcreateDiv( { class: "gsuiPopup-head" } ), 7 | GSUcreateElement( "form", { class: "gsuiPopup-body" }, 8 | GSUcreateDiv( { class: "gsuiPopup-content" } ), 9 | GSUcreateDiv( { class: "gsuiPopup-message" } ), 10 | GSUcreateInput( { class: "gsuiPopup-inputText", type: "text" } ), 11 | GSUcreateDiv( { class: "gsuiPopup-btns" }, 12 | GSUcreateElement( "gsui-com-button", { class: "gsuiPopup-cancel", text: "Cancel" } ), 13 | GSUcreateElement( "gsui-com-button", { class: "gsuiPopup-ok", text: "Ok", type: "submit" } ), 14 | ), 15 | ), 16 | ) 17 | ] ); 18 | -------------------------------------------------------------------------------- /gsuiPropSelect/gsuiPropSelect.css: -------------------------------------------------------------------------------- 1 | gsui-prop-select { 2 | overflow: auto; 3 | display: flex; 4 | flex-wrap: wrap; 5 | align-content: safe center; 6 | justify-content: center; 7 | box-sizing: border-box; 8 | gap: 3px; 9 | line-height: 18px; 10 | } 11 | 12 | /* .......................................................................... */ 13 | .gsuiPropSelect-sep { 14 | flex-basis: 100%; 15 | height: 18px; 16 | font-size: 11px; 17 | text-align: center; 18 | opacity: .5; 19 | } 20 | 21 | /* .......................................................................... */ 22 | .gsuiPropSelect-btn { 23 | outline: 0; 24 | height: 18px; 25 | border: 1px dashed; 26 | border-radius: 3px; 27 | padding: 0 6px; 28 | font-size: 11px; 29 | font-weight: bold; 30 | cursor: pointer; 31 | } 32 | .gsuiPropSelect-btn[ data-prop="gainLFOSpeed" ], 33 | .gsuiPropSelect-btn[ data-prop="gainLFOAmp" ], 34 | .gsuiPropSelect-btn[ data-prop="gain" ] { color: var( --gsui-col-gain ) } 35 | .gsuiPropSelect-btn[ data-prop="pan" ] { color: var( --gsui-col-pan ) } 36 | .gsuiPropSelect-btn[ data-prop="detune" ] { color: var( --gsui-col-detune ) } 37 | .gsuiPropSelect-btn[ data-prop="lowpass" ] { color: var( --gsui-col-lowpass ) } 38 | .gsuiPropSelect-btn[ data-prop="highpass" ] { color: var( --gsui-col-highpass ) } 39 | .gsuiPropSelect-btn:hover { 40 | background-color: #fff1; 41 | } 42 | .gsuiPropSelect-btn[ data-selected ] { 43 | background-color: currentColor; 44 | } 45 | .gsuiPropSelect-btn[ data-value ]::before { 46 | content: attr( data-value ); 47 | color: #333; 48 | font-size: 14px; 49 | font-family: var( --gsui-font-number ); 50 | } 51 | .gsuiPropSelect-btn:not( [ data-value ] )::before { 52 | content: attr( data-label ); 53 | } 54 | .gsuiPropSelect-btn[ data-selected ]:not( [ data-value ] )::before { 55 | color: #333; 56 | } 57 | -------------------------------------------------------------------------------- /gsuiPropSelect/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /gsuiReorder/gsuiReorder.css: -------------------------------------------------------------------------------- 1 | .gsuiReorder-dragging.gsuiReorder-reordering { 2 | opacity: .2 !important; 3 | } 4 | 5 | /* .......................................................................... */ 6 | .gsuiReorder-dropArea { 7 | position: relative; 8 | } 9 | .gsuiReorder-dropArea::after { 10 | content: ""; 11 | position: absolute; 12 | z-index: 1; 13 | inset: 0; 14 | border-radius: inherit; 15 | background-color: transparent; 16 | opacity: .3; 17 | animation: gsuiReorder-dropArea-anim .5s forwards ease; 18 | animation-delay: inherit; 19 | } 20 | .gsuiReorder-dropArea-hover::after { 21 | opacity: 1; 22 | } 23 | @keyframes gsuiReorder-dropArea-anim { 24 | 0% { background-color: transparent } 25 | 20% { background-color: #1e90ff } 26 | 100% { background-color: #1e90ff50 } 27 | } 28 | 29 | /* .......................................................................... */ 30 | #gsuiReorder-fake { 31 | z-index: 1; 32 | position: absolute; 33 | overflow: hidden; 34 | box-sizing: border-box; 35 | width: 60px; 36 | height: 60px; 37 | color: var( --gsuiReorder-skeleton-txt ); 38 | border: 2px solid; 39 | pointer-events: none; 40 | } 41 | #gsuiReorder-fake::before, 42 | #gsuiReorder-fake-grip::before { 43 | content: ""; 44 | position: absolute; 45 | inset: 0; 46 | background-color: currentColor; 47 | opacity: .2; 48 | } 49 | #gsuiReorder-fake-grip { 50 | position: absolute; 51 | box-sizing: border-box; 52 | border: inherit; 53 | margin: -2px; 54 | } 55 | #gsuiReorder-fake-grip::before { 56 | opacity: .4; 57 | } 58 | -------------------------------------------------------------------------------- /gsuiRipple/gsuiRipple.css: -------------------------------------------------------------------------------- 1 | .gsuiRipple { 2 | position: relative; 3 | overflow: hidden; 4 | } 5 | .gsuiRipple-circle { 6 | position: absolute; 7 | margin: -60%; 8 | width: 120%; 9 | padding-top: 120%; 10 | border-radius: 50%; 11 | background-color: #fff; 12 | opacity: .6; 13 | transform: scale( 0 ); 14 | transition-duration: 0s; 15 | pointer-events: none; 16 | } 17 | .gsuiRipple-active .gsuiRipple-circle { 18 | transform: scale( 2 ); 19 | opacity: 0; 20 | transition-duration: .7s; 21 | transition-property: opacity, transform; 22 | } 23 | -------------------------------------------------------------------------------- /gsuiRipple/gsuiRipple.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiRipple { 4 | static #map = new Map(); 5 | static #spaceDown = false; 6 | 7 | static $init( el ) { 8 | el.classList.add( "gsuiRipple" ); 9 | el.addEventListener( "pointerdown", gsuiRipple.#ptrdown, false ); 10 | el.addEventListener( "keydown", gsuiRipple.#keydown, false ); 11 | el.addEventListener( "keyup", gsuiRipple.#keyup, false ); 12 | gsuiRipple.#map.set( el, {} ); 13 | } 14 | 15 | static #keydown( e ) { 16 | if ( e.key === " " && !gsuiRipple.#spaceDown ) { 17 | gsuiRipple.#spaceDown = true; 18 | gsuiRipple.#exec( e, .5, .5 ); 19 | } 20 | } 21 | static #keyup( e ) { 22 | if ( e.key === " " ) { 23 | gsuiRipple.#spaceDown = false; 24 | } 25 | } 26 | static #ptrdown( e ) { 27 | const el = e.currentTarget; 28 | const bcr = el.getBoundingClientRect(); 29 | 30 | gsuiRipple.#exec( e, 31 | ( e.clientX - bcr.left ) / bcr.width, 32 | ( e.clientY - bcr.top ) / bcr.height, 33 | ); 34 | } 35 | static #exec( e, x, y ) { 36 | const el = e.currentTarget; 37 | const obj = gsuiRipple.#map.get( el ); 38 | const circ = GSUcreateSpan( { class: "gsuiRipple-circle", style: { 39 | left: `${ x * 100 }%`, 40 | top: `${ y * 100 }%`, 41 | } } ); 42 | 43 | GSUclearTimeout( obj.$timeoutId ); 44 | if ( obj.$elCirc ) { 45 | obj.$elCirc.remove(); 46 | } 47 | obj.$elCirc = circ; 48 | el.prepend( circ ); 49 | el.classList.remove( "gsuiRipple-active" ); 50 | obj.$timeoutId = GSUsetTimeout( () => { 51 | el.classList.add( "gsuiRipple-active" ); 52 | obj.$timeoutId = GSUsetTimeout( () => { 53 | el.classList.remove( "gsuiRipple-active" ); 54 | circ.remove(); 55 | }, .7 ); 56 | }, .1 ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /gsuiSVGPatterns/gsuiSVGPatternsAutomation.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiSVGPatternsAutomation { 4 | static $render( data, dur ) { 5 | const poly = GSUcreateElementSVG( "polyline" ); 6 | 7 | GSUsetAttribute( poly, "points", gsuiDotlineSVG.$draw( data, dur * 1, 1, dur, 1, 0, 0 ) ); 8 | return [ poly ]; 9 | } 10 | } 11 | 12 | Object.freeze( gsuiSVGPatternsAutomation ); 13 | -------------------------------------------------------------------------------- /gsuiSVGPatterns/gsuiSVGPatternsDrums.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiSVGPatternsDrums { 4 | static $render( drums, drumrows, sPB ) { 5 | const rowsArr = Object.entries( drumrows ); 6 | const drmW = 1 / sPB; 7 | const drmH = 1 / rowsArr.length; 8 | const orders = rowsArr 9 | .sort( ( a, b ) => a[ 1 ].order - b[ 1 ].order ) 10 | .reduce( ( obj, [ id ], i ) => { 11 | obj[ id ] = i; 12 | return obj; 13 | }, {} ); 14 | 15 | return Object.values( drums ) 16 | .map( d => ( "gain" in d 17 | ? gsuiSVGPatternsDrums.#createDrum 18 | : gsuiSVGPatternsDrums.#createDrumcut )( d.when, orders[ d.row ] * drmH, drmW, drmH ) ); 19 | } 20 | static #createDrum( x, y, w, h ) { 21 | return GSUcreateElementSVG( "polygon", { 22 | points: GSUmathFix( [ x, y, x, y + h * .75, x + w, y + h * .75 / 2 ], 3 ).join( "," ), 23 | } ); 24 | } 25 | static #createDrumcut( x, y, w, h ) { 26 | return GSUcreateElementSVG( "rect", { 27 | x: GSUmathFix( x, 3 ), 28 | y: GSUmathFix( y + h * .8, 3 ), 29 | width: GSUmathFix( w * .9, 3 ), 30 | height: GSUmathFix( h * .2, 3 ), 31 | } ); 32 | } 33 | } 34 | 35 | Object.freeze( gsuiSVGPatternsDrums ); 36 | -------------------------------------------------------------------------------- /gsuiSVGPatterns/gsuiSVGPatternsKeys.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiSVGPatternsKeys { 4 | static $render( keys ) { 5 | const arrKeys = Object.values( keys ); 6 | const { min, size } = gsuiSVGPatternsKeys.#calcMinMax( arrKeys ); 7 | const rowH = GSUmathFix( 1 / ( size + 1 ), 3 ); 8 | 9 | return arrKeys.map( k => GSUcreateElementSVG( "rect", { 10 | x: GSUmathFix( k.when, 3 ), 11 | y: GSUmathFix( ( size - k.key + min ) * rowH, 3 ), 12 | width: GSUmathFix( k.duration, 3 ), 13 | height: rowH, 14 | } ) ); 15 | } 16 | static #calcMinMax( arrKeys ) { 17 | let min = Infinity; 18 | let max = -Infinity; 19 | 20 | arrKeys.forEach( k => { 21 | min = Math.min( min, k.key ); 22 | max = Math.max( max, k.key ); 23 | } ); 24 | min -= min % 12; 25 | max += 11 - max % 12; 26 | return { min, size: max - min }; 27 | } 28 | } 29 | 30 | Object.freeze( gsuiSVGPatternsKeys ); 31 | -------------------------------------------------------------------------------- /gsuiSVGPatterns/gsuiSVGPatternsSlices.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiSVGPatternsSlices { 4 | static $render( slices, dur ) { 5 | return Object.values( slices ).map( sli => [ 6 | gsuiSVGPatternsSlices.#renderSliceRect( sli, dur, null ), 7 | gsuiSVGPatternsSlices.#renderSliceRect( sli, dur, .25 ), 8 | ] ).flat( 1 ); 9 | } 10 | static #renderSliceRect( { x, y, w }, dur, opacity ) { 11 | return GSUcreateElementSVG( "rect", { 12 | x: dur * x, 13 | y, 14 | opacity, 15 | width: dur * ( w - .005 ), 16 | height: opacity === null ? .05 : 1 - y, 17 | } ); 18 | } 19 | } 20 | 21 | Object.freeze( gsuiSVGPatternsSlices ); 22 | -------------------------------------------------------------------------------- /gsuiScrollShadow/gsuiScrollShadow.css: -------------------------------------------------------------------------------- 1 | .gsuiScrollShadow { 2 | --gsuiScrollShadow-dist: 0; 3 | --gsuiScrollShadow-x: 0; 4 | --gsuiScrollShadow-y: 0; 5 | z-index: 1; 6 | } 7 | .gsuiScrollShadow[ data-dir='top' ] { --gsuiScrollShadow-y: 1; } 8 | .gsuiScrollShadow[ data-dir='left' ] { --gsuiScrollShadow-x: 1; } 9 | .gsuiScrollShadow[ data-dir='right' ] { --gsuiScrollShadow-x: -1; } 10 | .gsuiScrollShadow[ data-dir='bottom' ] { --gsuiScrollShadow-y: -1; } 11 | .gsuiScrollShadow-shadowed { 12 | box-shadow: 13 | calc( var( --gsuiScrollShadow-dist ) * var( --gsuiScrollShadow-x ) ) 14 | calc( var( --gsuiScrollShadow-dist ) * var( --gsuiScrollShadow-y ) ) 15 | calc( var( --gsuiScrollShadow-dist ) * 2 ) 16 | #0004; 17 | } 18 | -------------------------------------------------------------------------------- /gsuiScrollShadow/gsuiScrollShadow.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiScrollShadow { 4 | #scrolledElem = null; 5 | #topShadow = null; 6 | #leftShadow = null; 7 | #rightShadow = null; 8 | #bottomShadow = null; 9 | #onscrollBind = this.#onscroll.bind( this ); 10 | 11 | constructor( o ) { 12 | Object.seal( this ); 13 | this.#scrolledElem = o.scrolledElem; 14 | this.#topShadow = o.topShadow ? [ o.topShadow ].flat() : []; 15 | this.#leftShadow = o.leftShadow ? [ o.leftShadow ].flat() : []; 16 | this.#rightShadow = o.rightShadow ? [ o.rightShadow ].flat() : []; 17 | this.#bottomShadow = o.bottomShadow ? [ o.bottomShadow ].flat() : []; 18 | gsuiScrollShadow.#initShadow( this.#topShadow, "top" ); 19 | gsuiScrollShadow.#initShadow( this.#leftShadow, "left" ); 20 | gsuiScrollShadow.#initShadow( this.#rightShadow, "right" ); 21 | gsuiScrollShadow.#initShadow( this.#bottomShadow, "bottom" ); 22 | this.#scrolledElem.addEventListener( "scroll", this.#onscrollBind ); 23 | GSUobserveSizeOf( this.#scrolledElem, this.#onscrollBind ); 24 | } 25 | 26 | // ......................................................................... 27 | $disconnected() { 28 | GSUunobserveSizeOf( this.#scrolledElem, this.#onscrollBind ); 29 | } 30 | 31 | // ......................................................................... 32 | $update() { 33 | this.#onscroll(); 34 | } 35 | static #initShadow( elems, dir ) { 36 | elems.forEach( el => { 37 | el.classList.add( "gsuiScrollShadow" ); 38 | el.dataset.dir = dir; 39 | } ); 40 | } 41 | #onscroll() { 42 | const el = this.#scrolledElem; 43 | 44 | gsuiScrollShadow.#onscroll2( this.#topShadow, el.scrollTop ); 45 | gsuiScrollShadow.#onscroll2( this.#leftShadow, el.scrollLeft ); 46 | gsuiScrollShadow.#onscroll2( this.#rightShadow, el.scrollWidth - el.clientWidth - el.scrollLeft ); 47 | gsuiScrollShadow.#onscroll2( this.#bottomShadow, el.scrollHeight - el.clientHeight - el.scrollTop ); 48 | } 49 | static #onscroll2( elems, scroll ) { 50 | elems.forEach( el => { 51 | GSUsetStyle( el, "--gsuiScrollShadow-dist", `${ Math.min( scroll / 5, 5 ) }px` ); 52 | el.classList.toggle( "gsuiScrollShadow-shadowed", scroll > 0 ); 53 | } ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /gsuiSlicer/gsuiSlicer.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-slicer", () => [ 4 | GSUcreateDiv( { class: "gsuiSlicer-source" }, 5 | GSUcreateDiv( { class: "gsuiSlicer-source-head" }, 6 | GSUcreateIcon( { class: "gsuiSlicer-source-icon", icon: "waveform" } ), 7 | GSUcreateSpan( { class: "gsuiSlicer-source-name" } ), 8 | ), 9 | GSUcreateDiv( { class: "gsuiSlicer-source-sample-wrap" }, 10 | GSUcreateDiv( { class: "gsuiSlicer-source-sample" }, 11 | GSUcreateElementSVG( "svg", { class: "gsuiSlicer-source-wave", viewBox: "0 0 1000 64", preserveAspectRatio: "none" }, 12 | GSUcreateElementSVG( "polyline" ), 13 | ), 14 | GSUcreateDiv( { class: "gsuiSlicer-currentTime gsuiSlicer-source-currentTime" } ), 15 | ), 16 | ), 17 | ), 18 | GSUcreateDiv( { class: "gsuiSlicer-time" }, 19 | GSUcreateElement( "gsui-timeline" ), 20 | ), 21 | GSUcreateDiv( { class: "gsuiSlicer-preview" }, 22 | GSUcreateDiv( { class: "gsuiSlicer-currentTime gsuiSlicer-preview-currentTime" } ), 23 | ), 24 | GSUcreateDiv( { class: "gsuiSlicer-slices" }, 25 | GSUcreateDiv( { class: "gsuiSlicer-slices-scroll" }, 26 | GSUcreateDiv( { class: "gsuiSlicer-slices-in" }, 27 | GSUcreateDiv( { class: "gsuiSlicer-slices-beatlinesWrap" }, 28 | GSUcreateElement( "gsui-beatlines" ), 29 | GSUcreateElement( "gsui-beatlines", { vertical: true } ), 30 | ), 31 | GSUcreateDiv( { class: "gsuiSlicer-currentTime gsuiSlicer-slices-currentTime" } ), 32 | GSUcreateElementSVG( "svg", { class: "gsuiSlicer-slices-line", preserveAspectRatio: "none" }, 33 | GSUcreateElementSVG( "line" ), 34 | ), 35 | GSUcreateDiv( { class: "gsuiSlicer-slices-wrap" } ), 36 | ), 37 | ), 38 | ), 39 | GSUcreateDiv( { class: "gsuiSlicer-menu" }, 40 | GSUcreateElement( "gsui-step-select" ), 41 | GSUcreateButton( { class: "gsuiSlicer-btn", "data-action": "moveY", icon: "hand-pointer", title: "Move slices vertically" } ), 42 | GSUcreateButton( { class: "gsuiSlicer-btn", "data-action": "reset", icon: "slices", title: "Reset slices vertically" } ), 43 | GSUcreateButton( { class: "gsuiSlicer-btn", "data-action": "split", icon: "cut", title: "Cut slices in half" } ), 44 | GSUcreateButton( { class: "gsuiSlicer-btn", "data-action": "merge", icon: "erase", title: "Merge slices together" } ), 45 | ), 46 | ] ); 47 | -------------------------------------------------------------------------------- /gsuiSlider/gsuiSlider.css: -------------------------------------------------------------------------------- 1 | gsui-slider { 2 | --gsuiSlider-lineColor2: var( --gsuiSlider-lineColor ); 3 | display: block; 4 | position: relative; 5 | width: 100%; 6 | height: 100%; 7 | outline: 0; 8 | touch-action: none; 9 | } 10 | gsui-slider[ disabled ] { 11 | --gsuiSlider-lineColor2: var( --gsuiSlider-lineColor-disabled ); 12 | cursor: not-allowed; 13 | } 14 | 15 | .gsuiSlider-line, 16 | .gsuiSlider-eventCatcher { 17 | position: absolute; 18 | inset: 0; 19 | } 20 | 21 | .gsuiSlider-linear .gsuiSlider-svg, 22 | .gsuiSlider-circular .gsuiSlider-line { 23 | display: none; 24 | } 25 | 26 | .gsuiSlider-line { 27 | overflow: hidden; 28 | border-radius: var( --gsuiSlider-lineRadius, 2px ); 29 | background-color: var( --gsuiSlider-lineBgColor ); 30 | } 31 | .gsuiSlider-lineColor { 32 | position: absolute; 33 | background-color: var( --gsuiSlider-lineColor2 ); 34 | } 35 | 36 | .gsuiSlider-svg { 37 | position: absolute; 38 | width: 100%; 39 | height: 100%; 40 | pointer-events: none; 41 | } 42 | .gsuiSlider-svgLine, 43 | .gsuiSlider-svgLineColor { 44 | fill: none; 45 | transform-origin: center; 46 | } 47 | .gsuiSlider-svgLine { stroke: var( --gsuiSlider-lineBgColor ); } 48 | .gsuiSlider-svgLineColor { stroke: var( --gsuiSlider-lineColor2 ); } 49 | -------------------------------------------------------------------------------- /gsuiSlider/gsuiSlider.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-slider", () => [ 4 | GSUcreateDiv( { class: "gsuiSlider-line" }, 5 | GSUcreateDiv( { class: "gsuiSlider-lineColor" } ), 6 | ), 7 | GSUcreateElementSVG( "svg", { class: "gsuiSlider-svg" }, 8 | GSUcreateElementSVG( "circle", { class: "gsuiSlider-svgLine" } ), 9 | GSUcreateElementSVG( "circle", { class: "gsuiSlider-svgLineColor" } ), 10 | ), 11 | GSUcreateDiv( { class: "gsuiSlider-eventCatcher" } ), 12 | ] ); 13 | -------------------------------------------------------------------------------- /gsuiSliderGroup/gsuiSliderGroup.css: -------------------------------------------------------------------------------- 1 | gsui-slidergroup { 2 | display: block; 3 | position: relative; 4 | overflow: hidden; 5 | height: 100%; 6 | z-index: 0; 7 | } 8 | .gsuiSliderGroup-slidersWrap { 9 | position: absolute; 10 | width: 100%; 11 | top: 0; 12 | bottom: 0; 13 | overflow: auto; 14 | scrollbar-width: none; 15 | } 16 | .gsuiSliderGroup-slidersWrap::-webkit-scrollbar { 17 | display: none; 18 | } 19 | .gsuiSliderGroup-sliders { 20 | position: absolute; 21 | width: 1000000px; 22 | height: 100%; 23 | } 24 | 25 | /* .......................................................................... */ 26 | .gsuiSliderGroup-slider { 27 | position: absolute; 28 | overflow: hidden; 29 | top: 0; 30 | bottom: 0; 31 | cursor: pointer; 32 | } 33 | .gsuiSliderGroup-slider::before { 34 | content: ""; 35 | position: absolute; 36 | height: 100%; 37 | width: 6px; 38 | background-color: #0002; 39 | } 40 | gsui-slidergroup:has( .gsuiSliderGroup-sliderSelected ) .gsuiSliderGroup-slider:not( .gsuiSliderGroup-sliderSelected ) { 41 | pointer-events: none; 42 | } 43 | .gsuiSliderGroup-sliderInner { 44 | position: absolute; 45 | width: 6px; 46 | color: var( --gsui-col-pattern8 ); 47 | background-color: currentColor; 48 | } 49 | .gsuiSliderGroup-sliderSelected .gsuiSliderGroup-sliderInner { 50 | color: var( --gsui-col-patternSelected8 ); 51 | } 52 | .gsuiSliderGroup-sliderInner::after { 53 | content: ""; 54 | position: absolute; 55 | box-sizing: border-box; 56 | width: 1000000px; 57 | height: 100%; 58 | border: 0 solid; 59 | border-width: 1px 0 0; 60 | } 61 | .gsuiSliderGroup-sliderInnerDown::after { 62 | border-width: 0 0 1px; 63 | } 64 | 65 | /* .......................................................................... */ 66 | .gsuiSliderGroup-loop, 67 | .gsuiSliderGroup-currentTime { 68 | position: absolute; 69 | z-index: 2147483647; 70 | top: 0; 71 | bottom: 0; 72 | pointer-events: none; 73 | } 74 | .gsuiSliderGroup-defaultValue { 75 | position: absolute; 76 | left: 0; 77 | right: 0; 78 | border-top: 1px dotted #0008; 79 | pointer-events: none; 80 | } 81 | .gsuiSliderGroup-currentTime { 82 | width: 2px; 83 | margin-left: -1px; 84 | background-color: var( --gsui-col-currentTimeCursor ); 85 | } 86 | gsui-slidergroup[ currenttime="0" ] .gsuiSliderGroup-currentTime { 87 | display: none; 88 | } 89 | .gsuiSliderGroup-loop { 90 | background-color: transparent; 91 | transition: .2s background-color; 92 | } 93 | .gsuiSliderGroup-loopA { left: 0; } 94 | .gsuiSliderGroup-loopB { right: 0; } 95 | .gsuiSliderGroup-loopOn { 96 | background-color: #0004; 97 | } 98 | -------------------------------------------------------------------------------- /gsuiSliderGroup/gsuiSliderGroup.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-slidergroup", () => 4 | GSUcreateDiv( { class: "gsuiSliderGroup-slidersWrap" }, 5 | GSUcreateDiv( { class: "gsuiSliderGroup-sliders" }, 6 | GSUcreateElement( "gsui-beatlines", { coloredbeats: "" } ), 7 | GSUcreateDiv( { class: "gsuiSliderGroup-currentTime" } ), 8 | GSUcreateDiv( { class: "gsuiSliderGroup-defaultValue" } ), 9 | GSUcreateDiv( { class: "gsuiSliderGroup-loop gsuiSliderGroup-loopA" } ), 10 | GSUcreateDiv( { class: "gsuiSliderGroup-loop gsuiSliderGroup-loopB" } ), 11 | ), 12 | ) 13 | ); 14 | 15 | GSUsetTemplate( "gsui-slidergroup-slider", () => 16 | GSUcreateDiv( { class: "gsuiSliderGroup-slider" }, 17 | GSUcreateDiv( { class: "gsuiSliderGroup-sliderInner" } ), 18 | ) 19 | ); 20 | -------------------------------------------------------------------------------- /gsuiSliderGroup/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 29 | 30 | 31 | 32 | 33 |
34 | 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /gsuiStepSelect/gsuiStepSelect.css: -------------------------------------------------------------------------------- 1 | gsui-step-select { 2 | display: inline-flex; 3 | align-items: center; 4 | justify-content: end; 5 | gap: 6px; 6 | height: 20px; 7 | padding: 0 6px; 8 | border-radius: 4px; 9 | font-size: 14px; 10 | cursor: pointer; 11 | background-color: #0005; 12 | background-color: #fff3; 13 | opacity: .8; 14 | } 15 | gsui-step-select:hover { 16 | opacity: 1; 17 | } 18 | 19 | .gsuiStepSelect-frac { 20 | font-family: var( --gsui-font-number ); 21 | } 22 | 23 | gsui-step-select .gsuiIcon { 24 | line-height: 20px; 25 | opacity: .5; 26 | } 27 | gsui-step-select:hover .gsuiIcon { 28 | opacity: .6; 29 | } 30 | -------------------------------------------------------------------------------- /gsuiStepSelect/gsuiStepSelect.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-step-select", () => [ 4 | GSUcreateSpan( { class: "gsuiStepSelect-frac", inert: true } ), 5 | GSUcreateIcon( { icon: "magnet" } ), 6 | ] ); 7 | -------------------------------------------------------------------------------- /gsuiStepSelect/gsuiStepSelect.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiStepSelect extends gsui0ne { 4 | #step = 1; 5 | 6 | constructor() { 7 | super( { 8 | $cmpName: "gsuiStepSelect", 9 | $tagName: "gsui-step-select", 10 | $elements: { 11 | $frac: ".gsuiStepSelect-frac", 12 | }, 13 | $attributes: { 14 | step: 1, 15 | title: "Grid snap", 16 | }, 17 | } ); 18 | Object.seal( this ); 19 | this.onclick = this.#onclick.bind( this ); 20 | } 21 | 22 | // ......................................................................... 23 | static get observedAttributes() { 24 | return [ "step" ]; 25 | } 26 | $attributeChanged( prop, val ) { 27 | if ( prop === "step" ) { 28 | this.#step = +val; 29 | this.$elements.$frac.textContent = gsuiStepSelect.#stepToFraction( this.#step ); 30 | } 31 | } 32 | 33 | // ......................................................................... 34 | $getStep() { 35 | return this.#step; 36 | } 37 | 38 | // ......................................................................... 39 | #onclick() { 40 | const step = gsuiStepSelect.#nextStep( this.#step ); 41 | 42 | GSUsetAttribute( this, "step", step ); 43 | this.$dispatch( "onchange", step ); 44 | } 45 | static #nextStep( v ) { 46 | return ( 47 | v >= 1 ? .5 : 48 | v >= .5 ? .25 : 49 | v >= .25 ? .125 : 1 50 | ); 51 | } 52 | static #stepToFraction( v ) { 53 | return ( 54 | v >= 1 ? "1" : 55 | v >= .5 ? "1/2" : 56 | v >= .25 ? "1/4" : "1/8" 57 | ); 58 | } 59 | } 60 | 61 | GSUdefineElement( "gsui-step-select", gsuiStepSelect ); 62 | -------------------------------------------------------------------------------- /gsuiStepSelect/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /gsuiSynthesizer/gsuiSynthesizer.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-synthesizer", () => [ 4 | GSUcreateDiv( { class: "gsuiSynthesizer-shadowTop" } ), 5 | GSUcreateDiv( { class: "gsuiSynthesizer-scrollArea" }, 6 | // ..................................................................... 7 | GSUcreateDiv( { class: "gsuiSynthesizer-head" }, 8 | GSUgetTemplate( "gsui-synthesizer-headTitle", { name: "envelopes", help: "synth-envelopes" } ), 9 | GSUgetTemplate( "gsui-synthesizer-headTab", { id: "env gain", name: "gain" } ), 10 | GSUgetTemplate( "gsui-synthesizer-headTab", { id: "env detune", name: "pitch" } ), 11 | GSUgetTemplate( "gsui-synthesizer-headTab", { id: "env lowpass", name: "lowpass" } ), 12 | ), 13 | GSUcreateElement( "gsui-envelope" ), 14 | // ..................................................................... 15 | GSUcreateDiv( { class: "gsuiSynthesizer-head" }, 16 | GSUgetTemplate( "gsui-synthesizer-headTitle", { name: "LFOs", help: "synth-LFOs" } ), 17 | GSUgetTemplate( "gsui-synthesizer-headTab", { id: "lfo gain", name: "gain" } ), 18 | GSUgetTemplate( "gsui-synthesizer-headTab", { id: "lfo detune", name: "pitch" } ), 19 | ), 20 | GSUcreateElement( "gsui-lfo" ), 21 | // ..................................................................... 22 | GSUcreateDiv( { class: "gsuiSynthesizer-head gsuiSynthesizer-headNoise" }, 23 | GSUgetTemplate( "gsui-synthesizer-headTitle", { name: "noise", help: "synth-noise" } ), 24 | GSUcreateElement( "gsui-toggle", { off: true } ), 25 | ), 26 | GSUcreateElement( "gsui-noise" ), 27 | // ..................................................................... 28 | GSUcreateDiv( { class: "gsuiSynthesizer-head gsuiSynthesizer-headOscs" }, 29 | GSUgetTemplate( "gsui-synthesizer-headTitle", { name: "oscillators", help: "synth-oscillator" } ), 30 | ), 31 | GSUcreateDiv( { class: "gsuiSynthesizer-oscList" }, 32 | GSUcreateButton( { class: "gsuiSynthesizer-newOsc" }, 33 | GSUcreateIcon( { icon: "plus" } ), 34 | ), 35 | ), 36 | ), 37 | GSUcreateDiv( { class: "gsuiSynthesizer-shadowBottom" } ), 38 | ] ); 39 | 40 | GSUsetTemplate( "gsui-synthesizer-headTitle", p => 41 | GSUcreateSpan( { class: "gsuiSynthesizer-headTitle" }, 42 | GSUcreateSpan( null, p.name ), 43 | GSUcreateElement( "gsui-help-link", { page: p.help } ), 44 | ) 45 | ); 46 | 47 | GSUsetTemplate( "gsui-synthesizer-headTab", p => 48 | GSUcreateDiv( { class: "gsuiSynthesizer-headTab", "data-tab": p.id }, 49 | GSUcreateElement( "gsui-toggle", { off: true } ), 50 | GSUcreateSpan( { inert: true }, p.name ), 51 | ) 52 | ); 53 | -------------------------------------------------------------------------------- /gsuiTempo/gsuiTempo.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-tempo", () => 4 | GSUcreateButton( { class: "gsuiTempo-btn", title: "Edit the time signature and BPM" }, 5 | GSUcreateDiv( { class: "gsuiTempo-timeDivision" }, 6 | GSUcreateSpan( { class: "gsuiTempo-beatsPerMeasure" } ), 7 | GSUcreateSpan( { class: "gsuiTempo-stepsPerBeat" } ), 8 | ), 9 | GSUcreateSpan( { class: "gsuiTempo-bpm" } ), 10 | ), 11 | ); 12 | 13 | GSUsetTemplate( "gsui-tempo-dropdown", () => 14 | GSUcreateElement( "form", { class: "gsuiTempo-popup", tabindex: -1 }, 15 | GSUcreateDiv( { class: "gsuiTempo-row" }, 16 | GSUcreateLabel( null, "Beats per measure" ), 17 | GSUcreateInput( { type: "number", min: 1, max: 16, required: true } ), 18 | ), 19 | GSUcreateDiv( { class: "gsuiTempo-row" }, 20 | GSUcreateLabel( null, "Steps per beat" ), 21 | GSUcreateInput( { type: "number", min: 1, max: 16, required: true } ), 22 | ), 23 | GSUcreateDiv( { class: "gsuiTempo-row gsuiTempo-row-bpm" }, 24 | GSUcreateLabel( null, "BPM " ), 25 | GSUcreateDiv( null, 26 | GSUcreateInput( { type: "number", step: .01, min: 1, max: 999.99, required: true } ), 27 | GSUcreateButton( { class: "gsuiTempo-tap", icon: "tint" } ), 28 | ), 29 | ), 30 | GSUcreateDiv( { class: "gsuiTempo-row" }, 31 | GSUcreateButton( { type: "submit" }, "Ok" ), 32 | ), 33 | ), 34 | ); 35 | -------------------------------------------------------------------------------- /gsuiTempo/gsuiTempo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiTempo extends gsui0ne { 4 | #dropdown = new gsuiDropdown(); 5 | #popup = GSUfindElements( GSUgetTemplate( "gsui-tempo-dropdown" ), { 6 | $form: ".gsuiTempo-popup", 7 | $bpmTap: ".gsuiTempo-tap", 8 | } ); 9 | 10 | constructor() { 11 | super( { 12 | $cmpName: "gsuiTempo", 13 | $tagName: "gsui-tempo", 14 | $elements: { 15 | $btn: ".gsuiTempo-btn", 16 | $bpm: ".gsuiTempo-bpm", 17 | $timediv: ".gsuiTempo-timeDivision", 18 | }, 19 | $attributes: { 20 | timedivision: "4/4", 21 | bpm: 60, 22 | }, 23 | } ); 24 | Object.seal( this ); 25 | this.#dropdown.$setDirection( "bottom" ); 26 | this.#dropdown.$bindTargetElement( this.$elements.$btn ); 27 | this.#dropdown.$onopenCreateElement( this.#createPopup.bind( this ) ); 28 | this.#popup.$form.onsubmit = this.$onsubmitPopup.bind( this ); 29 | this.#popup.$form.onkeydown = e => e.stopPropagation(); 30 | this.#popup.$bpmTap.onclick = () => this.#popup.$form[ 2 ].value = gswaBPMTap.$tap(); 31 | } 32 | 33 | // ......................................................................... 34 | static get observedAttributes() { 35 | return [ "bpm", "timedivision" ]; 36 | } 37 | $attributeChanged( prop, val ) { 38 | switch ( prop ) { 39 | case "bpm": 40 | this.#popup.$form[ 2 ].value = 41 | this.$elements.$bpm.textContent = val; 42 | break; 43 | case "timedivision": { 44 | const div = GSUsplitNums( val, "/" ); 45 | 46 | this.#popup.$form[ 0 ].value = this.$elements.$timediv.firstChild.textContent = div[ 0 ]; 47 | this.#popup.$form[ 1 ].value = this.$elements.$timediv.lastChild.textContent = div[ 1 ]; 48 | } break; 49 | } 50 | } 51 | 52 | // ......................................................................... 53 | #createPopup() { 54 | const f = this.#popup.$form; 55 | const time = GSUsplitNums( GSUgetAttribute( this, "timedivision" ), "/" ); 56 | 57 | f[ 0 ].value = time[ 0 ]; 58 | f[ 1 ].value = time[ 1 ]; 59 | f[ 2 ].value = GSUgetAttributeNum( this, "bpm" ); 60 | return f; 61 | } 62 | $onsubmitPopup( e ) { 63 | const f = e.target; 64 | const time = `${ f[ 0 ].value }/${ f[ 1 ].value }`; 65 | const bpm = f[ 2 ].value; 66 | 67 | if ( time !== GSUgetAttribute( this, "timedivision" ) || bpm !== GSUgetAttribute( this, "bpm" ) ) { 68 | this.$dispatch( "change", { 69 | bpm: +bpm, 70 | timedivision: time, 71 | } ); 72 | } 73 | this.#dropdown.$close(); 74 | return false; 75 | } 76 | } 77 | 78 | GSUdefineElement( "gsui-tempo", gsuiTempo ); 79 | -------------------------------------------------------------------------------- /gsuiTempo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /gsuiTimeline/gsuiTimeline.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-timeline", () => [ 4 | GSUcreateDiv( { class: "gsuiTimeline-steps" } ), 5 | GSUcreateDiv( { class: "gsuiTimeline-beats" } ), 6 | GSUcreateDiv( { class: "gsuiTimeline-measures" } ), 7 | GSUcreateDiv( { class: "gsuiTimeline-loop" }, 8 | GSUcreateDiv( { class: "gsuiTimeline-loopBody" } ), 9 | GSUcreateDiv( { class: "gsuiTimeline-loopHandle gsuiTimeline-loopHandleA" } ), 10 | GSUcreateDiv( { class: "gsuiTimeline-loopBorder gsuiTimeline-loopBorderA" } ), 11 | GSUcreateDiv( { class: "gsuiTimeline-loopHandle gsuiTimeline-loopHandleB" } ), 12 | GSUcreateDiv( { class: "gsuiTimeline-loopBorder gsuiTimeline-loopBorderB" } ), 13 | ), 14 | GSUcreateElementSVG( "svg", { class: "gsuiTimeline-cursor", width: "16", height: "10" }, 15 | GSUcreateElementSVG( "polygon", { points: "2,2 8,8 14,2" } ), 16 | ), 17 | GSUcreateElementSVG( "svg", { class: "gsuiTimeline-cursorPreview", width: "16", height: "10" }, 18 | GSUcreateElementSVG( "polygon", { points: "2,2 8,8 14,2" } ), 19 | ), 20 | ] ); 21 | -------------------------------------------------------------------------------- /gsuiTimewindow/gsuiTimewindow.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-timewindow", () => [ 4 | GSUcreateDiv( { class: "gsuiTimewindow-scrollArea" }, 5 | GSUcreateDiv( { class: "gsuiTimewindow-panel" }, 6 | GSUcreateDiv( { class: "gsuiTimewindow-panelUp" }, 7 | GSUcreateElement( "gsui-step-select" ), 8 | GSUcreateElement( "gsui-slider", { "data-zoom": "y", type: "linear-y", "mousemove-size": 400, min: 0, max: 1, step: .01, title: "zooming Y" } ), 9 | GSUcreateElement( "gsui-slider", { "data-zoom": "x", type: "linear-y", "mousemove-size": 400, min: 0, max: 1, step: .01, title: "zooming X" } ), 10 | ), 11 | GSUcreateDiv( { class: "gsuiTimewindow-panelContent" } ), 12 | GSUcreateDiv( { class: "gsuiTimewindow-panelContentDown" }, 13 | GSUcreateDiv( { class: "gsuiTimewindow-panelExtend gsuiTimewindow-panelExtendX" } ), 14 | ), 15 | GSUcreateDiv( { class: "gsuiTimewindow-panelExtend gsuiTimewindow-panelExtendY" } ), 16 | ), 17 | GSUcreateDiv( { class: "gsuiTimewindow-main" }, 18 | GSUcreateDiv( { class: "gsuiTimewindow-time" }, 19 | GSUcreateElement( "gsui-timeline" ), 20 | ), 21 | GSUcreateDiv( { class: "gsuiTimewindow-mainBody" }, 22 | GSUcreateDiv( { class: "gsuiTimewindow-mainContent" }, 23 | GSUcreateElement( "gsui-beatlines", { coloredbeats: "" } ), 24 | GSUcreateDiv( { class: "gsuiTimewindow-currentTime" } ), 25 | GSUcreateDiv( { class: "gsuiTimewindow-loop gsuiTimewindow-loopA" } ), 26 | GSUcreateDiv( { class: "gsuiTimewindow-loop gsuiTimewindow-loopB" } ), 27 | GSUcreateDiv( { class: "gsuiTimewindow-rows" } ), 28 | ), 29 | ), 30 | GSUcreateDiv( { class: "gsuiTimewindow-contentDown" }, 31 | GSUcreateDiv( { class: "gsuiTimewindow-panelExtend gsuiTimewindow-panelExtendX" } ), 32 | ), 33 | ), 34 | ), 35 | GSUcreateDiv( { class: "gsuiTimewindow-minimap" }, 36 | GSUcreateDiv( { class: "gsuiTimewindow-minimapPanel" } ), 37 | GSUcreateDiv( { class: "gsuiTimewindow-minimapTrack", "data-action": "track" }, 38 | GSUcreateDiv( { class: "gsuiTimewindow-minimapLoop", inert: true } ), 39 | GSUcreateDiv( { class: "gsuiTimewindow-minimapCurrentTime", inert: true } ), 40 | GSUcreateDiv( { class: "gsuiTimewindow-minimapThumb", "data-action": "thumb" }, 41 | GSUcreateDiv( { class: "gsuiTimewindow-minimapThumb-crop", "data-action": "cropA" } ), 42 | GSUcreateDiv( { class: "gsuiTimewindow-minimapThumb-crop", "data-action": "cropB" } ), 43 | ), 44 | ), 45 | ), 46 | ] ); 47 | 48 | -------------------------------------------------------------------------------- /gsuiTitleUser/gsuiTitleUser.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-titleuser", () => [ 4 | GSUcreateDiv( { class: "gsuiTitleUser-top" }, 5 | GSUcreateSpan( { class: "gsuiTitleUser-gs", inert: true }, "GridSound" ), 6 | GSUcreateButton( { class: "gsuiTitleUser-login", icon: "profile", title: "Login / connection" } ), 7 | GSUcreateAExt( { class: "gsuiTitleUser-user", title: "Go to your profile" }, 8 | GSUcreateDiv( { class: "gsuiTitleUser-avatar", inert: true } ), 9 | GSUcreateDiv( { class: "gsuiTitleUser-names", inert: true }, 10 | GSUcreateSpan( { class: "gsuiTitleUser-name" } ), 11 | GSUcreateSpan( { class: "gsuiTitleUser-username" } ), 12 | ), 13 | ), 14 | GSUcreateButton( { class: "gsuiTitleUser-logout", icon: "logout", title: "Logout / disconnect" } ), 15 | ), 16 | GSUcreateDiv( { class: "gsuiTitleUser-cmp" }, 17 | GSUcreateButton( { class: "gsuiTitleUser-save", icon: "upload", title: "Save composition" } ), 18 | GSUcreateSpan( { class: "gsuiTitleUser-justSaved", inert: true }, "SAVED" ), 19 | GSUcreateButton( { class: "gsuiTitleUser-rename", title: "Edit composition's title" }, 20 | GSUcreateSpan( { class: "gsuiTitleUser-cmpName", inert: true } ), 21 | GSUcreateIcon( { class: "gsuiTitleUser-cmpEditIcon", icon: "pen" } ), 22 | GSUcreateSpan( { class: "gsuiTitleUser-cmpDur", inert: true } ), 23 | ), 24 | GSUcreateInput( { class: "gsuiTitleUser-rename-inp", type: "text", placeholder: "Untitled..." } ), 25 | ), 26 | ] ); 27 | 28 | GSUsetTemplate( "gsui-titleuser-popup", () => 29 | GSUcreateDiv( { class: "gsuiTitleUser-popup" }, 30 | GSUcreateElement( "fieldset", null, 31 | GSUcreateElement( "legend", null, "Sign in" ), 32 | GSUcreateDiv( { class: "gsuiPopup-row" }, 33 | GSUcreateDiv( { class: "gsuiPopup-row-title" }, 34 | GSUcreateSpan( null, "Username" ), 35 | GSUcreateElement( "br" ), 36 | GSUcreateElement( "small", null, "(or email)" ), 37 | ), 38 | GSUcreateDiv( { class: "gsuiPopup-row-values" }, 39 | GSUcreateInput( { class: "gsuiPopup-inputText", required: true, name: "email", type: "text" } ), 40 | ), 41 | ), 42 | GSUcreateDiv( { class: "gsuiPopup-row" }, 43 | GSUcreateDiv( { class: "gsuiPopup-row-title" }, "Password" ), 44 | GSUcreateDiv( { class: "gsuiPopup-row-values" }, 45 | GSUcreateInput( { class: "gsuiPopup-inputText", required: true, name: "password", type: "password" } ), 46 | ), 47 | ), 48 | GSUcreateDiv( { class: "gsuiTitleUser-popup-error" } ), 49 | ), 50 | GSUcreateAExt( { href: "https://gridsound.com/#/forgotPassword" }, "Forgot password ?" ), 51 | GSUcreateElement( "br" ), 52 | GSUcreateAExt( { href: "https://gridsound.com/#/auth" }, "Create a new account" ), 53 | ) 54 | ); 55 | -------------------------------------------------------------------------------- /gsuiToggle/gsuiToggle.css: -------------------------------------------------------------------------------- 1 | gsui-toggle { 2 | position: relative; 3 | display: inline-flex; 4 | align-items: center; 5 | justify-content: center; 6 | font-size: 8px; 7 | outline: 0; 8 | cursor: pointer; 9 | color: var( --gsui-toggle-on ); 10 | transition: color .25s; 11 | } 12 | gsui-toggle[ off ] { 13 | color: var( --gsui-toggle-off ); 14 | } 15 | -------------------------------------------------------------------------------- /gsuiToggle/gsuiToggle.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiToggle extends gsui0ne { 4 | constructor() { 5 | super( { 6 | $cmpName: "gsuiToggle", 7 | $tagName: "gsui-toggle", 8 | $template: GSUcreateIcon( { icon: "hexagon" } ), 9 | $attributes: { tabindex: 0 }, 10 | } ); 11 | Object.seal( this ); 12 | this.oncontextmenu = GSUnoopFalse; 13 | this.onmousedown = e => { 14 | if ( e.button === 2 ) { 15 | this.$dispatch( "toggleSolo" ); 16 | } else if ( e.button === 0 ) { 17 | const off = GSUhasAttribute( this, "off" ); 18 | 19 | GSUsetAttribute( this, "off", !off ); 20 | this.$dispatch( "toggle", off ); 21 | } 22 | }; 23 | } 24 | $isOn() { 25 | return !GSUhasAttribute( this, "off" ); 26 | } 27 | } 28 | 29 | GSUdefineElement( "gsui-toggle", gsuiToggle ); 30 | -------------------------------------------------------------------------------- /gsuiToggle/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /gsuiTrack/gsuiTrack.css: -------------------------------------------------------------------------------- 1 | gsui-track { 2 | box-sizing: border-box; 3 | display: flex; 4 | height: 100%; 5 | border-top: var( --gsui-item-brdtop ); 6 | border-bottom: var( --gsui-item-brdbottom ); 7 | background-color: var( --gsui-item-bg ); 8 | } 9 | gsui-track, 10 | .gsuiTrack-row { 11 | transition: .2s background-color; 12 | } 13 | gsui-track[ mute ] { 14 | border: 0; 15 | background-color: var( --gsui-item-bg-disabled ); 16 | } 17 | 18 | /* .......................................................................... */ 19 | .gsuiTrack-row { 20 | position: relative; 21 | z-index: 0; 22 | height: var( --gsuiTimewindow-lineH ); 23 | } 24 | .gsuiTrack-row::before { 25 | content: ""; 26 | position: absolute; 27 | inset: 0; 28 | border-top: 2px solid #ffffff0b; 29 | border-bottom: 2px solid #00000026; 30 | } 31 | .gsuiTrack-row.gsui-mute { 32 | border: 0; 33 | background-color: #00000012; 34 | } 35 | .gsuiTrack-row > div { 36 | position: absolute; 37 | inset: 0; 38 | font-size: var( --gsuiTimewindow-pxperbeat ); 39 | } 40 | .gsuiTrack-row.gsui-mute > div { 41 | top: 0; 42 | } 43 | 44 | /* .......................................................................... */ 45 | gsui-track gsui-toggle { 46 | min-width: 24px; 47 | } 48 | .gsuiTrack-nameWrap { 49 | position: relative; 50 | flex: 1; 51 | } 52 | .gsuiTrack-name { 53 | box-sizing: border-box; 54 | position: absolute; 55 | inset: 0; 56 | width: 100%; 57 | border: 0; 58 | outline: 0; 59 | padding-right: 10px; 60 | font-size: 12px; 61 | font-weight: bold; 62 | font-family: inherit; 63 | text-overflow: ellipsis; 64 | color: inherit; 65 | cursor: default; 66 | background: none; 67 | } 68 | .gsuiTrack-name[ disabled ] { 69 | pointer-events: none; 70 | } 71 | gsui-track[ mute ] .gsuiTrack-name { 72 | opacity: .5; 73 | } 74 | .gsuiTrack-name:focus { 75 | font-weight: normal; 76 | } 77 | .gsuiTrack-name::placeholder { 78 | color: inherit; 79 | font-style: italic; 80 | font-weight: normal; 81 | } 82 | .gsuiTrack-name:focus::placeholder { 83 | color: transparent; 84 | } 85 | -------------------------------------------------------------------------------- /gsuiTrack/gsuiTrack.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-track", () => [ 4 | GSUcreateElement( "gsui-toggle", { title: "toggle track (right click for solo)" } ), 5 | GSUcreateDiv( { class: "gsuiTrack-nameWrap" }, 6 | GSUcreateInput( { class: "gsuiTrack-name", type: "text", disabled: true, spellcheck: "false" } ), 7 | ), 8 | ] ); 9 | 10 | GSUsetTemplate( "gsui-track-row", () => 11 | GSUcreateDiv( { class: "gsuiTrack-row gsui-row" }, 12 | GSUcreateDiv(), 13 | ) 14 | ); 15 | -------------------------------------------------------------------------------- /gsuiTrack/gsuiTrack.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiTrack extends gsui0ne { 4 | rowElement = GSUgetTemplate( "gsui-track-row" ); 5 | 6 | constructor() { 7 | super( { 8 | $cmpName: "gsuiTrack", 9 | $tagName: "gsui-track", 10 | $elements: { 11 | $inpName: ".gsuiTrack-name", 12 | }, 13 | $attributes: { 14 | name: "", 15 | order: 0, 16 | }, 17 | } ); 18 | Object.seal( this ); 19 | this.onchange = this.#onchange.bind( this ); 20 | this.onkeydown = this.#onkeydown.bind( this ); 21 | this.ondblclick = this.#ondblclick.bind( this ); 22 | this.$elements.$inpName.onblur = this.#onblur.bind( this ); 23 | GSUlistenEvents( this, { 24 | gsuiToggle: { 25 | toggle: d => { 26 | GSUsetAttribute( this, "mute", !d.args[ 0 ] ); 27 | this.$dispatch( "toggle", d.args[ 0 ] ); 28 | }, 29 | toggleSolo: () => { 30 | GSUsetAttribute( this, "mute", false ); 31 | this.$dispatch( "toggleSolo" ); 32 | }, 33 | }, 34 | } ); 35 | } 36 | 37 | // ......................................................................... 38 | static get observedAttributes() { 39 | return [ "mute", "name", "order" ]; 40 | } 41 | $attributeChanged( prop, val ) { 42 | switch ( prop ) { 43 | case "mute": 44 | this.rowElement.classList.toggle( "gsui-mute", val !== null ); 45 | GSUsetAttribute( this.firstElementChild, "off", val !== null ); 46 | break; 47 | case "name": 48 | this.$elements.$inpName.value = val; 49 | break; 50 | case "order": 51 | this.$elements.$inpName.placeholder = `track ${ +val + 1 }`; 52 | break; 53 | } 54 | } 55 | 56 | // ......................................................................... 57 | #ondblclick( e ) { 58 | if ( e.target.classList.contains( "gsuiTrack-nameWrap" ) ) { 59 | this.$elements.$inpName.disabled = false; 60 | this.$elements.$inpName.select(); 61 | this.$elements.$inpName.focus(); 62 | } 63 | } 64 | #onkeydown( e ) { 65 | if ( e.target === this.$elements.$inpName ) { 66 | e.stopPropagation(); 67 | switch ( e.key ) { 68 | case "Escape": this.$elements.$inpName.value = GSUgetAttribute( this, "name" ); 69 | case "Enter": this.$elements.$inpName.blur(); 70 | } 71 | } 72 | } 73 | #onchange() { 74 | const n = this.$elements.$inpName.value.trim(); 75 | 76 | this.$elements.$inpName.disabled = true; 77 | GSUsetAttribute( this, "name", n ); 78 | this.$dispatch( "rename", n ); 79 | } 80 | #onblur() { 81 | this.$elements.$inpName.disabled = true; 82 | } 83 | } 84 | 85 | GSUdefineElement( "gsui-track", gsuiTrack ); 86 | -------------------------------------------------------------------------------- /gsuiTrack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 | 31 |
32 | 33 |
34 |
35 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /gsuiTracklist/gsuiTracklist.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiTracklist extends gsui0ne { 4 | #tracks = new Map(); 5 | 6 | constructor() { 7 | super( { 8 | $cmpName: "gsuiTracklist", 9 | $tagName: "gsui-tracklist", 10 | } ); 11 | Object.seal( this ); 12 | GSUlistenEvents( this, { 13 | gsuiTrack: { 14 | rename: ( d, tr ) => this.$dispatch( "renameTrack", tr.dataset.id, d.args[ 0 ] ), 15 | toggle: ( d, tr ) => this.$dispatch( "toggleTrack", tr.dataset.id ), 16 | toggleSolo: ( d, tr ) => this.$dispatch( "toggleSoloTrack", tr.dataset.id ), 17 | }, 18 | } ); 19 | } 20 | 21 | // ......................................................................... 22 | $getTrack( id ) { 23 | return this.#tracks.get( id ); 24 | } 25 | $addTrack( id ) { 26 | const tr = GSUcreateElement( "gsui-track", { "data-id": id } ); 27 | 28 | tr.rowElement.dataset.id = id; 29 | this.#tracks.set( id, tr ); 30 | this.append( tr ); 31 | return tr; 32 | } 33 | $removeTrack( id ) { 34 | const tr = this.#tracks.get( id ); 35 | 36 | tr.remove(); 37 | tr.rowElement.remove(); 38 | this.#tracks.delete( id ); 39 | } 40 | } 41 | 42 | GSUdefineElement( "gsui-tracklist", gsuiTracklist ); 43 | -------------------------------------------------------------------------------- /gsuiWavetableGraph/gsuiWavetableGraph.css: -------------------------------------------------------------------------------- 1 | gsui-wavetable-graph { 2 | --gsuiWavetableGraph-waves-selected: #8f8; 3 | --gsuiWavetableGraph-waves: #a0a9ff; 4 | --gsuiWavetableGraph-morph: #fff; 5 | position: relative; 6 | display: block; 7 | color: var( --gsuiWavetableGraph-waves ); 8 | cursor: grab; 9 | touch-action: none; 10 | } 11 | gsui-wavetable-graph svg { 12 | position: absolute; 13 | inset: 0; 14 | stroke: currentColor; 15 | } 16 | 17 | /* .......................................................................... */ 18 | .gsuiWavetableGraph-box line { 19 | stroke-width: 2px; 20 | opacity: .1; 21 | } 22 | 23 | /* .......................................................................... */ 24 | .gsuiWavetableGraph-morph polyline:nth-child( even ) { 25 | fill: none; 26 | stroke-width: 3px; 27 | stroke: var( --gsuiWavetableGraph-morph ); 28 | } 29 | .gsuiWavetableGraph-morph polyline:nth-child( odd ) { 30 | stroke: none; 31 | fill: var( --gsuiWavetableGraph-morph ); 32 | fill-opacity: .4; 33 | } 34 | 35 | /* .......................................................................... */ 36 | gsui-wavetable-graph:has( .gsuiWavetableGraph-morph polyline[ points ] ) .gsuiWavetableGraph-waves { 37 | opacity: .5; 38 | } 39 | .gsuiWavetableGraph-waves polyline:nth-child( even ) { 40 | fill: none; 41 | stroke-width: 2px; 42 | stroke: currentColor; 43 | } 44 | .gsuiWavetableGraph-waves polyline:nth-child( odd ) { 45 | stroke: none; 46 | fill: currentColor; 47 | fill-opacity: .2; 48 | } 49 | .gsuiWavetableGraph-waves polyline:nth-child( even )[ data-selected ] { 50 | color: var( --gsuiWavetableGraph-waves-selected ); 51 | } 52 | .gsuiWavetableGraph-waves polyline:nth-child( odd )[ data-selected ] { 53 | color: var( --gsuiWavetableGraph-waves-selected ); 54 | } 55 | 56 | /* .......................................................................... */ 57 | .gsuiWavetableGraph-interp polyline { 58 | fill: none; 59 | stroke: currentColor; 60 | stroke-width: 1px; 61 | opacity: .2; 62 | } 63 | -------------------------------------------------------------------------------- /gsuiWindows/gsuiWindow.html.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | GSUsetTemplate( "gsui-window", id => [ 4 | GSUcreateDiv( { class: "gsuiWindow-handlers" }, 5 | GSUcreateDiv( { class: "gsuiWindow-handler", "data-dir": "n" } ), 6 | GSUcreateDiv( { class: "gsuiWindow-handler", "data-dir": "e" } ), 7 | GSUcreateDiv( { class: "gsuiWindow-handler", "data-dir": "s" } ), 8 | GSUcreateDiv( { class: "gsuiWindow-handler", "data-dir": "w" } ), 9 | GSUcreateDiv( { class: "gsuiWindow-handler", "data-dir": "nw" } ), 10 | GSUcreateDiv( { class: "gsuiWindow-handler", "data-dir": "ne" } ), 11 | GSUcreateDiv( { class: "gsuiWindow-handler", "data-dir": "sw" } ), 12 | GSUcreateDiv( { class: "gsuiWindow-handler", "data-dir": "se" } ), 13 | ), 14 | GSUcreateDiv( { class: "gsuiWindow-wrap" }, 15 | GSUcreateDiv( { class: "gsuiWindow-head" }, 16 | GSUcreateButton( { class: "gsuiWindow-icon gsuiIcon", tabindex: -1 } ), 17 | GSUcreateDiv( { class: "gsuiWindow-name", inert: true } ), 18 | GSUcreateDiv( { class: "gsuiWindow-headContent" } ), 19 | GSUcreateDiv( { class: "gsuiWindow-headBtns" }, 20 | GSUcreateButton( { class: "gsuiWindow-headBtn", "data-action": "minimize", icon: "minimize", title: "Minimize" } ), 21 | GSUcreateButton( { class: "gsuiWindow-headBtn", "data-action": "restore", title: "Restore" }, 22 | GSUcreateIcon( { icon: "restore" } ), 23 | GSUcreateIcon( { icon: "compress" } ), 24 | ), 25 | GSUcreateButton( { class: "gsuiWindow-headBtn", "data-action": "maximize", title: "Maximize" }, 26 | GSUcreateIcon( { icon: "maximize" } ), 27 | GSUcreateIcon( { icon: "expand" } ), 28 | ), 29 | GSUcreateButton( { class: "gsuiWindow-headBtn", "data-action": "close", icon: "close", title: "Close" } ), 30 | ), 31 | ), 32 | GSUcreateDiv( { class: "gsuiWindow-content" } ), 33 | ), 34 | ] ); 35 | -------------------------------------------------------------------------------- /gsuiWindows/gsuiWindows.css: -------------------------------------------------------------------------------- 1 | gsui-windows { 2 | display: block; 3 | width: 100%; 4 | height: 100%; 5 | position: relative; 6 | overflow: hidden; 7 | scroll-behavior: smooth; 8 | } 9 | -------------------------------------------------------------------------------- /gsuiWindows/gsuiWindows.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class gsuiWindows extends gsui0ne { 4 | #elWindows = {}; 5 | #focusedWindow = null; 6 | 7 | constructor() { 8 | super( { 9 | $cmpName: "gsuiWindows", 10 | $tagName: "gsui-windows", 11 | } ); 12 | Object.seal( this ); 13 | GSUlistenEvents( this, { 14 | gsuiWindow: { 15 | open: ( d, win ) => this.#onopen( win ), 16 | close: ( d, win ) => this.#onclose( win ), 17 | startMousemoving: d => this.#startMousemoving( ...d.args ), 18 | }, 19 | } ); 20 | this.onpointerleave = e => this.onpointerup?.( e ); 21 | } 22 | 23 | // ......................................................................... 24 | $createWindow( id ) { 25 | const win = GSUcreateElement( "gsui-window", { "data-id": id } ); 26 | 27 | win.addEventListener( "focusin", this.#onfocusinWin.bind( this, win ) ); 28 | this.#elWindows[ id ] = win; 29 | this.append( win ); 30 | return win; 31 | } 32 | $window( winId ) { 33 | return this.#elWindows[ winId ]; 34 | } 35 | 36 | // ......................................................................... 37 | #startMousemoving( cursor, ptrId, fnMove, fnUp ) { 38 | GSUunselectText(); 39 | this.setPointerCapture( ptrId ); 40 | this.onpointerup = this.#stopMousemoving.bind( this, fnUp ); 41 | this.onpointermove = fnMove; 42 | this.style.cursor = cursor; 43 | } 44 | #stopMousemoving( fnUp, e ) { 45 | this.releasePointerCapture( e.pointerId ); 46 | this.onpointerup = 47 | this.onpointermove = null; 48 | this.style.cursor = ""; 49 | fnUp( e ); 50 | } 51 | #onopen( win ) { 52 | this.#onfocusinWin( win ); 53 | this.$dispatch( "open", win ); 54 | } 55 | #onclose( win ) { 56 | if ( win === this.#focusedWindow ) { 57 | this.#focusedWindow = null; 58 | } 59 | this.$dispatch( "close", win ); 60 | } 61 | #onfocusinWin( win, e ) { 62 | if ( win !== this.#focusedWindow ) { 63 | const z = +win.style.zIndex || 0; 64 | 65 | this.childNodes.forEach( win => { 66 | const zz = +win.style.zIndex || 0; 67 | 68 | if ( zz > z ) { 69 | win.style.zIndex = zz - 1; 70 | } 71 | } ); 72 | win.style.zIndex = this.childElementCount - 1; 73 | this.#focusedWindow = win; 74 | this.$dispatch( "focus", win.dataset.id ); 75 | } 76 | } 77 | } 78 | 79 | GSUdefineElement( "gsui-windows", gsuiWindows ); 80 | -------------------------------------------------------------------------------- /test-assets/440-2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gridsound/gs-ui-components/4aed242692aa62bfb623b374dda9ce53aecc57de/test-assets/440-2.wav -------------------------------------------------------------------------------- /test-assets/440-simple.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gridsound/gs-ui-components/4aed242692aa62bfb623b374dda9ce53aecc57de/test-assets/440-simple.wav -------------------------------------------------------------------------------- /test-assets/440.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gridsound/gs-ui-components/4aed242692aa62bfb623b374dda9ce53aecc57de/test-assets/440.wav -------------------------------------------------------------------------------- /test-assets/808kick.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gridsound/gs-ui-components/4aed242692aa62bfb623b374dda9ce53aecc57de/test-assets/808kick.wav -------------------------------------------------------------------------------- /test-assets/Cymatics-IcedOut.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gridsound/gs-ui-components/4aed242692aa62bfb623b374dda9ce53aecc57de/test-assets/Cymatics-IcedOut.wav -------------------------------------------------------------------------------- /test-assets/Too Long - Steam Machine.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gridsound/gs-ui-components/4aed242692aa62bfb623b374dda9ce53aecc57de/test-assets/Too Long - Steam Machine.wav -------------------------------------------------------------------------------- /test-assets/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gridsound/gs-ui-components/4aed242692aa62bfb623b374dda9ce53aecc57de/test-assets/avatar.jpg -------------------------------------------------------------------------------- /test-assets/basssine.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gridsound/gs-ui-components/4aed242692aa62bfb623b374dda9ce53aecc57de/test-assets/basssine.wav -------------------------------------------------------------------------------- /test-assets/clap-013.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gridsound/gs-ui-components/4aed242692aa62bfb623b374dda9ce53aecc57de/test-assets/clap-013.wav -------------------------------------------------------------------------------- /test-assets/hat-024.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gridsound/gs-ui-components/4aed242692aa62bfb623b374dda9ce53aecc57de/test-assets/hat-024.wav -------------------------------------------------------------------------------- /test-assets/kick-017.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gridsound/gs-ui-components/4aed242692aa62bfb623b374dda9ce53aecc57de/test-assets/kick-017.wav -------------------------------------------------------------------------------- /test-assets/okay.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gridsound/gs-ui-components/4aed242692aa62bfb623b374dda9ce53aecc57de/test-assets/okay.wav -------------------------------------------------------------------------------- /test-assets/openhat-012.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gridsound/gs-ui-components/4aed242692aa62bfb623b374dda9ce53aecc57de/test-assets/openhat-012.wav -------------------------------------------------------------------------------- /test-assets/snare-018.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gridsound/gs-ui-components/4aed242692aa62bfb623b374dda9ce53aecc57de/test-assets/snare-018.wav --------------------------------------------------------------------------------