@prismer/runtime 2.0.7 → 2.0.8

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 (207) hide show
  1. package/CHANGELOG.md +201 -0
  2. package/built-in-skills/agent-coordination/SKILL.md +235 -0
  3. package/built-in-skills/agent-meta/SKILL.md +52 -0
  4. package/built-in-skills/assets/SKILL.md +131 -0
  5. package/built-in-skills/canvas-design/LICENSE.txt +202 -0
  6. package/built-in-skills/canvas-design/SKILL.md +156 -0
  7. package/built-in-skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +93 -0
  8. package/built-in-skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  9. package/built-in-skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  10. package/built-in-skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +93 -0
  11. package/built-in-skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  12. package/built-in-skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +93 -0
  13. package/built-in-skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  14. package/built-in-skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  15. package/built-in-skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
  16. package/built-in-skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  17. package/built-in-skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  18. package/built-in-skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  19. package/built-in-skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +93 -0
  20. package/built-in-skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  21. package/built-in-skills/canvas-design/canvas-fonts/DMMono-OFL.txt +93 -0
  22. package/built-in-skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  23. package/built-in-skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +94 -0
  24. package/built-in-skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  25. package/built-in-skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  26. package/built-in-skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +93 -0
  27. package/built-in-skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  28. package/built-in-skills/canvas-design/canvas-fonts/Gloock-OFL.txt +93 -0
  29. package/built-in-skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  30. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  31. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +93 -0
  32. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  33. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  34. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  35. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  36. package/built-in-skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  37. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  38. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  39. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  40. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
  41. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  42. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  43. package/built-in-skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  44. package/built-in-skills/canvas-design/canvas-fonts/Italiana-OFL.txt +93 -0
  45. package/built-in-skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  46. package/built-in-skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  47. package/built-in-skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
  48. package/built-in-skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  49. package/built-in-skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  50. package/built-in-skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  51. package/built-in-skills/canvas-design/canvas-fonts/Jura-OFL.txt +93 -0
  52. package/built-in-skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +93 -0
  53. package/built-in-skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  54. package/built-in-skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  55. package/built-in-skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  56. package/built-in-skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  57. package/built-in-skills/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
  58. package/built-in-skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  59. package/built-in-skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  60. package/built-in-skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +93 -0
  61. package/built-in-skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  62. package/built-in-skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
  63. package/built-in-skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  64. package/built-in-skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  65. package/built-in-skills/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
  66. package/built-in-skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  67. package/built-in-skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  68. package/built-in-skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +93 -0
  69. package/built-in-skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +93 -0
  70. package/built-in-skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  71. package/built-in-skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  72. package/built-in-skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +93 -0
  73. package/built-in-skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  74. package/built-in-skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +93 -0
  75. package/built-in-skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  76. package/built-in-skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  77. package/built-in-skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +93 -0
  78. package/built-in-skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  79. package/built-in-skills/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
  80. package/built-in-skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  81. package/built-in-skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  82. package/built-in-skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  83. package/built-in-skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  84. package/built-in-skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +93 -0
  85. package/built-in-skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  86. package/built-in-skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
  87. package/built-in-skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  88. package/built-in-skills/claim-agent-ownership/SKILL.md +254 -0
  89. package/built-in-skills/claude-api/LICENSE.txt +202 -0
  90. package/built-in-skills/claude-api/SKILL.md +324 -0
  91. package/built-in-skills/claude-api/csharp/claude-api.md +402 -0
  92. package/built-in-skills/claude-api/curl/examples.md +216 -0
  93. package/built-in-skills/claude-api/curl/managed-agents.md +336 -0
  94. package/built-in-skills/claude-api/go/claude-api.md +421 -0
  95. package/built-in-skills/claude-api/go/managed-agents/README.md +561 -0
  96. package/built-in-skills/claude-api/java/claude-api.md +432 -0
  97. package/built-in-skills/claude-api/java/managed-agents/README.md +442 -0
  98. package/built-in-skills/claude-api/php/claude-api.md +375 -0
  99. package/built-in-skills/claude-api/php/managed-agents/README.md +435 -0
  100. package/built-in-skills/claude-api/python/claude-api/README.md +420 -0
  101. package/built-in-skills/claude-api/python/claude-api/batches.md +185 -0
  102. package/built-in-skills/claude-api/python/claude-api/files-api.md +165 -0
  103. package/built-in-skills/claude-api/python/claude-api/streaming.md +162 -0
  104. package/built-in-skills/claude-api/python/claude-api/tool-use.md +590 -0
  105. package/built-in-skills/claude-api/python/managed-agents/README.md +332 -0
  106. package/built-in-skills/claude-api/ruby/claude-api.md +113 -0
  107. package/built-in-skills/claude-api/ruby/managed-agents/README.md +389 -0
  108. package/built-in-skills/claude-api/shared/agent-design.md +101 -0
  109. package/built-in-skills/claude-api/shared/error-codes.md +213 -0
  110. package/built-in-skills/claude-api/shared/live-sources.md +135 -0
  111. package/built-in-skills/claude-api/shared/managed-agents-api-reference.md +378 -0
  112. package/built-in-skills/claude-api/shared/managed-agents-client-patterns.md +209 -0
  113. package/built-in-skills/claude-api/shared/managed-agents-core.md +238 -0
  114. package/built-in-skills/claude-api/shared/managed-agents-environments.md +215 -0
  115. package/built-in-skills/claude-api/shared/managed-agents-events.md +195 -0
  116. package/built-in-skills/claude-api/shared/managed-agents-memory.md +197 -0
  117. package/built-in-skills/claude-api/shared/managed-agents-multiagent.md +99 -0
  118. package/built-in-skills/claude-api/shared/managed-agents-onboarding.md +114 -0
  119. package/built-in-skills/claude-api/shared/managed-agents-outcomes.md +106 -0
  120. package/built-in-skills/claude-api/shared/managed-agents-overview.md +68 -0
  121. package/built-in-skills/claude-api/shared/managed-agents-self-hosted-sandboxes.md +173 -0
  122. package/built-in-skills/claude-api/shared/managed-agents-tools.md +321 -0
  123. package/built-in-skills/claude-api/shared/managed-agents-webhooks.md +110 -0
  124. package/built-in-skills/claude-api/shared/model-migration.md +779 -0
  125. package/built-in-skills/claude-api/shared/models.md +121 -0
  126. package/built-in-skills/claude-api/shared/prompt-caching.md +171 -0
  127. package/built-in-skills/claude-api/shared/tool-use-concepts.md +327 -0
  128. package/built-in-skills/claude-api/typescript/claude-api/README.md +333 -0
  129. package/built-in-skills/claude-api/typescript/claude-api/batches.md +106 -0
  130. package/built-in-skills/claude-api/typescript/claude-api/files-api.md +98 -0
  131. package/built-in-skills/claude-api/typescript/claude-api/streaming.md +178 -0
  132. package/built-in-skills/claude-api/typescript/claude-api/tool-use.md +527 -0
  133. package/built-in-skills/claude-api/typescript/managed-agents/README.md +359 -0
  134. package/built-in-skills/doc-coauthoring/SKILL.md +375 -0
  135. package/built-in-skills/frontend-design/LICENSE.txt +177 -0
  136. package/built-in-skills/frontend-design/SKILL.md +42 -0
  137. package/built-in-skills/human-approval/SKILL.md +114 -0
  138. package/built-in-skills/image-generate/SKILL.md +327 -0
  139. package/built-in-skills/ingest/SKILL.md +105 -0
  140. package/built-in-skills/internal-comms/LICENSE.txt +202 -0
  141. package/built-in-skills/internal-comms/SKILL.md +32 -0
  142. package/built-in-skills/internal-comms/examples/3p-updates.md +47 -0
  143. package/built-in-skills/internal-comms/examples/company-newsletter.md +65 -0
  144. package/built-in-skills/internal-comms/examples/faq-answers.md +30 -0
  145. package/built-in-skills/internal-comms/examples/general-comms.md +16 -0
  146. package/built-in-skills/liteparse/SKILL.md +156 -0
  147. package/built-in-skills/mcp-builder/LICENSE.txt +202 -0
  148. package/built-in-skills/mcp-builder/SKILL.md +236 -0
  149. package/built-in-skills/mcp-builder/reference/evaluation.md +602 -0
  150. package/built-in-skills/mcp-builder/reference/mcp_best_practices.md +249 -0
  151. package/built-in-skills/mcp-builder/reference/node_mcp_server.md +970 -0
  152. package/built-in-skills/mcp-builder/reference/python_mcp_server.md +719 -0
  153. package/built-in-skills/mcp-builder/scripts/connections.py +151 -0
  154. package/built-in-skills/mcp-builder/scripts/evaluation.py +373 -0
  155. package/built-in-skills/mcp-builder/scripts/example_evaluation.xml +22 -0
  156. package/built-in-skills/mcp-builder/scripts/requirements.txt +2 -0
  157. package/built-in-skills/memory/SKILL.md +106 -0
  158. package/built-in-skills/memory-curation/SKILL.md +135 -0
  159. package/built-in-skills/office-artifacts/SKILL.md +198 -0
  160. package/built-in-skills/prismer-im-collab/SKILL.md +148 -0
  161. package/built-in-skills/skill-authoring/SKILL.md +124 -0
  162. package/built-in-skills/skill-authoring/skill.json +74 -0
  163. package/built-in-skills/skill-creator/LICENSE.txt +202 -0
  164. package/built-in-skills/skill-creator/SKILL.md +485 -0
  165. package/built-in-skills/skill-creator/agents/analyzer.md +274 -0
  166. package/built-in-skills/skill-creator/agents/comparator.md +202 -0
  167. package/built-in-skills/skill-creator/agents/grader.md +223 -0
  168. package/built-in-skills/skill-creator/assets/eval_review.html +146 -0
  169. package/built-in-skills/skill-creator/eval-viewer/generate_review.py +471 -0
  170. package/built-in-skills/skill-creator/eval-viewer/viewer.html +1325 -0
  171. package/built-in-skills/skill-creator/references/schemas.md +430 -0
  172. package/built-in-skills/skill-creator/scripts/__init__.py +0 -0
  173. package/built-in-skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  174. package/built-in-skills/skill-creator/scripts/generate_report.py +326 -0
  175. package/built-in-skills/skill-creator/scripts/improve_description.py +247 -0
  176. package/built-in-skills/skill-creator/scripts/package_skill.py +136 -0
  177. package/built-in-skills/skill-creator/scripts/quick_validate.py +103 -0
  178. package/built-in-skills/skill-creator/scripts/run_eval.py +310 -0
  179. package/built-in-skills/skill-creator/scripts/run_loop.py +328 -0
  180. package/built-in-skills/skill-creator/scripts/utils.py +47 -0
  181. package/built-in-skills/slack-gif-creator/LICENSE.txt +202 -0
  182. package/built-in-skills/slack-gif-creator/SKILL.md +271 -0
  183. package/built-in-skills/slack-gif-creator/core/easing.py +234 -0
  184. package/built-in-skills/slack-gif-creator/core/frame_composer.py +176 -0
  185. package/built-in-skills/slack-gif-creator/core/gif_builder.py +269 -0
  186. package/built-in-skills/slack-gif-creator/core/validators.py +136 -0
  187. package/built-in-skills/slack-gif-creator/requirements.txt +4 -0
  188. package/built-in-skills/tasks/SKILL.md +398 -0
  189. package/built-in-skills/team/SKILL.md +76 -0
  190. package/built-in-skills/web-artifacts-builder/LICENSE.txt +202 -0
  191. package/built-in-skills/web-artifacts-builder/SKILL.md +104 -0
  192. package/built-in-skills/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
  193. package/built-in-skills/web-artifacts-builder/scripts/init-artifact.sh +334 -0
  194. package/built-in-skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
  195. package/built-in-skills/webapp-testing/LICENSE.txt +202 -0
  196. package/built-in-skills/webapp-testing/SKILL.md +96 -0
  197. package/built-in-skills/webapp-testing/examples/console_logging.py +35 -0
  198. package/built-in-skills/webapp-testing/examples/element_discovery.py +40 -0
  199. package/built-in-skills/webapp-testing/examples/static_html_automation.py +33 -0
  200. package/built-in-skills/webapp-testing/scripts/with_server.py +106 -0
  201. package/dist/cli.cjs +6195 -1631
  202. package/dist/cli.js +6148 -1585
  203. package/dist/index.cjs +6032 -1468
  204. package/dist/index.d.cts +813 -43
  205. package/dist/index.d.ts +813 -43
  206. package/dist/index.js +6017 -1454
  207. package/package.json +4 -2
