diff --git a/v1-com-officielle/glitcher/glitcher.css b/v1-com-officielle/glitcher/glitcher.css new file mode 100644 index 0000000..c11813c --- /dev/null +++ b/v1-com-officielle/glitcher/glitcher.css @@ -0,0 +1,72 @@ +/*=============Typo*/ +@font-face { + font-family: 'lineal'; + src: url('../typo/Lineal-Heavy.ttf'); +} + +@font-face { + font-family: 'pressStart2P'; + src: url('../typo/PressStart2P-Regular.ttf'); +} + +@font-face { + font-family: 'velvelyne'; + src: url('../typo/Velvelyne-Light.ttf') format('truetype'); + font-weight:lighter; +} + +@font-face { + font-family: 'velvelyne'; + src:url('../typo/Velvelyne-Bold.ttf') format('truetype'); + font-weight: bold; +} + +/*============General*/ + +body, html { + padding: 0; + margin: 0; +} + +body { + width: 100vw; + height: 100vh; + overflow: hidden; +} + +:root { + --back-color: black; + --main-color: white; + --accent-color: #3CFF00; + --neon-color: #3CFF00; + + color: var(--main-color); + font-size: 1rem; + background: var(--back-color); + font-family: 'velvelyne', sans-serif; + font-weight: bold; +} + +body > *{ + width: 100%; + height: 100%; + box-sizing: border-box; + padding: 15px; +} + +#welcome-panel { + text-align: center; +} + +h1 { + font-family: 'lineal', sans-serif; + font-weight: bolder; +} + +#ui-canvas { + display: block; + max-width: calc(100vw - 30px); + max-height: calc(100vh - 30px); + margin-left: auto; + margin-right: auto; +} \ No newline at end of file diff --git a/v1-com-officielle/glitcher/glitcher.js b/v1-com-officielle/glitcher/glitcher.js new file mode 100644 index 0000000..df39c63 --- /dev/null +++ b/v1-com-officielle/glitcher/glitcher.js @@ -0,0 +1,112 @@ +import { Glitcher } from "./lib/glitcher"; + +const CANVAS = document.getElementById("ui-canvas") +const UI_GLITCHER = new Glitcher(CANVAS) +const FORM = document.getElementById("glitcher-form") + +async function updateFromForm(){ + let data = new FormData(FORM) + + let image = data.get("image") + if(image.size > 0){ + if(UI_GLITCHER.currentImageFile != image){ + await UI_GLITCHER.setImage(image) + UI_GLITCHER.clearGlitch() + } + } else { + UI_GLITCHER.clearImage() + } + + document.getElementById("welcome-panel").hidden = image.size > 0 + document.getElementById("glitch-panel").hidden = image.size == 0 + + UI_GLITCHER.render() +} + +for(let el of FORM.elements){ + el.addEventListener("change", () => FORM.requestSubmit()) +} + +FORM.addEventListener("submit", e => { + e.preventDefault() + updateFromForm() + }) + +FORM.addEventListener("reset", e => { + FORM.elements["image"].value = "" + updateFromForm() + }) + +document.getElementById("download-btn").addEventListener("click", async e => { + e.preventDefault(); + let data = await UI_GLITCHER.toBlob("image/jpeg", 0.9); + let a = document.createElement('a') + a.href = URL.createObjectURL(data) + a.download = UI_GLITCHER.currentImageFile.name + a.click() + setTimeout(() => URL.revokeObjectURL(a.href), 10000) +}) + +CANVAS.addEventListener("pointerdown", e => { + let pointerId = e.pointerId; + + let lastX = undefined; + let lastY = undefined; + let lastCommit = null; + + function applyGlitch(e){ + if(e.pointerId != pointerId){ + return + } + + let canvasRect = CANVAS.getBoundingClientRect() + let x = e.clientX - canvasRect.left + let y = e.clientY - canvasRect.top + + let now = Date.now() + + if(lastCommit === null || now - lastCommit > 100){ + let width = 50; + if(e.pointerType == "touch"){ + width *= 2 + } + + let deltaX = lastX !== undefined ? x - lastX : 0 + let deltaY = lastY !== undefined ? y - lastY : 0 + + width += Math.max(Math.abs(deltaX), Math.abs(deltaY)) + width += Math.random()*2 + + UI_GLITCHER.addGlitch(x, y, width) + UI_GLITCHER.render() + + lastCommit = now + } + + lastX = x + lastY = y + } + + function endGlitch(e){ + if(e.pointerId != pointerId){ + return + } + + window.removeEventListener("pointermove", applyGlitch) + window.removeEventListener("pointerup", endGlitch) + } + + window.addEventListener("pointerup", endGlitch) + window.addEventListener("pointermove", applyGlitch) + + applyGlitch(e) +}) + +/*function startRendering(){ + UI_GLITCHER.render() + requestAnimationFrame(startRendering) +} +startRendering()*/ + +setInterval(() => UI_GLITCHER.render(), 500) +updateFromForm() \ No newline at end of file diff --git a/v1-com-officielle/glitcher/index.html b/v1-com-officielle/glitcher/index.html new file mode 100644 index 0000000..a5ac87a --- /dev/null +++ b/v1-com-officielle/glitcher/index.html @@ -0,0 +1,29 @@ + + + + + + + Drags&Nerds Glitcher + + + +
+
+

Glitcher

+

Séléctionne un photo pour la glitcher

+ + +
+ +
+ + + + \ No newline at end of file diff --git a/v1-com-officielle/glitcher/lib/base.frag b/v1-com-officielle/glitcher/lib/base.frag new file mode 100644 index 0000000..b497e6e --- /dev/null +++ b/v1-com-officielle/glitcher/lib/base.frag @@ -0,0 +1,7 @@ +varying highp vec2 vTextureCoord; +uniform sampler2D uImageSampler; +uniform highp vec2 uWindowSize; + +void main() { + gl_FragColor = texture2D(uImageSampler, vTextureCoord); +} \ No newline at end of file diff --git a/v1-com-officielle/glitcher/lib/base.vert b/v1-com-officielle/glitcher/lib/base.vert new file mode 100644 index 0000000..cc2aaff --- /dev/null +++ b/v1-com-officielle/glitcher/lib/base.vert @@ -0,0 +1,9 @@ +attribute vec2 aVertexPosition; +attribute vec2 aTextureCoord; + +varying highp vec2 vTextureCoord; + +void main() { + gl_Position = vec4(aVertexPosition, 0.0, 1.0); + vTextureCoord = aTextureCoord; +} diff --git a/v1-com-officielle/glitcher/lib/glitch.frag b/v1-com-officielle/glitcher/lib/glitch.frag new file mode 100644 index 0000000..7d89115 --- /dev/null +++ b/v1-com-officielle/glitcher/lib/glitch.frag @@ -0,0 +1,10 @@ +uniform highp vec2 uWindowSize; +uniform sampler2D uVideoSampler; +uniform sampler2D uNoiseSampler; +uniform lowp vec4 uRandom; + +void main() { + gl_FragColor = texture2D(uVideoSampler, gl_FragCoord.xy/uWindowSize.xy) + + (texture2D(uNoiseSampler, (gl_FragCoord.xy/vec2(128, 128))+uRandom.zw) - 0.75) + * texture2D(uNoiseSampler, (gl_FragCoord.xy/vec2(128, 128))+uRandom.xw); +} \ No newline at end of file diff --git a/v1-com-officielle/glitcher/lib/glitch.vert b/v1-com-officielle/glitcher/lib/glitch.vert new file mode 100644 index 0000000..52b3582 --- /dev/null +++ b/v1-com-officielle/glitcher/lib/glitch.vert @@ -0,0 +1,22 @@ +attribute vec2 aVertexPosition; +attribute vec2 aTextureCoord; + +uniform vec2 uGlitchPosition; +uniform float uGlitchWidth; +uniform lowp vec4 uRandom; +uniform vec2 uImageRatio; + +varying highp vec2 vTextureCoord; + +void main() { + vec2 vertex = aVertexPosition.xy; + + vertex *= uImageRatio; + vertex *= 0.001; + vertex *= uGlitchWidth; + vertex += uGlitchPosition; + vertex += uRandom.xy*0.01; + + gl_Position = vec4(vertex, -1.0, 1.0); +} + diff --git a/v1-com-officielle/glitcher/lib/glitcher.js b/v1-com-officielle/glitcher/lib/glitcher.js new file mode 100644 index 0000000..9c83ea7 --- /dev/null +++ b/v1-com-officielle/glitcher/lib/glitcher.js @@ -0,0 +1,416 @@ +import resizeToFit from 'intrinsic-scale' + +import noiseUrl from "../noise-power.png" + +import baseVertSource from "./base.vert?raw" +import baseFragSource from "./base.frag?raw" + +import glitchVertSource from "./glitch.vert?raw" +import glitchFragSource from "./glitch.frag?raw" + +export class Glitcher { + + /** @type {HTMLCanvasElement} */ + canvas + + glitches = [] + + /** + * @param {HTMLCanvasElement} canvas + */ + constructor(canvas) { + this.canvas = canvas + this.videoCanvas = new OffscreenCanvas(10, 10); + this.video = document.createElement("video") + this.init() + } + + init(){ + this.video.muted = true + this.video.loop = true + this.video.autoplay = true + this.video.src = "/background.webm" + this.video.addEventListener("canplay", e => this.video.play()) + + this.ctx = this.canvas.getContext("webgl") + if(!this.ctx){ + throw new Error("WebGL isn't supported") + } + + let baseProgram = createProgram(this.ctx, baseVertSource, baseFragSource); + + let panelPositionBuffer = this.ctx.createBuffer() + this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, panelPositionBuffer) + const positions = [ + -1.0, 1.0, + 1.0, 1.0, + -1.0, -1.0, + 1.0, -1.0, + ] + this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(positions), this.ctx.STATIC_DRAW) + + let panelUVBuffer = this.ctx.createBuffer() + this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, panelUVBuffer) + const uv = [ + 0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 1.0, 1.0, + ] + this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(uv), this.ctx.STATIC_DRAW) + + let imageTexture = this.ctx.createTexture() + + this.base = { + program: baseProgram, + aVertexPosition: this.ctx.getAttribLocation(baseProgram, "aVertexPosition"), + aTextureCoord: this.ctx.getAttribLocation(baseProgram, "aTextureCoord"), + uImageSampler: this.ctx.getUniformLocation(baseProgram, "uImageSampler"), + uWindowSize: this.ctx.getUniformLocation(baseProgram, "uWindowSize"), + imageTexture: imageTexture, + panelUVBuffer: panelUVBuffer, + panelPositionsBuffer: panelPositionBuffer, + } + this.clearImage(); + + let glitchPositionBuffer = this.ctx.createBuffer() + this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, glitchPositionBuffer) + const glitchPosition = [ + -1.0, 1.0, + 1.0, 1.0, + -1.0, -1.0, + 1.0, -1.0, + ] + this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(glitchPosition), this.ctx.STATIC_DRAW) + + let glitchProgram = createProgram(this.ctx, glitchVertSource, glitchFragSource) + let videoFrameTexture = this.ctx.createTexture() + + let noiseTexture = this.ctx.createTexture() + this.ctx.bindTexture(this.ctx.TEXTURE_2D, noiseTexture) + this.ctx.texImage2D( + this.ctx.TEXTURE_2D, + 0, + this.ctx.RGBA, + 1, 1, + 0, + this.ctx.RGBA, + this.ctx.UNSIGNED_BYTE, + new Uint8Array([0, 0, 0, 0]) + ) + + let noiseImage = new Image() + noiseImage.addEventListener("load", () => { + this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.glitch.noiseTexture) + this.ctx.texImage2D( + this.ctx.TEXTURE_2D, + 0, + this.ctx.RGBA, + this.ctx.RGBA, + this.ctx.UNSIGNED_BYTE, + noiseImage + ) + + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, this.ctx.REPEAT); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, this.ctx.REPEAT); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MIN_FILTER, this.ctx.NEAREST); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MAG_FILTER, this.ctx.NEAREST); + }) + noiseImage.src = noiseUrl + + this.glitch = { + program: glitchProgram, + aVertexPosition: this.ctx.getAttribLocation(glitchProgram, "aVertexPosition"), + uGlitchPosition: this.ctx.getUniformLocation(glitchProgram, "uGlitchPosition"), + uGlitchWidth: this.ctx.getUniformLocation(glitchProgram, "uGlitchWidth"), + uImageRatio: this.ctx.getUniformLocation(glitchProgram, "uImageRatio"), + uWindowSize: this.ctx.getUniformLocation(glitchProgram, "uWindowSize"), + uVideoSampler: this.ctx.getUniformLocation(glitchProgram, "uVideoSampler"), + uNoiseSampler: this.ctx.getUniformLocation(glitchProgram, "uNoiseSampler"), + uRandom: this.ctx.getUniformLocation(glitchProgram, "uRandom"), + glitchPositionBuffer: glitchPositionBuffer, + videoFrameTexture: videoFrameTexture, + noiseTexture: noiseTexture + } + + console.log(this.glitch) + } + + clearImage(){ + this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.base.imageTexture) + this.ctx.texImage2D( + this.ctx.TEXTURE_2D, + 0, + this.ctx.RGBA, + 1, 1, + 0, + this.ctx.RGBA, + this.ctx.UNSIGNED_BYTE, + new Uint8Array([0, 0, 0, 0]) + ) + } + + clearGlitch(){ + this.glitches = [] + } + + addGlitch(x, y, width=1){ + let computedStyle = window.getComputedStyle(this.canvas) + let clientWidth = parseFloat(computedStyle.width) + let clientHeight = parseFloat(computedStyle.height) + + this.glitches.push({ + x: ((x*2)/clientWidth)-1, + y: (((y*2)/clientHeight)-1)*-1, + width + }) + } + + /** + * @param {File} imageFile + */ + async setImage(imageFile){ + if(imageFile == this.currentImageFile){ + return + } + + if(this.currentImage){ + URL.revokeObjectURL(this.currentImage.src) + } + + let image = new Image() + const loadProm = new Promise((res, rej) => { + function ok(){ + image.removeEventListener("error", nok) + res() + } + + function nok(){ + image.removeEventListener("load", ok) + rej() + } + + image.addEventListener("load", ok) + image.addEventListener("error", nok) + }) + image.src = URL.createObjectURL(imageFile) + + try { + await loadProm + } catch(e) { + URL.revokeObjectURL(image.src) + throw e + } + this.resize(image.width, image.height) + + this.currentImageFile = imageFile + this.currentImage = image + + this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.base.imageTexture) + this.ctx.texImage2D( + this.ctx.TEXTURE_2D, + 0, + this.ctx.RGBA, + this.ctx.RGBA, + this.ctx.UNSIGNED_BYTE, + this.currentImage + ) + + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, this.ctx.CLAMP_TO_EDGE); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, this.ctx.CLAMP_TO_EDGE); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MIN_FILTER, this.ctx.LINEAR); + } + + async toBlob(type, quality) { + this.resize(this.currentImage.width, this.currentImage.height, true) + this.render() + let blob = await new Promise((res, rej) => this.canvas.toBlob((imageBlob) => { + if(imageBlob){ + res(imageBlob) + } else { + rej() + } + }, type, quality)) + this.resize(this.currentImage.width, this.currentImage.height, false) + return blob + } + + resize(width, height, force=false){ + if(!force){ + + let computedStyle = window.getComputedStyle(this.canvas) + let maxWidth = parseFloat(computedStyle["max-width"]) + let maxHeight = parseFloat(computedStyle["max-height"]) + + if(window.devicePixelRatio){ + maxWidth *= window.devicePixelRatio + maxHeight *= window.devicePixelRatio + } + + if(Number.isNaN(maxWidth)){ + maxWidth = width + } + + if(Number.isNaN(maxHeight)){ + maxHeight = height + } + + let resize = resizeToFit("contain", {width, height}, {width: maxWidth, height: maxHeight}) + + this.canvas.width = resize.width + this.canvas.height = resize.height + + } else { + this.canvas.width = width + this.canvas.height = height + } + + this.videoCanvas.width = this.canvas.width + this.videoCanvas.height = this.canvas.height + this.ctx.viewport(0, 0, this.canvas.width, this.canvas.height); + } + + updateVideoTexture(){ + let vctx = this.videoCanvas.getContext("2d") + vctx.clearRect(0, 0, this.videoCanvas.width, this.videoCanvas.height) + vctx.drawImage(this.video, 0, 0, this.videoCanvas.width, this.videoCanvas.height); + + this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.glitch.videoFrameTexture) + this.ctx.texImage2D( + this.ctx.TEXTURE_2D, + 0, + this.ctx.RGBA, + this.ctx.RGBA, + this.ctx.UNSIGNED_BYTE, + this.videoCanvas + ) + + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, this.ctx.CLAMP_TO_EDGE); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, this.ctx.CLAMP_TO_EDGE); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MIN_FILTER, this.ctx.LINEAR); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MAG_FILTER, this.ctx.LINEAR); + } + + render(){ + if(!this.ctx){ + throw new Error("Glitcher not initialized, please run init()") + } + + this.updateVideoTexture() + + this.ctx.clearColor(0.0, 0.0, 0.0, 0.0) + this.ctx.clearDepth(1.0) + this.ctx.enable(this.ctx.DEPTH_TEST) + this.ctx.enable(this.ctx.LEQUAL) + + this.ctx.clear(this.ctx.COLOR_BUFFER_BIT | this.ctx.DEPTH_BUFFER_BIT) + + { // Base rendering + this.ctx.useProgram(this.base.program) + this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.base.panelPositionsBuffer) + this.ctx.vertexAttribPointer( + this.base.aVertexPosition, + 2, // N components per iteration + this.ctx.FLOAT, + false, //Normalize, + 0, // (stride) how many bytes to get from one set of values to the next + 0, // Start offset + ) + this.ctx.enableVertexAttribArray(this.base.aVertexPosition) + + this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.base.panelUVBuffer) + this.ctx.vertexAttribPointer( + this.base.aTextureCoord, + 2, + this.ctx.FLOAT, + false, + 0, + 0, + ) + this.ctx.enableVertexAttribArray(this.base.aTextureCoord) + + this.ctx.activeTexture(this.ctx.TEXTURE0) + this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.base.imageTexture) + + this.ctx.uniform2f(this.base.uWindowSize, this.canvas.width, this.canvas.height) + this.ctx.uniform1i(this.base.uImageSampler, 0) + + this.ctx.drawArrays( + this.ctx.TRIANGLE_STRIP, + 0, // Vertex offset + 4, // Total vertex + ); + } + + { // Glitch rendering + this.ctx.useProgram(this.glitch.program) + this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.glitch.glitchPositionBuffer) + this.ctx.vertexAttribPointer( + this.glitch.glitchPositionBuffer, + 2, // N components per iteration + this.ctx.FLOAT, + false, //Normalize, + 0, // (stride) how many bytes to get from one set of values to the next + 0, // Start offset + ) + this.ctx.enableVertexAttribArray(this.glitch.aVertexPosition) + + this.ctx.activeTexture(this.ctx.TEXTURE0) + this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.glitch.videoFrameTexture) + this.ctx.uniform1i(this.glitch.uVideoSampler, 0) + + this.ctx.activeTexture(this.ctx.TEXTURE1) + this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.glitch.noiseTexture) + this.ctx.uniform1i(this.glitch.uNoiseSampler, 1) + + this.ctx.uniform2f(this.glitch.uWindowSize, this.canvas.width, this.canvas.height) + if(this.canvas.width > this.canvas.height){ + this.ctx.uniform2f(this.glitch.uImageRatio, 1.0, this.canvas.width/this.canvas.height) + } else { + this.ctx.uniform2f(this.glitch.uImageRatio, this.canvas.height/this.canvas.width, 1.0) + } + + for(let glitch of this.glitches) { + this.ctx.uniform4f(this.glitch.uRandom, + Math.random(), Math.random(), Math.random(), Math.random() + ) + this.ctx.uniform2f(this.glitch.uGlitchPosition, glitch.x, glitch.y) + this.ctx.uniform1f(this.glitch.uGlitchWidth, glitch.width) + this.ctx.drawArrays( + this.ctx.TRIANGLE_STRIP, + 0, // Vertex offset + 4, // Total vertex + ); + } + } + } +} + +function createProgram(gl, vertSource, fragSource){ + let vertShader = gl.createShader(gl.VERTEX_SHADER) + gl.shaderSource(vertShader, vertSource) + gl.compileShader(vertShader) + + if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) { + throw new Error(`failed to compile vertex: ${gl.getShaderInfoLog(vertShader)}`) + } + + let fragShader = gl.createShader(gl.FRAGMENT_SHADER) + gl.shaderSource(fragShader, fragSource) + gl.compileShader(fragShader) + + if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) { + throw new Error(`failed to compile fragment: ${gl.getShaderInfoLog(fragShader)}`) + } + + let program = gl.createProgram() + gl.attachShader(program, vertShader) + gl.attachShader(program, fragShader) + gl.linkProgram(program) + + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + throw new Error(`failed to link shader: ${gl.getProgramInfoLog(program)}`) + } + + return program +} \ No newline at end of file diff --git a/v1-com-officielle/glitcher/noise-power.png b/v1-com-officielle/glitcher/noise-power.png new file mode 100644 index 0000000..47a02c5 Binary files /dev/null and b/v1-com-officielle/glitcher/noise-power.png differ diff --git a/v1-com-officielle/package-lock.json b/v1-com-officielle/package-lock.json index 2204690..5bfe706 100644 --- a/v1-com-officielle/package-lock.json +++ b/v1-com-officielle/package-lock.json @@ -8,6 +8,7 @@ "name": "v1-com-officielle", "version": "0.0.0", "dependencies": { + "intrinsic-scale": "^5.0.0", "vite-svg-loader": "^5.1.1", "vue": "^3.5.25", "vue3-clipboard": "^1.0.0", @@ -1442,6 +1443,18 @@ "delegate": "^3.1.2" } }, + "node_modules/intrinsic-scale": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/intrinsic-scale/-/intrinsic-scale-5.0.0.tgz", + "integrity": "sha512-NJY7170uG8gYkaGGV0ddakq5VAWSvM2FKhARgXj8OQwod1hQFRBWCXn7dk35bGd+vsRYYnJQw/Qpebk8MWp4dg==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/fregante" + } + }, "node_modules/keycode": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.1.tgz", diff --git a/v1-com-officielle/package.json b/v1-com-officielle/package.json index e11f394..f96e110 100644 --- a/v1-com-officielle/package.json +++ b/v1-com-officielle/package.json @@ -9,6 +9,7 @@ "preview": "vite preview" }, "dependencies": { + "intrinsic-scale": "^5.0.0", "vite-svg-loader": "^5.1.1", "vue": "^3.5.25", "vue3-clipboard": "^1.0.0", diff --git a/v1-com-officielle/src/indieComponents/VideoPannel.vue b/v1-com-officielle/src/indieComponents/VideoPannel.vue index 7813b65..d03a89d 100644 --- a/v1-com-officielle/src/indieComponents/VideoPannel.vue +++ b/v1-com-officielle/src/indieComponents/VideoPannel.vue @@ -21,8 +21,12 @@
+<<<<<<< HEAD +======= + +>>>>>>> 13df9c06016b4c751d7817b8f3b1108bf669a990

{{selectedVideo.like}}