takomi 2.1.39 → 2.1.41

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 (68) hide show
  1. package/.agents/plugins/marketplace.json +22 -22
  2. package/README.md +217 -422
  3. package/assets/.agent/skills/remotion/rules/assets/charts-bar-chart.tsx +173 -173
  4. package/assets/.agent/skills/remotion/rules/assets/text-animations-typewriter.tsx +100 -100
  5. package/assets/.agent/skills/remotion/rules/text-animations.md +20 -20
  6. package/assets/.agent/skills/remotion/rules/videos.md +171 -171
  7. package/assets/.agent/skills/remotion-real-ui-video/SKILL.md +264 -0
  8. package/assets/.agent/skills/remotion-real-ui-video/references/anti-patterns.md +114 -0
  9. package/assets/.agent/skills/remotion-real-ui-video/references/completion-checklist.md +75 -0
  10. package/assets/.agent/skills/remotion-real-ui-video/references/real-ui-bridge-example.md +93 -0
  11. package/assets/.agent/skills/takomi-flow/SKILL.md +244 -235
  12. package/docs/features/TakomiFlow_Portable_Plugin.md +82 -78
  13. package/docs/takomi-flow-onboarding.md +86 -76
  14. package/docs/takomi-flow-public-distribution.md +94 -94
  15. package/package.json +1 -1
  16. package/plugins/takomi-flow/.mcp.json +11 -11
  17. package/plugins/takomi-flow/assets/capabilities.json +203 -171
  18. package/plugins/takomi-flow/assets/collection.schema.json +29 -29
  19. package/plugins/takomi-flow/assets/examples.json +94 -94
  20. package/plugins/takomi-flow/assets/request.schema.json +91 -67
  21. package/plugins/takomi-flow/assets/result.schema.json +46 -45
  22. package/plugins/takomi-flow/assets/templates/image-request.json +17 -13
  23. package/plugins/takomi-flow/assets/templates/video-request.json +19 -15
  24. package/plugins/takomi-flow/package.json +38 -38
  25. package/plugins/takomi-flow/pnpm-lock.yaml +813 -813
  26. package/plugins/takomi-flow/references/flow-provider-contract.md +349 -326
  27. package/plugins/takomi-flow/scripts/lib/agent-plan.mjs +140 -130
  28. package/plugins/takomi-flow/scripts/lib/api.mjs +113 -113
  29. package/plugins/takomi-flow/scripts/lib/args.mjs +66 -57
  30. package/plugins/takomi-flow/scripts/lib/audit.mjs +65 -65
  31. package/plugins/takomi-flow/scripts/lib/browser.mjs +78 -39
  32. package/plugins/takomi-flow/scripts/lib/capabilities.mjs +11 -11
  33. package/plugins/takomi-flow/scripts/lib/collect.mjs +53 -53
  34. package/plugins/takomi-flow/scripts/lib/commands.mjs +192 -175
  35. package/plugins/takomi-flow/scripts/lib/doctor.mjs +77 -77
  36. package/plugins/takomi-flow/scripts/lib/examples.mjs +17 -17
  37. package/plugins/takomi-flow/scripts/lib/flow-media.mjs +39 -0
  38. package/plugins/takomi-flow/scripts/lib/flow-outcome.mjs +115 -90
  39. package/plugins/takomi-flow/scripts/lib/flow-project-session.mjs +153 -0
  40. package/plugins/takomi-flow/scripts/lib/flow-ui.mjs +204 -165
  41. package/plugins/takomi-flow/scripts/lib/generation.mjs +190 -90
  42. package/plugins/takomi-flow/scripts/lib/inspect.mjs +81 -81
  43. package/plugins/takomi-flow/scripts/lib/mcp-prompts.mjs +39 -39
  44. package/plugins/takomi-flow/scripts/lib/mcp-resources.mjs +16 -16
  45. package/plugins/takomi-flow/scripts/lib/mcp-tools.mjs +165 -150
  46. package/plugins/takomi-flow/scripts/lib/media.mjs +78 -78
  47. package/plugins/takomi-flow/scripts/lib/observe.mjs +54 -54
  48. package/plugins/takomi-flow/scripts/lib/paths.mjs +37 -37
  49. package/plugins/takomi-flow/scripts/lib/playwright-loader.mjs +23 -23
  50. package/plugins/takomi-flow/scripts/lib/prompts.mjs +72 -72
  51. package/plugins/takomi-flow/scripts/lib/report.mjs +141 -141
  52. package/plugins/takomi-flow/scripts/lib/request-validator.mjs +73 -58
  53. package/plugins/takomi-flow/scripts/lib/request.mjs +66 -55
  54. package/plugins/takomi-flow/scripts/lib/resource-files.mjs +69 -69
  55. package/plugins/takomi-flow/scripts/lib/result.mjs +31 -31
  56. package/plugins/takomi-flow/scripts/lib/review.mjs +44 -44
  57. package/plugins/takomi-flow/scripts/lib/selftest.mjs +153 -153
  58. package/plugins/takomi-flow/scripts/lib/settings-plan.mjs +44 -39
  59. package/plugins/takomi-flow/scripts/lib/templates.mjs +37 -37
  60. package/plugins/takomi-flow/scripts/lib/trusted-chrome.mjs +67 -67
  61. package/plugins/takomi-flow/scripts/lib/workflow.mjs +56 -56
  62. package/plugins/takomi-flow/scripts/mcp-server.mjs +18 -18
  63. package/plugins/takomi-flow/scripts/mcp-smoke.mjs +144 -142
  64. package/plugins/takomi-flow/scripts/takomi-flow.mjs +17 -17
  65. package/plugins/takomi-flow/skills/takomi-flow/SKILL.md +244 -235
  66. package/plugins/takomi-flow/skills/takomi-flow/agents/openai.yaml +7 -7
  67. package/scripts/install-takomi-flow.ps1 +85 -85
  68. package/src/skills-catalog.js +10 -9
