oil-ui-pro 0.15.4 → 0.22.2(上游 ui.oiloil.org 正式包)+ 记 P0-98 跨盘 rename 坑

- oil-ui-pro 升到 0.22.2:references 13→28 份(新增 16、删 imagery.md)、assets 增 cards/(22 组)与 fonts/、SKILL.md 顶层五步结构未改名。
- 升级路径:官方 `npx github:oil-oil/oil-cli update` 稳定失败(error=replace、报「检查目录权限」)。
  真身为 EXDEV —— 解压在 os.tmpdir()(C 盘)、技能目录在 E 盘,Windows 上 rename 不能跨盘;
  CLI 的 catch 把非 CliError 原生异常一律兜底成 replaceFailed,真错误一个字不吐。
  做法=借其 prepare() 只做下载+校验,再 cp 到同盘 stage、rename 交换;官方命令复验 up_to_date。
- 回归:product-planning 按名引用的 visual-review.md/tools.md/design-direction.md/style-explorer.md 全部健在;命名闸门 2061|0|27。
- session-mechanism/references/pitfalls.md 新增 P0-98(含通则:更新器把原生错误兜底成指错方向的人话)。
This commit is contained in:
admin committed 2026-10-14 10:21:46 +08:00
1 parent 053a6a6723
commit d9445b5888
126 files changed
+10345 -1401

No files matched your search

