@pinepaper.studio/mcp-server 1.5.5 → 1.6.0

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 (76) hide show
  1. package/README.de.md +71 -0
  2. package/README.es.md +71 -0
  3. package/README.fr.md +71 -0
  4. package/README.hi.md +71 -0
  5. package/README.ja.md +71 -0
  6. package/README.ko.md +71 -0
  7. package/README.md +513 -272
  8. package/README.pt-BR.md +71 -0
  9. package/README.zh-CN.md +71 -0
  10. package/assets/easing-splashes.svg +152 -0
  11. package/assets/equation-path.svg +77 -0
  12. package/assets/hero.svg +126 -0
  13. package/assets/live-badge.svg +62 -0
  14. package/assets/poster-sunburst.svg +106 -0
  15. package/dist/__tests__/fixtures/mock-relations.d.ts +48 -0
  16. package/dist/__tests__/fixtures/mock-relations.d.ts.map +1 -1
  17. package/dist/__tests__/fixtures/mock-relations.js +31 -0
  18. package/dist/__tests__/fixtures/mock-relations.js.map +1 -1
  19. package/dist/agent/types.d.ts +6 -6
  20. package/dist/browser/puppeteer-controller.d.ts +34 -0
  21. package/dist/browser/puppeteer-controller.d.ts.map +1 -1
  22. package/dist/browser/puppeteer-controller.js +116 -5
  23. package/dist/browser/puppeteer-controller.js.map +1 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +32 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/ontology/index.d.ts +2 -0
  28. package/dist/ontology/index.d.ts.map +1 -1
  29. package/dist/ontology/index.js +2 -0
  30. package/dist/ontology/index.js.map +1 -1
  31. package/dist/ontology/ontology-compiler.d.ts +78 -0
  32. package/dist/ontology/ontology-compiler.d.ts.map +1 -0
  33. package/dist/ontology/ontology-compiler.js +235 -0
  34. package/dist/ontology/ontology-compiler.js.map +1 -0
  35. package/dist/ontology/ontology-validator.d.ts +103 -0
  36. package/dist/ontology/ontology-validator.d.ts.map +1 -0
  37. package/dist/ontology/ontology-validator.js +573 -0
  38. package/dist/ontology/ontology-validator.js.map +1 -0
  39. package/dist/ontology/types.d.ts +1 -0
  40. package/dist/ontology/types.d.ts.map +1 -1
  41. package/dist/ontology/validator.d.ts +6 -0
  42. package/dist/ontology/validator.d.ts.map +1 -1
  43. package/dist/ontology/validator.js +34 -5
  44. package/dist/ontology/validator.js.map +1 -1
  45. package/dist/ontology/vocabulary.d.ts.map +1 -1
  46. package/dist/ontology/vocabulary.js +91 -3
  47. package/dist/ontology/vocabulary.js.map +1 -1
  48. package/dist/tools/definitions.d.ts +1 -1
  49. package/dist/tools/definitions.d.ts.map +1 -1
  50. package/dist/tools/definitions.js +978 -27
  51. package/dist/tools/definitions.js.map +1 -1
  52. package/dist/tools/handlers/maps.d.ts.map +1 -1
  53. package/dist/tools/handlers/maps.js +54 -1
  54. package/dist/tools/handlers/maps.js.map +1 -1
  55. package/dist/tools/handlers/ontology.d.ts.map +1 -1
  56. package/dist/tools/handlers/ontology.js +15 -3
  57. package/dist/tools/handlers/ontology.js.map +1 -1
  58. package/dist/tools/handlers.d.ts.map +1 -1
  59. package/dist/tools/handlers.js +137 -6
  60. package/dist/tools/handlers.js.map +1 -1
  61. package/dist/tools/minimal-descriptions.d.ts.map +1 -1
  62. package/dist/tools/minimal-descriptions.js +20 -1
  63. package/dist/tools/minimal-descriptions.js.map +1 -1
  64. package/dist/tools/toolkits.d.ts.map +1 -1
  65. package/dist/tools/toolkits.js +25 -2
  66. package/dist/tools/toolkits.js.map +1 -1
  67. package/dist/types/code-generator.d.ts +190 -2
  68. package/dist/types/code-generator.d.ts.map +1 -1
  69. package/dist/types/code-generator.js +1068 -30
  70. package/dist/types/code-generator.js.map +1 -1
  71. package/dist/types/schemas.d.ts +2726 -116
  72. package/dist/types/schemas.d.ts.map +1 -1
  73. package/dist/types/schemas.js +594 -4
  74. package/dist/types/schemas.js.map +1 -1
  75. package/manifest.json +299 -71
  76. package/package.json +14 -3