@@ -0,0 +1,271 @@
1
+ ---
2
+ name: slack-gif-creator
3
+ description: Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like "make me a GIF of X doing Y for Slack."
4
+ license: Complete terms in LICENSE.txt
5
+ ---
6
+
7
+ ## Delivering the GIF
8
+
9
+ When this skill finishes producing an animated GIF (`output.gif`), deliver
10
+ it with the **standard delivery commands** — `cloud deliver <abs-path>` to
11
+ attach it to your reply, or `cloud file send "$PRISMER_CONVERSATION_ID"
12
+ <abs-path>` to send it as a standalone message. See the **office-artifacts
13
+ delivery contract** for the full rules (incl. the hermes `--run-id` /
14
+ `--conversation-id` flags).
15
+
16
+ Do NOT hand-build `POST /api/im/assets` calls or assemble ContentBlock JSON
17
+ yourself, and do NOT paste a `data:image/gif;base64,…` URI or a local
18
+ `./output.gif` path into the reply body — local paths break the moment the
19
+ daemon is restarted and base64 defeats caching. The delivery command
20
+ registers the asset so the chat renderer surfaces the GIF preview
21
+ automatically.
22
+
23
+
24
+ # Slack GIF Creator
25
+
26
+ A toolkit providing utilities and knowledge for creating animated GIFs optimized for Slack.
27
+
28
+ ## Slack Requirements
29
+
30
+ **Dimensions:**
31
+ - Emoji GIFs: 128x128 (recommended)
32
+ - Message GIFs: 480x480
33
+
34
+ **Parameters:**
35
+ - FPS: 10-30 (lower is smaller file size)
36
+ - Colors: 48-128 (fewer = smaller file size)
37
+ - Duration: Keep under 3 seconds for emoji GIFs
38
+
39
+ ## Core Workflow
40
+
41
+ ```python
42
+ from core.gif_builder import GIFBuilder
43
+ from PIL import Image, ImageDraw
44
+
45
+ # 1. Create builder
46
+ builder = GIFBuilder(width=128, height=128, fps=10)
47
+
48
+ # 2. Generate frames
49
+ for i in range(12):
50
+ frame = Image.new('RGB', (128, 128), (240, 248, 255))
51
+ draw = ImageDraw.Draw(frame)
52
+
53
+ # Draw your animation using PIL primitives
54
+ # (circles, polygons, lines, etc.)
55
+
56
+ builder.add_frame(frame)
57
+
58
+ # 3. Save with optimization
59
+ builder.save('output.gif', num_colors=48, optimize_for_emoji=True)
60
+ ```
61
+
62
+ ## Drawing Graphics
63
+
64
+ ### Working with User-Uploaded Images
65
+ If a user uploads an image, consider whether they want to:
66
+ - **Use it directly** (e.g., "animate this", "split this into frames")
67
+ - **Use it as inspiration** (e.g., "make something like this")
68
+
69
+ Load and work with images using PIL:
70
+ ```python
71
+ from PIL import Image
72
+
73
+ uploaded = Image.open('file.png')
74
+ # Use directly, or just as reference for colors/style
75
+ ```
76
+
77
+ ### Drawing from Scratch
78
+ When drawing graphics from scratch, use PIL ImageDraw primitives:
79
+
80
+ ```python
81
+ from PIL import ImageDraw
82
+
83
+ draw = ImageDraw.Draw(frame)
84
+
85
+ # Circles/ovals
86
+ draw.ellipse([x1, y1, x2, y2], fill=(r, g, b), outline=(r, g, b), width=3)
87
+
88
+ # Stars, triangles, any polygon
89
+ points = [(x1, y1), (x2, y2), (x3, y3), ...]
90
+ draw.polygon(points, fill=(r, g, b), outline=(r, g, b), width=3)
91
+
92
+ # Lines
93
+ draw.line([(x1, y1), (x2, y2)], fill=(r, g, b), width=5)
94
+
95
+ # Rectangles
96
+ draw.rectangle([x1, y1, x2, y2], fill=(r, g, b), outline=(r, g, b), width=3)
97
+ ```
98
+
99
+ **Don't use:** Emoji fonts (unreliable across platforms) or assume pre-packaged graphics exist in this skill.
100
+
101
+ ### Making Graphics Look Good
102
+
103
+ Graphics should look polished and creative, not basic. Here's how:
104
+
105
+ **Use thicker lines** - Always set `width=2` or higher for outlines and lines. Thin lines (width=1) look choppy and amateurish.
106
+
107
+ **Add visual depth**:
108
+ - Use gradients for backgrounds (`create_gradient_background`)
109
+ - Layer multiple shapes for complexity (e.g., a star with a smaller star inside)
110
+
111
+ **Make shapes more interesting**:
112
+ - Don't just draw a plain circle - add highlights, rings, or patterns
113
+ - Stars can have glows (draw larger, semi-transparent versions behind)
114
+ - Combine multiple shapes (stars + sparkles, circles + rings)
115
+
116
+ **Pay attention to colors**:
117
+ - Use vibrant, complementary colors
118
+ - Add contrast (dark outlines on light shapes, light outlines on dark shapes)
119
+ - Consider the overall composition
120
+
121
+ **For complex shapes** (hearts, snowflakes, etc.):
122
+ - Use combinations of polygons and ellipses
123
+ - Calculate points carefully for symmetry
124
+ - Add details (a heart can have a highlight curve, snowflakes have intricate branches)
125
+
126
+ Be creative and detailed! A good Slack GIF should look polished, not like placeholder graphics.
127
+
128
+ ## Available Utilities
129
+
130
+ ### GIFBuilder (`core.gif_builder`)
131
+ Assembles frames and optimizes for Slack:
132
+ ```python
133
+ builder = GIFBuilder(width=128, height=128, fps=10)
134
+ builder.add_frame(frame) # Add PIL Image
135
+ builder.add_frames(frames) # Add list of frames
136
+ builder.save('out.gif', num_colors=48, optimize_for_emoji=True, remove_duplicates=True)
137
+ ```
138
+
139
+ ### Validators (`core.validators`)
140
+ Check if GIF meets Slack requirements:
141
+ ```python
142
+ from core.validators import validate_gif, is_slack_ready
143
+
144
+ # Detailed validation
145
+ passes, info = validate_gif('my.gif', is_emoji=True, verbose=True)
146
+
147
+ # Quick check
148
+ if is_slack_ready('my.gif'):
149
+ print("Ready!")
150
+ ```
151
+
152
+ ### Easing Functions (`core.easing`)
153
+ Smooth motion instead of linear:
154
+ ```python
155
+ from core.easing import interpolate
156
+
157
+ # Progress from 0.0 to 1.0
158
+ t = i / (num_frames - 1)
159
+
160
+ # Apply easing
161
+ y = interpolate(start=0, end=400, t=t, easing='ease_out')
162
+
163
+ # Available: linear, ease_in, ease_out, ease_in_out,
164
+ # bounce_out, elastic_out, back_out
165
+ ```
166
+
167
+ ### Frame Helpers (`core.frame_composer`)
168
+ Convenience functions for common needs:
169
+ ```python
170
+ from core.frame_composer import (
171
+ create_blank_frame, # Solid color background
172
+ create_gradient_background, # Vertical gradient
173
+ draw_circle, # Helper for circles
174
+ draw_text, # Simple text rendering
175
+ draw_star # 5-pointed star
176
+ )
177
+ ```
178
+
179
+ ## Animation Concepts
180
+
181
+ ### Shake/Vibrate
182
+ Offset object position with oscillation:
183
+ - Use `math.sin()` or `math.cos()` with frame index
184
+ - Add small random variations for natural feel
185
+ - Apply to x and/or y position
186
+
187
+ ### Pulse/Heartbeat
188
+ Scale object size rhythmically:
189
+ - Use `math.sin(t * frequency * 2 * math.pi)` for smooth pulse
190
+ - For heartbeat: two quick pulses then pause (adjust sine wave)
191
+ - Scale between 0.8 and 1.2 of base size
192
+
193
+ ### Bounce
194
+ Object falls and bounces:
195
+ - Use `interpolate()` with `easing='bounce_out'` for landing
196
+ - Use `easing='ease_in'` for falling (accelerating)
197
+ - Apply gravity by increasing y velocity each frame
198
+
199
+ ### Spin/Rotate
200
+ Rotate object around center:
201
+ - PIL: `image.rotate(angle, resample=Image.BICUBIC)`
202
+ - For wobble: use sine wave for angle instead of linear
203
+
204
+ ### Fade In/Out
205
+ Gradually appear or disappear:
206
+ - Create RGBA image, adjust alpha channel
207
+ - Or use `Image.blend(image1, image2, alpha)`
208
+ - Fade in: alpha from 0 to 1
209
+ - Fade out: alpha from 1 to 0
210
+
211
+ ### Slide
212
+ Move object from off-screen to position:
213
+ - Start position: outside frame bounds
214
+ - End position: target location
215
+ - Use `interpolate()` with `easing='ease_out'` for smooth stop
216
+ - For overshoot: use `easing='back_out'`
217
+
218
+ ### Zoom
219
+ Scale and position for zoom effect:
220
+ - Zoom in: scale from 0.1 to 2.0, crop center
221
+ - Zoom out: scale from 2.0 to 1.0
222
+ - Can add motion blur for drama (PIL filter)
223
+
224
+ ### Explode/Particle Burst
225
+ Create particles radiating outward:
226
+ - Generate particles with random angles and velocities
227
+ - Update each particle: `x += vx`, `y += vy`
228
+ - Add gravity: `vy += gravity_constant`
229
+ - Fade out particles over time (reduce alpha)
230
+
231
+ ## Optimization Strategies
232
+
233
+ Only when asked to make the file size smaller, implement a few of the following methods:
234
+
235
+ 1. **Fewer frames** - Lower FPS (10 instead of 20) or shorter duration
236
+ 2. **Fewer colors** - `num_colors=48` instead of 128
237
+ 3. **Smaller dimensions** - 128x128 instead of 480x480
238
+ 4. **Remove duplicates** - `remove_duplicates=True` in save()
239
+ 5. **Emoji mode** - `optimize_for_emoji=True` auto-optimizes
240
+
241
+ ```python
242
+ # Maximum optimization for emoji
243
+ builder.save(
244
+ 'emoji.gif',
245
+ num_colors=48,
246
+ optimize_for_emoji=True,
247
+ remove_duplicates=True
248
+ )
249
+ ```
250
+
251
+ ## Philosophy
252
+
253
+ This skill provides:
254
+ - **Knowledge**: Slack's requirements and animation concepts
255
+ - **Utilities**: GIFBuilder, validators, easing functions
256
+ - **Flexibility**: Create the animation logic using PIL primitives
257
+
258
+ It does NOT provide:
259
+ - Rigid animation templates or pre-made functions
260
+ - Emoji font rendering (unreliable across platforms)
261
+ - A library of pre-packaged graphics built into the skill
262
+
263
+ **Note on user uploads**: This skill doesn't include pre-built graphics, but if a user uploads an image, use PIL to load and work with it - interpret based on their request whether they want it used directly or just as inspiration.
264
+
265
+ Be creative! Combine concepts (bouncing + rotating, pulsing + sliding, etc.) and use PIL's full capabilities.
266
+
267
+ ## Dependencies
268
+
269
+ ```bash
270
+ pip install pillow imageio numpy
271
+ ```
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Easing Functions - Timing functions for smooth animations.
4
+
5
+ Provides various easing functions for natural motion and timing.
6
+ All functions take a value t (0.0 to 1.0) and return eased value (0.0 to 1.0).
7
+ """
8
+
9
+ import math
10
+
11
+
12
+ def linear(t: float) -> float:
13
+ """Linear interpolation (no easing)."""
14
+ return t
15
+
16
+
17
+ def ease_in_quad(t: float) -> float:
18
+ """Quadratic ease-in (slow start, accelerating)."""
19
+ return t * t
20
+
21
+
22
+ def ease_out_quad(t: float) -> float:
23
+ """Quadratic ease-out (fast start, decelerating)."""
24
+ return t * (2 - t)
25
+
26
+
27
+ def ease_in_out_quad(t: float) -> float:
28
+ """Quadratic ease-in-out (slow start and end)."""
29
+ if t < 0.5:
30
+ return 2 * t * t
31
+ return -1 + (4 - 2 * t) * t
32
+
33
+
34
+ def ease_in_cubic(t: float) -> float:
35
+ """Cubic ease-in (slow start)."""
36
+ return t * t * t
37
+
38
+
39
+ def ease_out_cubic(t: float) -> float:
40
+ """Cubic ease-out (fast start)."""
41
+ return (t - 1) * (t - 1) * (t - 1) + 1
42
+
43
+
44
+ def ease_in_out_cubic(t: float) -> float:
45
+ """Cubic ease-in-out."""
46
+ if t < 0.5:
47
+ return 4 * t * t * t
48
+ return (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
49
+
50
+
51
+ def ease_in_bounce(t: float) -> float:
52
+ """Bounce ease-in (bouncy start)."""
53
+ return 1 - ease_out_bounce(1 - t)
54
+
55
+
56
+ def ease_out_bounce(t: float) -> float:
57
+ """Bounce ease-out (bouncy end)."""
58
+ if t < 1 / 2.75:
59
+ return 7.5625 * t * t
60
+ elif t < 2 / 2.75:
61
+ t -= 1.5 / 2.75
62
+ return 7.5625 * t * t + 0.75
63
+ elif t < 2.5 / 2.75:
64
+ t -= 2.25 / 2.75
65
+ return 7.5625 * t * t + 0.9375
66
+ else:
67
+ t -= 2.625 / 2.75
68
+ return 7.5625 * t * t + 0.984375
69
+
70
+
71
+ def ease_in_out_bounce(t: float) -> float:
72
+ """Bounce ease-in-out."""
73
+ if t < 0.5:
74
+ return ease_in_bounce(t * 2) * 0.5
75
+ return ease_out_bounce(t * 2 - 1) * 0.5 + 0.5
76
+
77
+
78
+ def ease_in_elastic(t: float) -> float:
79
+ """Elastic ease-in (spring effect)."""
80
+ if t == 0 or t == 1:
81
+ return t
82
+ return -math.pow(2, 10 * (t - 1)) * math.sin((t - 1.1) * 5 * math.pi)
83
+
84
+
85
+ def ease_out_elastic(t: float) -> float:
86
+ """Elastic ease-out (spring effect)."""
87
+ if t == 0 or t == 1:
88
+ return t
89
+ return math.pow(2, -10 * t) * math.sin((t - 0.1) * 5 * math.pi) + 1
90
+
91
+
92
+ def ease_in_out_elastic(t: float) -> float:
93
+ """Elastic ease-in-out."""
94
+ if t == 0 or t == 1:
95
+ return t
96
+ t = t * 2 - 1
97
+ if t < 0:
98
+ return -0.5 * math.pow(2, 10 * t) * math.sin((t - 0.1) * 5 * math.pi)
99
+ return math.pow(2, -10 * t) * math.sin((t - 0.1) * 5 * math.pi) * 0.5 + 1
100
+
101
+
102
+ # Convenience mapping
103
+ EASING_FUNCTIONS = {
104
+ "linear": linear,
105
+ "ease_in": ease_in_quad,
106
+ "ease_out": ease_out_quad,
107
+ "ease_in_out": ease_in_out_quad,
108
+ "bounce_in": ease_in_bounce,
109
+ "bounce_out": ease_out_bounce,
110
+ "bounce": ease_in_out_bounce,
111
+ "elastic_in": ease_in_elastic,
112
+ "elastic_out": ease_out_elastic,
113
+ "elastic": ease_in_out_elastic,
114
+ }
115
+
116
+
117
+ def get_easing(name: str = "linear"):
118
+ """Get easing function by name."""
119
+ return EASING_FUNCTIONS.get(name, linear)
120
+
121
+
122
+ def interpolate(start: float, end: float, t: float, easing: str = "linear") -> float:
123
+ """
124
+ Interpolate between two values with easing.
125
+
126
+ Args:
127
+ start: Start value
128
+ end: End value
129
+ t: Progress from 0.0 to 1.0
130
+ easing: Name of easing function
131
+
132
+ Returns:
133
+ Interpolated value
134
+ """
135
+ ease_func = get_easing(easing)
136
+ eased_t = ease_func(t)
137
+ return start + (end - start) * eased_t
138
+
139
+
140
+ def ease_back_in(t: float) -> float:
141
+ """Back ease-in (slight overshoot backward before forward motion)."""
142
+ c1 = 1.70158
143
+ c3 = c1 + 1
144
+ return c3 * t * t * t - c1 * t * t
145
+
146
+
147
+ def ease_back_out(t: float) -> float:
148
+ """Back ease-out (overshoot forward then settle back)."""
149
+ c1 = 1.70158
150
+ c3 = c1 + 1
151
+ return 1 + c3 * pow(t - 1, 3) + c1 * pow(t - 1, 2)
152
+
153
+
154
+ def ease_back_in_out(t: float) -> float:
155
+ """Back ease-in-out (overshoot at both ends)."""
156
+ c1 = 1.70158
157
+ c2 = c1 * 1.525
158
+ if t < 0.5:
159
+ return (pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
160
+ return (pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2
161
+
162
+
163
+ def apply_squash_stretch(
164
+ base_scale: tuple[float, float], intensity: float, direction: str = "vertical"
165
+ ) -> tuple[float, float]:
166
+ """
167
+ Calculate squash and stretch scales for more dynamic animation.
168
+
169
+ Args:
170
+ base_scale: (width_scale, height_scale) base scales
171
+ intensity: Squash/stretch intensity (0.0-1.0)
172
+ direction: 'vertical', 'horizontal', or 'both'
173
+
174
+ Returns:
175
+ (width_scale, height_scale) with squash/stretch applied
176
+ """
177
+ width_scale, height_scale = base_scale
178
+
179
+ if direction == "vertical":
180
+ # Compress vertically, expand horizontally (preserve volume)
181
+ height_scale *= 1 - intensity * 0.5
182
+ width_scale *= 1 + intensity * 0.5
183
+ elif direction == "horizontal":
184
+ # Compress horizontally, expand vertically
185
+ width_scale *= 1 - intensity * 0.5
186
+ height_scale *= 1 + intensity * 0.5
187
+ elif direction == "both":
188
+ # General squash (both dimensions)
189
+ width_scale *= 1 - intensity * 0.3
190
+ height_scale *= 1 - intensity * 0.3
191
+
192
+ return (width_scale, height_scale)
193
+
194
+
195
+ def calculate_arc_motion(
196
+ start: tuple[float, float], end: tuple[float, float], height: float, t: float
197
+ ) -> tuple[float, float]:
198
+ """
199
+ Calculate position along a parabolic arc (natural motion path).
200
+
201
+ Args:
202
+ start: (x, y) starting position
203
+ end: (x, y) ending position
204
+ height: Arc height at midpoint (positive = upward)
205
+ t: Progress (0.0-1.0)
206
+
207
+ Returns:
208
+ (x, y) position along arc
209
+ """
210
+ x1, y1 = start
211
+ x2, y2 = end
212
+
213
+ # Linear interpolation for x
214
+ x = x1 + (x2 - x1) * t
215
+
216
+ # Parabolic interpolation for y
217
+ # y = start + progress * (end - start) + arc_offset
218
+ # Arc offset peaks at t=0.5
219
+ arc_offset = 4 * height * t * (1 - t)
220
+ y = y1 + (y2 - y1) * t - arc_offset
221
+
222
+ return (x, y)
223
+
224
+
225
+ # Add new easing functions to the convenience mapping
226
+ EASING_FUNCTIONS.update(
227
+ {
228
+ "back_in": ease_back_in,
229
+ "back_out": ease_back_out,
230
+ "back_in_out": ease_back_in_out,
231
+ "anticipate": ease_back_in, # Alias
232
+ "overshoot": ease_back_out, # Alias
233
+ }
234
+ )
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Frame Composer - Utilities for composing visual elements into frames.
4
+
5
+ Provides functions for drawing shapes, text, emojis, and compositing elements
6
+ together to create animation frames.
7
+ """
8
+
9
+ from typing import Optional
10
+
11
+ import numpy as np
12
+ from PIL import Image, ImageDraw, ImageFont
13
+
14
+
15
+ def create_blank_frame(
16
+ width: int, height: int, color: tuple[int, int, int] = (255, 255, 255)
17
+ ) -> Image.Image:
18
+ """
19
+ Create a blank frame with solid color background.
20
+
21
+ Args:
22
+ width: Frame width
23
+ height: Frame height
24
+ color: RGB color tuple (default: white)
25
+
26
+ Returns:
27
+ PIL Image
28
+ """
29
+ return Image.new("RGB", (width, height), color)
30
+
31
+
32
+ def draw_circle(
33
+ frame: Image.Image,
34
+ center: tuple[int, int],
35
+ radius: int,
36
+ fill_color: Optional[tuple[int, int, int]] = None,
37
+ outline_color: Optional[tuple[int, int, int]] = None,
38
+ outline_width: int = 1,
39
+ ) -> Image.Image:
40
+ """
41
+ Draw a circle on a frame.
42
+
43
+ Args:
44
+ frame: PIL Image to draw on
45
+ center: (x, y) center position
46
+ radius: Circle radius
47
+ fill_color: RGB fill color (None for no fill)
48
+ outline_color: RGB outline color (None for no outline)
49
+ outline_width: Outline width in pixels
50
+
51
+ Returns:
52
+ Modified frame
53
+ """
54
+ draw = ImageDraw.Draw(frame)
55
+ x, y = center
56
+ bbox = [x - radius, y - radius, x + radius, y + radius]
57
+ draw.ellipse(bbox, fill=fill_color, outline=outline_color, width=outline_width)
58
+ return frame
59
+
60
+
61
+ def draw_text(
62
+ frame: Image.Image,
63
+ text: str,
64
+ position: tuple[int, int],
65
+ color: tuple[int, int, int] = (0, 0, 0),
66
+ centered: bool = False,
67
+ ) -> Image.Image:
68
+ """
69
+ Draw text on a frame.
70
+
71
+ Args:
72
+ frame: PIL Image to draw on
73
+ text: Text to draw
74
+ position: (x, y) position (top-left unless centered=True)
75
+ color: RGB text color
76
+ centered: If True, center text at position
77
+
78
+ Returns:
79
+ Modified frame
80
+ """
81
+ draw = ImageDraw.Draw(frame)
82
+
83
+ # Uses Pillow's default font.
84
+ # If the font should be changed for the emoji, add additional logic here.
85
+ font = ImageFont.load_default()
86
+
87
+ if centered:
88
+ bbox = draw.textbbox((0, 0), text, font=font)
89
+ text_width = bbox[2] - bbox[0]
90
+ text_height = bbox[3] - bbox[1]
91
+ x = position[0] - text_width // 2
92
+ y = position[1] - text_height // 2
93
+ position = (x, y)
94
+
95
+ draw.text(position, text, fill=color, font=font)
96
+ return frame
97
+
98
+
99
+ def create_gradient_background(
100
+ width: int,
101
+ height: int,
102
+ top_color: tuple[int, int, int],
103
+ bottom_color: tuple[int, int, int],
104
+ ) -> Image.Image:
105
+ """
106
+ Create a vertical gradient background.
107
+
108
+ Args:
109
+ width: Frame width
110
+ height: Frame height
111
+ top_color: RGB color at top
112
+ bottom_color: RGB color at bottom
113
+
114
+ Returns:
115
+ PIL Image with gradient
116
+ """
117
+ frame = Image.new("RGB", (width, height))
118
+ draw = ImageDraw.Draw(frame)
119
+
120
+ # Calculate color step for each row
121
+ r1, g1, b1 = top_color
122
+ r2, g2, b2 = bottom_color
123
+
124
+ for y in range(height):
125
+ # Interpolate color
126
+ ratio = y / height
127
+ r = int(r1 * (1 - ratio) + r2 * ratio)
128
+ g = int(g1 * (1 - ratio) + g2 * ratio)
129
+ b = int(b1 * (1 - ratio) + b2 * ratio)
130
+
131
+ # Draw horizontal line
132
+ draw.line([(0, y), (width, y)], fill=(r, g, b))
133
+
134
+ return frame
135
+
136
+
137
+ def draw_star(
138
+ frame: Image.Image,
139
+ center: tuple[int, int],
140
+ size: int,
141
+ fill_color: tuple[int, int, int],
142
+ outline_color: Optional[tuple[int, int, int]] = None,
143
+ outline_width: int = 1,
144
+ ) -> Image.Image:
145
+ """
146
+ Draw a 5-pointed star.
147
+
148
+ Args:
149
+ frame: PIL Image to draw on
150
+ center: (x, y) center position
151
+ size: Star size (outer radius)
152
+ fill_color: RGB fill color
153
+ outline_color: RGB outline color (None for no outline)
154
+ outline_width: Outline width
155
+
156
+ Returns:
157
+ Modified frame
158
+ """
159
+ import math
160
+
161
+ draw = ImageDraw.Draw(frame)
162
+ x, y = center
163
+
164
+ # Calculate star points
165
+ points = []
166
+ for i in range(10):
167
+ angle = (i * 36 - 90) * math.pi / 180 # 36 degrees per point, start at top
168
+ radius = size if i % 2 == 0 else size * 0.4 # Alternate between outer and inner
169
+ px = x + radius * math.cos(angle)
170
+ py = y + radius * math.sin(angle)
171
+ points.append((px, py))
172
+
173
+ # Draw star
174
+ draw.polygon(points, fill=fill_color, outline=outline_color, width=outline_width)
175
+
176
+ return frame