kasaframe 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/NOTICE +13 -0
  2. package/README.md +2 -2
  3. package/docs/AGENTS.md +1 -0
  4. package/package.json +1 -1
  5. package/packages/adapters/src/project.ts +2 -0
  6. package/packages/check/package.json +1 -0
  7. package/packages/check/src/check.ts +8 -2
  8. package/packages/check/src/rules/frames.ts +4 -5
  9. package/packages/cli/src/commands/direct.ts +24 -4
  10. package/packages/cli/src/commands/make.ts +15 -5
  11. package/packages/cli/src/commands/render.ts +34 -7
  12. package/packages/cli/src/mix.ts +66 -4
  13. package/packages/cli/src/segments.ts +1 -1
  14. package/packages/cli/src/sfx.ts +429 -0
  15. package/packages/core/src/frame.ts +7 -4
  16. package/packages/core/src/index.ts +3 -1
  17. package/packages/core/src/project.ts +19 -0
  18. package/packages/core/src/sfx.ts +70 -0
  19. package/packages/director/src/assemble.ts +15 -6
  20. package/packages/director/src/fallback.ts +14 -2
  21. package/packages/director/src/index.ts +1 -1
  22. package/packages/director/src/pattern.ts +2 -0
  23. package/packages/director/src/patterns/fill/cells.ts +5 -2
  24. package/packages/director/src/scene.ts +17 -5
  25. package/packages/director/src/transitions.ts +21 -8
  26. package/packages/motion/src/enter.ts +7 -1
  27. package/packages/motion/src/impact.ts +5 -2
  28. package/packages/motion/src/moves/dress.ts +15 -2
  29. package/packages/motion/src/moves/menu.ts +11 -5
  30. package/packages/motion/src/transition.ts +3 -1
  31. package/packages/motion/src/transition3d/index.ts +3 -1
  32. package/packages/motion/src/transition3d/live.ts +5 -3
  33. package/packages/patterns/src/bars/rank.ts +6 -7
  34. package/packages/patterns/src/before-after/shrink.ts +3 -4
  35. package/packages/patterns/src/before-after/strike.ts +4 -5
  36. package/packages/patterns/src/code/terminal.ts +25 -16
  37. package/packages/patterns/src/end/lockup.ts +13 -10
  38. package/packages/patterns/src/end/slam.ts +14 -8
  39. package/packages/patterns/src/hook-number/counter.ts +15 -9
  40. package/packages/patterns/src/hook-number/slam.ts +6 -8
  41. package/packages/patterns/src/kit.ts +364 -59
  42. package/packages/patterns/src/limit/gauge.ts +19 -13
  43. package/packages/patterns/src/quote/karaoke.ts +17 -12
  44. package/packages/patterns/src/sfx.ts +80 -0
  45. package/packages/patterns/src/steps/path.ts +15 -7
  46. package/packages/patterns/src/term-reveal/chips.ts +24 -10
  47. package/packages/patterns/src/term-reveal/expand.ts +17 -13
  48. package/packages/patterns/src/timeline/axis.ts +79 -25
  49. package/packages/patterns/src/title/mask.ts +15 -10
  50. package/packages/patterns/src/title/slam.ts +1 -1
  51. package/packages/patterns/src/types.ts +1 -0
  52. package/packages/plan/src/brief.ts +5 -2
  53. package/packages/themes/src/index.ts +22 -0
  54. package/packages/themes/src/slots.ts +32 -2
  55. package/packages/themes/src/types.ts +28 -0
  56. package/sfx/README.md +82 -0
  57. package/sfx/click/appear.wav +0 -0
  58. package/sfx/click/burst.wav +0 -0
  59. package/sfx/click/drop.wav +0 -0
  60. package/sfx/click/end.wav +0 -0
  61. package/sfx/click/hop.wav +0 -0
  62. package/sfx/click/land.wav +0 -0
  63. package/sfx/click/select.wav +0 -0
  64. package/sfx/click/stick.wav +0 -0
  65. package/sfx/click/tick.wav +0 -0
  66. package/sfx/click/type.wav +0 -0
  67. package/sfx/click/whoosh.wav +0 -0
  68. package/sfx/pop/appear.wav +0 -0
  69. package/sfx/pop/burst.wav +0 -0
  70. package/sfx/pop/drop.wav +0 -0
  71. package/sfx/pop/end.wav +0 -0
  72. package/sfx/pop/hop.wav +0 -0
  73. package/sfx/pop/land.wav +0 -0
  74. package/sfx/pop/select.wav +0 -0
  75. package/sfx/pop/stick.wav +0 -0
  76. package/sfx/pop/tick.wav +0 -0
  77. package/sfx/pop/type.wav +0 -0
  78. package/sfx/pop/whoosh.wav +0 -0
  79. package/sfx/tags.json +545 -0
  80. package/sfx/ui/appear.wav +0 -0
  81. package/sfx/ui/burst.wav +0 -0
  82. package/sfx/ui/drop.wav +0 -0
  83. package/sfx/ui/end.wav +0 -0
  84. package/sfx/ui/hop.wav +0 -0
  85. package/sfx/ui/land.wav +0 -0
  86. package/sfx/ui/select.wav +0 -0
  87. package/sfx/ui/stick.wav +0 -0
  88. package/sfx/ui/tick.wav +0 -0
  89. package/sfx/ui/type.wav +0 -0
  90. package/sfx/ui/whoosh.wav +0 -0
  91. package/themes/kawaii-sticker/effects.ts +15 -3
  92. package/themes/kawaii-sticker/theme.json +1 -0
  93. package/themes/og-measure/THEME.md +13 -2
  94. package/themes/og-measure/effects.ts +76 -59
  95. package/themes/og-measure/sample/scenes/end.ts +2 -2
  96. package/themes/og-measure/sample/scenes/gather.ts +2 -10
  97. package/themes/og-measure/sample/scenes/models.ts +2 -10
  98. package/themes/og-measure/sample/scenes/one.ts +2 -1
  99. package/themes/og-measure/sample/scenes/price.ts +4 -16
  100. package/themes/og-measure/slots.ts +29 -0
  101. package/themes/og-measure/still.png +0 -0
  102. package/themes/og-measure/style.json +3 -0
  103. package/themes/og-measure/theme.json +15 -1
  104. package/themes/og-measure/variants/snow-blue.json +3 -0
