Fix release runtime preparation

This commit is contained in:
coso
2026-07-18 13:02:08 +08:00
parent 7fb6664158
commit 8d66154060
10 changed files with 386 additions and 109 deletions
+7
View File
@@ -271,6 +271,13 @@ jobs:
echo "LIME_WINDOWS_SIGNING_CERTIFICATE_FILE=$CERTIFICATE_PATH" >> "$GITHUB_ENV"
echo "LIME_WINDOWS_SIGNING_CERTIFICATE_PASSWORD=$WINDOWS_SIGNING_CERTIFICATE_PASSWORD" >> "$GITHUB_ENV"
- name: Prepare sherpa-onnx runtime
shell: bash
run: |
set -euo pipefail
node scripts/prepare-sherpa-onnx-runtime.mjs \
--target "${{ matrix.target }}"
- name: Build Electron app
shell: bash
env:
+1
View File
@@ -16,6 +16,7 @@
- Fixed inconsistent loading, error, disconnect, close, and retry states in Settings and Browser Runtime, and removed the retired Chrome Relay guide path from the current product chain.
- Fixed re-entry boundaries in Electron updater, App Server host, and Windows startup paths, with deterministic configuration for unsigned macOS development packaging.
- Fixed a Windows Squirrel N-1 smoke navigation race that treated the preload-enabled startup page as the final renderer; packaged launches now also remove unsupported `NODE_OPTIONS` and the development-server URL.
- Fixed Electron Release workflow builds when the Rust cache omits sherpa-onnx prebuilt libraries; shared runtime libraries are now prepared explicitly for each target before packaging.
### Improvements and Refactors
+1
View File
@@ -14,6 +14,7 @@
- 修复设置页和 Browser Runtime 在加载、错误、断开、关闭与重试状态下的文案和控件不一致问题,并移除已脱离 current 主链的旧 Chrome Relay guide 双轨。
- 修复 Electron updater、App Server host 和 Windows 启动路径的并发/重入边界,补齐 macOS 未签名开发打包的确定性配置。
- 修复 Windows Squirrel N-1 smoke 将带 preload 的临时启动页误判为最终 renderer 的导航竞态;打包启动环境同时移除不受支持的 `NODE_OPTIONS` 与开发服务器地址。
- 修复 Electron Release workflow 在 Rust cache 未恢复 sherpa-onnx 预编译库时的构建失败;构建前会按目标平台显式准备共享运行库。
### 优化与重构
+8 -5
View File
@@ -1,6 +1,6 @@
# Lime v1.107.0 发布执行计划
状态:validated-ready-to-cover-release
状态:validated-ready-to-rerun-release
日期:2026-07-18
目标版本:`1.107.0`
目标 tag`v1.107.0`
@@ -12,7 +12,7 @@
## Release Candidate
- 基线:`v1.106.0`;任务开始时 `main``origin/main` 与该 tag 指向同一提交,目标 tag 在本地不存在。
- 初始候选盘点包含 213 个已跟踪改动和 72 个未跟踪文件,共 285 个候选路径;本轮追加 Windows Squirrel N-1 导航竞态修复、回归测试及首页皮肤资源,仍无排除项。
- 初始候选盘点包含 213 个已跟踪改动和 72 个未跟踪文件,共 285 个候选路径;本轮追加 Windows Squirrel N-1 导航竞态修复、Release sherpa-onnx 预编译库预热、回归测试及首页皮肤资源,仍无排除项。
- `release metadata``package.json``packages/lime-cli-npm/package.json``lime-rs/Cargo.toml``lime-rs/Cargo.lock``RELEASE_NOTES.md``RELEASE_NOTES.en.md` 与本计划。
- `candidate changes`:当前工作树全部产品、测试、脚本、schema、资源、治理和文档改动,无排除项。
- `excluded changes`:无。
@@ -34,7 +34,8 @@
- Release candidate 已冻结,纳入当前全部候选路径,无排除项;用户已明确要求覆盖 `v1.107.0` 并递交全部当前改动。
- 版本事实源与双语 release notes 已更新,并补记 Windows startup-page navigation race hotfix。
- 版本事实源、合同、Rust、Agent fixture、GUI smoke 与 Windows Squirrel 定向回归均已通过;Git staging、commit、tag 与 push 待执行
- 首轮覆盖提交 `7fb666415` 已完成并推送;Release workflow `29631194668` 因三平台构建时缺少 sherpa-onnx 预编译库失败,未进入 Windows N-1 smoke
- 已加入 Release 构建前按目标显式准备 sherpa-onnx 运行库的 workflow guard;本轮定向回归通过,待重新覆盖提交、tag 和 workflow。
## 验证记录
@@ -46,8 +47,10 @@
- `npm run verify:gui-smoke`:通过;renderer、Desktop Host、preload、App Server sidecar、Workbench、Memory Settings 与 reload 主路径可用,报告 `appserver.v0 / 1.107.0`
- `git diff --check`:通过。
- `npx vitest run scripts/electron/windows-squirrel-rc-smoke.test.mjs scripts/electron/release-workflow-guard.test.mjs scripts/electron/current-entrypoints.test.mjs`:通过,3 files / 52 tests。
- 更新 workflow 后 `npx vitest run scripts/electron/release-workflow-guard.test.mjs scripts/electron/windows-squirrel-rc-smoke.test.mjs scripts/electron/current-entrypoints.test.mjs src/components/agent/chat/components/EmptyStateLayout.test.tsx src/components/agent/chat/components/homeSkinPresentation.test.ts`:通过,5 files / 62 tests。
- 两个 Windows Squirrel 修改脚本 `node --check`:通过。
- 真实 Windows packaged L8/N-1 验证:待新的远端 Release workflow 执行;本机为 macOS,无法代替 Windows 证据
- 首轮远端 Release `29631194668`:失败于三平台 `app-server` 链接;Windows `sherpa-onnx-c-api.lib` 缺失,macOS `sherpa-onnx-c-api` 共享库目录缺失,未执行 N-1
- 真实 Windows packaged L8/N-1 验证:待修复后的远端 Release workflow 执行;本机为 macOS,无法代替 Windows 证据。
## 架构确认
@@ -56,4 +59,4 @@
- 责任人:release ownerv1.107.0)。
- 日期:2026-07-18。
当前完成度:`97%`。下一刀:`git add -A` 后复核 staged 摘要,创建覆盖提交,移动并强推 `v1.107.0`,再监控远端 Windows Release workflow 与资产
当前完成度:`94%`。下一刀:纳入提交后新增的首页皮肤改动与 sherpa workflow 修复,覆盖提交和 `v1.107.0`,再监控 Release workflow 至 Windows N-1 终态
@@ -318,6 +318,14 @@ function assertBuildSteps(buildJob) {
const buildStep = stepByName(steps, "Build Electron app");
const buildRun = buildStep?.run || "";
const sherpaStep = stepByName(steps, "Prepare sherpa-onnx runtime");
const sherpaRun = sherpaStep?.run || "";
for (const required of [
"scripts/prepare-sherpa-onnx-runtime.mjs",
'--target "${{ matrix.target }}"',
]) {
assertIncludes(sherpaRun, required, "sherpa-onnx runtime preparation");
}
for (const required of [
"npm run electron:build",
"npx electron-forge package",
@@ -132,6 +132,20 @@ describe("Electron release workflow guard", () => {
);
});
it("rejects release workflow without sherpa-onnx runtime preparation", () => {
const current = fs.readFileSync(".github/workflows/release.yml", "utf8");
const workflowPath = tempWorkflowPath(
current.replace(
/ - name: Prepare sherpa-onnx runtime[\s\S]*? --target "\$\{\{ matrix\.target \}\}"\n\n/,
"",
),
);
expect(() => validateReleaseWorkflow({ workflowPath })).toThrow(
/sherpa-onnx runtime preparation.*scripts\/prepare-sherpa-onnx-runtime\.mjs/,
);
});
it("rejects missing macOS transient package retry in Forge package step", () => {
const current = fs.readFileSync(".github/workflows/release.yml", "utf8");
const workflowPath = tempWorkflowPath(
@@ -15,55 +15,209 @@ const heroReveal = keyframes`
}
`;
const foregroundFloat = keyframes`
const portraitBreathe = keyframes`
0%,
100% {
transform: translate3d(0, 0, 0) rotate(0deg);
transform: translate3d(0, 0, 0) scale(1);
}
50% {
transform: translate3d(0, calc(var(--home-hero-motion-distance, 12px) * -1), 0)
rotate(var(--home-hero-motion-rotate, 0deg));
transform: translate3d(
0,
calc(var(--home-hero-motion-distance, 8px) * -0.35),
0
)
scale(var(--home-hero-motion-scale, 1.02));
}
`;
const foregroundSway = keyframes`
const foliageSway = keyframes`
0%,
100% {
transform: translate3d(0, 0, 0) rotate(0deg);
transform: rotate(calc(var(--home-hero-motion-rotate, 2deg) * -0.35));
}
50% {
transform: translate3d(calc(var(--home-hero-motion-distance, 12px) * -1), 0, 0)
rotate(var(--home-hero-motion-rotate, 0deg));
transform: translate3d(
calc(var(--home-hero-motion-distance, 8px) * -0.25),
0,
0
)
rotate(var(--home-hero-motion-rotate, 2deg));
}
`;
const foregroundPulse = keyframes`
const cheerBob = keyframes`
0%,
100% {
transform: translate3d(0, 0, 0) scale(1);
}
42% {
transform: translate3d(
0,
calc(var(--home-hero-motion-distance, 10px) * -1),
0
)
scale(1.01, 0.985);
}
54% {
transform: translate3d(
0,
calc(var(--home-hero-motion-distance, 10px) * -0.55),
0
)
scale(0.995, var(--home-hero-motion-scale, 1.02));
}
`;
const powerPulse = keyframes`
0%,
100% {
transform: scale(1);
opacity: 1;
filter: brightness(1) drop-shadow(0 0 0 transparent);
}
50% {
transform: scale(var(--home-hero-motion-scale, 1.04));
opacity: 0.94;
transform: scale(var(--home-hero-motion-scale, 1.035));
filter: brightness(1.08)
drop-shadow(
var(--home-hero-motion-glow, 0 0 16px rgba(73, 222, 255, 0.42))
);
}
`;
const foregroundDrift = keyframes`
const silkDrift = keyframes`
0%,
100% {
transform: translate3d(0, 0, 0) rotate(0deg);
}
50% {
transform: translate3d(
calc(var(--home-hero-motion-distance, 12px) * -1),
calc(var(--home-hero-motion-distance, 12px) * -0.55),
calc(var(--home-hero-motion-distance, 10px) * -1),
calc(var(--home-hero-motion-distance, 10px) * -0.3),
0
)
rotate(var(--home-hero-motion-rotate, 0deg));
}
`;
const stageBeat = keyframes`
0%,
58%,
100% {
transform: translate3d(0, 0, 0) rotate(0deg);
}
45% {
transform: translate3d(
0,
calc(var(--home-hero-motion-distance, 12px) * -1),
0
)
rotate(calc(var(--home-hero-motion-rotate, 0.8deg) * -1));
}
52% {
transform: translate3d(
0,
calc(var(--home-hero-motion-distance, 12px) * -0.4),
0
)
rotate(calc(var(--home-hero-motion-rotate, 0.8deg) * 0.35));
}
`;
const creativeTilt = keyframes`
0%,
100% {
transform: rotate(calc(var(--home-hero-motion-rotate, 1.4deg) * -0.3));
}
50% {
transform: translate3d(
calc(var(--home-hero-motion-distance, 9px) * -0.5),
calc(var(--home-hero-motion-distance, 9px) * -0.6),
0
)
rotate(var(--home-hero-motion-rotate, 1.4deg));
}
`;
const streetBeat = keyframes`
0%,
100% {
transform: translate3d(0, 0, 0) rotate(0deg);
}
32% {
transform: translate3d(
0,
calc(var(--home-hero-motion-distance, 11px) * -1),
0
)
rotate(calc(var(--home-hero-motion-rotate, 1.8deg) * -0.8));
}
42% {
transform: translate3d(
0,
calc(var(--home-hero-motion-distance, 11px) * -0.3),
0
)
rotate(calc(var(--home-hero-motion-rotate, 1.8deg) * 0.35));
}
68% {
transform: translate3d(
calc(var(--home-hero-motion-distance, 11px) * -0.45),
calc(var(--home-hero-motion-distance, 11px) * -0.7),
0
)
rotate(var(--home-hero-motion-rotate, 1.8deg));
}
`;
const spotlightBreathe = keyframes`
0%,
100% {
transform: scale(1);
filter: brightness(1) drop-shadow(0 0 0 transparent);
}
50% {
transform: scale(var(--home-hero-motion-scale, 1.018));
filter: brightness(1.06)
drop-shadow(
var(--home-hero-motion-glow, 0 0 14px rgba(255, 220, 176, 0.26))
);
}
`;
const portalPulse = keyframes`
0%,
100% {
transform: scale(1);
filter: brightness(0.96) drop-shadow(0 0 0 transparent);
}
50% {
transform: scale(var(--home-hero-motion-scale, 1.04));
filter: brightness(1.16)
drop-shadow(
var(--home-hero-motion-glow, 0 0 20px rgba(146, 194, 255, 0.46))
);
}
`;
const cityGlide = keyframes`
0%,
100% {
transform: translate3d(0, 0, 0) scale(1);
filter: brightness(1) drop-shadow(0 0 0 transparent);
}
50% {
transform: translate3d(
calc(var(--home-hero-motion-distance, 14px) * -1),
0,
0
)
scale(var(--home-hero-motion-scale, 1.012));
filter: brightness(1.06)
drop-shadow(
var(--home-hero-motion-glow, 0 0 16px rgba(229, 53, 69, 0.3))
);
}
`;
const HeroSection = styled.section`
position: relative;
display: flex;
@@ -291,32 +445,69 @@ const ArtworkForeground = styled.div`
width: var(--home-hero-foreground-width, 0);
overflow: visible;
pointer-events: none;
transform-origin: 70% 82%;
will-change: transform;
transform-origin: var(--home-hero-motion-origin, 50% 85%);
will-change: transform, filter;
&[data-home-hero-motion="float"] {
animation: ${foregroundFloat} var(--home-hero-motion-duration, 8s)
ease-in-out var(--home-hero-motion-delay, 0s) infinite;
&:not([data-home-hero-motion="none"]) {
animation-duration: var(--home-hero-motion-duration, 8s);
animation-timing-function: ease-in-out;
animation-delay: var(--home-hero-motion-delay, 0s);
animation-iteration-count: infinite;
animation-direction: var(--home-hero-motion-direction, normal);
animation-fill-mode: both;
}
&[data-home-hero-motion="sway"] {
animation: ${foregroundSway} var(--home-hero-motion-duration, 8s)
ease-in-out var(--home-hero-motion-delay, 0s) infinite;
&[data-home-hero-motion="portrait-breathe"] {
animation-name: ${portraitBreathe};
}
&[data-home-hero-motion="pulse"] {
animation: ${foregroundPulse} var(--home-hero-motion-duration, 8s)
ease-in-out var(--home-hero-motion-delay, 0s) infinite;
&[data-home-hero-motion="foliage-sway"] {
animation-name: ${foliageSway};
}
&[data-home-hero-motion="drift"] {
animation: ${foregroundDrift} var(--home-hero-motion-duration, 8s)
ease-in-out var(--home-hero-motion-delay, 0s) infinite;
&[data-home-hero-motion="cheer-bob"] {
animation-name: ${cheerBob};
animation-timing-function: cubic-bezier(0.34, 1.28, 0.64, 1);
}
&[data-home-hero-motion="power-pulse"] {
animation-name: ${powerPulse};
}
&[data-home-hero-motion="silk-drift"] {
animation-name: ${silkDrift};
}
&[data-home-hero-motion="stage-beat"] {
animation-name: ${stageBeat};
animation-timing-function: cubic-bezier(0.45, 0, 0.2, 1);
}
&[data-home-hero-motion="creative-tilt"] {
animation-name: ${creativeTilt};
}
&[data-home-hero-motion="street-beat"] {
animation-name: ${streetBeat};
animation-timing-function: cubic-bezier(0.34, 1.12, 0.64, 1);
}
&[data-home-hero-motion="spotlight-breathe"] {
animation-name: ${spotlightBreathe};
}
&[data-home-hero-motion="portal-pulse"] {
animation-name: ${portalPulse};
}
&[data-home-hero-motion="city-glide"] {
animation-name: ${cityGlide};
}
@media (prefers-reduced-motion: reduce) {
animation: none !important;
will-change: auto;
filter: none;
}
img {
@@ -552,6 +743,9 @@ export function EmptyStateHero({
"--home-hero-motion-distance": foregroundMotion?.distance ?? "12px",
"--home-hero-motion-rotate": foregroundMotion?.rotate ?? "0deg",
"--home-hero-motion-scale": foregroundMotion?.scale ?? "1.04",
"--home-hero-motion-origin": foregroundMotion?.origin ?? "50% 85%",
"--home-hero-motion-direction": foregroundMotion?.direction ?? "normal",
"--home-hero-motion-glow": foregroundMotion?.glow ?? "0 0 0 transparent",
"--home-hero-breakout-space": presentation.breakoutSpace ?? "0",
"--home-hero-breakout-space-mobile":
presentation.breakoutSpaceMobile ?? "0",
@@ -100,7 +100,7 @@ describe("EmptyStateLayout", () => {
artworkStage
?.querySelector('[data-testid="home-hero-foreground"]')
?.getAttribute("data-home-hero-motion"),
).toBe("float");
).toBe("portrait-breathe");
expect(
artworkStage?.style.getPropertyValue("--home-hero-motion-duration"),
).toBe("4.8s");
@@ -135,7 +135,7 @@ describe("EmptyStateLayout", () => {
artworkStage
?.querySelector('[data-testid="home-hero-foreground"]')
?.getAttribute("data-home-hero-motion"),
).toBe("pulse");
).toBe("power-pulse");
expect(
artworkStage?.style.getPropertyValue("--home-hero-art-blend-width"),
).toBe("68%");
@@ -24,7 +24,7 @@ describe("homeSkinPresentation", () => {
expect(presentation.stageHeightWide).toBe("430px");
expect(presentation.foreground?.top).toBe("-56px");
expect(presentation.breakoutSpace).toBe("68px");
expect(presentation.foreground?.motion?.kind).toBe("float");
expect(presentation.foreground?.motion?.kind).toBe("portrait-breathe");
expect(presentation.foreground?.motion?.duration).toBe("4.8s");
});
@@ -42,6 +42,12 @@ describe("homeSkinPresentation", () => {
"100% center",
);
expect(getHomeSkinPresentation("lime-forest").artFit).toBe("contain");
expect(getHomeSkinPresentation("lime-forest").foreground?.width).toBe(
"43%",
);
expect(getHomeSkinPresentation("lime-forest").foreground?.bottom).toBe(
"-2px",
);
expect(getHomeSkinPresentation("lime-forest").artBlendWidth).toBe("68%");
expect(getHomeSkinPresentation("lime-luxury").artBlendWidth).toBe("68%");
expect(getHomeSkinPresentation("lime-ocean").image).toContain(
@@ -83,6 +89,12 @@ describe("homeSkinPresentation", () => {
expect(getHomeSkinPresentation("lime-luxury").image).toContain(
"lime-luxury-hero.png",
);
expect(getHomeSkinPresentation("lime-luxury").foreground?.width).toBe(
"43%",
);
expect(getHomeSkinPresentation("lime-luxury").foreground?.bottom).toBe(
"-1px",
);
});
it("每套皮肤都应使用独立 Hero 素材", () => {
@@ -153,7 +165,7 @@ describe("homeSkinPresentation", () => {
).toBe(true);
expect(
new Set(foregrounds.map((foreground) => foreground?.motion?.kind)).size,
).toBe(4);
).toBe(11);
});
it("hero 外框和阴影应跟随皮肤色调", () => {
@@ -61,7 +61,18 @@ export type HomeKeepsakeVariant =
| "medallion"
| "bookmark";
export type HomeHeroMotionKind = "float" | "sway" | "pulse" | "drift";
export type HomeHeroMotionKind =
| "portrait-breathe"
| "foliage-sway"
| "cheer-bob"
| "power-pulse"
| "silk-drift"
| "stage-beat"
| "creative-tilt"
| "street-beat"
| "spotlight-breathe"
| "portal-pulse"
| "city-glide";
export interface HomeHeroMotion {
kind: HomeHeroMotionKind;
@@ -70,6 +81,9 @@ export interface HomeHeroMotion {
distance?: string;
rotate?: string;
scale?: string;
origin?: string;
direction?: "normal" | "reverse";
glow?: string;
}
export interface HomeHeroForeground {
@@ -177,11 +191,12 @@ const HOME_SKIN_PRESENTATIONS: Record<
right: "4%",
rightMobile: "1%",
motion: {
kind: "float",
kind: "portrait-breathe",
duration: "4.8s",
delay: "-1.2s",
distance: "13px",
rotate: "1.2deg",
distance: "8px",
scale: "1.025",
origin: "65% 88%",
},
},
heroDecorations: {
@@ -225,10 +240,11 @@ const HOME_SKIN_PRESENTATIONS: Record<
transform: "translate(2%, 2%) scale(0.94)",
transformMobile: "translate(2%, 2%) scale(1.02)",
motion: {
kind: "sway",
duration: "5.6s",
distance: "12px",
rotate: "2.2deg",
kind: "foliage-sway",
duration: "5.8s",
distance: "8px",
rotate: "2.4deg",
origin: "44% 100%",
},
},
heroDecorations: { trailingIcon: "leaf" },
@@ -259,20 +275,23 @@ const HOME_SKIN_PRESENTATIONS: Record<
image: limeForestHero,
foreground: {
image: limeForestForeground,
width: "39%",
widthMobile: "54%",
top: "-28px",
topMobile: "-14px",
right: "1%",
rightMobile: "-4%",
transform: "scale(1.08)",
transformMobile: "scale(1.16)",
width: "43%",
widthMobile: "58%",
top: "auto",
topMobile: "auto",
right: "-2%",
rightMobile: "-9%",
bottom: "-2px",
bottomMobile: "-1px",
transform: "scale(1.04)",
transformMobile: "scale(1.08)",
motion: {
kind: "sway",
duration: "4.4s",
kind: "cheer-bob",
duration: "3.6s",
delay: "-2.4s",
distance: "10px",
rotate: "1.3deg",
distance: "11px",
scale: "1.025",
origin: "55% 100%",
},
},
heroDecorations: { trailingIcon: "coins" },
@@ -304,18 +323,22 @@ const HOME_SKIN_PRESENTATIONS: Record<
image: limeOceanHero,
foreground: {
image: limeOceanForeground,
width: "34%",
widthMobile: "47%",
top: "-58px",
topMobile: "-32px",
right: "-1%",
rightMobile: "-6%",
transform: "translate(2%, 2%) scale(0.98)",
transformMobile: "translate(2%, 2%) scale(1.06)",
width: "40%",
widthMobile: "56%",
top: "auto",
topMobile: "auto",
right: "-3%",
rightMobile: "-10%",
bottom: "-2px",
bottomMobile: "-1px",
transform: "translate(2%, 0) scale(0.98)",
transformMobile: "translate(2%, 0) scale(1.02)",
motion: {
kind: "pulse",
duration: "4.2s",
scale: "1.04",
kind: "power-pulse",
duration: "4.6s",
scale: "1.035",
origin: "50% 86%",
glow: "0 0 18px rgba(50, 238, 255, 0.48)",
},
},
heroDecorations: { trailingIcon: "shield" },
@@ -356,11 +379,12 @@ const HOME_SKIN_PRESENTATIONS: Record<
transform: "scale(1.04)",
transformMobile: "scale(1.1)",
motion: {
kind: "float",
duration: "5.6s",
kind: "silk-drift",
duration: "6.8s",
delay: "-2.1s",
distance: "12px",
rotate: "1deg",
distance: "10px",
rotate: "0.7deg",
origin: "60% 92%",
},
},
heroDecorations: { trailingIcon: "sun" },
@@ -401,11 +425,12 @@ const HOME_SKIN_PRESENTATIONS: Record<
transform: "scale(1.05)",
transformMobile: "scale(1.14)",
motion: {
kind: "sway",
duration: "3.8s",
kind: "stage-beat",
duration: "3.2s",
delay: "-0.8s",
distance: "15px",
rotate: "2deg",
distance: "13px",
rotate: "0.7deg",
origin: "50% 100%",
},
},
heroDecorations: { trailingIcon: "music" },
@@ -446,11 +471,12 @@ const HOME_SKIN_PRESENTATIONS: Record<
transform: "scale(1.04)",
transformMobile: "scale(1.12)",
motion: {
kind: "float",
duration: "4.2s",
kind: "creative-tilt",
duration: "4.6s",
delay: "-1.7s",
distance: "14px",
distance: "9px",
rotate: "1.5deg",
origin: "55% 95%",
},
},
heroDecorations: { trailingIcon: "lightbulb" },
@@ -491,11 +517,12 @@ const HOME_SKIN_PRESENTATIONS: Record<
transform: "scale(1.08)",
transformMobile: "scale(1.14)",
motion: {
kind: "drift",
duration: "5.2s",
kind: "spotlight-breathe",
duration: "6s",
delay: "-3.1s",
distance: "10px",
rotate: "1.2deg",
scale: "1.018",
origin: "50% 90%",
glow: "0 0 16px rgba(232, 185, 116, 0.3)",
},
},
heroDecorations: { trailingIcon: "mic" },
@@ -536,10 +563,12 @@ const HOME_SKIN_PRESENTATIONS: Record<
transform: "translate(3%, 2%) scale(0.94)",
transformMobile: "translate(3%, 2%) scale(1.04)",
motion: {
kind: "pulse",
duration: "5.8s",
kind: "portal-pulse",
duration: "5.4s",
delay: "-2.8s",
scale: "1.055",
scale: "1.04",
origin: "50% 58%",
glow: "0 0 20px rgba(146, 194, 255, 0.48)",
},
},
heroDecorations: { trailingIcon: "circle" },
@@ -579,11 +608,12 @@ const HOME_SKIN_PRESENTATIONS: Record<
transform: "scale(1.05)",
transformMobile: "scale(1.14)",
motion: {
kind: "sway",
duration: "4.2s",
kind: "street-beat",
duration: "3.7s",
delay: "-1.5s",
distance: "14px",
distance: "11px",
rotate: "1.8deg",
origin: "50% 100%",
},
},
heroDecorations: { trailingIcon: "palette" },
@@ -624,11 +654,12 @@ const HOME_SKIN_PRESENTATIONS: Record<
transform: "scale(1.06)",
transformMobile: "scale(1.12)",
motion: {
kind: "float",
duration: "6s",
kind: "portrait-breathe",
duration: "6.2s",
delay: "-4s",
distance: "10px",
rotate: "0.8deg",
distance: "5px",
scale: "1.012",
origin: "55% 100%",
},
},
heroDecorations: { trailingIcon: "book-open" },
@@ -660,20 +691,23 @@ const HOME_SKIN_PRESENTATIONS: Record<
image: limeLuxuryHero,
foreground: {
image: limeLuxuryForeground,
width: "31%",
widthMobile: "46%",
top: "-36px",
topMobile: "-22px",
right: "2%",
rightMobile: "-6%",
transform: "translate(2%, 0) scale(1)",
transformMobile: "translate(2%, 0) scale(1.04)",
width: "43%",
widthMobile: "58%",
top: "auto",
topMobile: "auto",
right: "-2%",
rightMobile: "-10%",
bottom: "-1px",
bottomMobile: "-1px",
transform: "translate(1%, 0) scale(1)",
transformMobile: "translate(1%, 0) scale(1.02)",
motion: {
kind: "drift",
duration: "6.2s",
kind: "spotlight-breathe",
duration: "6.6s",
delay: "-2.6s",
distance: "9px",
rotate: "0.8deg",
scale: "1.02",
origin: "50% 100%",
glow: "0 0 18px rgba(131, 117, 255, 0.34)",
},
},
heroDecorations: { trailingIcon: "gem" },
@@ -714,10 +748,13 @@ const HOME_SKIN_PRESENTATIONS: Record<
transform: "translate(-1%, 3%) scale(0.92)",
transformMobile: "translate(-1%, 3%) scale(1.02)",
motion: {
kind: "pulse",
duration: "4.8s",
kind: "city-glide",
duration: "8s",
delay: "-1.9s",
scale: "1.04",
distance: "14px",
scale: "1.012",
origin: "50% 80%",
glow: "0 0 16px rgba(229, 53, 69, 0.3)",
},
},
heroDecorations: { trailingIcon: "orbit" },