@fungi.computer/bonsai 0.0.0-stage → 0.1.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 (133) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/LICENSE +21 -0
  3. package/README.md +196 -2
  4. package/backend/background.ts +259 -0
  5. package/backend/change-copy.ts +99 -0
  6. package/backend/changes.ts +530 -0
  7. package/backend/diff.ts +228 -0
  8. package/backend/file-edits.ts +850 -0
  9. package/backend/files-copy.ts +361 -0
  10. package/backend/gills.ts +125 -0
  11. package/backend/git.ts +123 -0
  12. package/backend/graph-commit-copies.ts +321 -0
  13. package/backend/history.ts +178 -0
  14. package/backend/index.ts +144 -0
  15. package/backend/insights.ts +256 -0
  16. package/backend/merge.ts +999 -0
  17. package/backend/methods.ts +848 -0
  18. package/backend/policy.ts +160 -0
  19. package/backend/read-copy-page.ts +77 -0
  20. package/backend/repository-copy.ts +70 -0
  21. package/backend/reviews.ts +317 -0
  22. package/backend/service.ts +892 -0
  23. package/backend/state.ts +914 -0
  24. package/demo/koi-pond/README.md +17 -0
  25. package/demo/koi-pond/index.html +11 -0
  26. package/demo/koi-pond/package.json +13 -0
  27. package/demo/koi-pond/pnpm-lock.yaml +450 -0
  28. package/demo/koi-pond/src/fish.ts +32 -0
  29. package/demo/koi-pond/src/main.ts +24 -0
  30. package/demo/koi-pond/src/pond.ts +27 -0
  31. package/docs/media/bonsai-insights.png +0 -0
  32. package/docs/media/overview-phone.png +0 -0
  33. package/docs/media/overview.png +0 -0
  34. package/package.json +152 -3
  35. package/scripts/agent.sh +55 -0
  36. package/scripts/demo.sh +233 -0
  37. package/scripts/rebuild-copies.mjs +34 -0
  38. package/scripts/release.mjs +52 -0
  39. package/scripts/runtime-proof.mjs +299 -0
  40. package/scripts/seed-demo.mjs +724 -0
  41. package/scripts/seed-step.mjs +37 -0
  42. package/scripts/shed-terminal-proof.mjs +774 -0
  43. package/shared/contract.ts +404 -0
  44. package/shared/hooks.ts +208 -0
  45. package/shared/insights.ts +90 -0
  46. package/shared/picker-options.ts +23 -0
  47. package/shared/presentation.ts +21 -0
  48. package/shared/repository-deletion.ts +4 -0
  49. package/test/apps-build-install.test.ts +302 -0
  50. package/test/background-runtime.test.ts +206 -0
  51. package/test/browser/board.browser.mjs +52 -0
  52. package/test/browser/capture.browser.mjs +1264 -0
  53. package/test/browser/edit-in-inkcap.browser.mjs +83 -0
  54. package/test/browser/repository-settings-fixture.tsx +108 -0
  55. package/test/browser/repository-settings.browser.mjs +136 -0
  56. package/test/browser/repository-settings.html +10 -0
  57. package/test/browser/visual-fixture.tsx +667 -0
  58. package/test/browser/visual.html +10 -0
  59. package/test/browser/vite.config.mjs +9 -0
  60. package/test/change-page.test.ts +916 -0
  61. package/test/changes.test.ts +1063 -0
  62. package/test/checks-workflow-template.test.mjs +234 -0
  63. package/test/diff.test.ts +70 -0
  64. package/test/edit-in-inkcap.test.ts +227 -0
  65. package/test/file-edits.test.ts +2714 -0
  66. package/test/files-read-copy-calls.test.ts +374 -0
  67. package/test/fork-adoption.test.ts +111 -0
  68. package/test/gills.test.ts +125 -0
  69. package/test/git-binding.fixture.ts +30 -0
  70. package/test/git-push.config.jsonc +9 -0
  71. package/test/git-push.native.mjs +413 -0
  72. package/test/git-push.worker.ts +280 -0
  73. package/test/graph-commit-read-copy-calls.test.ts +401 -0
  74. package/test/home-page.test.ts +170 -0
  75. package/test/hooks.test.ts +250 -0
  76. package/test/insight-data.test.ts +51 -0
  77. package/test/insight-layout.test.mjs +230 -0
  78. package/test/insights.test.ts +154 -0
  79. package/test/merge-background.test.ts +756 -0
  80. package/test/merge-conflict.test.ts +337 -0
  81. package/test/merge-declaration.test.ts +46 -0
  82. package/test/merge-repro.test.ts +561 -0
  83. package/test/merge-template-build.test.mjs +234 -0
  84. package/test/network-layout.test.ts +200 -0
  85. package/test/outbox.test.ts +424 -0
  86. package/test/parts.test.ts +86 -0
  87. package/test/pkt-line.test.ts +54 -0
  88. package/test/policy.test.ts +172 -0
  89. package/test/present.test.ts +27 -0
  90. package/test/read-copy-calls.test.ts +691 -0
  91. package/test/read-copy-composer.test.ts +388 -0
  92. package/test/read-copy-lifetimes.test.ts +214 -0
  93. package/test/read-copy-rpc.config.jsonc +12 -0
  94. package/test/read-copy-rpc.native.mjs +47 -0
  95. package/test/read-copy-rpc.worker.ts +92 -0
  96. package/test/recent-activity.test.ts +62 -0
  97. package/test/repository-deletion.test.ts +500 -0
  98. package/test/seed-resume.test.mjs +91 -0
  99. package/test/storyboard-save-https-git.fixture.mjs +119 -0
  100. package/test/storyboard-save-template.test.mjs +545 -0
  101. package/test/ui-errors.test.ts +976 -0
  102. package/test/workflow-store.test.ts +115 -0
  103. package/tsconfig.json +36 -0
  104. package/turbo.jsonc +25 -0
  105. package/vite.config.ts +19 -0
  106. package/vitest.config.ts +47 -0
  107. package/web/api.ts +224 -0
  108. package/web/app-build-panel.tsx +171 -0
  109. package/web/board.tsx +259 -0
  110. package/web/bonsai.css +642 -0
  111. package/web/change-page.tsx +719 -0
  112. package/web/change-rows.tsx +135 -0
  113. package/web/code-tab.tsx +267 -0
  114. package/web/commit-tree.tsx +152 -0
  115. package/web/format.ts +54 -0
  116. package/web/home-page.tsx +274 -0
  117. package/web/index.html +17 -0
  118. package/web/insight-data.ts +76 -0
  119. package/web/insight-tiles.tsx +534 -0
  120. package/web/insights.tsx +250 -0
  121. package/web/issues-panel.tsx +305 -0
  122. package/web/main.tsx +72 -0
  123. package/web/network-graph.tsx +647 -0
  124. package/web/overview.tsx +339 -0
  125. package/web/pages.tsx +469 -0
  126. package/web/parts.tsx +381 -0
  127. package/web/public/favicon.svg +1 -0
  128. package/web/repo-navigation.tsx +256 -0
  129. package/web/repo-settings.tsx +145 -0
  130. package/web/router.tsx +151 -0
  131. package/web/transport.ts +91 -0
  132. package/web/views.tsx +151 -0
  133. package/web/working-now.tsx +98 -0
