| import streamlit as st |
| import json |
| from pathlib import Path |
|
|
| st.set_page_config(page_title="Shader + Comments JSON", layout="wide") |
|
|
| st.title("🌀 Shader + Comments (Hugging Face Ready)") |
|
|
| |
| |
| |
| DATA_DIR = Path("./data") |
| DATA_DIR.mkdir(exist_ok=True) |
| COMMENTS_FILE = DATA_DIR / "comments.json" |
|
|
| |
| if COMMENTS_FILE.exists(): |
| with open(COMMENTS_FILE, "r") as f: |
| comments = json.load(f) |
| else: |
| comments = [] |
|
|
| |
| |
| |
| shader_code_default = """ |
| precision mediump float; |
| uniform vec2 iResolution; |
| uniform float iTime; |
| |
| void mainImage(out vec4 fragColor, in vec2 fragCoord) { |
| vec2 uv = fragCoord / iResolution.xy; |
| vec3 col = 0.5 + 0.5*cos(iTime + uv.xyx + vec3(0,2,4)); |
| fragColor = vec4(col,1.0); |
| } |
| void main() { mainImage(gl_FragColor, gl_FragCoord.xy); } |
| """ |
|
|
| latest_shader = shader_code_default |
| for c in reversed(comments): |
| if c.get("type") == "shader": |
| latest_shader = c["content"] |
| break |
|
|
| |
| |
| |
| shader_code = st.text_area("Shader Code (GLSL Fragment Shader)", latest_shader, height=250) |
|
|
| |
| |
| |
| html_code = f""" |
| <canvas id="glcanvas" width="800" height="500"></canvas> |
| <script> |
| const canvas = document.getElementById('glcanvas'); |
| const gl = canvas.getContext('webgl'); |
| if (!gl) {{ |
| alert('WebGL not supported'); |
| }} |
| |
| const vertCode = ` |
| attribute vec4 a_position; |
| void main() {{ |
| gl_Position = a_position; |
| }} |
| `; |
| |
| const fragCode = `{shader_code}`; |
| |
| function createShader(gl, type, source) {{ |
| const shader = gl.createShader(type); |
| gl.shaderSource(shader, source); |
| gl.compileShader(shader); |
| if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {{ |
| alert(gl.getShaderInfoLog(shader)); |
| }} |
| return shader; |
| }} |
| |
| const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertCode); |
| const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragCode); |
| |
| const program = gl.createProgram(); |
| gl.attachShader(program, vertexShader); |
| gl.attachShader(program, fragmentShader); |
| gl.linkProgram(program); |
| gl.useProgram(program); |
| |
| const positionBuffer = gl.createBuffer(); |
| gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); |
| gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ |
| -1, -1, 1, -1, -1, 1, |
| -1, 1, 1, -1, 1, 1 |
| ]), gl.STATIC_DRAW); |
| |
| const positionLoc = gl.getAttribLocation(program, "a_position"); |
| gl.enableVertexAttribArray(positionLoc); |
| gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0); |
| |
| const iResolution = gl.getUniformLocation(program, "iResolution"); |
| const iTime = gl.getUniformLocation(program, "iTime"); |
| |
| function render(time) {{ |
| gl.uniform2f(iResolution, canvas.width, canvas.height); |
| gl.uniform1f(iTime, time * 0.001); |
| gl.drawArrays(gl.TRIANGLES, 0, 6); |
| requestAnimationFrame(render); |
| }} |
| |
| requestAnimationFrame(render); |
| </script> |
| """ |
|
|
| st.components.v1.html(html_code, height=520) |
|
|
| |
| |
| |
| st.markdown("---") |
| st.subheader("💬 Comments / Add New Shader") |
|
|
| |
|
|
| with st.form("comment_form", clear_on_submit=True): |
| user_name = st.text_input("Your name", "") |
| user_content = st.text_area("Content", "", height=80) |
| submitted = st.form_submit_button("Submit") |
|
|
| if submitted and user_content.strip(): |
| new_comment = { |
| "name": user_name or "Anonymous", |
| "content": user_content, |
| |
| } |
| comments.append(new_comment) |
|
|
| |
| with open(COMMENTS_FILE, "w") as f: |
| json.dump(comments, f, indent=2) |
|
|
| st.success("Saved!") |
|
|
| |
|
|
| |
| for c in reversed(comments): |
| st.markdown(f"**{c['name']}**: {c['content']}") |