@@ -0,0 +1,71 @@
1
+ # Servidor MCP do PinePaper
2
+
3
+ > Crie gráficos vetoriais animados com IA usando o Model Context Protocol
4
+
5
+ [English](README.md) · [简体中文](README.zh-CN.md) · [日本語](README.ja.md) · [한국어](README.ko.md) · [Español](README.es.md) · **Português (BR)** · [Français](README.fr.md) · [Deutsch](README.de.md) · [हिन्दी](README.hi.md)
6
+
7
+ <p align="center">
8
+ <img src="assets/hero.svg" alt="PinePaper MCP — gráficos animados feitos por chamadas de ferramenta" width="840">
9
+ </p>
10
+
11
+ *O banner acima é um SVG animado, exportado direto de chamadas de ferramenta do PinePaper — sem vídeo, sem GIF. Abra no GitHub e veja o movimento.*
12
+
13
+ ## Visão geral
14
+
15
+ O servidor MCP do PinePaper permite que assistentes de IA criem e animem gráficos no [PinePaper Studio](https://pinepaper.studio) via Model Context Protocol. Funciona com qualquer IA que suporte chamadas de ferramenta MCP (Claude, GPT, Gemini, modelos locais etc.).
16
+
17
+ São **121 ferramentas**: desenho, animação, diagramas, mapas, tipografia, física, edição de imagens, visualização de dados e exportação:
18
+
19
+ - Criar texto, formas, geometria e gráficos complexos
20
+ - Animar com **relações** declarativas em vez de keyframes
21
+ - Fundos procedurais e trajetórias por equações
22
+ - Diagramas, mapas, gráficos e colagens de letras
23
+ - Edição de imagens: recorte, chroma key (remover fundo verde), filtros GPU, recorte com laço, detecção de objetos
24
+ - Exportar SVG animado, quadros de vídeo, widgets embutíveis e dados de treinamento para LLM
25
+
26
+ ## Início rápido
27
+
28
+ ```bash
29
+ npm install -g @pinepaper.studio/mcp-server
30
+ ```
31
+
32
+ **Claude Desktop** (`claude_desktop_config.json`):
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "pinepaper": {
38
+ "command": "npx",
39
+ "args": ["-y", "@pinepaper.studio/mcp-server"]
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ Experimente pedir à sua IA:
46
+
47
+ > "Crie um texto HELLO vermelho com animação de pulso"
48
+
49
+ > "Crie um sol e uma terra, e faça a terra orbitar o sol"
50
+
51
+ > "Adicione um fundo de raios de sol em azul e roxo"
52
+
53
+ <p align="center">
54
+ <img src="assets/poster-sunburst.svg" width="220" alt="Pôster de sol girando">
55
+ <img src="assets/easing-splashes.svg" width="300" alt="Formas quicando em cascata">
56
+ <img src="assets/live-badge.svg" width="180" alt="Selo LIVE piscando">
57
+ </p>
58
+
59
+ ## Novidades da 1.6.0
60
+
61
+ - **Ferramentas de edição de imagem**: `pinepaper_crop_image` (recorte em um passo, mantém id e relações) e `pinepaper_chroma_key` (chroma key com limiar autoestimado)
62
+ - **`pinepaper_media` ganha `set_clip`** — reajusta o corte de um clipe já enviado
63
+ - **Auras de shader** em `pinepaper_apply_effect`: `heatmap`, `liquid_metal`, `gem_smoke`
64
+ - **`pinepaper_image_filter` corrigido e ampliado** — ligado ao motor real de filtros GPU com os 15 filtros (família de meio-tom, posterizar, vinheta, HSL, dither…)
65
+ - **README como recurso MCP** — clientes podem ler `pinepaper://docs/readme` (com variantes por idioma) sem sair do protocolo
66
+
67
+ ## Mais
68
+
69
+ A referência completa de ferramentas, toolkits e orçamento de tokens, e a lista de relações estão no [README em inglês](README.md). O fluxo canônico para agentes está no recurso `pinepaper://docs/agent-guide`.
70
+
71
+ Licença MIT · [pinepaper.studio](https://pinepaper.studio)
@@ -0,0 +1,71 @@
1
+ # PinePaper MCP 服务器
2
+
3
+ > 通过 Model Context Protocol,让 AI 创作矢量动画图形
4
+
5
+ [English](README.md) · **简体中文** · [日本語](README.ja.md) · [한국어](README.ko.md) · [Español](README.es.md) · [Português (BR)](README.pt-BR.md) · [Français](README.fr.md) · [Deutsch](README.de.md) · [हिन्दी](README.hi.md)
6
+
7
+ <p align="center">
8
+ <img src="assets/hero.svg" alt="PinePaper MCP — 由工具调用生成的矢量动画" width="840">
9
+ </p>
10
+
11
+ *上面的横幅是动画 SVG——由 PinePaper 工具调用直接导出,没有视频、没有 GIF。在 GitHub 上打开本页即可看到动效。*
12
+
13
+ ## 概述
14
+
15
+ PinePaper MCP 服务器让 AI 助手通过 MCP 协议在 [PinePaper Studio](https://pinepaper.studio) 中创建和驱动图形动画。兼容任何支持 MCP 工具调用的 AI(Claude、GPT、Gemini、本地模型等)。
16
+
17
+ 服务器提供 **121 个工具**,覆盖绘图、动画、图表、地图、排版、物理、图像编辑、数据可视化与导出:
18
+
19
+ - 创建文本、形状、几何图形与复杂图稿
20
+ - 用行为驱动的**关系(relations)**做动画,而不是逐帧关键帧
21
+ - 程序化背景与参数方程路径
22
+ - 图表、地图、流程图与字母拼贴
23
+ - 图像编辑:裁剪、绿幕抠像、GPU 滤镜、套索抠图、对象检测
24
+ - 导出动画 SVG、视频帧、可嵌入组件与 LLM 训练数据
25
+
26
+ ## 快速开始
27
+
28
+ ```bash
29
+ npm install -g @pinepaper.studio/mcp-server
30
+ ```
31
+
32
+ **Claude Desktop**(`claude_desktop_config.json`):
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "pinepaper": {
38
+ "command": "npx",
39
+ "args": ["-y", "@pinepaper.studio/mcp-server"]
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ 试试对 AI 说:
46
+
47
+ > “创建一个红色的、带脉冲动画的 HELLO 文字”
48
+
49
+ > “创建太阳和地球,让地球绕太阳转”
50
+
51
+ > “加一个蓝紫色的放射状背景”
52
+
53
+ <p align="center">
54
+ <img src="assets/poster-sunburst.svg" width="220" alt="旋转的太阳海报">
55
+ <img src="assets/easing-splashes.svg" width="300" alt="交错缓动的泼溅形状">
56
+ <img src="assets/live-badge.svg" width="180" alt="闪烁的 LIVE 徽章">
57
+ </p>
58
+
59
+ ## 1.6.0 新特性
60
+
61
+ - **图像编辑工具**:`pinepaper_crop_image`(一步裁剪,保留元素 id 与关系)、`pinepaper_chroma_key`(绿幕抠像,阈值自动估计)
62
+ - **`pinepaper_media` 新增 `set_clip`** —— 对已上传的视频/音频重新裁剪时长
63
+ - **着色器光效**:`pinepaper_apply_effect` 支持 `heatmap`、`liquid_metal`、`gem_smoke`
64
+ - **`pinepaper_image_filter` 修复并扩充** —— 直连真实 GPU 滤镜引擎,完整 15 种滤镜(半调家族、色调分离、暗角、HSL、抖动等)
65
+ - **README 成为 MCP 资源** —— 客户端可直接读取 `pinepaper://docs/readme`(含多语言变体)
66
+
67
+ ## 更多
68
+
69
+ 完整的工具参考、工具包(toolkit)与令牌预算、关系类型列表等,请见 [英文版 README](README.md)。文档也可以在 MCP 协议内读取:资源 `pinepaper://docs/agent-guide` 是为 AI 代理准备的规范工作流。
70
+
71
+ MIT 许可证 · [pinepaper.studio](https://pinepaper.studio)
@@ -0,0 +1,152 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 560 230" width="560" height="230">
2
+ <title>The Easing Comparison — five splines, one shared clock</title>
3
+ <rect x="0" y="0" width="560" height="230" fill="#ffffff"/>
4
+
5
+ <!-- ============ header ============ -->
6
+ <text x="36" y="24" font-family="Helvetica, Arial, sans-serif" font-size="7.5" letter-spacing="2.5" fill="#57646f">PINEPAPER STUDIO · MOTION REFERENCE</text>
7
+ <text x="524" y="24" text-anchor="end" font-family="Helvetica, Arial, sans-serif" font-size="7" letter-spacing="2" fill="#57646f">SHARED PERIOD</text>
8
+
9
+ <text x="36" y="46" font-family="Georgia, 'Times New Roman', serif" font-size="21" fill="#16202b">The Easing Comparison</text>
10
+ <text x="282" y="46" font-family="Georgia, 'Times New Roman', serif" font-style="italic" font-size="11.5" fill="#57646f">— five splines, one shared clock</text>
11
+
12
+ <!-- period dial: one revolution = one loop -->
13
+ <circle cx="466" cy="40" r="9" fill="#eef2f6" stroke="#a9bfd4" stroke-width="1"/>
14
+ <line x1="466" y1="40" x2="466" y2="33.5" stroke="#2e5e8f" stroke-width="1.5" stroke-linecap="round">
15
+ <animateTransform attributeName="transform" type="rotate" from="0 466 40" to="360 466 40" dur="4s" begin="0s" repeatCount="indefinite"/>
16
+ </line>
17
+ <circle cx="466" cy="40" r="1.2" fill="#16202b"/>
18
+ <text x="524" y="46" text-anchor="end" font-family="Georgia, 'Times New Roman', serif" font-size="17" fill="#16202b">4.0 s</text>
19
+
20
+ <line x1="36" y1="56" x2="524" y2="56" stroke="#16202b" stroke-width="1"/>
21
+
22
+ <!-- ============ stage frame ============ -->
23
+ <rect x="32" y="66" width="496" height="114" fill="none" stroke="#16202b" stroke-width="1.5"/>
24
+
25
+ <!-- start / finish guides -->
26
+ <line x1="40" y1="76" x2="520" y2="76" stroke="#a9bfd4" stroke-width="1" stroke-dasharray="5 5"/>
27
+ <line x1="40" y1="168" x2="520" y2="168" stroke="#a9bfd4" stroke-width="1" stroke-dasharray="5 5"/>
28
+ <text x="442" y="73.5" text-anchor="end" font-family="'Courier New', monospace" font-size="6" fill="#9aa3ab">x = 0</text>
29
+ <text x="442" y="165.5" text-anchor="end" font-family="'Courier New', monospace" font-size="6" fill="#9aa3ab">x = 1</text>
30
+
31
+ <!-- ============ column 1 · LINEAR ============ -->
32
+ <line x1="46" y1="76" x2="46" y2="168" stroke="#a9bfd4" stroke-width="1.2"/>
33
+ <path d="M67 98 V143 H115" fill="none" stroke="#a9bfd4" stroke-width="0.8"/>
34
+ <path d="M70 100 L114 140" fill="none" stroke="#a9bfd4" stroke-width="1.4"/>
35
+ <circle cx="46" cy="76" r="5" fill="#2e5e8f">
36
+ <animate attributeName="cy" values="76;168;168;76;76" keyTimes="0;0.425;0.5;0.925;1"
37
+ calcMode="spline" keySplines="0 0 1 1;0 0 1 1;0 0 1 1;0 0 1 1"
38
+ dur="4s" begin="0s" repeatCount="indefinite"/>
39
+ </circle>
40
+ <circle cx="70" cy="100" r="2.4" fill="#2e5e8f">
41
+ <animate attributeName="cx" values="70;114;114;70;70" keyTimes="0;0.425;0.5;0.925;1"
42
+ calcMode="linear" dur="4s" begin="0s" repeatCount="indefinite"/>
43
+ <animate attributeName="cy" values="100;140;140;100;100" keyTimes="0;0.425;0.5;0.925;1"
44
+ calcMode="spline" keySplines="0 0 1 1;0 0 1 1;0 0 1 1;0 0 1 1"
45
+ dur="4s" begin="0s" repeatCount="indefinite"/>
46
+ <animate attributeName="opacity" values="1;1;0.35;0.35;1" keyTimes="0;0.5;0.55;0.925;1"
47
+ dur="4s" begin="0s" repeatCount="indefinite"/>
48
+ </circle>
49
+ <text x="92" y="151" text-anchor="middle" font-family="Helvetica, Arial, sans-serif" font-size="6" fill="#9aa3ab">time →</text>
50
+ <text x="64" y="104" font-family="Helvetica, Arial, sans-serif" font-size="6" fill="#9aa3ab" transform="rotate(-90 64 104)">pos</text>
51
+ <text x="78" y="190" text-anchor="middle" font-family="Helvetica, Arial, sans-serif" font-size="8.5" font-weight="bold" letter-spacing="1.5" fill="#16202b">LINEAR</text>
52
+ <rect x="36" y="194" width="84" height="14" fill="#eef2f6"/>
53
+ <rect x="36" y="194" width="5" height="14" fill="#2e5e8f"/>
54
+ <text x="80.5" y="203.5" text-anchor="middle" font-family="'Courier New', monospace" font-size="7.5" fill="#57646f">0 0 1 1</text>
55
+
56
+ <!-- ============ column 2 · EASE-IN ============ -->
57
+ <line x1="148" y1="76" x2="148" y2="168" stroke="#a9bfd4" stroke-width="1.2"/>
58
+ <path d="M169 98 V143 H217" fill="none" stroke="#a9bfd4" stroke-width="0.8"/>
59
+ <path d="M172 100 C190.5 100 216 140 216 140" fill="none" stroke="#a9bfd4" stroke-width="1.4"/>
60
+ <circle cx="148" cy="76" r="5" fill="#2e5e8f">
61
+ <animate attributeName="cy" values="76;168;168;76;76" keyTimes="0;0.425;0.5;0.925;1"
62
+ calcMode="spline" keySplines="0.42 0 1 1;0 0 1 1;0 0 0.58 1;0 0 1 1"
63
+ dur="4s" begin="0s" repeatCount="indefinite"/>
64
+ </circle>
65
+ <circle cx="172" cy="100" r="2.4" fill="#2e5e8f">
66
+ <animate attributeName="cx" values="172;216;216;172;172" keyTimes="0;0.425;0.5;0.925;1"
67
+ calcMode="linear" dur="4s" begin="0s" repeatCount="indefinite"/>
68
+ <animate attributeName="cy" values="100;140;140;100;100" keyTimes="0;0.425;0.5;0.925;1"
69
+ calcMode="spline" keySplines="0.42 0 1 1;0 0 1 1;0 0 0.58 1;0 0 1 1"
70
+ dur="4s" begin="0s" repeatCount="indefinite"/>
71
+ <animate attributeName="opacity" values="1;1;0.35;0.35;1" keyTimes="0;0.5;0.55;0.925;1"
72
+ dur="4s" begin="0s" repeatCount="indefinite"/>
73
+ </circle>
74
+ <text x="180" y="190" text-anchor="middle" font-family="Helvetica, Arial, sans-serif" font-size="8.5" font-weight="bold" letter-spacing="1.5" fill="#16202b">EASE-IN</text>
75
+ <rect x="138" y="194" width="84" height="14" fill="#eef2f6"/>
76
+ <rect x="138" y="194" width="5" height="14" fill="#2e5e8f"/>
77
+ <text x="182.5" y="203.5" text-anchor="middle" font-family="'Courier New', monospace" font-size="7.5" fill="#57646f">.42 0 1 1</text>
78
+
79
+ <!-- ============ column 3 · EASE-OUT ============ -->
80
+ <line x1="250" y1="76" x2="250" y2="168" stroke="#a9bfd4" stroke-width="1.2"/>
81
+ <path d="M271 98 V143 H319" fill="none" stroke="#a9bfd4" stroke-width="0.8"/>
82
+ <path d="M274 100 C274 100 299.5 140 318 140" fill="none" stroke="#a9bfd4" stroke-width="1.4"/>
83
+ <circle cx="250" cy="76" r="5" fill="#2e5e8f">
84
+ <animate attributeName="cy" values="76;168;168;76;76" keyTimes="0;0.425;0.5;0.925;1"
85
+ calcMode="spline" keySplines="0 0 0.58 1;0 0 1 1;0.42 0 1 1;0 0 1 1"
86
+ dur="4s" begin="0s" repeatCount="indefinite"/>
87
+ </circle>
88
+ <circle cx="274" cy="100" r="2.4" fill="#2e5e8f">
89
+ <animate attributeName="cx" values="274;318;318;274;274" keyTimes="0;0.425;0.5;0.925;1"
90
+ calcMode="linear" dur="4s" begin="0s" repeatCount="indefinite"/>
91
+ <animate attributeName="cy" values="100;140;140;100;100" keyTimes="0;0.425;0.5;0.925;1"
92
+ calcMode="spline" keySplines="0 0 0.58 1;0 0 1 1;0.42 0 1 1;0 0 1 1"
93
+ dur="4s" begin="0s" repeatCount="indefinite"/>
94
+ <animate attributeName="opacity" values="1;1;0.35;0.35;1" keyTimes="0;0.5;0.55;0.925;1"
95
+ dur="4s" begin="0s" repeatCount="indefinite"/>
96
+ </circle>
97
+ <text x="282" y="190" text-anchor="middle" font-family="Helvetica, Arial, sans-serif" font-size="8.5" font-weight="bold" letter-spacing="1.5" fill="#16202b">EASE-OUT</text>
98
+ <rect x="240" y="194" width="84" height="14" fill="#eef2f6"/>
99
+ <rect x="240" y="194" width="5" height="14" fill="#2e5e8f"/>
100
+ <text x="284.5" y="203.5" text-anchor="middle" font-family="'Courier New', monospace" font-size="7.5" fill="#57646f">0 0 .58 1</text>
101
+
102
+ <!-- ============ column 4 · EASE-IN-OUT ============ -->
103
+ <line x1="352" y1="76" x2="352" y2="168" stroke="#a9bfd4" stroke-width="1.2"/>
104
+ <path d="M373 98 V143 H421" fill="none" stroke="#a9bfd4" stroke-width="0.8"/>
105
+ <path d="M376 100 C394.5 100 401.5 140 420 140" fill="none" stroke="#a9bfd4" stroke-width="1.4"/>
106
+ <circle cx="352" cy="76" r="5" fill="#2e5e8f">
107
+ <animate attributeName="cy" values="76;168;168;76;76" keyTimes="0;0.425;0.5;0.925;1"
108
+ calcMode="spline" keySplines="0.42 0 0.58 1;0 0 1 1;0.42 0 0.58 1;0 0 1 1"
109
+ dur="4s" begin="0s" repeatCount="indefinite"/>
110
+ </circle>
111
+ <circle cx="376" cy="100" r="2.4" fill="#2e5e8f">
112
+ <animate attributeName="cx" values="376;420;420;376;376" keyTimes="0;0.425;0.5;0.925;1"
113
+ calcMode="linear" dur="4s" begin="0s" repeatCount="indefinite"/>
114
+ <animate attributeName="cy" values="100;140;140;100;100" keyTimes="0;0.425;0.5;0.925;1"
115
+ calcMode="spline" keySplines="0.42 0 0.58 1;0 0 1 1;0.42 0 0.58 1;0 0 1 1"
116
+ dur="4s" begin="0s" repeatCount="indefinite"/>
117
+ <animate attributeName="opacity" values="1;1;0.35;0.35;1" keyTimes="0;0.5;0.55;0.925;1"
118
+ dur="4s" begin="0s" repeatCount="indefinite"/>
119
+ </circle>
120
+ <text x="384" y="190" text-anchor="middle" font-family="Helvetica, Arial, sans-serif" font-size="8.5" font-weight="bold" letter-spacing="1.5" fill="#16202b">EASE-IN-OUT</text>
121
+ <rect x="342" y="194" width="84" height="14" fill="#eef2f6"/>
122
+ <rect x="342" y="194" width="5" height="14" fill="#2e5e8f"/>
123
+ <text x="386.5" y="203.5" text-anchor="middle" font-family="'Courier New', monospace" font-size="7.5" fill="#57646f">.42 0 .58 1</text>
124
+
125
+ <!-- ============ column 5 · SHUTTLE (ping-pong) ============ -->
126
+ <line x1="454" y1="76" x2="454" y2="168" stroke="#a9bfd4" stroke-width="1.2"/>
127
+ <path d="M475 98 V143 H523" fill="none" stroke="#a9bfd4" stroke-width="0.8"/>
128
+ <path d="M478 100 C482.6 100 484.4 140 489 140 C493.6 140 495.4 100 500 100 C504.6 100 506.4 140 511 140 C515.6 140 517.4 100 522 100" fill="none" stroke="#a9bfd4" stroke-width="1.4"/>
129
+ <circle cx="454" cy="76" r="5" fill="#f0a030">
130
+ <animate attributeName="cy" values="76;168;76;168;76" keyTimes="0;0.25;0.5;0.75;1"
131
+ calcMode="spline" keySplines="0.42 0 0.58 1;0.42 0 0.58 1;0.42 0 0.58 1;0.42 0 0.58 1"
132
+ dur="4s" begin="0s" repeatCount="indefinite"/>
133
+ </circle>
134
+ <circle cx="478" cy="100" r="2.4" fill="#f0a030">
135
+ <animate attributeName="cx" values="478;522" keyTimes="0;1"
136
+ calcMode="linear" dur="4s" begin="0s" repeatCount="indefinite"/>
137
+ <animate attributeName="cy" values="100;140;100;140;100" keyTimes="0;0.25;0.5;0.75;1"
138
+ calcMode="spline" keySplines="0.42 0 0.58 1;0.42 0 0.58 1;0.42 0 0.58 1;0.42 0 0.58 1"
139
+ dur="4s" begin="0s" repeatCount="indefinite"/>
140
+ <animate attributeName="opacity" values="1;1;0;0;1" keyTimes="0;0.985;0.993;0.997;1"
141
+ dur="4s" begin="0s" repeatCount="indefinite"/>
142
+ </circle>
143
+ <text x="486" y="190" text-anchor="middle" font-family="Helvetica, Arial, sans-serif" font-size="8.5" font-weight="bold" letter-spacing="1.5" fill="#16202b">SHUTTLE</text>
144
+ <rect x="444" y="194" width="84" height="14" fill="#eef2f6"/>
145
+ <rect x="444" y="194" width="5" height="14" fill="#2e5e8f"/>
146
+ <text x="488.5" y="200.8" text-anchor="middle" font-family="'Courier New', monospace" font-size="7.5" fill="#57646f">.42 0 .58 1</text>
147
+ <text x="488.5" y="206.8" text-anchor="middle" font-family="Helvetica, Arial, sans-serif" font-size="5.5" fill="#9aa3ab">4 segments</text>
148
+
149
+ <!-- ============ footer ============ -->
150
+ <text x="36" y="222" font-family="Helvetica, Arial, sans-serif" font-size="7" fill="#9aa3ab">PinePaper Studio — Motion Reference Card No. 04</text>
151
+ <text x="524" y="222" text-anchor="end" font-family="'Courier New', monospace" font-size="7" fill="#9aa3ab">calcMode="spline" · keySplines · SMIL</text>
152
+ </svg>
@@ -0,0 +1,77 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 560 250" width="560" height="250" role="img" aria-label="Plate 07: the rose curve r equals cosine of two theta draws itself, then a slate dot rides the curve forever.">
2
+ <title>The Equation Figure — r = cos(2θ)</title>
3
+
4
+ <!-- paper -->
5
+ <rect x="0" y="0" width="560" height="250" fill="#ffffff"/>
6
+
7
+ <!-- ================= FIGURE STAGE (left) ================= -->
8
+ <rect x="26" y="16" width="280" height="198" fill="#ffffff" stroke="#16202b" stroke-width="1.5"/>
9
+
10
+ <!-- fig. number, book-style, top-left inside the stage -->
11
+ <text x="38" y="34" font-family="Helvetica, Arial, sans-serif" font-size="8" letter-spacing="1.5" fill="#9aa3ab">FIG. 07</text>
12
+
13
+ <!-- faint polar grid: rings at r = 1/3, 2/3, 1 and the two petal axes -->
14
+ <g fill="none" stroke="#a9bfd4" stroke-width="0.75">
15
+ <circle cx="166" cy="115" r="26"/>
16
+ <circle cx="166" cy="115" r="52"/>
17
+ <circle cx="166" cy="115" r="78"/>
18
+ <line x1="88" y1="115" x2="244" y2="115" stroke-dasharray="5 5"/>
19
+ <line x1="166" y1="37" x2="166" y2="193" stroke-dasharray="5 5"/>
20
+ </g>
21
+ <text x="230" y="53" font-family="Helvetica, Arial, sans-serif" font-size="7" fill="#9aa3ab">r = 1</text>
22
+
23
+ <!-- the pole -->
24
+ <circle cx="166" cy="115" r="1.8" fill="#16202b"/>
25
+
26
+ <!-- the rose r = cos(2θ), inked on by an eased dashoffset reveal -->
27
+ <path id="rosePath" fill="none" stroke="#16202b" stroke-width="1.8" stroke-linejoin="round" stroke-linecap="round"
28
+ stroke-dasharray="755.4" stroke-dashoffset="755.4"
29
+ d="M244 115L243.8 112.3L243.1 109.6L241.9 107L240.2 104.6L238.2 102.3L235.7 100.2L232.8 98.3L229.6 96.8L226 95.5L222.1 94.6L218 94L213.7 93.8L209.2 93.9L204.5 94.5L199.8 95.5L195 96.9L190.2 98.7L185.5 100.8L180.9 103.4L176.4 106.3L172.1 109.5L168 113.1L164.1 117L160.5 121.1L157.3 125.4L154.4 129.9L151.8 134.5L149.7 139.2L147.9 144L146.5 148.8L145.5 153.5L144.9 158.2L144.8 162.7L145 167L145.6 171.1L146.5 175L147.8 178.6L149.3 181.8L151.2 184.7L153.3 187.2L155.6 189.2L158 190.9L160.6 192.1L163.3 192.8L166 193L168.7 192.8L171.4 192.1L174 190.9L176.4 189.2L178.7 187.2L180.8 184.7L182.7 181.8L184.2 178.6L185.5 175L186.4 171.1L187 167L187.2 162.7L187.1 158.2L186.5 153.5L185.5 148.8L184.1 144L182.3 139.2L180.2 134.5L177.6 129.9L174.7 125.4L171.5 121.1L167.9 117L164 113.1L159.9 109.5L155.6 106.3L151.1 103.4L146.5 100.8L141.8 98.7L137 96.9L132.2 95.5L127.5 94.5L122.8 93.9L118.3 93.8L114 94L109.9 94.6L106 95.5L102.4 96.8L99.2 98.3L96.3 100.2L93.8 102.3L91.8 104.6L90.1 107L88.9 109.6L88.2 112.3L88 115L88.2 117.7L88.9 120.4L90.1 123L91.8 125.4L93.8 127.7L96.3 129.8L99.2 131.7L102.4 133.2L106 134.5L109.9 135.4L114 136L118.3 136.2L122.8 136.1L127.5 135.5L132.2 134.5L137 133.1L141.8 131.3L146.5 129.2L151.1 126.6L155.6 123.7L159.9 120.5L164 116.9L167.9 113L171.5 108.9L174.7 104.6L177.6 100.1L180.2 95.5L182.3 90.8L184.1 86L185.5 81.2L186.5 76.5L187.1 71.8L187.2 67.3L187 63L186.4 58.9L185.5 55L184.2 51.4L182.7 48.2L180.8 45.3L178.7 42.8L176.4 40.8L174 39.1L171.4 37.9L168.7 37.2L166 37L163.3 37.2L160.6 37.9L158 39.1L155.6 40.8L153.3 42.8L151.2 45.3L149.3 48.2L147.8 51.4L146.5 55L145.6 58.9L145 63L144.8 67.3L144.9 71.8L145.5 76.5L146.5 81.2L147.9 86L149.7 90.8L151.8 95.5L154.4 100.1L157.3 104.6L160.5 108.9L164.1 113L168 116.9L172.1 120.5L176.4 123.7L180.9 126.6L185.5 129.2L190.2 131.3L195 133.1L199.8 134.5L204.5 135.5L209.2 136.1L213.7 136.2L218 136L222.1 135.4L226 134.5L229.6 133.2L232.8 131.7L235.7 129.8L238.2 127.7L240.2 125.4L241.9 123L243.1 120.4L243.8 117.7L244 115Z">
30
+ <animate attributeName="stroke-dashoffset" values="755.4;0" keyTimes="0;1"
31
+ calcMode="spline" keySplines="0.42 0 0.16 1"
32
+ begin="0.4s" dur="3.8s" fill="freeze"/>
33
+ </path>
34
+
35
+ <!-- the rider: snaps in at the petal tip as the ink finishes, then laps forever at arc-length pace -->
36
+ <g opacity="0">
37
+ <animate attributeName="opacity" values="0;1" begin="4.2s" dur="0.25s" fill="freeze"/>
38
+ <circle r="8" fill="none" stroke="#2e5e8f" stroke-width="1" stroke-opacity="0.25"/>
39
+ <circle r="4.2" fill="#2e5e8f"/>
40
+ <animateMotion begin="4.2s" dur="9s" repeatCount="indefinite" rotate="0">
41
+ <mpath xlink:href="#rosePath" href="#rosePath"/>
42
+ </animateMotion>
43
+ </g>
44
+
45
+ <!-- serif caption beneath the stage, landing together with the finished figure at 4.2s -->
46
+ <text x="166" y="236" text-anchor="middle" font-family="Georgia, 'Times New Roman', serif" font-style="italic" font-size="18" fill="#16202b" opacity="0">r = cos(2θ)<animate attributeName="opacity" values="0;1" begin="3.3s" dur="0.9s" fill="freeze"/></text>
47
+
48
+ <!-- ================= TEXT COLUMN (right) ================= -->
49
+ <text x="332" y="40" font-family="Helvetica, Arial, sans-serif" font-size="10" letter-spacing="2" fill="#2e5e8f">PLATE 07 · EQUATION PATHS</text>
50
+ <line x1="332" y1="50" x2="532" y2="50" stroke="#16202b" stroke-width="1"/>
51
+
52
+ <text font-family="Georgia, 'Times New Roman', serif" font-size="19" fill="#16202b">
53
+ <tspan x="332" y="80">Four petals,</tspan>
54
+ <tspan x="332" y="103">drawn by one signal.</tspan>
55
+ </text>
56
+
57
+ <text font-family="Helvetica, Arial, sans-serif" font-size="10" fill="#57646f">
58
+ <tspan x="332" y="126">The polar form is sampled into a path;</tspan>
59
+ <tspan x="332" y="140">the dot is paced by arc length, so it</tspan>
60
+ <tspan x="332" y="154">rides like a pen, not a metronome.</tspan>
61
+ </text>
62
+
63
+ <!-- code chip -->
64
+ <rect x="332" y="170" width="200" height="24" fill="#eef2f6"/>
65
+ <rect x="332" y="170" width="5" height="24" fill="#2e5e8f"/>
66
+ <text x="345" y="185" font-family="'Courier New', monospace" font-size="10" fill="#16202b">createEquationPath('cos(2t)')</text>
67
+
68
+ <!-- status line: dim until the ink completes, brightens at 4.2s, then blinks while tracing -->
69
+ <circle cx="337.5" cy="211" r="3" fill="#2e9b4e" opacity="0.25">
70
+ <animate attributeName="opacity" values="0.25;1" begin="4.2s" dur="0.3s" fill="freeze"/>
71
+ <animate attributeName="opacity" values="1;0.25;1" keyTimes="0;0.5;1" begin="4.5s" dur="2.4s" repeatCount="indefinite"/>
72
+ </circle>
73
+ <text x="347" y="214" font-family="Helvetica, Arial, sans-serif" font-size="9" fill="#57646f">tracing · θ from 0 to 2π · lap 9 s</text>
74
+
75
+ <!-- footer credit -->
76
+ <text x="532" y="242" text-anchor="end" font-family="Helvetica, Arial, sans-serif" font-size="8" fill="#9aa3ab">PinePaper Studio · hand-set SMIL · equation-path plate</text>
77
+ </svg>
@@ -0,0 +1,126 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="840" height="280" viewBox="0 0 840 280">
2
+ <title>PinePaper MCP &#8212; a tool call becomes motion</title>
3
+
4
+ <!-- paper -->
5
+ <rect x="0" y="0" width="840" height="280" fill="#ffffff"/>
6
+
7
+ <defs>
8
+ <mask id="guideReveal" maskUnits="userSpaceOnUse" x="458" y="22" width="352" height="236">
9
+ <path d="M498,198 C560,90 620,240 690,130 C720,84 750,72 772,68"
10
+ pathLength="520" fill="none" stroke="#ffffff" stroke-width="9"
11
+ stroke-linecap="round" stroke-dasharray="520 520" stroke-dashoffset="520">
12
+ <animate attributeName="stroke-dashoffset"
13
+ values="520;520;0;0;-520;-520"
14
+ keyTimes="0;0.12;0.36;0.76;0.95;1"
15
+ calcMode="spline"
16
+ keySplines="0 0 1 1;.3 0 .25 1;0 0 1 1;.4 0 .6 1;0 0 1 1"
17
+ dur="7s" repeatCount="indefinite"/>
18
+ </path>
19
+ </mask>
20
+ </defs>
21
+
22
+ <!-- ============ LEFT COLUMN : masthead ============ -->
23
+ <text x="48" y="44" font-family="Helvetica, Arial, sans-serif" font-size="9.5"
24
+ letter-spacing="2" fill="#57646f">MODEL CONTEXT PROTOCOL &#215; PAPER.JS</text>
25
+
26
+ <text x="48" y="94" font-family="Georgia, 'Times New Roman', serif" font-size="56"
27
+ fill="#16202b">PinePaper <tspan fill="#2e5e8f">MCP</tspan></text>
28
+
29
+ <line x1="48" y1="106" x2="418" y2="106" stroke="#16202b" stroke-width="1"/>
30
+
31
+ <text x="48" y="128" font-family="Helvetica, Arial, sans-serif" font-size="15"
32
+ fill="#57646f">Animated vector graphics, made by tool calls</text>
33
+
34
+ <!-- code chip : the call -->
35
+ <rect x="48" y="146" width="300" height="78" fill="#eef2f6"/>
36
+ <rect x="48" y="146" width="5" height="78" fill="#2e5e8f">
37
+ <animate attributeName="opacity" values="1;0.35;1;1" keyTimes="0;0.03;0.09;1"
38
+ dur="7s" repeatCount="indefinite"/>
39
+ </rect>
40
+ <g font-family="'Courier New', monospace" font-size="12" fill="#16202b">
41
+ <text x="65" y="164">pinepaper_add_relation({</text>
42
+ <text x="80" y="180">relationType: <tspan fill="#2e5e8f">"moves_along_path"</tspan>,</text>
43
+ <text x="80" y="196">easing: <tspan fill="#2e5e8f">"easeInOut"</tspan>, loop: <tspan fill="#2e9b4e">true</tspan></text>
44
+ <text x="65" y="212">})</text>
45
+ </g>
46
+
47
+ <!-- wire : the call travels from chip to stage -->
48
+ <path id="wirePath" d="M354,185 C400,185 432,193 464,196"
49
+ fill="none" stroke="#a9bfd4" stroke-width="1" stroke-dasharray="4 4"/>
50
+ <circle r="3" fill="#2e5e8f" opacity="0">
51
+ <animate attributeName="opacity" values="0;1;1;0;0" keyTimes="0;0.01;0.1;0.12;1"
52
+ dur="7s" repeatCount="indefinite"/>
53
+ <animateMotion id="wireMove" dur="0.84s" begin="0s;wireMove.end+6.16s"
54
+ fill="freeze" calcMode="spline" keyTimes="0;1" keySplines=".3 0 .7 1">
55
+ <mpath xlink:href="#wirePath" href="#wirePath"/>
56
+ </animateMotion>
57
+ </circle>
58
+
59
+ <!-- ============ RIGHT : framed canvas stage ============ -->
60
+ <rect x="468" y="32" width="332" height="216" fill="#ffffff"
61
+ stroke="#16202b" stroke-width="1.5"/>
62
+ <text x="480" y="48" font-family="Helvetica, Arial, sans-serif" font-size="9"
63
+ letter-spacing="1" fill="#57646f">canvas &#8212; <tspan font-family="'Courier New', monospace">moves_along_path</tspan></text>
64
+
65
+ <!-- origin tick : blinks slate on call arrival, handing off to the guide draw -->
66
+ <circle cx="498" cy="198" r="2.5" fill="#a9bfd4">
67
+ <animate attributeName="fill" values="#a9bfd4;#a9bfd4;#2e5e8f;#a9bfd4;#a9bfd4"
68
+ keyTimes="0;0.12;0.14;0.20;1" dur="7s" repeatCount="indefinite"/>
69
+ </circle>
70
+
71
+ <!-- dashed guide path : draws itself on, then is consumed at cycle end -->
72
+ <path id="guidePath" d="M498,198 C560,90 620,240 690,130 C720,84 750,72 772,68"
73
+ fill="none" stroke="#a9bfd4" stroke-width="1.6" stroke-dasharray="5 5"
74
+ mask="url(#guideReveal)"/>
75
+
76
+ <!-- solid trace drawn behind the rider as it moves -->
77
+ <path d="M498,198 C560,90 620,240 690,130 C720,84 750,72 772,68"
78
+ pathLength="520" fill="none" stroke="#2e5e8f" stroke-width="2"
79
+ stroke-linecap="round" stroke-dasharray="520 520" stroke-dashoffset="520" opacity="0">
80
+ <animate attributeName="stroke-dashoffset" values="520;520;0;0"
81
+ keyTimes="0;0.4;0.72;1" calcMode="spline"
82
+ keySplines="0 0 1 1;.42 0 .1 1;0 0 1 1"
83
+ dur="7s" repeatCount="indefinite"/>
84
+ <animate attributeName="opacity" values="0;0;1;1;0;0"
85
+ keyTimes="0;0.395;0.41;0.74;0.84;1"
86
+ dur="7s" repeatCount="indefinite"/>
87
+ </path>
88
+
89
+ <!-- destination target : blooms as the drawn guide arrives -->
90
+ <g opacity="0">
91
+ <animate attributeName="opacity" values="0;0;1;1;0;0"
92
+ keyTimes="0;0.36;0.40;0.86;0.92;1"
93
+ dur="7s" repeatCount="indefinite"/>
94
+ <circle cx="772" cy="68" r="5.5" fill="none" stroke="#f0a030" stroke-width="1.5"/>
95
+ <circle cx="772" cy="68" r="1.5" fill="#f0a030"/>
96
+ </g>
97
+
98
+ <!-- the rider : an item executing the relation with true eased motion -->
99
+ <g opacity="0">
100
+ <animate attributeName="opacity" values="0;0;1;1;0;0"
101
+ keyTimes="0;0.40;0.41;0.72;0.76;1"
102
+ dur="7s" repeatCount="indefinite"/>
103
+ <rect x="-7" y="-7" width="14" height="14" rx="3.5"
104
+ fill="#2e5e8f" stroke="#ffffff" stroke-width="2"/>
105
+ <animateMotion id="riderMove" dur="2.24s" begin="2.8s;riderMove.end+4.76s"
106
+ fill="freeze" rotate="auto" calcMode="spline"
107
+ keyTimes="0;1" keySplines=".42 0 .1 1">
108
+ <mpath xlink:href="#guidePath" href="#guidePath"/>
109
+ </animateMotion>
110
+ </g>
111
+
112
+ <!-- run status : lit while the relation executes -->
113
+ <circle cx="732" cy="235" r="3" fill="#2e9b4e" opacity="0.3">
114
+ <animate attributeName="opacity" values="0.3;0.3;1;1;0.3;0.3"
115
+ keyTimes="0;0.38;0.42;0.72;0.78;1"
116
+ dur="7s" repeatCount="indefinite"/>
117
+ </circle>
118
+ <text x="742" y="238" font-family="Helvetica, Arial, sans-serif" font-size="9"
119
+ fill="#57646f">running</text>
120
+
121
+ <!-- footer credits : one hairline-aligned band -->
122
+ <text x="48" y="266" font-family="Helvetica, Arial, sans-serif" font-size="9"
123
+ fill="#9aa3ab">composed with PinePaper Studio</text>
124
+ <text x="800" y="266" text-anchor="end" font-family="Helvetica, Arial, sans-serif"
125
+ font-size="9" fill="#9aa3ab">spec 01 &#8212; a tool call becomes motion</text>
126
+ </svg>
@@ -0,0 +1,62 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="300" height="104" viewBox="0 0 300 104" role="img" aria-label="LIVE status chip - widget loops forever">
2
+ <defs>
3
+ <linearGradient id="panelTint" x1="0" y1="0" x2="0" y2="1">
4
+ <stop offset="0" stop-color="#eef2f6"/>
5
+ <stop offset="1" stop-color="#e8eff5"/>
6
+ </linearGradient>
7
+ <clipPath id="chipClip">
8
+ <rect x="1.5" y="1.5" width="297" height="101" rx="10"/>
9
+ </clipPath>
10
+ </defs>
11
+
12
+ <!-- chip panel: fill first, slate accent bar clipped inside, ink frame on top -->
13
+ <rect x="1.5" y="1.5" width="297" height="101" rx="10" fill="url(#panelTint)"/>
14
+ <rect x="1.5" y="1.5" width="6" height="101" fill="#2e5e8f" clip-path="url(#chipClip)"/>
15
+ <rect x="1.5" y="1.5" width="297" height="101" rx="10" fill="none" stroke="#16202b" stroke-width="1.5"/>
16
+
17
+ <!-- status dot: radar ping ring (born fully under the dot, fades in, nearly gone before the type column) -->
18
+ <circle cx="34" cy="47" r="7" fill="none" stroke="#2e9b4e" stroke-width="1.4">
19
+ <animate attributeName="r" values="6.3;15" keyTimes="0;1" dur="2s"
20
+ calcMode="spline" keySplines="0.2 0.4 0.4 1" repeatCount="indefinite"/>
21
+ <animate attributeName="opacity" values="0;0.5;0.08;0" keyTimes="0;0.08;0.7;1" dur="2s"
22
+ repeatCount="indefinite"/>
23
+ </circle>
24
+ <!-- status dot: breathes via r (true center pivot); peak at 0.1 so the ping reads as emitted by the breath -->
25
+ <circle cx="34" cy="47" r="7" fill="#2e9b4e">
26
+ <animate attributeName="r" values="7;8.3;7;7" keyTimes="0;0.1;0.45;1" dur="2s"
27
+ calcMode="spline" keySplines="0.3 0 0.3 1;0.3 0 0.3 1;0 0 1 1"
28
+ repeatCount="indefinite"/>
29
+ <animate attributeName="opacity" values="1;0.85;1;1" keyTimes="0;0.1;0.45;1" dur="2s"
30
+ calcMode="spline" keySplines="0.3 0 0.3 1;0.3 0 0.3 1;0 0 1 1"
31
+ repeatCount="indefinite"/>
32
+ </circle>
33
+
34
+ <!-- left column: overline / display / caption -->
35
+ <text x="58" y="23" font-family="Helvetica, Arial, sans-serif" font-size="7.5"
36
+ letter-spacing="2.4" fill="#9aa3ab">STATUS</text>
37
+ <text x="58" y="60" font-family="Georgia, 'Times New Roman', serif" font-size="38"
38
+ letter-spacing="0.5" fill="#16202b">LIVE</text>
39
+ <text x="58" y="81" font-family="Helvetica, Arial, sans-serif" font-size="11"
40
+ letter-spacing="0.3" fill="#57646f">widget &#183; loops forever</text>
41
+
42
+ <!-- right column: hairline rule, big-stat serif number, loop-period meter, two-line caption -->
43
+ <line x1="205" y1="26" x2="205" y2="82" stroke="#a9bfd4" stroke-width="1"/>
44
+ <text x="219" y="52" font-family="Georgia, 'Times New Roman', serif" font-size="26"
45
+ fill="#16202b">2.0s</text>
46
+ <!-- meter track + sweep: fills while the ping expands, then clears; one visible loop period -->
47
+ <line x1="219" y1="58.5" x2="271" y2="58.5" stroke="#a9bfd4" stroke-width="1"/>
48
+ <line x1="219" y1="58.5" x2="271" y2="58.5" stroke="#2e5e8f" stroke-width="1.5"
49
+ stroke-dasharray="52 52" stroke-dashoffset="52">
50
+ <animate attributeName="stroke-dashoffset" values="52;0;-52" keyTimes="0;0.5;1" dur="2s"
51
+ calcMode="spline" keySplines="0.4 0 0.3 1;0.4 0 0.3 1"
52
+ repeatCount="indefinite"/>
53
+ </line>
54
+ <text x="219" y="72" font-family="Helvetica, Arial, sans-serif" font-size="8.5"
55
+ letter-spacing="0.3" fill="#57646f">loop period</text>
56
+ <text x="219" y="84" font-family="Helvetica, Arial, sans-serif" font-size="8.5"
57
+ letter-spacing="0.3" fill="#9aa3ab">pure SMIL</text>
58
+
59
+ <!-- footer credit -->
60
+ <text x="289" y="95" text-anchor="end" font-family="Helvetica, Arial, sans-serif"
61
+ font-size="7" letter-spacing="0.4" fill="#9aa3ab">pinepaper.studio</text>
62
+ </svg>