@@ -0,0 +1,17 @@
1
+ # koi-pond
2
+
3
+ A small canvas pond: fish swim, lily pads drift, and you can drop food. Built in
4
+ plain TypeScript with no framework.
5
+
6
+ ## Run
7
+
8
+ ```sh
9
+ npm install
10
+ npm run dev
11
+ ```
12
+
13
+ ## Layout
14
+
15
+ - `src/pond.ts` — the world: size, water, lily pads
16
+ - `src/fish.ts` — fish movement
17
+ - `src/main.ts` — the game loop and input
@@ -0,0 +1,11 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>koi-pond</title>
6
+ </head>
7
+ <body style="margin: 0; background: #0b3d4f">
8
+ <canvas id="pond"></canvas>
9
+ <script type="module" src="/src/main.ts"></script>
10
+ </body>
11
+ </html>
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "koi-pond",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "vite",
7
+ "build": "tsc && vite build"
8
+ },
9
+ "devDependencies": {
10
+ "typescript": "5.9.3",
11
+ "vite": "8.2.2"
12
+ }
13
+ }
@@ -0,0 +1,450 @@
1
+ lockfileVersion: '9.0'
2
+
3
+ settings:
4
+ autoInstallPeers: true
5
+ excludeLinksFromLockfile: false
6
+
7
+ importers:
8
+
9
+ .:
10
+ devDependencies:
11
+ typescript:
12
+ specifier: 5.9.3
13
+ version: 5.9.3
14
+ vite:
15
+ specifier: 8.2.2
16
+ version: 8.2.2
17
+
18
+ packages:
19
+
20
+ '@oxc-project/types@0.153.0':
21
+ resolution: {integrity: sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==}
22
+
23
+ '@rolldown/binding-android-arm-eabi@1.2.13':
24
+ resolution: {integrity: sha512-Vu/NICrgdRvUOeaJZIyB2FP63s6fO/4ud5l8dXmxDx9yDyn+mN0y+gsvsVviSli+T361Aqo0h46EuMoobrwawA==}
25
+ engines: {node: ^20.19.0 || >=22.12.0}
26
+ cpu: [arm]
27
+ os: [android]
28
+
29
+ '@rolldown/binding-android-arm64@1.2.13':
30
+ resolution: {integrity: sha512-6LEoIrGy5vvCd2boNnv2soPSUZj/PRvhp8R/t9KlX+mIZxThIE/ZqxrGXxmuNX0FuvQFvLdZr7S4HrYpYBVhEQ==}
31
+ engines: {node: ^20.19.0 || >=22.12.0}
32
+ cpu: [arm64]
33
+ os: [android]
34
+
35
+ '@rolldown/binding-darwin-arm64@1.2.13':
36
+ resolution: {integrity: sha512-CaJSRzlQ6Q+zkDMVA9OkVxBjVoPRCDEE3v0FSJ9kIQBgRjFApA3Si4ijhbNVUji7ZJt+PfO0e46qy4nPInHUPA==}
37
+ engines: {node: ^20.19.0 || >=22.12.0}
38
+ cpu: [arm64]
39
+ os: [darwin]
40
+
41
+ '@rolldown/binding-darwin-x64@1.2.13':
42
+ resolution: {integrity: sha512-e4r340I5hxSzJkD3YJBxgzq/rJ3wwxwdLLb2FPt1Zpyem20nrQPT11DDEEkazwxaHumH8wpglsp/Ozgj/wT6mw==}
43
+ engines: {node: ^20.19.0 || >=22.12.0}
44
+ cpu: [x64]
45
+ os: [darwin]
46
+
47
+ '@rolldown/binding-freebsd-x64@1.2.13':
48
+ resolution: {integrity: sha512-hoBWDZ4/K/mJKVMbsgUd1jfpA2DiOrJ54mpegObU9TBHBBrtX4FLIXJJQRDPZ/2Dn91GvPob+Z2hwKJzf4E5SQ==}
49
+ engines: {node: ^20.19.0 || >=22.12.0}
50
+ cpu: [x64]
51
+ os: [freebsd]
52
+
53
+ '@rolldown/binding-linux-arm-gnueabihf@1.2.13':
54
+ resolution: {integrity: sha512-oF9fZu3vTWnTiVAkDFxU9PJLt1nh20K9RRpuHsR7ZnE2L7XookBbDBVLojCVSLGbvtSlxeUNnjAj6DbNsKyzbg==}
55
+ engines: {node: ^20.19.0 || >=22.12.0}
56
+ cpu: [arm]
57
+ os: [linux]
58
+
59
+ '@rolldown/binding-linux-arm64-gnu@1.2.13':
60
+ resolution: {integrity: sha512-DTpb/+jMiqWMPgphn4ksW1zW+XLJ6dJJ9FMvnIk9I291LhCctMPhMYJrP5t9p6C9qa3KXadDdUEZHBaPh3kNxQ==}
61
+ engines: {node: ^20.19.0 || >=22.12.0}
62
+ cpu: [arm64]
63
+ os: [linux]
64
+ libc: [glibc]
65
+
66
+ '@rolldown/binding-linux-arm64-musl@1.2.13':
67
+ resolution: {integrity: sha512-xU+Kvv9N2A3SY35zFh1FWWNjxbadE/BKeqnkXe7vScU4GZsw757BlVmDLITZEq8DPpk6m7uoDYkK7P+x/uU5Ow==}
68
+ engines: {node: ^20.19.0 || >=22.12.0}
69
+ cpu: [arm64]
70
+ os: [linux]
71
+ libc: [musl]
72
+
73
+ '@rolldown/binding-linux-ppc64-gnu@1.2.13':
74
+ resolution: {integrity: sha512-XMk/hnhh25oQ756Lclb1Q11h4ll0xC0Esa5w4Vzp+mAE2S3AGJgeM0SonrIgY8fHhjarb6hcoTuwoFG0VVP2ig==}
75
+ engines: {node: ^20.19.0 || >=22.12.0}
76
+ cpu: [ppc64]
77
+ os: [linux]
78
+ libc: [glibc]
79
+
80
+ '@rolldown/binding-linux-s390x-gnu@1.2.13':
81
+ resolution: {integrity: sha512-U1BoTHYmlywE2n3HUWjzZ+8EQsKr+OwpxYuDWaubx42tUL8QSL7nOqRz/NVHanibbFP3M1Rxuprz/wNcbMKjqw==}
82
+ engines: {node: ^20.19.0 || >=22.12.0}
83
+ cpu: [s390x]
84
+ os: [linux]
85
+ libc: [glibc]
86
+
87
+ '@rolldown/binding-linux-x64-gnu@1.2.13':
88
+ resolution: {integrity: sha512-J4cm80Pc6wEwV5AZ42o62sIQgDk+Y7R2s646jIwuvO+4V46QB58N4vVuMPWCBaNgursXLpmUWJV//WQAkPvHBQ==}
89
+ engines: {node: ^20.19.0 || >=22.12.0}
90
+ cpu: [x64]
91
+ os: [linux]
92
+ libc: [glibc]
93
+
94
+ '@rolldown/binding-linux-x64-musl@1.2.13':
95
+ resolution: {integrity: sha512-W0euZS40U4V6JkmEnVCIX9mcsL6cbXQ4X8c2w+MMOIjFg68TXUW+u3RT+2e1t1pv9mg/EP3pDtK3POXdQ7Rxyw==}
96
+ engines: {node: ^20.19.0 || >=22.12.0}
97
+ cpu: [x64]
98
+ os: [linux]
99
+ libc: [musl]
100
+
101
+ '@rolldown/binding-openharmony-arm64@1.2.13':
102
+ resolution: {integrity: sha512-do/AF/4n9MRshia3a5aXoy8xM2c6mgfT6D7H/dY6my5z+hfCIR4ab7mk4IRj++nsEQpKy39kd0DQnDq+r692tw==}
103
+ engines: {node: ^20.19.0 || >=22.12.0}
104
+ cpu: [arm64]
105
+ os: [openharmony]
106
+
107
+ '@rolldown/binding-win32-arm64-msvc@1.2.13':
108
+ resolution: {integrity: sha512-6FtUf2cJZobutJdxnGYX8z0vtbgqU0b2v8vfBhkXb9wp2MppMFtkunE+IaOntyi01rkIQoPW2GHABtfzsC0BRQ==}
109
+ engines: {node: ^20.19.0 || >=22.12.0}
110
+ cpu: [arm64]
111
+ os: [win32]
112
+
113
+ '@rolldown/binding-win32-x64-msvc@1.2.13':
114
+ resolution: {integrity: sha512-UKhFaZTeTcbKrb3iU82m17jVcPEAoqDkCN5MBqgPyZSwp/cQmib21crTS0DdH7T1B7wZRWEI89YQAOJMet5yhg==}
115
+ engines: {node: ^20.19.0 || >=22.12.0}
116
+ cpu: [x64]
117
+ os: [win32]
118
+
119
+ '@rolldown/pluginutils@1.0.1':
120
+ resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
121
+
122
+ detect-libc@2.1.2:
123
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
124
+ engines: {node: '>=8'}
125
+
126
+ fdir@6.5.0:
127
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
128
+ engines: {node: '>=12.0.0'}
129
+ peerDependencies:
130
+ picomatch: ^3 || ^4
131
+ peerDependenciesMeta:
132
+ picomatch:
133
+ optional: true
134
+
135
+ fsevents@2.3.3:
136
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
137
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
138
+ os: [darwin]
139
+
140
+ lightningcss-android-arm64@1.33.0:
141
+ resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==}
142
+ engines: {node: '>= 12.0.0'}
143
+ cpu: [arm64]
144
+ os: [android]
145
+
146
+ lightningcss-darwin-arm64@1.33.0:
147
+ resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==}
148
+ engines: {node: '>= 12.0.0'}
149
+ cpu: [arm64]
150
+ os: [darwin]
151
+
152
+ lightningcss-darwin-x64@1.33.0:
153
+ resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==}
154
+ engines: {node: '>= 12.0.0'}
155
+ cpu: [x64]
156
+ os: [darwin]
157
+
158
+ lightningcss-freebsd-x64@1.33.0:
159
+ resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==}
160
+ engines: {node: '>= 12.0.0'}
161
+ cpu: [x64]
162
+ os: [freebsd]
163
+
164
+ lightningcss-linux-arm-gnueabihf@1.33.0:
165
+ resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==}
166
+ engines: {node: '>= 12.0.0'}
167
+ cpu: [arm]
168
+ os: [linux]
169
+
170
+ lightningcss-linux-arm64-gnu@1.33.0:
171
+ resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==}
172
+ engines: {node: '>= 12.0.0'}
173
+ cpu: [arm64]
174
+ os: [linux]
175
+ libc: [glibc]
176
+
177
+ lightningcss-linux-arm64-musl@1.33.0:
178
+ resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==}
179
+ engines: {node: '>= 12.0.0'}
180
+ cpu: [arm64]
181
+ os: [linux]
182
+ libc: [musl]
183
+
184
+ lightningcss-linux-x64-gnu@1.33.0:
185
+ resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==}
186
+ engines: {node: '>= 12.0.0'}
187
+ cpu: [x64]
188
+ os: [linux]
189
+ libc: [glibc]
190
+
191
+ lightningcss-linux-x64-musl@1.33.0:
192
+ resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==}
193
+ engines: {node: '>= 12.0.0'}
194
+ cpu: [x64]
195
+ os: [linux]
196
+ libc: [musl]
197
+
198
+ lightningcss-win32-arm64-msvc@1.33.0:
199
+ resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==}
200
+ engines: {node: '>= 12.0.0'}
201
+ cpu: [arm64]
202
+ os: [win32]
203
+
204
+ lightningcss-win32-x64-msvc@1.33.0:
205
+ resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==}
206
+ engines: {node: '>= 12.0.0'}
207
+ cpu: [x64]
208
+ os: [win32]
209
+
210
+ lightningcss@1.33.0:
211
+ resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==}
212
+ engines: {node: '>= 12.0.0'}
213
+
214
+ nanoid@3.3.20:
215
+ resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==}
216
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
217
+ hasBin: true
218
+
219
+ picocolors@1.1.1:
220
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
221
+
222
+ picomatch@4.0.7:
223
+ resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==}
224
+ engines: {node: '>=12'}
225
+
226
+ postcss@8.5.29:
227
+ resolution: {integrity: sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==}
228
+ engines: {node: ^10 || ^12 || >=14}
229
+
230
+ rolldown@1.2.13:
231
+ resolution: {integrity: sha512-/5apOkwWxtACcQtZ5WlrAsKS4JwXVThQPJZ3RqIruRY9UG9M8e0VlXN068xum/JRKA4aIUoJ13ghe/kGCvidiQ==}
232
+ engines: {node: ^20.19.0 || >=22.12.0}
233
+ hasBin: true
234
+
235
+ source-map-js@1.2.2:
236
+ resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==}
237
+ engines: {node: '>=0.10.0'}
238
+
239
+ tinyglobby@0.2.17:
240
+ resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
241
+ engines: {node: '>=12.0.0'}
242
+
243
+ typescript@5.9.3:
244
+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
245
+ engines: {node: '>=14.17'}
246
+ hasBin: true
247
+
248
+ vite@8.2.2:
249
+ resolution: {integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==}
250
+ engines: {node: ^20.19.0 || >=22.12.0}
251
+ hasBin: true
252
+ peerDependencies:
253
+ '@types/node': ^20.19.0 || >=22.12.0
254
+ '@vitejs/devtools': ^0.4.0 || ^0.5.0
255
+ esbuild: ^0.27.0 || ^0.28.0
256
+ jiti: '>=1.21.0'
257
+ less: ^4.0.0
258
+ sass: ^1.70.0
259
+ sass-embedded: ^1.70.0
260
+ stylus: '>=0.54.8'
261
+ sugarss: ^5.0.0
262
+ terser: ^5.16.0
263
+ tsx: ^4.8.1
264
+ yaml: ^2.4.2
265
+ peerDependenciesMeta:
266
+ '@types/node':
267
+ optional: true
268
+ '@vitejs/devtools':
269
+ optional: true
270
+ esbuild:
271
+ optional: true
272
+ jiti:
273
+ optional: true
274
+ less:
275
+ optional: true
276
+ sass:
277
+ optional: true
278
+ sass-embedded:
279
+ optional: true
280
+ stylus:
281
+ optional: true
282
+ sugarss:
283
+ optional: true
284
+ terser:
285
+ optional: true
286
+ tsx:
287
+ optional: true
288
+ yaml:
289
+ optional: true
290
+
291
+ snapshots:
292
+
293
+ '@oxc-project/types@0.153.0': {}
294
+
295
+ '@rolldown/binding-android-arm-eabi@1.2.13':
296
+ optional: true
297
+
298
+ '@rolldown/binding-android-arm64@1.2.13':
299
+ optional: true
300
+
301
+ '@rolldown/binding-darwin-arm64@1.2.13':
302
+ optional: true
303
+
304
+ '@rolldown/binding-darwin-x64@1.2.13':
305
+ optional: true
306
+
307
+ '@rolldown/binding-freebsd-x64@1.2.13':
308
+ optional: true
309
+
310
+ '@rolldown/binding-linux-arm-gnueabihf@1.2.13':
311
+ optional: true
312
+
313
+ '@rolldown/binding-linux-arm64-gnu@1.2.13':
314
+ optional: true
315
+
316
+ '@rolldown/binding-linux-arm64-musl@1.2.13':
317
+ optional: true
318
+
319
+ '@rolldown/binding-linux-ppc64-gnu@1.2.13':
320
+ optional: true
321
+
322
+ '@rolldown/binding-linux-s390x-gnu@1.2.13':
323
+ optional: true
324
+
325
+ '@rolldown/binding-linux-x64-gnu@1.2.13':
326
+ optional: true
327
+
328
+ '@rolldown/binding-linux-x64-musl@1.2.13':
329
+ optional: true
330
+
331
+ '@rolldown/binding-openharmony-arm64@1.2.13':
332
+ optional: true
333
+
334
+ '@rolldown/binding-win32-arm64-msvc@1.2.13':
335
+ optional: true
336
+
337
+ '@rolldown/binding-win32-x64-msvc@1.2.13':
338
+ optional: true
339
+
340
+ '@rolldown/pluginutils@1.0.1': {}
341
+
342
+ detect-libc@2.1.2: {}
343
+
344
+ fdir@6.5.0(picomatch@4.0.7):
345
+ optionalDependencies:
346
+ picomatch: 4.0.7
347
+
348
+ fsevents@2.3.3:
349
+ optional: true
350
+
351
+ lightningcss-android-arm64@1.33.0:
352
+ optional: true
353
+
354
+ lightningcss-darwin-arm64@1.33.0:
355
+ optional: true
356
+
357
+ lightningcss-darwin-x64@1.33.0:
358
+ optional: true
359
+
360
+ lightningcss-freebsd-x64@1.33.0:
361
+ optional: true
362
+
363
+ lightningcss-linux-arm-gnueabihf@1.33.0:
364
+ optional: true
365
+
366
+ lightningcss-linux-arm64-gnu@1.33.0:
367
+ optional: true
368
+
369
+ lightningcss-linux-arm64-musl@1.33.0:
370
+ optional: true
371
+
372
+ lightningcss-linux-x64-gnu@1.33.0:
373
+ optional: true
374
+
375
+ lightningcss-linux-x64-musl@1.33.0:
376
+ optional: true
377
+
378
+ lightningcss-win32-arm64-msvc@1.33.0:
379
+ optional: true
380
+
381
+ lightningcss-win32-x64-msvc@1.33.0:
382
+ optional: true
383
+
384
+ lightningcss@1.33.0:
385
+ dependencies:
386
+ detect-libc: 2.1.2
387
+ optionalDependencies:
388
+ lightningcss-android-arm64: 1.33.0
389
+ lightningcss-darwin-arm64: 1.33.0
390
+ lightningcss-darwin-x64: 1.33.0
391
+ lightningcss-freebsd-x64: 1.33.0
392
+ lightningcss-linux-arm-gnueabihf: 1.33.0
393
+ lightningcss-linux-arm64-gnu: 1.33.0
394
+ lightningcss-linux-arm64-musl: 1.33.0
395
+ lightningcss-linux-x64-gnu: 1.33.0
396
+ lightningcss-linux-x64-musl: 1.33.0
397
+ lightningcss-win32-arm64-msvc: 1.33.0
398
+ lightningcss-win32-x64-msvc: 1.33.0
399
+
400
+ nanoid@3.3.20: {}
401
+
402
+ picocolors@1.1.1: {}
403
+
404
+ picomatch@4.0.7: {}
405
+
406
+ postcss@8.5.29:
407
+ dependencies:
408
+ nanoid: 3.3.20
409
+ picocolors: 1.1.1
410
+ source-map-js: 1.2.2
411
+
412
+ rolldown@1.2.13:
413
+ dependencies:
414
+ '@oxc-project/types': 0.153.0
415
+ '@rolldown/pluginutils': 1.0.1
416
+ optionalDependencies:
417
+ '@rolldown/binding-android-arm-eabi': 1.2.13
418
+ '@rolldown/binding-android-arm64': 1.2.13
419
+ '@rolldown/binding-darwin-arm64': 1.2.13
420
+ '@rolldown/binding-darwin-x64': 1.2.13
421
+ '@rolldown/binding-freebsd-x64': 1.2.13
422
+ '@rolldown/binding-linux-arm-gnueabihf': 1.2.13
423
+ '@rolldown/binding-linux-arm64-gnu': 1.2.13
424
+ '@rolldown/binding-linux-arm64-musl': 1.2.13
425
+ '@rolldown/binding-linux-ppc64-gnu': 1.2.13
426
+ '@rolldown/binding-linux-s390x-gnu': 1.2.13
427
+ '@rolldown/binding-linux-x64-gnu': 1.2.13
428
+ '@rolldown/binding-linux-x64-musl': 1.2.13
429
+ '@rolldown/binding-openharmony-arm64': 1.2.13
430
+ '@rolldown/binding-win32-arm64-msvc': 1.2.13
431
+ '@rolldown/binding-win32-x64-msvc': 1.2.13
432
+
433
+ source-map-js@1.2.2: {}
434
+
435
+ tinyglobby@0.2.17:
436
+ dependencies:
437
+ fdir: 6.5.0(picomatch@4.0.7)
438
+ picomatch: 4.0.7
439
+
440
+ typescript@5.9.3: {}
441
+
442
+ vite@8.2.2:
443
+ dependencies:
444
+ lightningcss: 1.33.0
445
+ picomatch: 4.0.7
446
+ postcss: 8.5.29
447
+ rolldown: 1.2.13
448
+ tinyglobby: 0.2.17
449
+ optionalDependencies:
450
+ fsevents: 2.3.3
@@ -0,0 +1,32 @@
1
+ import type { Pond, Vec } from "./pond.js";
2
+
3
+ export type Fish = { position: Vec; heading: number; speed: number };
4
+
5
+ export function spawnFish(pond: Pond, count: number): Fish[] {
6
+ return Array.from({ length: count }, () => ({
7
+ position: { x: Math.random() * pond.width, y: Math.random() * pond.height },
8
+ heading: Math.random() * Math.PI * 2,
9
+ speed: 0.6 + Math.random() * 0.8,
10
+ }));
11
+ }
12
+
13
+ export function swim(fish: Fish, pond: Pond): void {
14
+ fish.heading += (Math.random() - 0.5) * 0.2;
15
+ fish.position.x =
16
+ (fish.position.x + Math.cos(fish.heading) * fish.speed + pond.width) %
17
+ pond.width;
18
+ fish.position.y =
19
+ (fish.position.y + Math.sin(fish.heading) * fish.speed + pond.height) %
20
+ pond.height;
21
+ }
22
+
23
+ export function drawFish(ctx: CanvasRenderingContext2D, fish: Fish): void {
24
+ ctx.save();
25
+ ctx.translate(fish.position.x, fish.position.y);
26
+ ctx.rotate(fish.heading);
27
+ ctx.fillStyle = "#f2994a";
28
+ ctx.beginPath();
29
+ ctx.ellipse(0, 0, 10, 5, 0, 0, Math.PI * 2);
30
+ ctx.fill();
31
+ ctx.restore();
32
+ }
@@ -0,0 +1,24 @@
1
+ import { drawFish, spawnFish, swim } from "./fish.js";
2
+ import { createPond, drawPond } from "./pond.js";
3
+
4
+ const canvas = document.querySelector<HTMLCanvasElement>("#pond");
5
+ if (!canvas) throw new Error("Missing #pond canvas");
6
+ canvas.width = innerWidth;
7
+ canvas.height = innerHeight;
8
+ const ctx = canvas.getContext("2d");
9
+ if (!ctx) throw new Error("Canvas 2D is unavailable");
10
+
11
+ const pond = createPond(canvas.width, canvas.height);
12
+ const school = spawnFish(pond, 12);
13
+
14
+ function frame(): void {
15
+ if (!ctx) return;
16
+ drawPond(ctx, pond);
17
+ for (const fish of school) {
18
+ swim(fish, pond);
19
+ drawFish(ctx, fish);
20
+ }
21
+ requestAnimationFrame(frame);
22
+ }
23
+
24
+ requestAnimationFrame(frame);
@@ -0,0 +1,27 @@
1
+ export type Vec = { x: number; y: number };
2
+
3
+ export type Pond = {
4
+ width: number;
5
+ height: number;
6
+ pads: Vec[];
7
+ };
8
+
9
+ export function createPond(width: number, height: number): Pond {
10
+ const pads = Array.from({ length: 6 }, () => ({
11
+ x: Math.random() * width,
12
+ y: Math.random() * height,
13
+ }));
14
+ return { width, height, pads };
15
+ }
16
+
17
+ export function drawPond(ctx: CanvasRenderingContext2D, pond: Pond): void {
18
+ ctx.fillStyle = "#0b3d4f";
19
+ ctx.fillRect(0, 0, pond.width, pond.height);
20
+ ctx.fillStyle = "#3f8f4a";
21
+ for (const pad of pond.pads) {
22
+ ctx.beginPath();
23
+ ctx.arc(pad.x, pad.y, 22, 0.3, Math.PI * 2);
24
+ ctx.lineTo(pad.x, pad.y);
25
+ ctx.fill();
26
+ }
27
+ }
Binary file
Binary file
Binary file