mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
* chore: unify product name display to OpenDesign repo-wide Rename the user-facing product name from "Open Design" to "OpenDesign" across documentation, web/desktop UI strings, i18n locales, prompts, plugin/skill/design-system content, changelogs, and specs, matching the landing-page rename already in flight. Deliberately excluded so installed-app identity stays untouched: - packages/release PRODUCT_NAME and everything derived from it (app bundle names, installer/registry identity, updater feeds) - tools/pack, tools/release, apps/packaged, landing page (PR #6983) - mocks/ recorded traces and LICENSE - test fixtures and comments that encode real on-disk install paths (Open Design.app, Program Files\Open Design, AppData paths) Validated with pnpm guard, pnpm typecheck, and targeted web/desktop/host test runs over the renamed strings. * fix(web): keep two-letter author monogram for CamelCase brand names authorInitials split words on whitespace only, so the renamed single-token author name "OpenDesign" produced a one-letter monogram. Treat lower-to-upper CamelCase boundaries as word breaks so the avatar fallback stays "OD". * fix(release): restore spaced Windows source artifact names in win.ps1 tools/pack still derives builder outputs from the unchanged PRODUCT_NAME ("Open Design"), so the asset script must look for the spaced filenames. Matches the mac/mac-intel/linux asset scripts, which were already kept on the original names. --------- Co-authored-by: Joey <236967869+joeylee12629-star@users.noreply.github.com>
97 lines
5.7 KiB
HTML
97 lines
5.7 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>Holographic Foil — OpenDesign</title>
|
|
<!--
|
|
Self-contained WebGL2 hero. Thin-film interference: a warped "thickness" field
|
|
drives a cosine interference palette that shifts with view angle (the pointer
|
|
tilts the film), plus a crushed-foil specular. No meshes, no textures, no build.
|
|
-->
|
|
<style>
|
|
:root{ color-scheme:dark; }
|
|
html,body{ margin:0; height:100%; overflow:hidden; background:#050509;
|
|
font-family:'Albert Sans',ui-sans-serif,system-ui,-apple-system,sans-serif; color:#ffffff; }
|
|
#gl{ position:fixed; inset:0; width:100%; height:100%; display:block; }
|
|
.overlay{ position:fixed; inset:0; z-index:2; pointer-events:none; display:flex; flex-direction:column; justify-content:space-between; padding:5vw; }
|
|
.top{ display:flex; justify-content:space-between; font-size:clamp(11px,1vw,14px); letter-spacing:0.32em; text-transform:uppercase; color:rgba(255,255,255,0.75); }
|
|
.top .dot{ width:8px; height:8px; border-radius:50%; background:#fff; box-shadow:0 0 14px #fff; display:inline-block; margin-right:10px; vertical-align:middle; }
|
|
h1{ margin:0; font-size:clamp(44px,11vw,168px); line-height:0.86; font-weight:800; letter-spacing:-0.04em; mix-blend-mode:overlay; }
|
|
h1 .thin{ font-weight:300; }
|
|
.bottom{ display:flex; justify-content:space-between; align-items:flex-end; gap:4vw; }
|
|
.sub{ font-size:clamp(14px,1.5vw,19px); max-width:42ch; color:rgba(255,255,255,0.85); line-height:1.45; margin:0; }
|
|
.badge{ font-size:11px; letter-spacing:0.28em; text-transform:uppercase; color:rgba(255,255,255,0.65); border:1px solid rgba(255,255,255,0.25); padding:9px 15px; border-radius:999px; backdrop-filter:blur(8px); white-space:nowrap; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<canvas id="gl"></canvas>
|
|
<div class="overlay">
|
|
<div class="top"><span><span class="dot"></span>OpenDesign · WebGL Preview</span><span>Real-time · GPU</span></div>
|
|
<div class="bottom">
|
|
<div><h1>HOLOGRAPHIC<br><span class="thin">FOIL</span></h1>
|
|
<p class="sub">Thin-film interference over a crushed-foil surface — the palette shifts with the viewing angle. Move the cursor to tilt the film.</p></div>
|
|
<div class="badge">Tilt the film ↗</div>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
(() => {
|
|
const canvas=document.getElementById('gl');
|
|
const gl=canvas.getContext('webgl2',{antialias:true});
|
|
if (!gl) { document.body.innerHTML='<p style="color:#fff;padding:2rem;font-family:sans-serif">WebGL2 not available.</p>'; return; }
|
|
const VERT=`#version 300 es
|
|
void main(){ vec2 p=vec2((gl_VertexID<<1)&2, gl_VertexID&2); gl_Position=vec4(p*2.0-1.0,0.0,1.0); }`;
|
|
const FRAG=`#version 300 es
|
|
precision highp float;
|
|
out vec4 o;
|
|
uniform vec2 u_res; uniform float u_time; uniform vec2 u_mouse;
|
|
mat2 rot(float a){ float c=cos(a),s=sin(a); return mat2(c,-s,s,c); }
|
|
float hash(vec2 p){ return fract(sin(dot(p,vec2(41.3,289.1)))*43758.5453); }
|
|
float noise(vec2 p){ vec2 i=floor(p),f=fract(p); vec2 u=f*f*(3.0-2.0*f);
|
|
return mix(mix(hash(i),hash(i+vec2(1,0)),u.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),u.x),u.y); }
|
|
float fbm(vec2 p){ float v=0.0,a=0.55; for(int i=0;i<6;i++){ v+=a*noise(p); p=rot(0.6)*p*1.95+9.0; a*=0.55; } return v; }
|
|
// interference palette: thickness+angle → spectral color
|
|
vec3 iri(float t){ return 0.55+0.45*cos(6.28318*(vec3(1.0,1.0,1.0)*t + vec3(0.0,0.33,0.67))); }
|
|
void main(){
|
|
vec2 uv=(gl_FragCoord.xy-0.5*u_res)/u_res.y;
|
|
float t=u_time*0.05;
|
|
vec2 tilt=(u_mouse-0.5)*1.4; // view-angle proxy
|
|
// crushed-foil thickness field (domain-warped)
|
|
vec2 q=vec2(fbm(uv*2.0+vec2(0.0,t)), fbm(uv*2.0+vec2(3.3,-t)));
|
|
float thick=fbm(uv*2.2 + 2.5*q + t*0.5);
|
|
// faceted foil normal from thickness gradient
|
|
float e=0.02;
|
|
float gx=fbm((uv+vec2(e,0))*2.2+2.5*q)-fbm((uv-vec2(e,0))*2.2+2.5*q);
|
|
float gy=fbm((uv+vec2(0,e))*2.2+2.5*q)-fbm((uv-vec2(0,e))*2.2+2.5*q);
|
|
vec2 n=vec2(gx,gy)/(2.0*e);
|
|
// interference phase driven by thickness + view angle (tilt·normal)
|
|
float phase=thick*3.0 + dot(n,tilt)*0.6 + 0.15;
|
|
vec3 col=iri(phase);
|
|
col=mix(col*0.4, col, smoothstep(0.2,0.9,thick)); // deepen thin areas
|
|
// crushed specular glints along facet ridges
|
|
float ridge=pow(clamp(1.0-abs(dot(normalize(n+1e-4), normalize(tilt+1e-4))),0.0,1.0),8.0);
|
|
col+=vec3(1.0)*pow(fbm(uv*8.0+q*3.0),6.0)*0.5; // sparkle
|
|
col+=vec3(0.9,0.95,1.0)*ridge*0.4;
|
|
col*=0.7+0.5*smoothstep(1.4,0.05,length(uv));
|
|
col=col/(col+0.7);
|
|
o=vec4(pow(max(col,0.0),vec3(0.85)),1.0);
|
|
}`;
|
|
function sh(t,s){const x=gl.createShader(t);gl.shaderSource(x,s);gl.compileShader(x);if(!gl.getShaderParameter(x,gl.COMPILE_STATUS))throw gl.getShaderInfoLog(x);return x;}
|
|
const pr=gl.createProgram();gl.attachShader(pr,sh(gl.VERTEX_SHADER,VERT));gl.attachShader(pr,sh(gl.FRAGMENT_SHADER,FRAG));gl.linkProgram(pr);
|
|
if(!gl.getProgramParameter(pr,gl.LINK_STATUS))throw gl.getProgramInfoLog(pr);
|
|
gl.useProgram(pr);
|
|
const uRes=gl.getUniformLocation(pr,'u_res'),uTime=gl.getUniformLocation(pr,'u_time'),uMouse=gl.getUniformLocation(pr,'u_mouse');
|
|
let mouse=[0.5,0.5],target=[0.5,0.5];
|
|
addEventListener('pointermove',e=>{target=[e.clientX/innerWidth,1.0-e.clientY/innerHeight];});
|
|
function resize(){const d=Math.min(devicePixelRatio||1,2),w=innerWidth*d|0,h=innerHeight*d|0;if(w===canvas.width&&h===canvas.height)return;canvas.width=w;canvas.height=h;gl.viewport(0,0,w,h);}
|
|
addEventListener('resize',resize);resize();
|
|
const t0=performance.now();
|
|
function frame(now){ resize(); mouse[0]+=(target[0]-mouse[0])*0.05; mouse[1]+=(target[1]-mouse[1])*0.05;
|
|
gl.uniform2f(uRes,canvas.width,canvas.height); gl.uniform1f(uTime,(now-t0)/1000); gl.uniform2f(uMouse,mouse[0],mouse[1]);
|
|
gl.drawArrays(gl.TRIANGLES,0,3); requestAnimationFrame(frame); }
|
|
requestAnimationFrame(frame);
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|