@@ -1,6 +1,6 @@
1
1
  import { applyStyle, clamp, combine, spring } from "@kasaframe/core";
2
2
  import { fitSize, textEm } from "@kasaframe/motion/moves";
3
- import { area, beatStops, fitOnce, glider, moving, node, riseName, slotsOf } from "../kit.ts";
3
+ import { area, beatStops, dwells, fitOnce, followSpoken, glider, moving, node, reread, riseName, sayTimes, slotsOf } from "../kit.ts";
4
4
  import type { Pattern } from "../types.ts";
5
5
 
6
6
  export interface EndContent {
@@ -12,12 +12,12 @@ export interface EndContent {
12
12
  }
13
13
 
14
14
  /**
15
- * 끝 카드 — 로고가 앉고 이름이 글자마다 서고, 한 줄·주소가 찍히는 동안 짚개가 이름 → 한 줄 → 주소를 박마다 옮겨 다닌다.
15
+ * 끝 카드 — 로고가 앉고 이름이 글자마다 서고 한 줄·주소가 찍힌다. 한 줄·주소가 있으면 짚개가 이름부터 짚는다(머무는 테마는 목소리가 한 줄·주소를 말할 때만 그리로).
16
16
  */
17
17
  const pattern: Pattern<EndContent> = {
18
18
  id: "end.lockup",
19
19
  type: "end",
20
- does: "로고가 앉고 이름이 서고 한 줄·주소가 찍히는 동안, 짚개가 이름·한 줄·주소를 박마다 옮겨 다닌다",
20
+ does: "로고가 앉고 이름이 서고 한 줄·주소가 찍히면, 짚개가 이름부터 짚는다",
21
21
  energy: ["low", "mid"],
22
22
  family: "mask",
23
23
  camera: ["none"],
@@ -57,14 +57,17 @@ const pattern: Pattern<EndContent> = {
57
57
  const logoAt = 0;
58
58
  const nameAt = logo ? 0.15 : kit.first ? -0.2 : 0;
59
59
  const nameEnd = nameAt + each * (count - 1) + 0.3;
60
- const lineAt = nameEnd;
60
+ // 머무는 짚개 테마는 한 줄·주소를 목소리가 그것을 말할 때 찍는다(못 들으면 이름이 선 뒤부터 장면 끝 무렵까지 고르게). 찍힐 때마다(없으면
61
+ // 이름이 서면) 카메라가 그쪽으로 다가간다 — 끝 카드가 일찍 다 서고 남은 시간이 비지 않게. 훑는 테마는 이름이 서자마자 잇달아 찍는다
61
62
  const lineDur = line ? clamp([...c.line!].length * 0.03, 0.25, 0.6) : 0;
62
- const urlAt = lineAt + lineDur;
63
63
  const urlDur = url ? clamp([...c.url!].length * 0.03, 0.25, 0.6) : 0;
64
- // 짚개는 이름이 서자마자 이름 → 한 줄 → 주소를 박마다 옮겨 다닌다 — 장면이 길어도 빠르기는 박이 정해 화면이 서지 않는다
65
- const point = glider(host, [name, line, url].filter((e): e is HTMLDivElement => !!e), { hub: true });
64
+ const [lineAt, urlWant] = dwells(kit, ctx) ? sayTimes(kit, [c.line, c.url], nameEnd, Math.max(nameEnd + 0.3, ctx.dur - 0.9)) : [nameEnd, nameEnd + lineDur];
65
+ const urlAt = Math.max(urlWant, lineAt + lineDur);
66
+ followSpoken(kit, ctx, line || url ? [...(line ? [{ at: lineAt, el: line }] : []), ...(url ? [{ at: urlAt, el: url }] : [])] : [{ at: nameEnd, el: name }]);
67
+ // 짚개는 이름이 서면 이름·한 줄·주소를 짚는다(짚기 방식은 테마 짚음). 한 줄·주소가 없고 훑는 테마면 이름 글자를 박마다 다시 짚는다
68
+ const point = glider(host, [{ el: name, text: c.title }, ...(line ? [{ el: line, text: c.line }] : []), ...(url ? [{ el: url, text: c.url }] : [])], { kit, ctx, from: nameEnd - 0.15 });
69
+ const again = !line && !url && !dwells(kit, ctx) ? beatStops(kit.hits, nameEnd - 0.15, ctx.dur) : null;
66
70
  const fit = fitOnce(name, w * 0.86, h * (vertical ? 0.18 : 0.3), c.title);
67
- const stops = beatStops(kit.hits, nameEnd - 0.15, ctx.dur);
68
71
 
69
72
  const writeLine = line ? slots.write(line, { ctx, text: c.line!, at: lineAt, dur: lineDur, seed: `${kit.seed}:line` }) : null;
70
73
  const writeUrl = url ? slots.write(url, { ctx, text: c.url!, at: urlAt, dur: urlDur, seed: `${kit.seed}:url` }) : null;
@@ -74,8 +77,8 @@ const pattern: Pattern<EndContent> = {
74
77
  moving("end.lockup", ctx);
75
78
  if (logo && logoLand) applyStyle(logo, combine({ scale: spring(t - logoAt, { from: 0.6, to: 1, stiffness: 280, damping: 16 }) }, logoLand(t)));
76
79
  fit();
77
- paintName(t, { riseAt: nameAt, each, fill: 0 });
78
- point(t, stops);
80
+ paintName(t, { riseAt: nameAt, each, fill: 0, up: again ? reread(t, again) : undefined });
81
+ point(t);
79
82
  writeLine?.(t);
80
83
  writeUrl?.(t);
81
84
  };
@@ -1,6 +1,6 @@
1
1
  import { applyStyle, clamp, combine, spring } from "@kasaframe/core";
2
2
  import { fitSize, textEm } from "@kasaframe/motion/moves";
3
- import { area, beatStops, cameraOk, fitOnce, glider, moving, node, sceneDrop, slam, slotsOf } from "../kit.ts";
3
+ import { area, beatStops, cameraOk, dwells, fitOnce, followSpoken, glider, moving, node, readOver, reread, sayTimes, sceneDrop, slam, slotsOf } from "../kit.ts";
4
4
  import type { EndContent } from "./lockup.ts";
5
5
  import type { Pattern } from "../types.ts";
6
6
 
@@ -60,14 +60,19 @@ const pattern: Pattern<EndContent> = {
60
60
  const at = di !== null ? ctx.beats.drop(di) : (hits.find((b) => b >= 0.1) ?? 0.12);
61
61
  const dropPaint = di !== null && slots.drop ? slots.drop(burst, { ctx, drop: di }) : null;
62
62
  if (di === null && kit.stage && cameraOk(ctx)) kit.stage.camera.impact(at, { amount: 0.04 });
63
- const lineAt = at + 0.2;
64
- const urlAt = lineAt + (c.line ? 0.35 : 0);
63
+ // 머무는 짚개 테마는 한 줄·주소를 자막·목소리가 말하는 순간에 붙이고, 카메라가 이름에서 그쪽으로 서지 않고 다가간다 — 쾅 뒤 다 선 끝 카드가 3초 넘게 섰다
64
+ const dwell = dwells(kit, ctx);
65
+ const [lineAt, urlAt] = dwell
66
+ ? sayTimes(kit, [c.line, c.url], at + 0.2, Math.max(at + 0.2, ctx.dur - 1))
67
+ : [at + 0.2, at + 0.2 + (c.line ? 0.35 : 0)];
68
+ if (dwell) followSpoken(kit, ctx, [{ at, el: stage }, ...(lineEl ? [{ at: lineAt, el: lineEl }] : []), ...(urlEl ? [{ at: urlAt, el: urlEl }] : [])], { reach: 0.25, zoom: 1.05 });
65
69
  const writeLine = lineEl ? slots.write(lineEl, { ctx, text: c.line!, at: lineAt, dur: 0.35, seed: `${kit.seed}:line` }) : null;
66
70
  const writeUrl = urlEl ? slots.write(urlEl, { ctx, text: c.url!, at: urlAt, dur: 0.3, seed: `${kit.seed}:url` }) : null;
67
71
  const landed = slots.land(name, { ctx, at, seed: `${kit.seed}:land` });
68
- // 앉은 뒤엔 짚개가 이름 ↔ 한 줄·주소를 박마다 번갈아 짚는다
69
- const point = glider(host, [stage, ...(lineEl ? [lineEl] : []), ...(urlEl ? [urlEl] : [])], { hub: true });
70
- const stops = beatStops(kit.hits, at + 0.2, ctx.dur);
72
+ // 앉으면 짚개가 이름·한 줄·주소를 짚는다(짚기 방식은 테마 짚음). 한 줄·주소가 없고 훑는 테마면 이름 글을 박마다 다시 짚는다
73
+ const point = glider(host, [{ el: stage, text: c.title }, ...(lineEl ? [{ el: lineEl, text: c.line }] : []), ...(urlEl ? [{ el: urlEl, text: c.url }] : [])], { kit, ctx, from: at + 0.2 });
74
+ const again = line || dwell ? null : readOver(name, c.title, ctx);
75
+ const againStops = again ? beatStops(kit.hits, at + 0.2, ctx.dur) : [];
71
76
 
72
77
  return (t: number) => {
73
78
  moving("end.slam", ctx);
@@ -78,8 +83,9 @@ const pattern: Pattern<EndContent> = {
78
83
  // 쾅 전엔 외곽선 이름을 왼쪽부터 짚어 자리를 알린다 — 앉은 뒤엔 꽉 찬 이름을 마지막 프레임까지 짚는다
79
84
  ghost.style.opacity = t < at ? "0.16" : "0";
80
85
  ghost.style.clipPath = t < at ? `inset(0 ${((1 - clamp(t / Math.max(0.1, at - 0.16))) * 100).toFixed(2)}% 0 0)` : "";
81
- point(t, stops);
82
- if (line) line.style.visibility = t >= lineAt ? "visible" : "hidden";
86
+ point(t);
87
+ again?.(reread(t, againStops));
88
+ if (line) line.style.visibility = t >= Math.min(lineAt, urlAt) ? "visible" : "hidden";
83
89
  writeLine?.(t);
84
90
  writeUrl?.(t);
85
91
  if (urlEl) applyStyle(urlEl, t >= urlAt ? { scale: spring(t - urlAt, { from: 0.5, to: 1, stiffness: 320, damping: 14 }) } : { opacity: 0 });
@@ -1,17 +1,18 @@
1
1
  import { applyStyle, clamp } from "@kasaframe/core";
2
2
  import { textEm } from "@kasaframe/motion/moves";
3
- import { area, beatStops, decimalsOf, fitOnce, glider, moving, node, odometer, slotsOf, timesSign } from "../kit.ts";
3
+ import { area, decimalsOf, dwells, fitOnce, followSpoken, glider, moving, node, odometer, sayTimes, slotsOf, timesSign } from "../kit.ts";
4
4
  import type { HookNumber } from "./roll.ts";
5
+ import { rollCues } from "../sfx.ts";
5
6
  import type { Pattern } from "../types.ts";
6
7
 
7
8
  /**
8
- * 계수기 — 숫자 자리마다 판이 서고 주행계 띠가 굴러 값에 앉는다(끝자리 띠가 연속으로 구르고 윗자리는 올림 때만). 굴림 2.2초로 박아 둔다.
9
- * 앉은 뒤엔 짚개가 숫자와 라벨을 박마다 번갈아 짚는다. 롤(roll)이 「값이 바뀐다」라면 이쪽은 「센다」.
9
+ * 계수기 — 숫자 자리마다 판이 서고 주행계 띠가 굴러 값에 앉는다(끝자리 띠가 연속으로 구르고 윗자리는 올림 때만). 목소리가 그 값을 말할 때(못 들으면 장면 끝 무렵) 앉는다.
10
+ * 앉으면 짚개가 숫자부터 짚는다(머무는 테마는 목소리가 라벨·단위를 말할 때만 그리로). 롤(roll)이 「값이 바뀐다」라면 이쪽은 「센다」.
10
11
  */
11
12
  const pattern: Pattern<HookNumber> = {
12
13
  id: "hook-number.counter",
13
14
  type: "hook-number",
14
- does: "숫자 자리마다 판이 서고 주행계 띠가 굴러 값에 앉은 뒤, 짚개가 숫자와 라벨을 번갈아 짚는다",
15
+ does: "숫자 자리마다 판이 서고 주행계 띠가 굴러 값에 앉으면, 짚개가 숫자부터 짚는다",
15
16
  energy: ["low", "mid"],
16
17
  family: "flap",
17
18
  camera: ["none"],
@@ -69,11 +70,15 @@ const pattern: Pattern<HookNumber> = {
69
70
  // 라벨 폭은 글꼴마다 다르다(고정폭 테마는 빈칸까지 한 칸) — 글자 수 어림 대신 그 글꼴로 재서 안전 영역 폭 92% 안에
70
71
  const fitLabel = fitOnce(label, w * 0.92, labelPx, c.label);
71
72
 
72
- const land = Math.min(2.4, ctx.dur - 0.6);
73
+ // 머무는 짚개 테마는 라벨을 목소리가 그것을 말할 때 세우고, 숫자는 그 값을 말할 때까지(못 들으면 장면 끝 무렵까지) 고른 빠르기로 센다 —
74
+ // 처음 2초에 다 서고 남은 시간이 비지 않게. 앉으면 카메라가 숫자 쪽으로 다가간다. 훑는 테마는 2.4초 안에 앉고 남은 시간을 짚개가 진다
75
+ const spread = dwells(kit, ctx);
76
+ const [labelAt, valueAt] = spread ? sayTimes(kit, [c.label, c.value.toFixed(dec)], 0, Math.max(1.2, ctx.dur - 0.9)) : [0, Math.min(2.4, ctx.dur - 0.6)];
77
+ const land = clamp(valueAt, Math.min(1.2, ctx.dur - 0.6), ctx.dur - 0.6);
78
+ followSpoken(kit, ctx, [{ at: land, el: board }]);
73
79
  // 배수 기호 하나를 칸으로 짚으면 색 네모 안 × — 닫기 단추로 읽힌다
74
- const point = glider(host, [board, label, ...(unitEl && !times ? [unitEl] : [])], { hub: true });
75
- const stops = beatStops(kit.hits, land + 0.15, ctx.dur);
76
- const writeLabel = slots.write(label, { ctx, text: c.label, at: 0, dur: 0.35, seed: `${kit.seed}:l` });
80
+ const point = glider(host, [{ el: board, text: c.value.toFixed(dec) }, { el: label, text: c.label }, ...(unitEl && !times ? [{ el: unitEl, text: unit }] : [])], { kit, ctx, from: land + 0.15 });
81
+ const writeLabel = slots.write(label, { ctx, text: c.label, at: Math.min(labelAt, land - 0.2), dur: 0.35, seed: `${kit.seed}:l` });
77
82
  const landed = slots.land(board, { ctx, at: land, seed: `${kit.seed}:land` });
78
83
  // 출처는 앉은 뒤에 찍고 장면 끝 0.35초 전에 다 찍는다 — 테마 글 찍기가 마지막 글자를 앉힐 틈(끝 프레임에 덜 찍힌 글자가 안 남게)
79
84
  const srcDur = clamp([...srcText].length * 0.035, 0.3, 0.9);
@@ -87,13 +92,14 @@ const pattern: Pattern<HookNumber> = {
87
92
  writeSource?.(t);
88
93
  // 고른 빠르기로 센다 — 감속하면 끝자리 띠가 서서 그 사이가 멈춘 화면이 된다
89
94
  const p = clamp((t - 0.1) / (land - 0.1));
95
+ rollCues(t, { at: 0.1, dur: land - 0.1, p: (u) => u });
90
96
  const v = from + (c.value - from) * p;
91
97
  if (odo) odo(p >= 1 ? c.value : v);
92
98
  num.style.visibility = odo && t < land ? "visible" : "hidden";
93
99
  plain.style.visibility = !odo || t >= land ? "visible" : "hidden";
94
100
  if (!odo) plain.textContent = (p >= 1 ? c.value : v).toFixed(dec).padStart(template.length, " ");
95
101
  applyStyle(board, landed(t));
96
- point(t, stops);
102
+ point(t);
97
103
  };
98
104
  },
99
105
  };
@@ -1,6 +1,6 @@
1
1
  import { applyStyle, clamp, combine } from "@kasaframe/core";
2
2
  import { textEm } from "@kasaframe/motion/moves";
3
- import { area, beatStops, cameraOk, decimalsOf, factRoll, fitOnce, glider, moving, node, sceneDrop, slam, slotsOf } from "../kit.ts";
3
+ import { area, cameraOk, decimalsOf, factRoll, fitOnce, glider, moving, node, sceneDrop, slam, slotsOf } from "../kit.ts";
4
4
  import type { HookNumber } from "./roll.ts";
5
5
  import type { Pattern } from "../types.ts";
6
6
 
@@ -61,13 +61,11 @@ const pattern: Pattern<HookNumber> = {
61
61
  if (di === null && kit.stage && cameraOk(ctx)) kit.stage.camera.impact(at, { amount: 0.04 });
62
62
  const writeLabel = slots.write(label, { ctx, text: c.label, at: 0, dur: labelDur, seed: `${kit.seed}:l` });
63
63
  const landed = slots.land(row, { ctx, at, seed: `${kit.seed}:land` });
64
- // 짚개 — 쾅 전엔 라벨 ↔ 외곽선 자리(무엇의 숫자가 어디 오나), 쾅 뒤엔 숫자 ↔ 라벨을 박마다 번갈아 짚는다
65
- const before = glider(host, [labelBox, ghostRow], { hub: true });
66
- const after = glider(host, [numBox, labelBox], { hub: true });
67
- const preStops = beatStops(kit.hits, labelDur, at - 0.2);
64
+ // 짚개 — 쾅 전엔 라벨(무엇의 숫자인가)과 외곽선 자리, 쾅 뒤엔 숫자와 라벨. 짚기 방식(박마다 훑기·요점에 머묾)은 테마 짚음이 고른다
65
+ const before = glider(host, [{ el: labelBox, text: c.label }, { el: ghostRow }], { kit, ctx, from: labelDur, to: at - 0.2, until: at - 0.16 });
66
+ const after = glider(host, [{ el: numBox, text }, { el: labelBox, text: c.label }], { kit, ctx, from: at + 0.25 });
68
67
  // 라벨은 그 글꼴로 재서 안전 영역 폭 92% 안에
69
68
  const fitLabel = fitOnce(labelBox, w * 0.92, labelPx, c.label);
70
- const postStops = beatStops(kit.hits, at + 0.25, ctx.dur);
71
69
 
72
70
  return (t: number) => {
73
71
  moving("hook-number.slam", ctx);
@@ -81,8 +79,8 @@ const pattern: Pattern<HookNumber> = {
81
79
  ghostRow.style.left = `${row.offsetLeft}px`;
82
80
  ghostRow.style.top = `${row.offsetTop}px`;
83
81
  }
84
- before(t, preStops, at - 0.16);
85
- after(t, postStops);
82
+ before(t);
83
+ after(t);
86
84
  if (s) applyStyle(host, s.shake);
87
85
  };
88
86
  },