@@ -1,173 +1,173 @@
1
- import {loadFont} from '@remotion/google-fonts/Inter';
2
- import {AbsoluteFill, spring, useCurrentFrame, useVideoConfig} from 'remotion';
3
-
4
- const {fontFamily} = loadFont();
5
-
6
- const COLOR_BAR = '#D4AF37';
7
- const COLOR_TEXT = '#ffffff';
8
- const COLOR_MUTED = '#888888';
9
- const COLOR_BG = '#0a0a0a';
10
- const COLOR_AXIS = '#333333';
11
-
12
- // Ideal composition size: 1280x720
13
-
14
- const Title: React.FC<{children: React.ReactNode}> = ({children}) => (
15
- <div style={{textAlign: 'center', marginBottom: 40}}>
16
- <div style={{color: COLOR_TEXT, fontSize: 48, fontWeight: 600}}>
17
- {children}
18
- </div>
19
- </div>
20
- );
21
-
22
- const YAxis: React.FC<{steps: number[]; height: number}> = ({
23
- steps,
24
- height,
25
- }) => (
26
- <div
27
- style={{
28
- display: 'flex',
29
- flexDirection: 'column',
30
- justifyContent: 'space-between',
31
- height,
32
- paddingRight: 16,
33
- }}
34
- >
35
- {steps
36
- .slice()
37
- .reverse()
38
- .map((step) => (
39
- <div
40
- key={step}
41
- style={{
42
- color: COLOR_MUTED,
43
- fontSize: 20,
44
- textAlign: 'right',
45
- }}
46
- >
47
- {step.toLocaleString()}
48
- </div>
49
- ))}
50
- </div>
51
- );
52
-
53
- const Bar: React.FC<{
54
- height: number;
55
- progress: number;
56
- }> = ({height, progress}) => (
57
- <div
58
- style={{
59
- flex: 1,
60
- display: 'flex',
61
- flexDirection: 'column',
62
- justifyContent: 'flex-end',
63
- }}
64
- >
65
- <div
66
- style={{
67
- width: '100%',
68
- height,
69
- backgroundColor: COLOR_BAR,
70
- borderRadius: '8px 8px 0 0',
71
- opacity: progress,
72
- }}
73
- />
74
- </div>
75
- );
76
-
77
- const XAxis: React.FC<{
78
- children: React.ReactNode;
79
- labels: string[];
80
- height: number;
81
- }> = ({children, labels, height}) => (
82
- <div style={{flex: 1, display: 'flex', flexDirection: 'column'}}>
83
- <div
84
- style={{
85
- display: 'flex',
86
- alignItems: 'flex-end',
87
- gap: 16,
88
- height,
89
- borderLeft: `2px solid ${COLOR_AXIS}`,
90
- borderBottom: `2px solid ${COLOR_AXIS}`,
91
- paddingLeft: 16,
92
- }}
93
- >
94
- {children}
95
- </div>
96
- <div
97
- style={{
98
- display: 'flex',
99
- gap: 16,
100
- paddingLeft: 16,
101
- marginTop: 12,
102
- }}
103
- >
104
- {labels.map((label) => (
105
- <div
106
- key={label}
107
- style={{
108
- flex: 1,
109
- textAlign: 'center',
110
- color: COLOR_MUTED,
111
- fontSize: 20,
112
- }}
113
- >
114
- {label}
115
- </div>
116
- ))}
117
- </div>
118
- </div>
119
- );
120
-
121
- export const MyAnimation = () => {
122
- const frame = useCurrentFrame();
123
- const {fps, height} = useVideoConfig();
124
-
125
- const data = [
126
- {month: 'Jan', price: 2039},
127
- {month: 'Mar', price: 2160},
128
- {month: 'May', price: 2327},
129
- {month: 'Jul', price: 2426},
130
- {month: 'Sep', price: 2634},
131
- {month: 'Nov', price: 2672},
132
- ];
133
-
134
- const minPrice = 2000;
135
- const maxPrice = 2800;
136
- const priceRange = maxPrice - minPrice;
137
- const chartHeight = height - 280;
138
- const yAxisSteps = [2000, 2400, 2800];
139
-
140
- return (
141
- <AbsoluteFill
142
- style={{
143
- backgroundColor: COLOR_BG,
144
- padding: 60,
145
- display: 'flex',
146
- flexDirection: 'column',
147
- fontFamily,
148
- }}
149
- >
150
- <Title>Gold Price 2024</Title>
151
-
152
- <div style={{display: 'flex', flex: 1}}>
153
- <YAxis steps={yAxisSteps} height={chartHeight} />
154
- <XAxis height={chartHeight} labels={data.map((d) => d.month)}>
155
- {data.map((item, i) => {
156
- const progress = spring({
157
- frame: frame - i * 5 - 10,
158
- fps,
159
- config: {damping: 18, stiffness: 80},
160
- });
161
-
162
- const barHeight =
163
- ((item.price - minPrice) / priceRange) * chartHeight * progress;
164
-
165
- return (
166
- <Bar key={item.month} height={barHeight} progress={progress} />
167
- );
168
- })}
169
- </XAxis>
170
- </div>
171
- </AbsoluteFill>
172
- );
173
- };
1
+ import {loadFont} from '@remotion/google-fonts/Inter';
2
+ import {AbsoluteFill, spring, useCurrentFrame, useVideoConfig} from 'remotion';
3
+
4
+ const {fontFamily} = loadFont();
5
+
6
+ const COLOR_BAR = '#D4AF37';
7
+ const COLOR_TEXT = '#ffffff';
8
+ const COLOR_MUTED = '#888888';
9
+ const COLOR_BG = '#0a0a0a';
10
+ const COLOR_AXIS = '#333333';
11
+
12
+ // Ideal composition size: 1280x720
13
+
14
+ const Title: React.FC<{children: React.ReactNode}> = ({children}) => (
15
+ <div style={{textAlign: 'center', marginBottom: 40}}>
16
+ <div style={{color: COLOR_TEXT, fontSize: 48, fontWeight: 600}}>
17
+ {children}
18
+ </div>
19
+ </div>
20
+ );
21
+
22
+ const YAxis: React.FC<{steps: number[]; height: number}> = ({
23
+ steps,
24
+ height,
25
+ }) => (
26
+ <div
27
+ style={{
28
+ display: 'flex',
29
+ flexDirection: 'column',
30
+ justifyContent: 'space-between',
31
+ height,
32
+ paddingRight: 16,
33
+ }}
34
+ >
35
+ {steps
36
+ .slice()
37
+ .reverse()
38
+ .map((step) => (
39
+ <div
40
+ key={step}
41
+ style={{
42
+ color: COLOR_MUTED,
43
+ fontSize: 20,
44
+ textAlign: 'right',
45
+ }}
46
+ >
47
+ {step.toLocaleString()}
48
+ </div>
49
+ ))}
50
+ </div>
51
+ );
52
+
53
+ const Bar: React.FC<{
54
+ height: number;
55
+ progress: number;
56
+ }> = ({height, progress}) => (
57
+ <div
58
+ style={{
59
+ flex: 1,
60
+ display: 'flex',
61
+ flexDirection: 'column',
62
+ justifyContent: 'flex-end',
63
+ }}
64
+ >
65
+ <div
66
+ style={{
67
+ width: '100%',
68
+ height,
69
+ backgroundColor: COLOR_BAR,
70
+ borderRadius: '8px 8px 0 0',
71
+ opacity: progress,
72
+ }}
73
+ />
74
+ </div>
75
+ );
76
+
77
+ const XAxis: React.FC<{
78
+ children: React.ReactNode;
79
+ labels: string[];
80
+ height: number;
81
+ }> = ({children, labels, height}) => (
82
+ <div style={{flex: 1, display: 'flex', flexDirection: 'column'}}>
83
+ <div
84
+ style={{
85
+ display: 'flex',
86
+ alignItems: 'flex-end',
87
+ gap: 16,
88
+ height,
89
+ borderLeft: `2px solid ${COLOR_AXIS}`,
90
+ borderBottom: `2px solid ${COLOR_AXIS}`,
91
+ paddingLeft: 16,
92
+ }}
93
+ >
94
+ {children}
95
+ </div>
96
+ <div
97
+ style={{
98
+ display: 'flex',
99
+ gap: 16,
100
+ paddingLeft: 16,
101
+ marginTop: 12,
102
+ }}
103
+ >
104
+ {labels.map((label) => (
105
+ <div
106
+ key={label}
107
+ style={{
108
+ flex: 1,
109
+ textAlign: 'center',
110
+ color: COLOR_MUTED,
111
+ fontSize: 20,
112
+ }}
113
+ >
114
+ {label}
115
+ </div>
116
+ ))}
117
+ </div>
118
+ </div>
119
+ );
120
+
121
+ export const MyAnimation = () => {
122
+ const frame = useCurrentFrame();
123
+ const {fps, height} = useVideoConfig();
124
+
125
+ const data = [
126
+ {month: 'Jan', price: 2039},
127
+ {month: 'Mar', price: 2160},
128
+ {month: 'May', price: 2327},
129
+ {month: 'Jul', price: 2426},
130
+ {month: 'Sep', price: 2634},
131
+ {month: 'Nov', price: 2672},
132
+ ];
133
+
134
+ const minPrice = 2000;
135
+ const maxPrice = 2800;
136
+ const priceRange = maxPrice - minPrice;
137
+ const chartHeight = height - 280;
138
+ const yAxisSteps = [2000, 2400, 2800];
139
+
140
+ return (
141
+ <AbsoluteFill
142
+ style={{
143
+ backgroundColor: COLOR_BG,
144
+ padding: 60,
145
+ display: 'flex',
146
+ flexDirection: 'column',
147
+ fontFamily,
148
+ }}
149
+ >
150
+ <Title>Gold Price 2024</Title>
151
+
152
+ <div style={{display: 'flex', flex: 1}}>
153
+ <YAxis steps={yAxisSteps} height={chartHeight} />
154
+ <XAxis height={chartHeight} labels={data.map((d) => d.month)}>
155
+ {data.map((item, i) => {
156
+ const progress = spring({
157
+ frame: frame - i * 5 - 10,
158
+ fps,
159
+ config: {damping: 18, stiffness: 80},
160
+ });
161
+
162
+ const barHeight =
163
+ ((item.price - minPrice) / priceRange) * chartHeight * progress;
164
+
165
+ return (
166
+ <Bar key={item.month} height={barHeight} progress={progress} />
167
+ );
168
+ })}
169
+ </XAxis>
170
+ </div>
171
+ </AbsoluteFill>
172
+ );
173
+ };
@@ -1,100 +1,100 @@
1
- import {
2
- AbsoluteFill,
3
- interpolate,
4
- useCurrentFrame,
5
- useVideoConfig,
6
- } from 'remotion';
7
-
8
- const COLOR_BG = '#ffffff';
9
- const COLOR_TEXT = '#000000';
10
- const FULL_TEXT = 'From prompt to motion graphics. This is Remotion.';
11
- const PAUSE_AFTER = 'From prompt to motion graphics.';
12
- const FONT_SIZE = 72;
13
- const FONT_WEIGHT = 700;
14
- const CHAR_FRAMES = 2;
15
- const CURSOR_BLINK_FRAMES = 16;
16
- const PAUSE_SECONDS = 1;
17
-
18
- // Ideal composition size: 1280x720
19
-
20
- const getTypedText = ({
21
- frame,
22
- fullText,
23
- pauseAfter,
24
- charFrames,
25
- pauseFrames,
26
- }: {
27
- frame: number;
28
- fullText: string;
29
- pauseAfter: string;
30
- charFrames: number;
31
- pauseFrames: number;
32
- }): string => {
33
- const pauseIndex = fullText.indexOf(pauseAfter);
34
- const preLen =
35
- pauseIndex >= 0 ? pauseIndex + pauseAfter.length : fullText.length;
36
-
37
- let typedChars = 0;
38
- if (frame < preLen * charFrames) {
39
- typedChars = Math.floor(frame / charFrames);
40
- } else if (frame < preLen * charFrames + pauseFrames) {
41
- typedChars = preLen;
42
- } else {
43
- const postPhase = frame - preLen * charFrames - pauseFrames;
44
- typedChars = Math.min(
45
- fullText.length,
46
- preLen + Math.floor(postPhase / charFrames),
47
- );
48
- }
49
- return fullText.slice(0, typedChars);
50
- };
51
-
52
- const Cursor: React.FC<{
53
- frame: number;
54
- blinkFrames: number;
55
- symbol?: string;
56
- }> = ({frame, blinkFrames, symbol = '\u258C'}) => {
57
- const opacity = interpolate(
58
- frame % blinkFrames,
59
- [0, blinkFrames / 2, blinkFrames],
60
- [1, 0, 1],
61
- {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'},
62
- );
63
-
64
- return <span style={{opacity}}>{symbol}</span>;
65
- };
66
-
67
- export const MyAnimation = () => {
68
- const frame = useCurrentFrame();
69
- const {fps} = useVideoConfig();
70
-
71
- const pauseFrames = Math.round(fps * PAUSE_SECONDS);
72
-
73
- const typedText = getTypedText({
74
- frame,
75
- fullText: FULL_TEXT,
76
- pauseAfter: PAUSE_AFTER,
77
- charFrames: CHAR_FRAMES,
78
- pauseFrames,
79
- });
80
-
81
- return (
82
- <AbsoluteFill
83
- style={{
84
- backgroundColor: COLOR_BG,
85
- }}
86
- >
87
- <div
88
- style={{
89
- color: COLOR_TEXT,
90
- fontSize: FONT_SIZE,
91
- fontWeight: FONT_WEIGHT,
92
- fontFamily: 'sans-serif',
93
- }}
94
- >
95
- <span>{typedText}</span>
96
- <Cursor frame={frame} blinkFrames={CURSOR_BLINK_FRAMES} />
97
- </div>
98
- </AbsoluteFill>
99
- );
100
- };
1
+ import {
2
+ AbsoluteFill,
3
+ interpolate,
4
+ useCurrentFrame,
5
+ useVideoConfig,
6
+ } from 'remotion';
7
+
8
+ const COLOR_BG = '#ffffff';
9
+ const COLOR_TEXT = '#000000';
10
+ const FULL_TEXT = 'From prompt to motion graphics. This is Remotion.';
11
+ const PAUSE_AFTER = 'From prompt to motion graphics.';
12
+ const FONT_SIZE = 72;
13
+ const FONT_WEIGHT = 700;
14
+ const CHAR_FRAMES = 2;
15
+ const CURSOR_BLINK_FRAMES = 16;
16
+ const PAUSE_SECONDS = 1;
17
+
18
+ // Ideal composition size: 1280x720
19
+
20
+ const getTypedText = ({
21
+ frame,
22
+ fullText,
23
+ pauseAfter,
24
+ charFrames,
25
+ pauseFrames,
26
+ }: {
27
+ frame: number;
28
+ fullText: string;
29
+ pauseAfter: string;
30
+ charFrames: number;
31
+ pauseFrames: number;
32
+ }): string => {
33
+ const pauseIndex = fullText.indexOf(pauseAfter);
34
+ const preLen =
35
+ pauseIndex >= 0 ? pauseIndex + pauseAfter.length : fullText.length;
36
+
37
+ let typedChars = 0;
38
+ if (frame < preLen * charFrames) {
39
+ typedChars = Math.floor(frame / charFrames);
40
+ } else if (frame < preLen * charFrames + pauseFrames) {
41
+ typedChars = preLen;
42
+ } else {
43
+ const postPhase = frame - preLen * charFrames - pauseFrames;
44
+ typedChars = Math.min(
45
+ fullText.length,
46
+ preLen + Math.floor(postPhase / charFrames),
47
+ );
48
+ }
49
+ return fullText.slice(0, typedChars);
50
+ };
51
+
52
+ const Cursor: React.FC<{
53
+ frame: number;
54
+ blinkFrames: number;
55
+ symbol?: string;
56
+ }> = ({frame, blinkFrames, symbol = '\u258C'}) => {
57
+ const opacity = interpolate(
58
+ frame % blinkFrames,
59
+ [0, blinkFrames / 2, blinkFrames],
60
+ [1, 0, 1],
61
+ {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'},
62
+ );
63
+
64
+ return <span style={{opacity}}>{symbol}</span>;
65
+ };
66
+
67
+ export const MyAnimation = () => {
68
+ const frame = useCurrentFrame();
69
+ const {fps} = useVideoConfig();
70
+
71
+ const pauseFrames = Math.round(fps * PAUSE_SECONDS);
72
+
73
+ const typedText = getTypedText({
74
+ frame,
75
+ fullText: FULL_TEXT,
76
+ pauseAfter: PAUSE_AFTER,
77
+ charFrames: CHAR_FRAMES,
78
+ pauseFrames,
79
+ });
80
+
81
+ return (
82
+ <AbsoluteFill
83
+ style={{
84
+ backgroundColor: COLOR_BG,
85
+ }}
86
+ >
87
+ <div
88
+ style={{
89
+ color: COLOR_TEXT,
90
+ fontSize: FONT_SIZE,
91
+ fontWeight: FONT_WEIGHT,
92
+ fontFamily: 'sans-serif',
93
+ }}
94
+ >
95
+ <span>{typedText}</span>
96
+ <Cursor frame={frame} blinkFrames={CURSOR_BLINK_FRAMES} />
97
+ </div>
98
+ </AbsoluteFill>
99
+ );
100
+ };
@@ -1,20 +1,20 @@
1
- ---
2
- name: text-animations
3
- description: Typography and text animation patterns for Remotion.
4
- metadata:
5
- tags: typography, text, typewriter, highlighter ken
6
- ---
7
-
8
- ## Text animations
9
-
10
- Based on `useCurrentFrame()`, reduce the string character by character to create a typewriter effect.
11
-
12
- ## Typewriter Effect
13
-
14
- See [Typewriter](assets/text-animations-typewriter.tsx) for an advanced example with a blinking cursor and a pause after the first sentence.
15
-
16
- Always use string slicing for typewriter effects. Never use per-character opacity.
17
-
18
- ## Word Highlighting
19
-
20
- See [Word Highlight](assets/text-animations-word-highlight.tsx) for an example for how a word highlight is animated, like with a highlighter pen.
1
+ ---
2
+ name: text-animations
3
+ description: Typography and text animation patterns for Remotion.
4
+ metadata:
5
+ tags: typography, text, typewriter, highlighter ken
6
+ ---
7
+
8
+ ## Text animations
9
+
10
+ Based on `useCurrentFrame()`, reduce the string character by character to create a typewriter effect.
11
+
12
+ ## Typewriter Effect
13
+
14
+ See [Typewriter](assets/text-animations-typewriter.tsx) for an advanced example with a blinking cursor and a pause after the first sentence.
15
+
16
+ Always use string slicing for typewriter effects. Never use per-character opacity.
17
+
18
+ ## Word Highlighting
19
+
20
+ See [Word Highlight](assets/text-animations-word-highlight.tsx) for an example for how a word highlight is animated, like with a highlighter pen.