feat: add child motion picture book stage tooling
Some checks failed
CI / verify (push) Has been cancelled
Some checks failed
CI / verify (push) Has been cancelled
This commit is contained in:
@@ -2897,6 +2897,86 @@ test('owned public puzzle detail edits original draft instead of remixing', asyn
|
||||
expect(getPuzzleAgentSession).toHaveBeenCalledWith('puzzle-session-1');
|
||||
expect(remixPuzzleGalleryWork).not.toHaveBeenCalled();
|
||||
expect(await screen.findByText('拼图结果页')).toBeTruthy();
|
||||
const generatingPuzzleDraft: PuzzleResultDraft = {
|
||||
workTitle: '暖灯猫街作品',
|
||||
workDescription: '一套雨夜猫街主题拼图。',
|
||||
levelName: '雨夜猫街',
|
||||
summary: '屋檐下的猫与暖灯街角。',
|
||||
themeTags: ['猫咪', '雨夜', '暖灯'],
|
||||
forbiddenDirectives: [],
|
||||
creatorIntent: null,
|
||||
anchorPack: {
|
||||
themePromise: {
|
||||
key: 'theme_promise',
|
||||
label: '主题承诺',
|
||||
value: '雨夜猫街',
|
||||
status: 'confirmed',
|
||||
},
|
||||
visualSubject: {
|
||||
key: 'visual_subject',
|
||||
label: '视觉主体',
|
||||
value: '屋檐下的猫',
|
||||
status: 'confirmed',
|
||||
},
|
||||
visualMood: {
|
||||
key: 'visual_mood',
|
||||
label: '视觉气质',
|
||||
value: '温暖',
|
||||
status: 'confirmed',
|
||||
},
|
||||
compositionHooks: {
|
||||
key: 'composition_hooks',
|
||||
label: '构图钩子',
|
||||
value: '雨滴与灯牌',
|
||||
status: 'confirmed',
|
||||
},
|
||||
tagsAndForbidden: {
|
||||
key: 'tags_and_forbidden',
|
||||
label: '标签与禁区',
|
||||
value: '猫咪、雨夜',
|
||||
status: 'confirmed',
|
||||
},
|
||||
},
|
||||
candidates: [
|
||||
{
|
||||
candidateId: 'candidate-1',
|
||||
imageSrc: '/puzzle/candidate-1.png',
|
||||
assetId: 'asset-1',
|
||||
prompt: '雨夜猫咪',
|
||||
actualPrompt: null,
|
||||
sourceType: 'generated',
|
||||
selected: true,
|
||||
},
|
||||
],
|
||||
selectedCandidateId: 'candidate-1',
|
||||
coverImageSrc: '/puzzle/candidate-1.png',
|
||||
coverAssetId: 'asset-1',
|
||||
generationStatus: 'generating',
|
||||
levels: [
|
||||
{
|
||||
levelId: 'puzzle-level-1',
|
||||
levelName: '雨夜猫街',
|
||||
pictureDescription: '屋檐下的猫与暖灯街角。',
|
||||
pictureReference: null,
|
||||
candidates: [
|
||||
{
|
||||
candidateId: 'candidate-1',
|
||||
imageSrc: '/puzzle/candidate-1.png',
|
||||
assetId: 'asset-1',
|
||||
prompt: '雨夜猫咪',
|
||||
actualPrompt: null,
|
||||
sourceType: 'generated',
|
||||
selected: true,
|
||||
},
|
||||
],
|
||||
selectedCandidateId: 'candidate-1',
|
||||
coverImageSrc: '/puzzle/candidate-1.png',
|
||||
coverAssetId: 'asset-1',
|
||||
generationStatus: 'generating',
|
||||
},
|
||||
],
|
||||
metadata: null,
|
||||
};
|
||||
|
||||
vi.mocked(executePuzzleAgentAction).mockResolvedValueOnce({
|
||||
operation: {
|
||||
@@ -2944,92 +3024,13 @@ test('owned public puzzle detail edits original draft instead of remixing', asyn
|
||||
status: 'confirmed',
|
||||
},
|
||||
},
|
||||
draft: {
|
||||
workTitle: '暖灯猫街作品',
|
||||
workDescription: '一套雨夜猫街主题拼图。',
|
||||
levelName: '雨夜猫街',
|
||||
summary: '屋檐下的猫与暖灯街角。',
|
||||
themeTags: ['猫咪', '雨夜', '暖灯'],
|
||||
forbiddenDirectives: [],
|
||||
creatorIntent: null,
|
||||
anchorPack: {
|
||||
themePromise: {
|
||||
key: 'theme_promise',
|
||||
label: '主题承诺',
|
||||
value: '雨夜猫街',
|
||||
status: 'confirmed',
|
||||
},
|
||||
visualSubject: {
|
||||
key: 'visual_subject',
|
||||
label: '视觉主体',
|
||||
value: '屋檐下的猫',
|
||||
status: 'confirmed',
|
||||
},
|
||||
visualMood: {
|
||||
key: 'visual_mood',
|
||||
label: '视觉气质',
|
||||
value: '温暖',
|
||||
status: 'confirmed',
|
||||
},
|
||||
compositionHooks: {
|
||||
key: 'composition_hooks',
|
||||
label: '构图钩子',
|
||||
value: '雨滴与灯牌',
|
||||
status: 'confirmed',
|
||||
},
|
||||
tagsAndForbidden: {
|
||||
key: 'tags_and_forbidden',
|
||||
label: '标签与禁区',
|
||||
value: '猫咪、雨夜',
|
||||
status: 'confirmed',
|
||||
},
|
||||
},
|
||||
candidates: [
|
||||
{
|
||||
candidateId: 'candidate-1',
|
||||
imageSrc: '/puzzle/candidate-1.png',
|
||||
assetId: 'asset-1',
|
||||
prompt: '雨夜猫咪',
|
||||
actualPrompt: null,
|
||||
sourceType: 'generated',
|
||||
selected: true,
|
||||
},
|
||||
],
|
||||
selectedCandidateId: 'candidate-1',
|
||||
coverImageSrc: '/puzzle/candidate-1.png',
|
||||
coverAssetId: 'asset-1',
|
||||
generationStatus: 'generating',
|
||||
levels: [
|
||||
{
|
||||
levelId: 'puzzle-level-1',
|
||||
levelName: '雨夜猫街',
|
||||
pictureDescription: '屋檐下的猫与暖灯街角。',
|
||||
pictureReference: null,
|
||||
candidates: [
|
||||
{
|
||||
candidateId: 'candidate-1',
|
||||
imageSrc: '/puzzle/candidate-1.png',
|
||||
assetId: 'asset-1',
|
||||
prompt: '雨夜猫咪',
|
||||
actualPrompt: null,
|
||||
sourceType: 'generated',
|
||||
selected: true,
|
||||
},
|
||||
],
|
||||
selectedCandidateId: 'candidate-1',
|
||||
coverImageSrc: '/puzzle/candidate-1.png',
|
||||
coverAssetId: 'asset-1',
|
||||
generationStatus: 'generating',
|
||||
},
|
||||
],
|
||||
metadata: null,
|
||||
},
|
||||
draft: generatingPuzzleDraft,
|
||||
messages: [],
|
||||
lastAssistantReply: '大鱼结果页草稿已经生成,可以补正式资产。',
|
||||
publishedProfileId: null,
|
||||
suggestedActions: [],
|
||||
resultPreview: {
|
||||
draft: null,
|
||||
draft: generatingPuzzleDraft,
|
||||
publishReady: false,
|
||||
blockers: [],
|
||||
qualityFindings: [],
|
||||
|
||||
229
src/index.css
229
src/index.css
@@ -5696,13 +5696,18 @@ button {
|
||||
}
|
||||
|
||||
.child-motion-demo {
|
||||
--child-motion-bg: #07151c;
|
||||
--child-motion-panel: rgba(6, 24, 30, 0.64);
|
||||
--child-motion-panel-border: rgba(178, 239, 220, 0.25);
|
||||
--child-motion-text: #eefcf7;
|
||||
--child-motion-soft: rgba(238, 252, 247, 0.7);
|
||||
--child-motion-green: #5ff08f;
|
||||
--child-motion-sky: #8fd8ff;
|
||||
--child-motion-bg: #dff1d6;
|
||||
--child-motion-sky: #cfefff;
|
||||
--child-motion-cloud: rgba(255, 255, 255, 0.82);
|
||||
--child-motion-ground: #78b76a;
|
||||
--child-motion-ground-deep: #3b7f46;
|
||||
--child-motion-ground-shadow: rgba(56, 110, 60, 0.3);
|
||||
--child-motion-panel: rgba(255, 250, 241, 0.76);
|
||||
--child-motion-panel-border: rgba(98, 132, 88, 0.18);
|
||||
--child-motion-text: #27412a;
|
||||
--child-motion-soft: rgba(39, 65, 42, 0.74);
|
||||
--child-motion-green: #70c16b;
|
||||
--child-motion-sky-accent: #95d2ff;
|
||||
display: grid;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
@@ -5711,9 +5716,9 @@ button {
|
||||
place-items: center;
|
||||
overflow: hidden;
|
||||
background:
|
||||
radial-gradient(circle at 18% 14%, rgba(143, 216, 255, 0.24), transparent 32%),
|
||||
radial-gradient(circle at 82% 22%, rgba(95, 240, 143, 0.18), transparent 30%),
|
||||
linear-gradient(180deg, #092433 0%, var(--child-motion-bg) 54%, #0a1f18 100%);
|
||||
radial-gradient(circle at 18% 12%, rgba(255, 255, 255, 0.92), transparent 18%),
|
||||
radial-gradient(circle at 82% 18%, rgba(255, 255, 255, 0.56), transparent 17%),
|
||||
linear-gradient(180deg, #f8fcff 0%, #eaf7ff 26%, var(--child-motion-sky) 52%, #dcefd0 70%, #cde3bd 100%);
|
||||
color: var(--child-motion-text);
|
||||
font-family: Inter, ui-sans-serif, system-ui, sans-serif;
|
||||
}
|
||||
@@ -5725,15 +5730,47 @@ button {
|
||||
}
|
||||
}
|
||||
|
||||
.child-motion-demo::before,
|
||||
.child-motion-demo::after {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.child-motion-demo::before {
|
||||
background:
|
||||
radial-gradient(circle at 12% 16%, var(--child-motion-cloud) 0 3.4%, transparent 3.6%),
|
||||
radial-gradient(circle at 16% 15%, rgba(255, 255, 255, 0.86) 0 2.2%, transparent 2.5%),
|
||||
radial-gradient(circle at 17.8% 16.2%, rgba(255, 255, 255, 0.9) 0 2.7%, transparent 3%),
|
||||
radial-gradient(circle at 76% 13%, var(--child-motion-cloud) 0 4.1%, transparent 4.3%),
|
||||
radial-gradient(circle at 82% 12.6%, rgba(255, 255, 255, 0.88) 0 2.5%, transparent 2.8%),
|
||||
radial-gradient(circle at 85% 14.2%, rgba(255, 255, 255, 0.82) 0 2.1%, transparent 2.4%),
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0) 0 62%, rgba(255, 255, 255, 0.08) 100%);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.child-motion-demo::after {
|
||||
background:
|
||||
radial-gradient(ellipse at 50% 100%, rgba(61, 120, 76, 0.26) 0 32%, transparent 58%),
|
||||
linear-gradient(180deg, transparent 0 58%, rgba(255, 255, 255, 0.12) 76%, transparent 100%);
|
||||
mix-blend-mode: soft-light;
|
||||
opacity: 0.68;
|
||||
}
|
||||
|
||||
.child-motion-stage {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: min(100vw, calc(100vh * 16 / 9));
|
||||
height: min(100vh, calc(100vw * 9 / 16));
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(16, 64, 86, 0.86), rgba(9, 42, 39, 0.9)),
|
||||
var(--child-motion-bg);
|
||||
box-shadow: 0 30px 100px rgba(0, 0, 0, 0.38);
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0)),
|
||||
radial-gradient(circle at 50% 18%, rgba(255, 255, 255, 0.6), transparent 24%),
|
||||
linear-gradient(180deg, #f3fbff 0%, #e4f3ff 32%, #d9efc4 56%, #bbdea1 100%);
|
||||
box-shadow: 0 30px 100px rgba(62, 98, 53, 0.18);
|
||||
isolation: isolate;
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
}
|
||||
@@ -5745,18 +5782,48 @@ button {
|
||||
}
|
||||
}
|
||||
|
||||
.child-motion-stage::before,
|
||||
.child-motion-stage::after {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.child-motion-stage::before {
|
||||
z-index: 0;
|
||||
background-image: url('/child-motion-demo/picture-book-grass-stage.webp');
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
opacity: 0.88;
|
||||
filter: saturate(1.02) contrast(0.98) brightness(1.02);
|
||||
}
|
||||
|
||||
.child-motion-stage::after {
|
||||
z-index: 1;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.08) 0%, transparent 18%),
|
||||
radial-gradient(ellipse at 50% 82%, rgba(255, 245, 220, 0.16), transparent 42%),
|
||||
linear-gradient(180deg, transparent 0 58%, rgba(80, 141, 72, 0.14) 100%);
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.child-motion-camera-layer {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
background:
|
||||
radial-gradient(circle at 50% 33%, rgba(255, 255, 255, 0.12), transparent 28%),
|
||||
linear-gradient(110deg, rgba(255, 255, 255, 0.06) 0 12%, transparent 12% 20%, rgba(255, 255, 255, 0.04) 20% 31%, transparent 31% 100%);
|
||||
filter: blur(7px) saturate(0.8);
|
||||
opacity: 0.62;
|
||||
transform: scale(1.05);
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.58), rgba(255, 255, 255, 0.08)),
|
||||
radial-gradient(circle at 50% 33%, rgba(255, 255, 255, 0.42), transparent 30%),
|
||||
linear-gradient(120deg, rgba(255, 255, 255, 0.1) 0 11%, transparent 11% 20%, rgba(255, 255, 255, 0.08) 20% 30%, transparent 30% 100%);
|
||||
filter: blur(8px) saturate(0.92);
|
||||
opacity: 0.34;
|
||||
transform: scale(1.04);
|
||||
mix-blend-mode: soft-light;
|
||||
}
|
||||
|
||||
.child-motion-camera-state {
|
||||
@@ -5765,14 +5832,19 @@ button {
|
||||
left: 50%;
|
||||
z-index: 7;
|
||||
transform: translateX(-50%);
|
||||
border: 1px solid rgba(238, 252, 247, 0.2);
|
||||
border: 1px solid rgba(103, 140, 94, 0.18);
|
||||
border-radius: 999px;
|
||||
background: rgba(6, 24, 30, 0.52);
|
||||
color: rgba(238, 252, 247, 0.82);
|
||||
background: rgba(255, 250, 241, 0.7);
|
||||
color: rgba(39, 65, 42, 0.88);
|
||||
padding: 0.45rem 0.9rem;
|
||||
font-size: clamp(0.68rem, 1.35vw, 0.84rem);
|
||||
font-weight: 800;
|
||||
backdrop-filter: blur(12px);
|
||||
box-shadow: 0 10px 28px rgba(90, 120, 82, 0.14);
|
||||
}
|
||||
|
||||
.child-motion-camera-state--ready {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.child-motion-floor {
|
||||
@@ -5780,12 +5852,49 @@ button {
|
||||
right: -8%;
|
||||
bottom: -19%;
|
||||
left: -8%;
|
||||
z-index: 2;
|
||||
height: 47%;
|
||||
border-radius: 50% 50% 0 0;
|
||||
background:
|
||||
radial-gradient(ellipse at 50% 8%, rgba(190, 255, 220, 0.22), transparent 36%),
|
||||
linear-gradient(180deg, rgba(24, 86, 67, 0.84), rgba(7, 43, 34, 0.96));
|
||||
box-shadow: inset 0 22px 70px rgba(255, 255, 255, 0.07);
|
||||
radial-gradient(ellipse at 50% 10%, rgba(255, 255, 255, 0.22), transparent 30%),
|
||||
radial-gradient(ellipse at 42% 30%, rgba(255, 246, 205, 0.2) 0 8%, transparent 18%),
|
||||
radial-gradient(ellipse at 70% 25%, rgba(255, 255, 255, 0.18) 0 5%, transparent 14%),
|
||||
linear-gradient(180deg, rgba(135, 194, 104, 0.92), rgba(69, 145, 76, 0.98));
|
||||
box-shadow:
|
||||
inset 0 26px 70px rgba(255, 255, 255, 0.16),
|
||||
inset 0 -38px 68px rgba(52, 94, 46, 0.18);
|
||||
}
|
||||
|
||||
.child-motion-floor::before,
|
||||
.child-motion-floor::after {
|
||||
position: absolute;
|
||||
border-radius: 999px;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.child-motion-floor::before {
|
||||
inset: 14% 10% auto 16%;
|
||||
height: 18%;
|
||||
background:
|
||||
radial-gradient(circle at 8% 50%, rgba(96, 148, 60, 0.68) 0 12%, transparent 13%),
|
||||
radial-gradient(circle at 21% 42%, rgba(96, 148, 60, 0.58) 0 9%, transparent 10%),
|
||||
radial-gradient(circle at 33% 55%, rgba(255, 255, 255, 0.2) 0 7%, transparent 8%),
|
||||
radial-gradient(circle at 45% 40%, rgba(96, 148, 60, 0.62) 0 11%, transparent 12%),
|
||||
radial-gradient(circle at 58% 52%, rgba(255, 255, 255, 0.16) 0 6%, transparent 7%),
|
||||
radial-gradient(circle at 69% 42%, rgba(96, 148, 60, 0.62) 0 10%, transparent 11%),
|
||||
radial-gradient(circle at 82% 50%, rgba(255, 255, 255, 0.18) 0 7%, transparent 8%);
|
||||
opacity: 0.78;
|
||||
}
|
||||
|
||||
.child-motion-floor::after {
|
||||
inset: auto 6% 10%;
|
||||
height: 15%;
|
||||
background:
|
||||
radial-gradient(circle at 18% 50%, rgba(55, 104, 53, 0.42) 0 10%, transparent 11%),
|
||||
radial-gradient(circle at 38% 50%, rgba(255, 255, 255, 0.12) 0 6%, transparent 7%),
|
||||
radial-gradient(circle at 60% 48%, rgba(55, 104, 53, 0.38) 0 11%, transparent 12%),
|
||||
radial-gradient(circle at 80% 52%, rgba(255, 255, 255, 0.1) 0 5%, transparent 6%);
|
||||
opacity: 0.68;
|
||||
}
|
||||
|
||||
.child-motion-hud {
|
||||
@@ -5797,7 +5906,7 @@ button {
|
||||
border: 1px solid var(--child-motion-panel-border);
|
||||
border-radius: clamp(0.75rem, 2vw, 1.25rem);
|
||||
background: var(--child-motion-panel);
|
||||
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 18px 48px rgba(72, 112, 68, 0.12);
|
||||
backdrop-filter: blur(14px);
|
||||
}
|
||||
|
||||
@@ -5834,12 +5943,13 @@ button {
|
||||
flex: 0 0 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid rgba(238, 252, 247, 0.2);
|
||||
border: 1px solid rgba(112, 143, 97, 0.2);
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.8), rgba(242, 248, 236, 0.92));
|
||||
color: var(--child-motion-text);
|
||||
font-size: clamp(0.72rem, 1.45vw, 0.95rem);
|
||||
font-weight: 900;
|
||||
box-shadow: 0 8px 20px rgba(96, 132, 82, 0.12);
|
||||
}
|
||||
|
||||
.child-motion-ring {
|
||||
@@ -5848,24 +5958,28 @@ button {
|
||||
z-index: 3;
|
||||
width: clamp(5.8rem, 13vw, 9rem);
|
||||
aspect-ratio: 1;
|
||||
transform: translateX(-50%) rotateX(62deg);
|
||||
transform: translateX(-50%) rotateX(66deg);
|
||||
border-radius: 999px;
|
||||
background:
|
||||
conic-gradient(
|
||||
from -90deg,
|
||||
rgba(255, 255, 255, 0.95) 0 var(--child-motion-ring-progress),
|
||||
rgba(95, 240, 143, 0.18) var(--child-motion-ring-progress) 360deg
|
||||
rgba(255, 255, 255, 0.88) 0 var(--child-motion-ring-progress),
|
||||
rgba(102, 190, 95, 0.22) var(--child-motion-ring-progress) 360deg
|
||||
);
|
||||
box-shadow:
|
||||
0 0 28px rgba(95, 240, 143, 0.42),
|
||||
inset 0 0 26px rgba(255, 255, 255, 0.18);
|
||||
0 0 18px rgba(120, 191, 110, 0.34),
|
||||
0 0 0 6px rgba(255, 255, 255, 0.12),
|
||||
inset 0 0 24px rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.child-motion-ring::before {
|
||||
position: absolute;
|
||||
inset: 14%;
|
||||
border-radius: inherit;
|
||||
background: rgba(8, 44, 36, 0.94);
|
||||
background:
|
||||
radial-gradient(circle at 50% 45%, rgba(255, 255, 255, 0.1), transparent 40%),
|
||||
linear-gradient(180deg, rgba(151, 215, 139, 0.82), rgba(73, 151, 74, 0.94));
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.38);
|
||||
content: '';
|
||||
}
|
||||
|
||||
@@ -5873,8 +5987,9 @@ button {
|
||||
position: absolute;
|
||||
inset: 34%;
|
||||
border-radius: 999px;
|
||||
background: var(--child-motion-green);
|
||||
opacity: 0.28;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.68), rgba(150, 231, 137, 0.86));
|
||||
opacity: 0.62;
|
||||
box-shadow: 0 0 22px rgba(124, 199, 112, 0.44);
|
||||
}
|
||||
|
||||
.child-motion-ring--active {
|
||||
@@ -5887,7 +6002,7 @@ button {
|
||||
}
|
||||
|
||||
to {
|
||||
filter: brightness(1.25);
|
||||
filter: brightness(1.08);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5899,6 +6014,7 @@ button {
|
||||
height: clamp(6rem, 13vw, 10rem);
|
||||
transform: translateX(-50%);
|
||||
transition: left 260ms ease, transform 220ms ease;
|
||||
filter: drop-shadow(0 6px 14px rgba(56, 92, 55, 0.18));
|
||||
}
|
||||
|
||||
.child-motion-avatar--jumping {
|
||||
@@ -5911,8 +6027,13 @@ button {
|
||||
.child-motion-avatar__leg {
|
||||
position: absolute;
|
||||
display: block;
|
||||
background: rgba(7, 18, 24, 0.82);
|
||||
box-shadow: 0 0 24px rgba(143, 216, 255, 0.18);
|
||||
background:
|
||||
linear-gradient(180deg, rgba(77, 109, 79, 0.44), rgba(41, 65, 44, 0.7)),
|
||||
rgba(245, 250, 245, 0.1);
|
||||
opacity: 0.6;
|
||||
border: 1px solid rgba(239, 249, 235, 0.18);
|
||||
box-shadow: 0 0 24px rgba(143, 216, 255, 0.12);
|
||||
backdrop-filter: blur(1px);
|
||||
}
|
||||
|
||||
.child-motion-avatar__head {
|
||||
@@ -5985,12 +6106,13 @@ button {
|
||||
transform: translate(-50%, -50%);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 2px solid rgba(95, 240, 143, 0.64);
|
||||
border: 2px solid rgba(117, 186, 92, 0.56);
|
||||
border-radius: 999px;
|
||||
background: rgba(95, 240, 143, 0.1);
|
||||
background: rgba(247, 251, 243, 0.18);
|
||||
color: var(--child-motion-text);
|
||||
font-size: clamp(1rem, 2.4vw, 1.55rem);
|
||||
font-weight: 900;
|
||||
box-shadow: 0 8px 24px rgba(79, 126, 67, 0.12);
|
||||
}
|
||||
|
||||
.child-motion-gesture-guide__hand {
|
||||
@@ -5998,7 +6120,7 @@ button {
|
||||
top: 28%;
|
||||
width: clamp(4rem, 9vw, 7rem);
|
||||
aspect-ratio: 1;
|
||||
border: 2px dashed rgba(95, 240, 143, 0.58);
|
||||
border: 2px dashed rgba(117, 186, 92, 0.5);
|
||||
border-radius: 999px;
|
||||
animation: child-motion-hand-guide 1.1s ease-in-out infinite alternate;
|
||||
}
|
||||
@@ -6027,8 +6149,8 @@ button {
|
||||
height: 0.8rem;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 999px;
|
||||
background: #b9ffd0;
|
||||
box-shadow: 0 0 16px rgba(95, 240, 143, 0.56);
|
||||
background: #f6fff1;
|
||||
box-shadow: 0 0 16px rgba(119, 194, 111, 0.56);
|
||||
}
|
||||
|
||||
.child-motion-floating-reward {
|
||||
@@ -6040,7 +6162,7 @@ button {
|
||||
color: #ffffff;
|
||||
font-size: clamp(1.4rem, 4vw, 2.4rem);
|
||||
font-weight: 900;
|
||||
text-shadow: 0 4px 26px rgba(0, 0, 0, 0.42);
|
||||
text-shadow: 0 4px 26px rgba(61, 90, 54, 0.42);
|
||||
animation: child-motion-reward-rise 0.72s ease-out forwards;
|
||||
}
|
||||
|
||||
@@ -6070,6 +6192,7 @@ button {
|
||||
background: var(--child-motion-panel);
|
||||
padding: 0.45rem;
|
||||
backdrop-filter: blur(14px);
|
||||
box-shadow: 0 14px 32px rgba(82, 124, 72, 0.1);
|
||||
}
|
||||
|
||||
.child-motion-calibration div {
|
||||
@@ -6078,7 +6201,7 @@ button {
|
||||
gap: 0.08rem;
|
||||
justify-items: center;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
background: rgba(255, 255, 255, 0.48);
|
||||
padding: 0.36rem 0.55rem;
|
||||
}
|
||||
|
||||
@@ -6103,11 +6226,11 @@ button {
|
||||
transform: translate(-50%, -50%);
|
||||
align-items: center;
|
||||
gap: 0.85rem;
|
||||
border: 1px solid rgba(178, 239, 220, 0.32);
|
||||
border: 1px solid rgba(143, 176, 124, 0.24);
|
||||
border-radius: 1.4rem;
|
||||
background: rgba(6, 24, 30, 0.7);
|
||||
background: rgba(255, 250, 241, 0.76);
|
||||
padding: clamp(0.85rem, 2vw, 1.15rem);
|
||||
box-shadow: 0 24px 70px rgba(0, 0, 0, 0.28);
|
||||
box-shadow: 0 24px 70px rgba(82, 124, 72, 0.18);
|
||||
backdrop-filter: blur(14px);
|
||||
}
|
||||
|
||||
@@ -6116,12 +6239,12 @@ button {
|
||||
min-height: clamp(3rem, 7vw, 4.2rem);
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(135deg, #5ff08f, #8fd8ff);
|
||||
color: #062018;
|
||||
background: linear-gradient(135deg, #88cf74, #9dd3ff);
|
||||
color: #214228;
|
||||
font-size: clamp(1rem, 2.5vw, 1.4rem);
|
||||
font-weight: 950;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 16px 44px rgba(95, 240, 143, 0.28);
|
||||
box-shadow: 0 16px 44px rgba(124, 182, 98, 0.24);
|
||||
}
|
||||
|
||||
.child-motion-start-panel span {
|
||||
@@ -6136,7 +6259,9 @@ button {
|
||||
z-index: 30;
|
||||
display: none;
|
||||
place-items: center;
|
||||
background: #07151c;
|
||||
background:
|
||||
radial-gradient(circle at 24% 22%, rgba(255, 255, 255, 0.88), transparent 20%),
|
||||
linear-gradient(180deg, #f7fcff 0%, #dff3ff 54%, #c9e6b9 100%);
|
||||
color: var(--child-motion-text);
|
||||
font-size: 1.25rem;
|
||||
font-weight: 900;
|
||||
|
||||
@@ -78,4 +78,47 @@ describe('parseMocapPacket', () => {
|
||||
expect.objectContaining({x: 0.9, y: 0.8, source: 'direct'}),
|
||||
);
|
||||
});
|
||||
|
||||
test('解析 mocap frame 的身体中心和左右手来源', () => {
|
||||
const command = parseMocapPacket({
|
||||
schema_version: '1.0',
|
||||
stream: { type: 'mocap.frame' },
|
||||
general: {
|
||||
body: {
|
||||
center_norm: [0.34, 0.58],
|
||||
},
|
||||
},
|
||||
actions: [{ gesture: 'wave-left-hand' }],
|
||||
hands: [
|
||||
{
|
||||
label: 'Left',
|
||||
state: 'open_palm',
|
||||
landmarks: [
|
||||
{ name: 'wrist', x: 0.21, y: 0.31 },
|
||||
{ name: 'index_mcp', x: 0.25, y: 0.33 },
|
||||
{ name: 'middle_mcp', x: 0.27, y: 0.34 },
|
||||
{ name: 'ring_mcp', x: 0.28, y: 0.35 },
|
||||
{ name: 'pinky_mcp', x: 0.29, y: 0.36 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Right',
|
||||
state: 'unknown',
|
||||
x: 0.72,
|
||||
y: 0.32,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(command.bodyCenter).toEqual({x: 0.34, y: 0.58});
|
||||
expect(command.actions).toEqual(
|
||||
expect.arrayContaining(['wave_left_hand', 'open_palm']),
|
||||
);
|
||||
expect(command.leftHand).toEqual(
|
||||
expect.objectContaining({side: 'left', source: 'palm_center'}),
|
||||
);
|
||||
expect(command.rightHand).toEqual(
|
||||
expect.objectContaining({x: 0.72, y: 0.32, side: 'right'}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user