pi-extensions 0.1.9

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 (135) hide show
  1. package/.ralph/import-cc-codex.md +31 -0
  2. package/.ralph/import-cc-codex.state.json +14 -0
  3. package/.ralph/mario-not-impl.md +69 -0
  4. package/.ralph/mario-not-impl.state.json +14 -0
  5. package/.ralph/mario-not-spec.md +163 -0
  6. package/.ralph/mario-not-spec.state.json +14 -0
  7. package/LICENSE +21 -0
  8. package/README.md +65 -0
  9. package/RELEASING.md +34 -0
  10. package/agent-guidance/CHANGELOG.md +4 -0
  11. package/agent-guidance/README.md +102 -0
  12. package/agent-guidance/agent-guidance.ts +147 -0
  13. package/agent-guidance/package.json +22 -0
  14. package/agent-guidance/setup.sh +75 -0
  15. package/agent-guidance/templates/CLAUDE.md +5 -0
  16. package/agent-guidance/templates/CODEX.md +92 -0
  17. package/agent-guidance/templates/GEMINI.md +5 -0
  18. package/arcade/CHANGELOG.md +4 -0
  19. package/arcade/README.md +85 -0
  20. package/arcade/assets/picman.png +0 -0
  21. package/arcade/assets/ping.png +0 -0
  22. package/arcade/assets/spice-invaders.png +0 -0
  23. package/arcade/assets/tetris.png +0 -0
  24. package/arcade/mario-not/README.md +30 -0
  25. package/arcade/mario-not/boss.js +103 -0
  26. package/arcade/mario-not/camera.js +59 -0
  27. package/arcade/mario-not/collision.js +91 -0
  28. package/arcade/mario-not/colors.js +36 -0
  29. package/arcade/mario-not/constants.js +97 -0
  30. package/arcade/mario-not/core.js +39 -0
  31. package/arcade/mario-not/death.js +77 -0
  32. package/arcade/mario-not/effects.js +84 -0
  33. package/arcade/mario-not/enemies.js +31 -0
  34. package/arcade/mario-not/engine.js +171 -0
  35. package/arcade/mario-not/fireballs.js +98 -0
  36. package/arcade/mario-not/items.js +24 -0
  37. package/arcade/mario-not/levels.js +403 -0
  38. package/arcade/mario-not/logic.js +104 -0
  39. package/arcade/mario-not/mario-not.ts +297 -0
  40. package/arcade/mario-not/player.js +244 -0
  41. package/arcade/mario-not/render.js +257 -0
  42. package/arcade/mario-not/spec.md +548 -0
  43. package/arcade/mario-not/state.js +246 -0
  44. package/arcade/mario-not/tests/e2e.test.js +855 -0
  45. package/arcade/mario-not/tests/engine.test.js +888 -0
  46. package/arcade/mario-not/tests/fixtures/story0-frame.txt +4 -0
  47. package/arcade/mario-not/tests/fixtures/story1-camera.txt +4 -0
  48. package/arcade/mario-not/tests/fixtures/story1-glyphs.txt +4 -0
  49. package/arcade/mario-not/tests/fixtures/story10-item.txt +4 -0
  50. package/arcade/mario-not/tests/fixtures/story11-hazards.txt +4 -0
  51. package/arcade/mario-not/tests/fixtures/story12-used-block.txt +4 -0
  52. package/arcade/mario-not/tests/fixtures/story13-pipes.txt +4 -0
  53. package/arcade/mario-not/tests/fixtures/story14-goal.txt +4 -0
  54. package/arcade/mario-not/tests/fixtures/story15-hud-narrow.txt +2 -0
  55. package/arcade/mario-not/tests/fixtures/story16-unknown-tile.txt +4 -0
  56. package/arcade/mario-not/tests/fixtures/story17-mix.txt +4 -0
  57. package/arcade/mario-not/tests/fixtures/story18-hud-score.txt +2 -0
  58. package/arcade/mario-not/tests/fixtures/story19-cue.txt +4 -0
  59. package/arcade/mario-not/tests/fixtures/story2-enemy.txt +4 -0
  60. package/arcade/mario-not/tests/fixtures/story20-camera-offset.txt +4 -0
  61. package/arcade/mario-not/tests/fixtures/story21-hud-zero.txt +2 -0
  62. package/arcade/mario-not/tests/fixtures/story22-big-viewport.txt +4 -0
  63. package/arcade/mario-not/tests/fixtures/story23-camera-negative.txt +4 -0
  64. package/arcade/mario-not/tests/fixtures/story24-camera-width.txt +4 -0
  65. package/arcade/mario-not/tests/fixtures/story25-camera-positive.txt +4 -0
  66. package/arcade/mario-not/tests/fixtures/story26-hud-lives.txt +2 -0
  67. package/arcade/mario-not/tests/fixtures/story27-hud-coins.txt +2 -0
  68. package/arcade/mario-not/tests/fixtures/story28-item-viewport.txt +4 -0
  69. package/arcade/mario-not/tests/fixtures/story29-enemy-viewport.txt +4 -0
  70. package/arcade/mario-not/tests/fixtures/story3-hud.txt +2 -0
  71. package/arcade/mario-not/tests/fixtures/story30-hud-score.txt +2 -0
  72. package/arcade/mario-not/tests/fixtures/story31-particles-viewport.txt +4 -0
  73. package/arcade/mario-not/tests/fixtures/story32-paused-frame.txt +4 -0
  74. package/arcade/mario-not/tests/fixtures/story4-big.txt +4 -0
  75. package/arcade/mario-not/tests/fixtures/story5-resume-hud.txt +2 -0
  76. package/arcade/mario-not/tests/fixtures/story6-particles.txt +4 -0
  77. package/arcade/mario-not/tests/fixtures/story6-paused.txt +4 -0
  78. package/arcade/mario-not/tests/fixtures/story7-powerup.txt +4 -0
  79. package/arcade/mario-not/tests/fixtures/story8-hud-time.txt +2 -0
  80. package/arcade/mario-not/tests/fixtures/story9-hud-level.txt +2 -0
  81. package/arcade/mario-not/tiles.js +79 -0
  82. package/arcade/mario-not/tsconfig.json +14 -0
  83. package/arcade/mario-not/types.js +225 -0
  84. package/arcade/package.json +26 -0
  85. package/arcade/picman.ts +328 -0
  86. package/arcade/ping.ts +594 -0
  87. package/arcade/spice-invaders.ts +1104 -0
  88. package/arcade/tetris.ts +662 -0
  89. package/code-actions/CHANGELOG.md +4 -0
  90. package/code-actions/README.md +65 -0
  91. package/code-actions/actions.ts +107 -0
  92. package/code-actions/index.ts +148 -0
  93. package/code-actions/package.json +22 -0
  94. package/code-actions/search.ts +79 -0
  95. package/code-actions/snippets.ts +179 -0
  96. package/code-actions/ui.ts +120 -0
  97. package/files-widget/CHANGELOG.md +90 -0
  98. package/files-widget/DESIGN.md +452 -0
  99. package/files-widget/README.md +122 -0
  100. package/files-widget/TODO.md +141 -0
  101. package/files-widget/browser.ts +922 -0
  102. package/files-widget/comment.ts +5 -0
  103. package/files-widget/constants.ts +18 -0
  104. package/files-widget/demo.svg +1 -0
  105. package/files-widget/file-tree.ts +224 -0
  106. package/files-widget/file-viewer.ts +93 -0
  107. package/files-widget/git.ts +107 -0
  108. package/files-widget/index.ts +140 -0
  109. package/files-widget/input-utils.ts +3 -0
  110. package/files-widget/package.json +22 -0
  111. package/files-widget/types.ts +28 -0
  112. package/files-widget/utils.ts +26 -0
  113. package/files-widget/viewer.ts +424 -0
  114. package/import-cc-codex/research/import-chats-from-other-agents.md +135 -0
  115. package/import-cc-codex/spec.md +79 -0
  116. package/package.json +29 -0
  117. package/ralph-wiggum/CHANGELOG.md +7 -0
  118. package/ralph-wiggum/README.md +96 -0
  119. package/ralph-wiggum/SKILL.md +73 -0
  120. package/ralph-wiggum/index.ts +792 -0
  121. package/ralph-wiggum/package.json +25 -0
  122. package/raw-paste/CHANGELOG.md +7 -0
  123. package/raw-paste/README.md +52 -0
  124. package/raw-paste/index.ts +112 -0
  125. package/raw-paste/package.json +22 -0
  126. package/tab-status/CHANGELOG.md +4 -0
  127. package/tab-status/README.md +61 -0
  128. package/tab-status/assets/tab-status.png +0 -0
  129. package/tab-status/package.json +22 -0
  130. package/tab-status/tab-status.ts +179 -0
  131. package/usage-extension/CHANGELOG.md +17 -0
  132. package/usage-extension/README.md +120 -0
  133. package/usage-extension/index.ts +628 -0
  134. package/usage-extension/package.json +22 -0
  135. package/usage-extension/screenshot.png +0 -0