@@ -0,0 +1,12 @@
.vector-space {
height:440px; aspect-ratio:auto; --_card-stage-track-min:min-content;
& .card-stage { display:flex; flex-direction:column; justify-content:space-between; gap:100px; overflow:clip; background:radial-gradient(ellipse at 63% 52% in oklab,color-mix(in oklab,var(--card-accent) 5%,transparent),transparent 70%); }
& .vector-canvas,& .vector-fallback { position:absolute; width:100%; height:100%; inset:0; }
&[data-vector-ready="true"] .vector-fallback { display:none; }
& .vector-query { display:flex; align-items:center; gap:9px; position:relative; z-index:2; align-self:flex-start; margin:4px 24px 0; max-width:calc(100% - 48px); padding:9px 13px; border:1px solid var(--card-line); border-radius:6px; background:var(--card-panel); font-size:12px; box-shadow:0 3px 9px #00000003; }
& .vector-query span { min-width:0; }
& .vector-query svg { flex:none; width:14px; height:14px; fill:none; stroke:var(--card-muted); stroke-width:1.4; }
& .vector-result { display:flex; align-items:center; gap:7px; flex-wrap:wrap; position:relative; z-index:2; margin:0 24px 21px; font-size:11px; }
& .vector-result i { flex:none; width:5px; height:5px; border-radius:50%; background:var(--card-accent); }
& .vector-tag { margin-left:auto; color:var(--card-muted); }
}
@@ -0,0 +1,10 @@
<article class="card vector-space" data-card="vector-space" data-motion="auto">
<h2 class="card-heading" data-text="title">从海量信息中,<br>找到彼此的关联</h2>
<div class="card-stage">
<div class="vector-query"><svg viewBox="0 0 20 20" aria-hidden="true"><circle cx="8" cy="8" r="5"/><path d="m12 12 5 5"/></svg><span data-text="query">自然的灵感</span></div>
<svg class="vector-fallback" viewBox="0 0 400 300" aria-hidden="true"><g fill="none" stroke="currentColor" stroke-width=".8" opacity=".3"><path d="m200 55 107 58v122l-107 58-107-58V113Z M93 113l107 58 107-58 M200 171v122 M200 55v116"/></g><g fill="var(--card-accent)"><circle cx="195" cy="179" r="5"/><circle cx="228" cy="161" r="5"/><circle cx="212" cy="197" r="5"/><circle cx="250" cy="185" r="5"/></g></svg>
<canvas class="vector-canvas" aria-hidden="true"></canvas>
<div class="vector-result"><i></i><span data-text="result">4 个相关结果</span><span class="vector-tag" data-text="mode">语义搜索</span></div>
</div>
</article>
@@ -0,0 +1,41 @@
import {loop,shell,random,glProgram,fitCanvas,watchColors,mix,staticMotion} from '../shared/runtime.js';
export function mount(element, options = {}) {
const clean=shell(element,options);
const canvas=element.querySelector('canvas');
const vertex=`attribute vec3 a_position;attribute float a_selected;uniform vec2 u_size,u_rotation;uniform float u_dpr,u_halo;varying float v_selected,v_depth;
void main(){float c=cos(u_rotation.x),s=sin(u_rotation.x);vec3 p=a_position;p.xz=mat2(c,-s,s,c)*p.xz;c=cos(u_rotation.y);s=sin(u_rotation.y);p.yz=mat2(c,-s,s,c)*p.yz;
float perspective=1./(1.+p.z*.24);v_depth=perspective;v_selected=a_selected;
gl_Position=vec4(p.x*perspective*.94/(u_size.x/u_size.y),p.y*perspective*.84-.02,0.,1.);
gl_PointSize=mix(2.5,7.,a_selected)*u_dpr*perspective+u_halo*a_selected*15.*u_dpr;}`;
const fragment=`precision highp float;uniform vec3 u_accent,u_ink;uniform float u_points,u_halo,u_lineAlpha;varying float v_selected,v_depth;
void main(){float a=1.;if(u_points>.5){float r=length(gl_PointCoord-.5)*2.;a=1.-smoothstep(.65,1.,r);if(u_halo>.5){a=exp(-r*r*5.)*.23*v_selected;}}
vec3 color=mix(u_ink,u_accent,v_selected);a*=u_points>.5?mix(.18,.95,v_selected)*v_depth:u_lineAlpha;
gl_FragColor=vec4(color*a,a);}`;
const gpu=glProgram(canvas,vertex,fragment),rng=random(29),positions=[],selected=[];
const add=(v,s=0)=>{positions.push(...v);selected.push(s);};
const corners=Array.from({length:8},(_,i)=>[(i&1?.65:-.65),(i&2?.65:-.65),(i&4?.65:-.65)]);
for(let i=0;i<8;i++)for(const bit of [1,2,4])if(!(i&bit)){add(corners[i]);add(corners[i|bit]);}
const related=[[-.08,-.06,.28],[.12,.04,.35],[.29,-.12,.23],[.08,-.27,.29]];
for(let i=0;i<3;i++){add(related[i],1);add(related[i+1],1);}
for(let i=0;i<140;i++)add([(rng()-.5)*2.1,(rng()-.5)*1.75,(rng()-.5)*1.8]);
related.forEach(v=>add(v,1));
if(gpu){gpu.attribute('a_position',positions,3);gpu.attribute('a_selected',selected,1);element.dataset.vectorReady='true';}
else canvas.hidden=true;
let palette,tx=0,ty=0,rx=0,ry=0,lost=false;
const still=staticMotion(element,options);
const draw=(time,dt)=>{if(!gpu||!palette||lost)return;if(still())rx=ry=tx=ty=0;if(dt){rx=mix(rx,tx,1-Math.exp(-dt*2.5));ry=mix(ry,ty,1-Math.exp(-dt*2.5));}const {w,h,dpr}=fitCanvas(canvas);const {gl}=gpu;
gl.useProgram(gpu.program);gl.viewport(0,0,canvas.width,canvas.height);gl.clearColor(0,0,0,0);gl.clear(gl.COLOR_BUFFER_BIT);gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE_MINUS_SRC_ALPHA);
gl.uniform2f(gpu.uniform('u_size'),w,h);gl.uniform2f(gpu.uniform('u_rotation'),-.62+Math.sin(time*.12)*.06+rx,.35+ry);
gl.uniform1f(gpu.uniform('u_dpr'),dpr);gl.uniform3fv(gpu.uniform('u_accent'),palette.a);gl.uniform3fv(gpu.uniform('u_ink'),palette.dark?[.8,.84,.9]:[.18,.23,.3]);
gl.uniform1f(gpu.uniform('u_halo'),0);gl.uniform1f(gpu.uniform('u_points'),0);gl.uniform1f(gpu.uniform('u_lineAlpha'),.21);gl.drawArrays(gl.LINES,0,24);
gl.uniform1f(gpu.uniform('u_lineAlpha'),.32);gl.drawArrays(gl.LINES,24,6);
gl.uniform1f(gpu.uniform('u_points'),1);gl.uniform1f(gpu.uniform('u_halo'),1);gl.drawArrays(gl.POINTS,30,144);
gl.uniform1f(gpu.uniform('u_halo'),0);gl.drawArrays(gl.POINTS,30,144);
};
const clock=loop(element,draw,{...options,settled:()=>Math.abs(rx-tx)+Math.abs(ry-ty)<.001}),off=watchColors(element,p=>{palette=p;clock.invalidate();}),ro=new ResizeObserver(()=>clock.invalidate());ro.observe(canvas);
const move=e=>{if(e.pointerType==='touch' || still())return;const r=element.getBoundingClientRect();tx=((e.clientX-r.left)/r.width-.5)*.25;ty=((e.clientY-r.top)/r.height-.5)*.16;};
const leave=()=>{tx=ty=0;},loss=e=>{e.preventDefault();lost=true;canvas.hidden=true;delete element.dataset.vectorReady;};
element.addEventListener('pointermove',move);element.addEventListener('pointerleave',leave);element.addEventListener('pointercancel',leave);canvas.addEventListener('webglcontextlost',loss);
return{destroy(){clean();clock.destroy();off();ro.disconnect();gpu?.destroy();element.removeEventListener('pointermove',move);element.removeEventListener('pointerleave',leave);element.removeEventListener('pointercancel',leave);canvas.removeEventListener('webglcontextlost',loss);delete element.dataset.vectorReady;}};
}