Files
workbuddy_skills/oil-ui-pro/assets/cards/line-ribbons/card.js
T
admin d9445b5888 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(含通则:更新器把原生错误兜底成指错方向的人话)。
2026-10-14 10:21:46 +08:00

27 lines
2.2 KiB
JavaScript

import {shell,loop,svgStops} from '../shared/runtime.js';
import {createSurface} from './surface.js';
export function mount(element, options = {}) {
const clean=shell(element,options);
const svg=element.querySelector('.ribbon-art'),ns='http://www.w3.org/2000/svg';
// Unique IDs per instance, no shared counters or page-singleton assumptions.
const id='ribbon-'+crypto.randomUUID();
svg.innerHTML=`<defs><linearGradient id="${id}" x1="0" y1="1" x2="1" y2="0">${svgStops([[0.0,"var(--_card-accent-2)",1.0],[0.5,"var(--_card-accent-2)",1.0],[1.0,"var(--card-accent)",1.0]])}</linearGradient></defs>`;
const ribbons=[
[[-160,390,210,285,850,410,1350,-170],[-160,460,430,465,775,250,1390,-50]],
[[-170,455,400,540,800,50,1120,-210],[-170,310,490,640,855,250,1300,-40]],
[[280,650,650,180,1110,500,1420,60],[480,680,730,420,1150,80,1450,320]]
];
for(let r=0;r<ribbons.length;r++)for(let i=0;i<128;i++){
const t=(1-Math.cos(Math.PI*i/127))/2,[a,b]=ribbons[r],p=a.map((n,j)=>n+(b[j]-n)*t);
const path=document.createElementNS(ns,'path');path.setAttribute('d',`M${p[0]} ${p[1]}C${p.slice(2).join(' ')}`);path.setAttribute('fill','none');path.setAttribute('stroke',`url(#${id})`);path.setAttribute('stroke-width','.8');path.setAttribute('opacity',String(r===1?.64:.46));svg.append(path);
}
let surface=null,destroyed=false;
const frame=()=>svg.setAttribute('viewBox',element.clientWidth<=650?'100 60 560 474':'0 0 1280 474');
const framing=new ResizeObserver(frame);framing.observe(element);frame();
const clock=loop(element,t=>{if(surface)surface.draw(t);svg.style.transform=`translate(${Math.sin(t/17)*5}px,${Math.sin(t/19)*3}px)`;},options);
const setRenderer=value=>{if(destroyed)return null;surface?.destroy();surface=null;if(['webgl','dots','grid'].includes(value))surface=createSurface(element,ribbons,()=>clock.invalidate(),value);const actual=surface?value:'svg';element.dataset.renderer=actual;clock.invalidate();return actual;};
if(['webgl','dots','grid'].includes(element.dataset.renderer))setRenderer(element.dataset.renderer);
return {setRenderer,destroy(){if(destroyed)return;destroyed=true;clock.destroy();surface?.destroy();surface=null;framing.disconnect();clean();svg.replaceChildren();svg.style.removeProperty('transform');}};
}