@@ -0,0 +1,4 @@
1
+
2
+
3
+ <>
4
+ ################
@@ -0,0 +1,4 @@
1
+
2
+
3
+ <>
4
+ ################
@@ -0,0 +1,4 @@
1
+ []??o |>
2
+
3
+ <>
4
+ ############
@@ -0,0 +1,4 @@
1
+
2
+
3
+ %} <>
4
+ ########
@@ -0,0 +1,4 @@
1
+ /\~~
2
+
3
+ <>
4
+ ########
@@ -0,0 +1,4 @@
1
+ ||
2
+ ||
3
+
4
+ ######
@@ -0,0 +1,2 @@
1
+ NOT MARIO L
2
+ SCORE 0000
@@ -0,0 +1,4 @@
1
+
2
+
3
+ OO%}<>
4
+ ########
@@ -0,0 +1,2 @@
1
+ NOT MARIO L2 TIME 123
2
+ SCORE 000042 COIN 03 LIVES 5
@@ -0,0 +1,4 @@
1
+
2
+
3
+ READY
4
+ ########
@@ -0,0 +1,4 @@
1
+
2
+
3
+ OO <>
4
+ ########
@@ -0,0 +1,4 @@
1
+ o |>####
2
+
3
+
4
+ ########
@@ -0,0 +1,2 @@
1
+ NOT MARIO L1 TIME 0
2
+ SCORE 000000 COIN 00 LIVES 3
@@ -0,0 +1,4 @@
1
+ []??o |>
2
+
3
+
4
+ ########
@@ -0,0 +1,4 @@
1
+ |>||||/\
2
+
3
+
4
+ ########
@@ -0,0 +1,4 @@
1
+ ||||/\~~
2
+
3
+
4
+ ########
@@ -0,0 +1,2 @@
1
+ NOT MARIO L1 TIME 300
2
+ SCORE 000000 COIN 00 LIVES 7
@@ -0,0 +1,2 @@
1
+ NOT MARIO L1 TIME 300
2
+ SCORE 000000 COIN 09 LIVES 3
@@ -0,0 +1,2 @@
1
+ NOT MARIO L1 TIME 299
2
+ SCORE 000100 COIN 01 LIVES 3
@@ -0,0 +1,2 @@
1
+ NOT MARIO L1 TIME 300
2
+ SCORE 000007 COIN 00 LIVES 3
@@ -0,0 +1,4 @@
1
+
2
+
3
+ PAUSED
4
+ ########
@@ -0,0 +1,4 @@
1
+
2
+ <>
3
+ []
4
+ ########
@@ -0,0 +1,2 @@
1
+ NOT MARIO L1 TIME 298
2
+ SCORE 000100 COIN 01 LIVES 3
@@ -0,0 +1,4 @@
1
+
2
+ **
3
+ <>
4
+ ########
@@ -0,0 +1,4 @@
1
+
2
+
3
+ <> PAUSED
4
+ ################
@@ -0,0 +1,4 @@
1
+
2
+
3
+ <>POWER UP
4
+ ################
@@ -0,0 +1,2 @@
1
+ NOT MARIO L1 TIME 13
2
+ SCORE 000000 COIN 00 LIVES 3
@@ -0,0 +1,2 @@
1
+ NOT MARIO L3 TIME 300
2
+ SCORE 000000 COIN 00 LIVES 3
@@ -0,0 +1,79 @@
1
+ // @ts-check
2
+ "use strict";
3
+
4
+ const { COLORS: C } = require("./colors.js");
5
+
6
+ /**
7
+ * @typedef {Object} Level
8
+ * @property {number} width
9
+ * @property {number} height
10
+ * @property {string[][]} tiles
11
+ */
12
+
13
+ /** @type {Set<string>} */
14
+ const SOLID_TILES = new Set(["#", "B", "?", "U", "T", "P", "C"]);
15
+ /** @type {Set<string>} */
16
+ const HAZARD_TILES = new Set(["^", "~", "L"]);
17
+
18
+ /** @type {Record<string, string>} */
19
+ const TILE_GLYPHS = {
20
+ "#": `${C.brown}##${C.reset}`,
21
+ "B": `${C.brown}[]${C.reset}`,
22
+ "?": `${C.brightYellow}??${C.reset}`,
23
+ "U": `${C.gray}..${C.reset}`,
24
+ "o": `${C.brightYellow}o ${C.reset}`,
25
+ "T": `${C.green}||${C.reset}`,
26
+ "P": `${C.green}||${C.reset}`,
27
+ "G": `${C.red}|>${C.reset}`,
28
+ "F": `${C.white}||${C.reset}`,
29
+ "^": `${C.gray}/\\${C.reset}`,
30
+ "~": `${C.blue}~~${C.reset}`,
31
+ "L": `${C.orange}}{${C.reset}`,
32
+ "C": `${C.gray}[]${C.reset}`,
33
+ "A": `${C.white}/\\${C.reset}`,
34
+ };
35
+
36
+ /** @param {string} tile @returns {string} */
37
+ function tileGlyph(tile) {
38
+ return TILE_GLYPHS[tile] || " ";
39
+ }
40
+
41
+ /** @param {Level} level @param {number} x @param {number} y @returns {string} */
42
+ function getTile(level, x, y) {
43
+ const tx = Math.floor(x);
44
+ const ty = Math.floor(y);
45
+ if (tx < 0 || tx >= level.width || ty < 0 || ty >= level.height) return " ";
46
+ return level.tiles[ty][tx];
47
+ }
48
+
49
+ /** @param {Level} level @param {number} x @param {number} y @param {string} tile */
50
+ function setTile(level, x, y, tile) {
51
+ const tx = Math.floor(x);
52
+ const ty = Math.floor(y);
53
+ if (tx < 0 || tx >= level.width || ty < 0 || ty >= level.height) return;
54
+ level.tiles[ty][tx] = tile;
55
+ }
56
+
57
+ /** @param {Level} level @param {number} x @param {number} y @returns {boolean} */
58
+ function isSolidAt(level, x, y) {
59
+ const tx = Math.floor(x);
60
+ const ty = Math.floor(y);
61
+ if (tx < 0 || tx >= level.width || ty < 0 || ty >= level.height) return true;
62
+ return SOLID_TILES.has(level.tiles[ty][tx]);
63
+ }
64
+
65
+ /** @param {Level} level @param {number} x @param {number} y @returns {boolean} */
66
+ function isHazardAt(level, x, y) {
67
+ return HAZARD_TILES.has(getTile(level, x, y));
68
+ }
69
+
70
+ module.exports = {
71
+ SOLID_TILES,
72
+ HAZARD_TILES,
73
+ TILE_GLYPHS,
74
+ tileGlyph,
75
+ getTile,
76
+ setTile,
77
+ isSolidAt,
78
+ isHazardAt,
79
+ };
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "allowJs": true,
6
+ "checkJs": true,
7
+ "noEmit": true,
8
+ "strict": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true
11
+ },
12
+ "include": ["./**/*.js"],
13
+ "exclude": ["./tests/**"]
14
+ }
@@ -0,0 +1,225 @@
1
+ // @ts-check
2
+ "use strict";
3
+
4
+ /**
5
+ * @typedef {Object} Config
6
+ * @property {number} dt
7
+ * @property {number} gravity
8
+ * @property {number} maxFall
9
+ * @property {number} jumpVel
10
+ * @property {number} walkSpeed
11
+ * @property {number} runSpeed
12
+ * @property {number} groundAccel
13
+ * @property {number} groundDecel
14
+ * @property {number} airAccel
15
+ * @property {number} enemySpeed
16
+ * @property {number} mushroomScore
17
+ * @property {number} viewportWidth
18
+ */
19
+
20
+ /**
21
+ * @typedef {Object} Level
22
+ * @property {number} width
23
+ * @property {number} height
24
+ * @property {string[][]} tiles
25
+ */
26
+
27
+ /**
28
+ * @typedef {Object} PlayerState
29
+ * @property {number} x
30
+ * @property {number} y
31
+ * @property {number} vx
32
+ * @property {number} vy
33
+ * @property {number} facing
34
+ * @property {boolean} onGround
35
+ * @property {"small" | "big"} size
36
+ * @property {number} invuln
37
+ */
38
+
39
+ /**
40
+ * @typedef {Object} EnemyState
41
+ * @property {number} x
42
+ * @property {number} y
43
+ * @property {number} vx
44
+ * @property {number} vy
45
+ * @property {boolean} alive
46
+ * @property {boolean} onGround
47
+ */
48
+
49
+ /**
50
+ * @typedef {Object} ItemState
51
+ * @property {number} x
52
+ * @property {number} y
53
+ * @property {number} vx
54
+ * @property {number} vy
55
+ * @property {boolean} alive
56
+ * @property {boolean} onGround
57
+ */
58
+
59
+ /**
60
+ * @typedef {Object} ParticleState
61
+ * @property {number} x
62
+ * @property {number} y
63
+ * @property {number} vx
64
+ * @property {number} vy
65
+ * @property {number} life
66
+ */
67
+
68
+ /**
69
+ * @typedef {Object} FireballState
70
+ * @property {number} x
71
+ * @property {number} y
72
+ * @property {number} vx
73
+ * @property {number} vy
74
+ * @property {boolean} alive
75
+ * @property {"linear" | "wave"} pattern
76
+ * @property {number} startY - starting Y for wave pattern
77
+ */
78
+
79
+ /**
80
+ * @typedef {Object} FireballSpawner
81
+ * @property {number} x
82
+ * @property {number} y
83
+ * @property {number} timer
84
+ * @property {number} interval
85
+ * @property {number} direction - 1 for right, -1 for left
86
+ */
87
+
88
+ /**
89
+ * @typedef {Object} BossState
90
+ * @property {number} x
91
+ * @property {number} y
92
+ * @property {number} vx
93
+ * @property {number} vy
94
+ * @property {boolean} alive
95
+ * @property {number} health
96
+ * @property {number} maxHealth
97
+ * @property {number} invuln
98
+ * @property {boolean} onGround
99
+ */
100
+
101
+ /**
102
+ * @typedef {Object} Cue
103
+ * @property {string} text
104
+ * @property {number} ttl
105
+ * @property {boolean} persist
106
+ */
107
+
108
+ /**
109
+ * @typedef {Object} GameState
110
+ * @property {Level} level
111
+ * @property {() => number} rng
112
+ * @property {Config} config
113
+ * @property {number} tick
114
+ * @property {PlayerState} player
115
+ * @property {EnemyState[]} enemies
116
+ * @property {ItemState[]} items
117
+ * @property {ParticleState[]} particles
118
+ * @property {Cue | null} cue
119
+ * @property {number} cameraX
120
+ * @property {number} score
121
+ * @property {number} coins
122
+ * @property {number} lives
123
+ * @property {number} time
124
+ * @property {number} levelIndex
125
+ * @property {boolean} mushroomSpawned
126
+ * @property {"playing" | "paused" | "dead" | "level_clear" | "game_over" | "level_intro"} mode
127
+ * @property {number} spawnX
128
+ * @property {number} spawnY
129
+ * @property {number} deathTimer
130
+ * @property {boolean} deathJumped
131
+ * @property {FireballState[]} fireballs
132
+ * @property {FireballSpawner[]} fireballSpawners
133
+ * @property {BossState | null} boss
134
+ */
135
+
136
+ /**
137
+ * @typedef {Object} InputState
138
+ * @property {boolean} [left]
139
+ * @property {boolean} [right]
140
+ * @property {boolean} [jump]
141
+ * @property {boolean} [run]
142
+ */
143
+
144
+ /**
145
+ * @typedef {Object} GameOptions
146
+ * @property {Level} level
147
+ * @property {Partial<Config>} [config]
148
+ * @property {number} [seed]
149
+ * @property {number} [startX]
150
+ * @property {number} [startY]
151
+ * @property {number} [levelIndex]
152
+ */
153
+
154
+ /** @typedef {"playing" | "paused" | "dead" | "level_clear" | "game_over" | "level_intro"} GameMode */
155
+
156
+ /**
157
+ * @typedef {Object} SnapshotPlayerState
158
+ * @property {number} x
159
+ * @property {number} y
160
+ * @property {number} vx
161
+ * @property {number} vy
162
+ * @property {boolean} onGround
163
+ * @property {number} facing
164
+ * @property {"small" | "big"} size
165
+ * @property {number} invuln
166
+ */
167
+
168
+ /**
169
+ * @typedef {Object} SnapshotEnemyState
170
+ * @property {number} x
171
+ * @property {number} y
172
+ * @property {number} vx
173
+ * @property {number} vy
174
+ * @property {boolean} alive
175
+ */
176
+
177
+ /**
178
+ * @typedef {Object} SnapshotItemState
179
+ * @property {number} x
180
+ * @property {number} y
181
+ * @property {number} vx
182
+ * @property {number} vy
183
+ * @property {boolean} alive
184
+ * @property {boolean} onGround
185
+ */
186
+
187
+ /**
188
+ * @typedef {Object} SnapshotState
189
+ * @property {number} tick
190
+ * @property {number} score
191
+ * @property {number} coins
192
+ * @property {number} lives
193
+ * @property {number} time
194
+ * @property {number} levelIndex
195
+ * @property {boolean} mushroomSpawned
196
+ * @property {GameMode} mode
197
+ * @property {SnapshotPlayerState} player
198
+ * @property {SnapshotEnemyState[]} enemies
199
+ * @property {SnapshotItemState[]} items
200
+ */
201
+
202
+ /**
203
+ * @typedef {Object} SaveState
204
+ * @property {number} version
205
+ * @property {{ lines: string[] }} level
206
+ * @property {{ x: number, y: number, vx: number, vy: number, facing: number, size: "small" | "big", invuln: number, dead?: boolean }} player
207
+ * @property {{ x: number, y: number, vx: number, vy: number, alive: boolean }[]} enemies
208
+ * @property {{ x: number, y: number, vx: number, vy: number, alive: boolean }[]} items
209
+ * @property {number} score
210
+ * @property {number} coins
211
+ * @property {number} lives
212
+ * @property {number} time
213
+ * @property {number} levelIndex
214
+ * @property {boolean} mushroomSpawned
215
+ * @property {number} [spawnX]
216
+ * @property {number} [spawnY]
217
+ * @property {GameMode} [mode]
218
+ * @property {boolean} [paused]
219
+ * @property {boolean} [gameOver]
220
+ * @property {{ x: number, y: number, vx: number, vy: number, alive: boolean, pattern: "linear" | "wave", startY: number }[]} [fireballs]
221
+ * @property {{ x: number, y: number, timer: number, interval: number, direction: number }[]} [fireballSpawners]
222
+ * @property {{ x: number, y: number, vx: number, vy: number, alive: boolean, health: number, maxHealth: number, invuln: number, onGround: boolean } | null} [boss]
223
+ */
224
+
225
+ module.exports = {};
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@tmustier/pi-arcade",
3
+ "version": "0.1.1",
4
+ "description": "Arcade minigames for the Pi coding agent.",
5
+ "license": "MIT",
6
+ "author": "Thomas Mustier",
7
+ "keywords": [
8
+ "pi-package"
9
+ ],
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/tmustier/pi-extensions.git",
13
+ "directory": "arcade"
14
+ },
15
+ "bugs": "https://github.com/tmustier/pi-extensions/issues",
16
+ "homepage": "https://github.com/tmustier/pi-extensions/tree/main/arcade",
17
+ "pi": {
18
+ "extensions": [
19
+ "spice-invaders.ts",
20
+ "picman.ts",
21
+ "ping.ts",
22
+ "tetris.ts",
23
+ "mario-not/mario-not.ts"
24
+ ]
25
+ }
26
+ }