enya-agent 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 (389) hide show
  1. package/.env.example +20 -0
  2. package/.github/workflows/ci.yml +70 -0
  3. package/.github/workflows/publish.yml +250 -0
  4. package/.gitmodules +3 -0
  5. package/Cargo.lock +3584 -0
  6. package/Cargo.toml +97 -0
  7. package/crates/enact/Cargo.toml +27 -0
  8. package/crates/enact/src/lib.rs +60 -0
  9. package/crates/enact-a2a/Cargo.toml +25 -0
  10. package/crates/enact-a2a/src/lib.rs +411 -0
  11. package/crates/enact-channels/Cargo.toml +64 -0
  12. package/crates/enact-channels/examples/README.md +80 -0
  13. package/crates/enact-channels/examples/channel_bot.rs +169 -0
  14. package/crates/enact-channels/examples/telegram-echo.rs +34 -0
  15. package/crates/enact-channels/examples/whatsapp-echo.rs +142 -0
  16. package/crates/enact-channels/src/config.rs +213 -0
  17. package/crates/enact-channels/src/lib.rs +25 -0
  18. package/crates/enact-channels/src/runtime.rs +237 -0
  19. package/crates/enact-channels/src/security/mod.rs +5 -0
  20. package/crates/enact-channels/src/security/pairing.rs +205 -0
  21. package/crates/enact-channels/src/teams.rs +601 -0
  22. package/crates/enact-channels/src/telegram.rs +2833 -0
  23. package/crates/enact-channels/src/traits.rs +200 -0
  24. package/crates/enact-channels/src/webhook.rs +262 -0
  25. package/crates/enact-channels/src/whatsapp.rs +310 -0
  26. package/crates/enact-cli/Cargo.toml +40 -0
  27. package/crates/enact-cli/src/commands/doctor.rs +62 -0
  28. package/crates/enact-cli/src/commands/mod.rs +3 -0
  29. package/crates/enact-cli/src/commands/run.rs +69 -0
  30. package/crates/enact-cli/src/commands/serve.rs +81 -0
  31. package/crates/enact-cli/src/config.rs +2 -0
  32. package/crates/enact-cli/src/main.rs +79 -0
  33. package/crates/enact-config/Cargo.toml +36 -0
  34. package/crates/enact-config/ENV_VAR_MAPPING.md +135 -0
  35. package/crates/enact-config/QUICK_REFERENCE.md +92 -0
  36. package/crates/enact-config/README.md +107 -0
  37. package/crates/enact-config/TESTING.md +161 -0
  38. package/crates/enact-config/examples/test-env-vars.rs +100 -0
  39. package/crates/enact-config/src/config.rs +399 -0
  40. package/crates/enact-config/src/encrypted_store.rs +211 -0
  41. package/crates/enact-config/src/lib.rs +298 -0
  42. package/crates/enact-config/src/secrets.rs +149 -0
  43. package/crates/enact-config/src/sync.rs +260 -0
  44. package/crates/enact-config/test-env-vars.sh +34 -0
  45. package/crates/enact-config/tests/README.md +99 -0
  46. package/crates/enact-config/tests/config_integration_test.rs +202 -0
  47. package/crates/enact-config/tests/security_test.rs +140 -0
  48. package/crates/enact-context/Cargo.toml +41 -0
  49. package/crates/enact-context/src/budget.rs +314 -0
  50. package/crates/enact-context/src/calibrator.rs +535 -0
  51. package/crates/enact-context/src/compactor.rs +392 -0
  52. package/crates/enact-context/src/condenser.rs +826 -0
  53. package/crates/enact-context/src/lib.rs +94 -0
  54. package/crates/enact-context/src/segment.rs +238 -0
  55. package/crates/enact-context/src/step_context.rs +645 -0
  56. package/crates/enact-context/src/token_counter.rs +148 -0
  57. package/crates/enact-context/src/window.rs +372 -0
  58. package/crates/enact-core/Cargo.toml +42 -0
  59. package/crates/enact-core/README.md +98 -0
  60. package/crates/enact-core/src/background/executor.rs +524 -0
  61. package/crates/enact-core/src/background/mod.rs +48 -0
  62. package/crates/enact-core/src/background/target_binding.rs +390 -0
  63. package/crates/enact-core/src/background/trigger.rs +511 -0
  64. package/crates/enact-core/src/callable/callable.rs +152 -0
  65. package/crates/enact-core/src/callable/composite.rs +817 -0
  66. package/crates/enact-core/src/callable/graph.rs +104 -0
  67. package/crates/enact-core/src/callable/llm.rs +211 -0
  68. package/crates/enact-core/src/callable/mod.rs +64 -0
  69. package/crates/enact-core/src/callable/registry.rs +206 -0
  70. package/crates/enact-core/src/context/execution_context.rs +757 -0
  71. package/crates/enact-core/src/context/invocation.rs +99 -0
  72. package/crates/enact-core/src/context/mod.rs +50 -0
  73. package/crates/enact-core/src/context/tenant.rs +175 -0
  74. package/crates/enact-core/src/context/trace.rs +127 -0
  75. package/crates/enact-core/src/flow/conditional.rs +293 -0
  76. package/crates/enact-core/src/flow/mod.rs +43 -0
  77. package/crates/enact-core/src/flow/parallel.rs +437 -0
  78. package/crates/enact-core/src/flow/repeat.rs +534 -0
  79. package/crates/enact-core/src/flow/sequential.rs +248 -0
  80. package/crates/enact-core/src/graph/checkpoint.rs +79 -0
  81. package/crates/enact-core/src/graph/checkpoint_store.rs +76 -0
  82. package/crates/enact-core/src/graph/compiled.rs +189 -0
  83. package/crates/enact-core/src/graph/edge.rs +59 -0
  84. package/crates/enact-core/src/graph/graph_schema.rs +218 -0
  85. package/crates/enact-core/src/graph/loader.rs +155 -0
  86. package/crates/enact-core/src/graph/mod.rs +18 -0
  87. package/crates/enact-core/src/graph/node/function.rs +49 -0
  88. package/crates/enact-core/src/graph/node/mod.rs +48 -0
  89. package/crates/enact-core/src/graph/schema.rs +62 -0
  90. package/crates/enact-core/src/inbox/message.rs +405 -0
  91. package/crates/enact-core/src/inbox/mod.rs +31 -0
  92. package/crates/enact-core/src/inbox/store.rs +355 -0
  93. package/crates/enact-core/src/kernel/artifact/filesystem.rs +546 -0
  94. package/crates/enact-core/src/kernel/artifact/metadata.rs +283 -0
  95. package/crates/enact-core/src/kernel/artifact/mod.rs +27 -0
  96. package/crates/enact-core/src/kernel/artifact/store.rs +427 -0
  97. package/crates/enact-core/src/kernel/enforcement.rs +1315 -0
  98. package/crates/enact-core/src/kernel/error.rs +1200 -0
  99. package/crates/enact-core/src/kernel/event.rs +1394 -0
  100. package/crates/enact-core/src/kernel/execution_model.rs +831 -0
  101. package/crates/enact-core/src/kernel/execution_state.rs +189 -0
  102. package/crates/enact-core/src/kernel/execution_strategy.rs +117 -0
  103. package/crates/enact-core/src/kernel/ids.rs +2086 -0
  104. package/crates/enact-core/src/kernel/interrupt.rs +125 -0
  105. package/crates/enact-core/src/kernel/kernel.rs +1283 -0
  106. package/crates/enact-core/src/kernel/mod.rs +205 -0
  107. package/crates/enact-core/src/kernel/persistence/event_store.rs +270 -0
  108. package/crates/enact-core/src/kernel/persistence/message_store.rs +908 -0
  109. package/crates/enact-core/src/kernel/persistence/mod.rs +102 -0
  110. package/crates/enact-core/src/kernel/persistence/state_store.rs +228 -0
  111. package/crates/enact-core/src/kernel/persistence/vector_store.rs +299 -0
  112. package/crates/enact-core/src/kernel/reducer.rs +808 -0
  113. package/crates/enact-core/src/kernel/replay.rs +153 -0
  114. package/crates/enact-core/src/lib.rs +413 -0
  115. package/crates/enact-core/src/memory/episodic.rs +0 -0
  116. package/crates/enact-core/src/memory/mod.rs +6 -0
  117. package/crates/enact-core/src/memory/semantic.rs +0 -0
  118. package/crates/enact-core/src/memory/trait.rs +0 -0
  119. package/crates/enact-core/src/memory/vector_db.rs +0 -0
  120. package/crates/enact-core/src/memory/working.rs +0 -0
  121. package/crates/enact-core/src/policy/execution_policy.rs +292 -0
  122. package/crates/enact-core/src/policy/filters.rs +458 -0
  123. package/crates/enact-core/src/policy/input_processor.rs +407 -0
  124. package/crates/enact-core/src/policy/long_running.rs +134 -0
  125. package/crates/enact-core/src/policy/mod.rs +193 -0
  126. package/crates/enact-core/src/policy/pii_input.rs +274 -0
  127. package/crates/enact-core/src/policy/tenant_policy.rs +453 -0
  128. package/crates/enact-core/src/policy/tool_policy.rs +407 -0
  129. package/crates/enact-core/src/providers/mod.rs +63 -0
  130. package/crates/enact-core/src/providers/trait.rs +292 -0
  131. package/crates/enact-core/src/runner/callbacks.rs +6 -0
  132. package/crates/enact-core/src/runner/execution_runner.rs +476 -0
  133. package/crates/enact-core/src/runner/loop.rs +117 -0
  134. package/crates/enact-core/src/runner/mod.rs +58 -0
  135. package/crates/enact-core/src/runner/protected_runner.rs +280 -0
  136. package/crates/enact-core/src/signal/inmemory.rs +231 -0
  137. package/crates/enact-core/src/signal/mod.rs +108 -0
  138. package/crates/enact-core/src/streaming/event_logger.rs +195 -0
  139. package/crates/enact-core/src/streaming/event_stream.rs +1423 -0
  140. package/crates/enact-core/src/streaming/mod.rs +108 -0
  141. package/crates/enact-core/src/streaming/pause_cancel.rs +0 -0
  142. package/crates/enact-core/src/streaming/protected_emitter.rs +173 -0
  143. package/crates/enact-core/src/streaming/protection/context.rs +136 -0
  144. package/crates/enact-core/src/streaming/protection/encryption.rs +289 -0
  145. package/crates/enact-core/src/streaming/protection/mod.rs +43 -0
  146. package/crates/enact-core/src/streaming/protection/pii_protection.rs +243 -0
  147. package/crates/enact-core/src/streaming/protection/processor.rs +166 -0
  148. package/crates/enact-core/src/streaming/sse.rs +0 -0
  149. package/crates/enact-core/src/telemetry/exporter.rs +0 -0
  150. package/crates/enact-core/src/telemetry/init.rs +0 -0
  151. package/crates/enact-core/src/telemetry/mod.rs +49 -0
  152. package/crates/enact-core/src/telemetry/spans.rs +245 -0
  153. package/crates/enact-core/src/tool/agent_tool.rs +177 -0
  154. package/crates/enact-core/src/tool/browser/mod.rs +0 -0
  155. package/crates/enact-core/src/tool/browser/webdriver.rs +0 -0
  156. package/crates/enact-core/src/tool/cost.rs +247 -0
  157. package/crates/enact-core/src/tool/discovery.rs +0 -0
  158. package/crates/enact-core/src/tool/dispatcher.rs +347 -0
  159. package/crates/enact-core/src/tool/filesystem.rs +231 -0
  160. package/crates/enact-core/src/tool/function.rs +99 -0
  161. package/crates/enact-core/src/tool/git.rs +162 -0
  162. package/crates/enact-core/src/tool/http.rs +214 -0
  163. package/crates/enact-core/src/tool/mcp/client.rs +0 -0
  164. package/crates/enact-core/src/tool/mcp/mod.rs +0 -0
  165. package/crates/enact-core/src/tool/mod.rs +51 -0
  166. package/crates/enact-core/src/tool/reasoning/debugging.rs +0 -0
  167. package/crates/enact-core/src/tool/reasoning/mcts.rs +0 -0
  168. package/crates/enact-core/src/tool/reasoning/mod.rs +0 -0
  169. package/crates/enact-core/src/tool/reasoning/sequential.rs +0 -0
  170. package/crates/enact-core/src/tool/sandbox/dagger.rs +0 -0
  171. package/crates/enact-core/src/tool/sandbox/mod.rs +0 -0
  172. package/crates/enact-core/src/tool/shell.rs +147 -0
  173. package/crates/enact-core/src/tool/trait.rs +33 -0
  174. package/crates/enact-core/src/tool/web_search.rs +277 -0
  175. package/crates/enact-core/src/util/config.rs +0 -0
  176. package/crates/enact-core/src/util/errors.rs +0 -0
  177. package/crates/enact-core/src/util/mod.rs +6 -0
  178. package/crates/enact-core/tests/airgapped_e2e_test.rs +291 -0
  179. package/crates/enact-core/tests/e2e_agentic_loop.rs +119 -0
  180. package/crates/enact-core/tests/e2e_test.rs +259 -0
  181. package/crates/enact-core/tests/graph_test.rs +130 -0
  182. package/crates/enact-core/tests/stream_event_id_validation.rs +435 -0
  183. package/crates/enact-cron/Cargo.toml +28 -0
  184. package/crates/enact-cron/src/lib.rs +44 -0
  185. package/crates/enact-cron/src/schedule.rs +156 -0
  186. package/crates/enact-cron/src/store.rs +589 -0
  187. package/crates/enact-cron/src/types.rs +148 -0
  188. package/crates/enact-gateway/Cargo.toml +31 -0
  189. package/crates/enact-gateway/README.md +30 -0
  190. package/crates/enact-gateway/examples/whatsapp-gateway-runner-mock.rs +59 -0
  191. package/crates/enact-gateway/examples/whatsapp-gateway.rs +42 -0
  192. package/crates/enact-gateway/src/lib.rs +582 -0
  193. package/crates/enact-mcp/Cargo.toml +24 -0
  194. package/crates/enact-mcp/src/lib.rs +178 -0
  195. package/crates/enact-memory/Cargo.toml +25 -0
  196. package/crates/enact-memory/src/backend.rs +20 -0
  197. package/crates/enact-memory/src/chunker.rs +230 -0
  198. package/crates/enact-memory/src/embeddings.rs +221 -0
  199. package/crates/enact-memory/src/lib.rs +67 -0
  200. package/crates/enact-memory/src/markdown.rs +127 -0
  201. package/crates/enact-memory/src/none.rs +61 -0
  202. package/crates/enact-memory/src/sqlite.rs +276 -0
  203. package/crates/enact-memory/src/traits.rs +65 -0
  204. package/crates/enact-memory/src/vector.rs +198 -0
  205. package/crates/enact-oauth/Cargo.toml +27 -0
  206. package/crates/enact-oauth/src/lib.rs +584 -0
  207. package/crates/enact-observability/Cargo.toml +22 -0
  208. package/crates/enact-observability/src/lib.rs +197 -0
  209. package/crates/enact-providers/Cargo.toml +33 -0
  210. package/crates/enact-providers/examples/hello-agent.rs +33 -0
  211. package/crates/enact-providers/src/anthropic.rs +182 -0
  212. package/crates/enact-providers/src/azure.rs +96 -0
  213. package/crates/enact-providers/src/bridge.rs +221 -0
  214. package/crates/enact-providers/src/gemini.rs +227 -0
  215. package/crates/enact-providers/src/http.rs +78 -0
  216. package/crates/enact-providers/src/lib.rs +53 -0
  217. package/crates/enact-providers/src/openai_compatible.rs +167 -0
  218. package/crates/enact-providers/src/openrouter.rs +33 -0
  219. package/crates/enact-runner/Cargo.toml +24 -0
  220. package/crates/enact-runner/README.md +76 -0
  221. package/crates/enact-runner/src/compaction.rs +225 -0
  222. package/crates/enact-runner/src/config.rs +118 -0
  223. package/crates/enact-runner/src/lib.rs +63 -0
  224. package/crates/enact-runner/src/loop_driver.rs +414 -0
  225. package/crates/enact-runner/src/parser.rs +421 -0
  226. package/crates/enact-runner/src/retry.rs +262 -0
  227. package/crates/enact-runner/tests/integration.rs +278 -0
  228. package/crates/enact-security/Cargo.toml +22 -0
  229. package/crates/enact-security/src/audit.rs +375 -0
  230. package/crates/enact-security/src/lib.rs +37 -0
  231. package/crates/enact-security/src/policy.rs +406 -0
  232. package/crates/enact-skills/Cargo.toml +25 -0
  233. package/crates/enact-skills/src/lib.rs +506 -0
  234. package/crates/enact-tools/Cargo.toml +22 -0
  235. package/crates/enact-tools/src/file_read.rs +166 -0
  236. package/crates/enact-tools/src/file_write.rs +216 -0
  237. package/crates/enact-tools/src/git_operations.rs +513 -0
  238. package/crates/enact-tools/src/http_request.rs +417 -0
  239. package/crates/enact-tools/src/lib.rs +104 -0
  240. package/crates/enact-tools/src/security.rs +227 -0
  241. package/crates/enact-tools/src/shell.rs +191 -0
  242. package/crates/enact-tools/src/traits.rs +159 -0
  243. package/docs/Makefile +74 -0
  244. package/docs/config.toml +62 -0
  245. package/docs/content/_index.md +174 -0
  246. package/docs/content/a2a/_index.md +431 -0
  247. package/docs/content/api/_index.md +323 -0
  248. package/docs/content/channels/_index.md +160 -0
  249. package/docs/content/channels/teams.md +205 -0
  250. package/docs/content/channels/telegram.md +182 -0
  251. package/docs/content/channels/webhook.md +423 -0
  252. package/docs/content/channels/whatsapp.md +240 -0
  253. package/docs/content/cli/_index.md +261 -0
  254. package/docs/content/concepts/_index.md +273 -0
  255. package/docs/content/configuration/_index.md +241 -0
  256. package/docs/content/cron/_index.md +248 -0
  257. package/docs/content/developers/_index.md +278 -0
  258. package/docs/content/getting-started/_index.md +180 -0
  259. package/docs/content/installation/_index.md +186 -0
  260. package/docs/content/installation/uninstall.md +101 -0
  261. package/docs/content/installation/updating.md +120 -0
  262. package/docs/content/mcp/_index.md +215 -0
  263. package/docs/content/memory/_index.md +163 -0
  264. package/docs/content/oauth/_index.md +515 -0
  265. package/docs/content/providers/_index.md +206 -0
  266. package/docs/content/roadmap/_index.md +199 -0
  267. package/docs/content/security/_index.md +219 -0
  268. package/docs/content/skills/_index.md +228 -0
  269. package/docs/content/tools/_index.md +485 -0
  270. package/docs/content/troubleshooting/_index.md +259 -0
  271. package/docs/content/yaml-schema/_index.md +294 -0
  272. package/docs/static/giallo-dark.css +91 -0
  273. package/docs/static/giallo-light.css +91 -0
  274. package/docs/themes/tanuki/.github/workflows/deploy.yml +44 -0
  275. package/docs/themes/tanuki/LICENSE +21 -0
  276. package/docs/themes/tanuki/README.md +166 -0
  277. package/docs/themes/tanuki/examples/blog/config.toml +58 -0
  278. package/docs/themes/tanuki/examples/blog/content/_index.md +4 -0
  279. package/docs/themes/tanuki/examples/blog/content/about.md +33 -0
  280. package/docs/themes/tanuki/examples/blog/content/blog/_index.md +7 -0
  281. package/docs/themes/tanuki/examples/blog/content/blog/api-design-best-practices.md +245 -0
  282. package/docs/themes/tanuki/examples/blog/content/blog/building-accessible-websites.md +147 -0
  283. package/docs/themes/tanuki/examples/blog/content/blog/css-grid-vs-flexbox.md +165 -0
  284. package/docs/themes/tanuki/examples/blog/content/blog/customizing-catppuccin-colors.md +137 -0
  285. package/docs/themes/tanuki/examples/blog/content/blog/dark-mode-best-practices.md +82 -0
  286. package/docs/themes/tanuki/examples/blog/content/blog/docker-essentials.md +301 -0
  287. package/docs/themes/tanuki/examples/blog/content/blog/getting-started-with-zola.md +129 -0
  288. package/docs/themes/tanuki/examples/blog/content/blog/git-workflow-for-content.md +112 -0
  289. package/docs/themes/tanuki/examples/blog/content/blog/introduction-to-webassembly.md +183 -0
  290. package/docs/themes/tanuki/examples/blog/content/blog/modern-javascript-features.md +234 -0
  291. package/docs/themes/tanuki/examples/blog/content/blog/testing-strategies.md +311 -0
  292. package/docs/themes/tanuki/examples/blog/content/blog/typography-for-developers.md +104 -0
  293. package/docs/themes/tanuki/examples/blog/content/blog/welcome-to-tanuki.md +67 -0
  294. package/docs/themes/tanuki/examples/blog/content/blog/why-static-sites.md +85 -0
  295. package/docs/themes/tanuki/examples/blog/content/projects.md +64 -0
  296. package/docs/themes/tanuki/examples/book/config.toml +17 -0
  297. package/docs/themes/tanuki/examples/book/content/_index.md +12 -0
  298. package/docs/themes/tanuki/examples/book/content/chapter-1.md +90 -0
  299. package/docs/themes/tanuki/examples/book/content/chapter-2.md +143 -0
  300. package/docs/themes/tanuki/examples/book/content/chapter-3.md +217 -0
  301. package/docs/themes/tanuki/examples/book/content/chapter-4.md +224 -0
  302. package/docs/themes/tanuki/examples/book/content/chapter-5.md +297 -0
  303. package/docs/themes/tanuki/examples/book/content/print.md +6 -0
  304. package/docs/themes/tanuki/examples/docs/config.toml +28 -0
  305. package/docs/themes/tanuki/examples/docs/content/_index.md +20 -0
  306. package/docs/themes/tanuki/examples/docs/content/components.md +156 -0
  307. package/docs/themes/tanuki/examples/docs/content/configuration.md +94 -0
  308. package/docs/themes/tanuki/examples/docs/content/customization.md +202 -0
  309. package/docs/themes/tanuki/examples/docs/content/deployment.md +204 -0
  310. package/docs/themes/tanuki/examples/docs/content/installation.md +59 -0
  311. package/docs/themes/tanuki/examples/docs/content/print.md +6 -0
  312. package/docs/themes/tanuki/examples/docs/static/img/tanuki-icon.avif +0 -0
  313. package/docs/themes/tanuki/examples/index.html +2104 -0
  314. package/docs/themes/tanuki/mise.toml +108 -0
  315. package/docs/themes/tanuki/sass/base/_catppuccin.scss +164 -0
  316. package/docs/themes/tanuki/sass/base/_fonts.scss +64 -0
  317. package/docs/themes/tanuki/sass/base/_reset.scss +152 -0
  318. package/docs/themes/tanuki/sass/base/_typography.scss +523 -0
  319. package/docs/themes/tanuki/sass/components/_buttons.scss +209 -0
  320. package/docs/themes/tanuki/sass/components/_code.scss +457 -0
  321. package/docs/themes/tanuki/sass/components/_landing.scss +633 -0
  322. package/docs/themes/tanuki/sass/components/_layout.scss +294 -0
  323. package/docs/themes/tanuki/sass/components/_navigation.scss +1200 -0
  324. package/docs/themes/tanuki/sass/components/_print.scss +237 -0
  325. package/docs/themes/tanuki/sass/components/_search.scss +224 -0
  326. package/docs/themes/tanuki/sass/components/_sidebar.scss +473 -0
  327. package/docs/themes/tanuki/sass/components/_theme-toggle.scss +186 -0
  328. package/docs/themes/tanuki/sass/modes/_blog.scss +366 -0
  329. package/docs/themes/tanuki/sass/modes/_product.scss +875 -0
  330. package/docs/themes/tanuki/sass/modes/_raskell.scss +1696 -0
  331. package/docs/themes/tanuki/sass/patterns/_buttons.scss +183 -0
  332. package/docs/themes/tanuki/sass/patterns/_cards.scss +144 -0
  333. package/docs/themes/tanuki/sass/patterns/_index.scss +9 -0
  334. package/docs/themes/tanuki/sass/patterns/_lists.scss +259 -0
  335. package/docs/themes/tanuki/sass/patterns/_sections.scss +243 -0
  336. package/docs/themes/tanuki/sass/style.scss +47 -0
  337. package/docs/themes/tanuki/sass/tokens/_colors.scss +139 -0
  338. package/docs/themes/tanuki/sass/tokens/_spacing.scss +100 -0
  339. package/docs/themes/tanuki/sass/tokens/_typography.scss +186 -0
  340. package/docs/themes/tanuki/screenshot.png +0 -0
  341. package/docs/themes/tanuki/sentinel.kdl +59 -0
  342. package/docs/themes/tanuki/static/elasticlunr.min.js +10 -0
  343. package/docs/themes/tanuki/static/fonts/GEIST-LICENSE.txt +92 -0
  344. package/docs/themes/tanuki/static/fonts/Geist-Variable.woff2 +0 -0
  345. package/docs/themes/tanuki/static/fonts/GeistMono-Variable.woff2 +0 -0
  346. package/docs/themes/tanuki/static/img/tanuki-icon.avif +0 -0
  347. package/docs/themes/tanuki/static/img/tanuki-icon.png +0 -0
  348. package/docs/themes/tanuki/static/js/anchors.js +18 -0
  349. package/docs/themes/tanuki/static/js/app.js +274 -0
  350. package/docs/themes/tanuki/static/js/code.js +394 -0
  351. package/docs/themes/tanuki/static/js/navigation.js +778 -0
  352. package/docs/themes/tanuki/static/js/scroll-to-top.js +33 -0
  353. package/docs/themes/tanuki/static/js/search-raskell.js +240 -0
  354. package/docs/themes/tanuki/static/js/search.js +215 -0
  355. package/docs/themes/tanuki/static/js/theme.js +169 -0
  356. package/docs/themes/tanuki/static/syntax-dark.css +151 -0
  357. package/docs/themes/tanuki/static/syntax-light.css +151 -0
  358. package/docs/themes/tanuki/static/wasm/sentinel_playground_wasm.js +486 -0
  359. package/docs/themes/tanuki/static/wasm/sentinel_playground_wasm_bg.wasm +0 -0
  360. package/docs/themes/tanuki/templates/404.html +52 -0
  361. package/docs/themes/tanuki/templates/base.html +428 -0
  362. package/docs/themes/tanuki/templates/blog.html +66 -0
  363. package/docs/themes/tanuki/templates/home.html +108 -0
  364. package/docs/themes/tanuki/templates/index.html +178 -0
  365. package/docs/themes/tanuki/templates/landing.html +168 -0
  366. package/docs/themes/tanuki/templates/macros/nav.html +128 -0
  367. package/docs/themes/tanuki/templates/macros/posts.html +101 -0
  368. package/docs/themes/tanuki/templates/macros/ui.html +159 -0
  369. package/docs/themes/tanuki/templates/page.html +135 -0
  370. package/docs/themes/tanuki/templates/partials/footer.html +38 -0
  371. package/docs/themes/tanuki/templates/partials/header.html +366 -0
  372. package/docs/themes/tanuki/templates/partials/nav-buttons.html +55 -0
  373. package/docs/themes/tanuki/templates/partials/nav-overlay.html +81 -0
  374. package/docs/themes/tanuki/templates/partials/page-toc-panel.html +43 -0
  375. package/docs/themes/tanuki/templates/partials/search.html +52 -0
  376. package/docs/themes/tanuki/templates/partials/sidebar.html +107 -0
  377. package/docs/themes/tanuki/templates/partials/theme-toggle.html +35 -0
  378. package/docs/themes/tanuki/templates/partials/toc-overlay.html +146 -0
  379. package/docs/themes/tanuki/templates/partials/version-picker.html +38 -0
  380. package/docs/themes/tanuki/templates/print.html +244 -0
  381. package/docs/themes/tanuki/templates/section.html +186 -0
  382. package/docs/themes/tanuki/templates/taxonomy_list.html +18 -0
  383. package/docs/themes/tanuki/templates/taxonomy_single.html +31 -0
  384. package/docs/themes/tanuki/theme.toml +58 -0
  385. package/examples/hello-agent.rs +55 -0
  386. package/package.json +36 -0
  387. package/proto/config.proto +60 -0
  388. package/proto/events.proto +0 -0
  389. package/proto/runtime.proto +215 -0
@@ -0,0 +1,778 @@
1
+ // =============================================================================
2
+ // Navigation - Tanuki Theme (タヌキ)
3
+ // Sidebar, keyboard navigation, ToC overlay, and mobile menu
4
+ // =============================================================================
5
+
6
+ (function() {
7
+ 'use strict';
8
+
9
+ const STORAGE_KEY_SIDEBAR = 'tanuki-sidebar-collapsed';
10
+ const STORAGE_KEY_SIDEBAR_WIDTH = 'tanuki-sidebar-width';
11
+
12
+ // =============================================================================
13
+ // Sidebar (Docs Mode - Collapsible)
14
+ // =============================================================================
15
+
16
+ function initSidebar() {
17
+ const sidebar = document.querySelector('.sidebar');
18
+ const overlay = document.querySelector('.sidebar-overlay');
19
+ const headerToggleBtn = document.querySelector('.header .sidebar-toggle');
20
+ const tocToggleBtn = document.querySelector('.toc-toggle--docs'); // Mobile ToC button for docs
21
+ const closeBtn = document.querySelector('.sidebar__close');
22
+
23
+ if (!sidebar) return;
24
+
25
+ // Check if we're on desktop
26
+ const isDesktop = () => window.innerWidth >= 1024;
27
+
28
+ // Restore collapsed state from localStorage (desktop only)
29
+ function restoreCollapsedState() {
30
+ if (isDesktop()) {
31
+ const isCollapsed = localStorage.getItem(STORAGE_KEY_SIDEBAR) === 'true';
32
+ if (isCollapsed) {
33
+ sidebar.classList.add('collapsed');
34
+ headerToggleBtn?.setAttribute('aria-expanded', 'false');
35
+ }
36
+ }
37
+ }
38
+
39
+ // Toggle collapsed state (desktop)
40
+ function toggleCollapsed() {
41
+ const isCollapsed = sidebar.classList.toggle('collapsed');
42
+ headerToggleBtn?.setAttribute('aria-expanded', !isCollapsed);
43
+ try {
44
+ localStorage.setItem(STORAGE_KEY_SIDEBAR, isCollapsed);
45
+ } catch (e) {}
46
+ // Notify other components of sidebar state change
47
+ window.dispatchEvent(new CustomEvent('sidebar-toggle'));
48
+ }
49
+
50
+ // Open mobile sidebar
51
+ function openSidebar() {
52
+ sidebar.classList.add('open');
53
+ sidebar.classList.remove('collapsed');
54
+ overlay?.classList.add('visible');
55
+ document.body.style.overflow = 'hidden';
56
+ }
57
+
58
+ // Close mobile sidebar
59
+ function closeSidebar() {
60
+ sidebar.classList.remove('open');
61
+ overlay?.classList.remove('visible');
62
+ document.body.style.overflow = '';
63
+ }
64
+
65
+ // Header toggle button behavior
66
+ headerToggleBtn?.addEventListener('click', () => {
67
+ if (isDesktop()) {
68
+ // Desktop: toggle collapsed state
69
+ toggleCollapsed();
70
+ } else {
71
+ // Mobile: open/close sidebar
72
+ if (sidebar.classList.contains('open')) {
73
+ closeSidebar();
74
+ } else {
75
+ openSidebar();
76
+ }
77
+ }
78
+ });
79
+
80
+ // Mobile ToC toggle button (docs mode) - always opens sidebar
81
+ tocToggleBtn?.addEventListener('click', () => {
82
+ if (sidebar.classList.contains('open')) {
83
+ closeSidebar();
84
+ tocToggleBtn.setAttribute('aria-expanded', 'false');
85
+ } else {
86
+ openSidebar();
87
+ tocToggleBtn.setAttribute('aria-expanded', 'true');
88
+ }
89
+ });
90
+
91
+ closeBtn?.addEventListener('click', () => {
92
+ closeSidebar();
93
+ tocToggleBtn?.setAttribute('aria-expanded', 'false');
94
+ });
95
+
96
+ overlay?.addEventListener('click', () => {
97
+ closeSidebar();
98
+ tocToggleBtn?.setAttribute('aria-expanded', 'false');
99
+ });
100
+
101
+ // Close on Escape
102
+ document.addEventListener('keydown', (e) => {
103
+ if (e.key === 'Escape' && sidebar.classList.contains('open')) {
104
+ closeSidebar();
105
+ tocToggleBtn?.setAttribute('aria-expanded', 'false');
106
+ }
107
+ });
108
+
109
+ // Restore state on load
110
+ restoreCollapsedState();
111
+
112
+ // Handle resize
113
+ window.addEventListener('resize', () => {
114
+ if (!isDesktop()) {
115
+ // On mobile, ensure sidebar is not collapsed
116
+ sidebar.classList.remove('collapsed');
117
+ } else {
118
+ // On desktop, restore collapsed state
119
+ restoreCollapsedState();
120
+ }
121
+ });
122
+ }
123
+
124
+ // =============================================================================
125
+ // Sidebar Resize (Desktop only)
126
+ // =============================================================================
127
+
128
+ function initSidebarResize() {
129
+ const sidebar = document.querySelector('.sidebar');
130
+ const resizeHandle = document.querySelector('.sidebar__resize-handle');
131
+
132
+ if (!sidebar || !resizeHandle) return;
133
+
134
+ const isDesktop = () => window.innerWidth >= 1024;
135
+
136
+ // Get min/max from CSS custom properties
137
+ const getConstraints = () => {
138
+ const styles = getComputedStyle(sidebar);
139
+ return {
140
+ min: parseInt(styles.getPropertyValue('--sidebar-min-width')) || 200,
141
+ max: parseInt(styles.getPropertyValue('--sidebar-max-width')) || 500
142
+ };
143
+ };
144
+
145
+ // Set width on both sidebar and root (for toggle positioning)
146
+ function setWidth(width) {
147
+ sidebar.style.setProperty('--sidebar-width', `${width}px`);
148
+ document.documentElement.style.setProperty('--sidebar-width', `${width}px`);
149
+ }
150
+
151
+ // Restore saved width
152
+ function restoreWidth() {
153
+ if (!isDesktop()) return;
154
+
155
+ const savedWidth = localStorage.getItem(STORAGE_KEY_SIDEBAR_WIDTH);
156
+ if (savedWidth) {
157
+ const width = parseInt(savedWidth);
158
+ const { min, max } = getConstraints();
159
+ if (width >= min && width <= max) {
160
+ setWidth(width);
161
+ }
162
+ }
163
+ }
164
+
165
+ // Handle resize
166
+ let isResizing = false;
167
+ let startX = 0;
168
+ let startWidth = 0;
169
+
170
+ function onMouseDown(e) {
171
+ if (!isDesktop()) return;
172
+
173
+ isResizing = true;
174
+ startX = e.clientX;
175
+ startWidth = sidebar.offsetWidth;
176
+
177
+ sidebar.classList.add('resizing');
178
+ document.body.classList.add('sidebar-resizing');
179
+
180
+ document.addEventListener('mousemove', onMouseMove);
181
+ document.addEventListener('mouseup', onMouseUp);
182
+
183
+ e.preventDefault();
184
+ }
185
+
186
+ function onMouseMove(e) {
187
+ if (!isResizing) return;
188
+
189
+ const { min, max } = getConstraints();
190
+ const delta = e.clientX - startX;
191
+ const newWidth = Math.min(max, Math.max(min, startWidth + delta));
192
+
193
+ setWidth(newWidth);
194
+ }
195
+
196
+ function onMouseUp() {
197
+ if (!isResizing) return;
198
+
199
+ isResizing = false;
200
+ sidebar.classList.remove('resizing');
201
+ document.body.classList.remove('sidebar-resizing');
202
+
203
+ document.removeEventListener('mousemove', onMouseMove);
204
+ document.removeEventListener('mouseup', onMouseUp);
205
+
206
+ // Save width
207
+ const width = sidebar.offsetWidth;
208
+ try {
209
+ localStorage.setItem(STORAGE_KEY_SIDEBAR_WIDTH, width);
210
+ } catch (e) {}
211
+ }
212
+
213
+ resizeHandle.addEventListener('mousedown', onMouseDown);
214
+
215
+ // Touch support
216
+ resizeHandle.addEventListener('touchstart', (e) => {
217
+ if (!isDesktop()) return;
218
+
219
+ const touch = e.touches[0];
220
+ isResizing = true;
221
+ startX = touch.clientX;
222
+ startWidth = sidebar.offsetWidth;
223
+
224
+ sidebar.classList.add('resizing');
225
+ document.body.classList.add('sidebar-resizing');
226
+
227
+ e.preventDefault();
228
+ }, { passive: false });
229
+
230
+ document.addEventListener('touchmove', (e) => {
231
+ if (!isResizing) return;
232
+
233
+ const touch = e.touches[0];
234
+ const { min, max } = getConstraints();
235
+ const delta = touch.clientX - startX;
236
+ const newWidth = Math.min(max, Math.max(min, startWidth + delta));
237
+
238
+ setWidth(newWidth);
239
+ }, { passive: true });
240
+
241
+ document.addEventListener('touchend', () => {
242
+ if (!isResizing) return;
243
+
244
+ isResizing = false;
245
+ sidebar.classList.remove('resizing');
246
+ document.body.classList.remove('sidebar-resizing');
247
+
248
+ // Save width
249
+ const width = sidebar.offsetWidth;
250
+ try {
251
+ localStorage.setItem(STORAGE_KEY_SIDEBAR_WIDTH, width);
252
+ } catch (e) {}
253
+ });
254
+
255
+ // Double-click to reset to default
256
+ resizeHandle.addEventListener('dblclick', () => {
257
+ sidebar.style.removeProperty('--sidebar-width');
258
+ document.documentElement.style.removeProperty('--sidebar-width');
259
+ try {
260
+ localStorage.removeItem(STORAGE_KEY_SIDEBAR_WIDTH);
261
+ } catch (e) {}
262
+ });
263
+
264
+ // Set default width on root, then restore saved width if any
265
+ if (isDesktop()) {
266
+ document.documentElement.style.setProperty('--sidebar-width', '280px');
267
+ }
268
+ restoreWidth();
269
+ }
270
+
271
+ // =============================================================================
272
+ // ToC Overlay (Book Mode)
273
+ // =============================================================================
274
+
275
+ function initTocOverlay() {
276
+ const overlay = document.getElementById('toc-overlay');
277
+ // Only target book mode toc-toggle (not docs mode which opens sidebar)
278
+ const openBtn = document.querySelector('.toc-toggle:not(.toc-toggle--docs)');
279
+ const closeBtn = document.querySelector('.toc-overlay__close');
280
+ const backdrop = document.querySelector('.toc-overlay__backdrop');
281
+
282
+ if (!overlay) return;
283
+
284
+ function openOverlay() {
285
+ overlay.setAttribute('aria-hidden', 'false');
286
+ openBtn?.setAttribute('aria-expanded', 'true');
287
+ document.body.style.overflow = 'hidden';
288
+ // Focus first link
289
+ setTimeout(() => {
290
+ overlay.querySelector('.toc-overlay__link')?.focus();
291
+ }, 100);
292
+ }
293
+
294
+ function closeOverlay() {
295
+ overlay.setAttribute('aria-hidden', 'true');
296
+ openBtn?.setAttribute('aria-expanded', 'false');
297
+ document.body.style.overflow = '';
298
+ openBtn?.focus();
299
+ }
300
+
301
+ openBtn?.addEventListener('click', openOverlay);
302
+ closeBtn?.addEventListener('click', closeOverlay);
303
+ backdrop?.addEventListener('click', closeOverlay);
304
+
305
+ // Close on Escape
306
+ document.addEventListener('keydown', (e) => {
307
+ if (e.key === 'Escape' && overlay.getAttribute('aria-hidden') === 'false') {
308
+ closeOverlay();
309
+ }
310
+ });
311
+
312
+ // Close when clicking a link
313
+ overlay.querySelectorAll('.toc-overlay__link').forEach(link => {
314
+ link.addEventListener('click', () => {
315
+ closeOverlay();
316
+ });
317
+ });
318
+ }
319
+
320
+ // =============================================================================
321
+ // TOC Collapsible Sections
322
+ // =============================================================================
323
+
324
+ function initTocCollapse() {
325
+ document.querySelectorAll('.toc__toggle').forEach(toggle => {
326
+ toggle.addEventListener('click', () => {
327
+ const expanded = toggle.getAttribute('aria-expanded') === 'true';
328
+ toggle.setAttribute('aria-expanded', !expanded);
329
+
330
+ const content = toggle.closest('.toc__item').querySelector('.toc__collapsible');
331
+ if (content) {
332
+ content.classList.toggle('open', !expanded);
333
+ }
334
+ });
335
+ });
336
+ }
337
+
338
+ // =============================================================================
339
+ // Keyboard Navigation (Arrow keys for prev/next)
340
+ // =============================================================================
341
+
342
+ function initKeyboardNav() {
343
+ document.addEventListener('keydown', (e) => {
344
+ // Don't trigger if user is typing
345
+ if (e.target.matches('input, textarea, select, [contenteditable]')) {
346
+ return;
347
+ }
348
+
349
+ // Left arrow = previous
350
+ if (e.key === 'ArrowLeft' && !e.ctrlKey && !e.metaKey && !e.altKey) {
351
+ const prevLink = document.querySelector('.nav-button--prev:not(.disabled)');
352
+ if (prevLink) {
353
+ prevLink.click();
354
+ }
355
+ }
356
+
357
+ // Right arrow = next
358
+ if (e.key === 'ArrowRight' && !e.ctrlKey && !e.metaKey && !e.altKey) {
359
+ const nextLink = document.querySelector('.nav-button--next:not(.disabled)');
360
+ if (nextLink) {
361
+ nextLink.click();
362
+ }
363
+ }
364
+ });
365
+ }
366
+
367
+ // =============================================================================
368
+ // Version Picker
369
+ // =============================================================================
370
+
371
+ function initVersionPicker() {
372
+ const picker = document.querySelector('.version-picker');
373
+ if (!picker) return;
374
+
375
+ const button = picker.querySelector('.version-picker__button');
376
+ const dropdown = picker.querySelector('.version-picker__dropdown');
377
+
378
+ function toggleDropdown(open) {
379
+ const isOpen = open ?? !dropdown.classList.contains('open');
380
+ dropdown.classList.toggle('open', isOpen);
381
+ button.setAttribute('aria-expanded', isOpen);
382
+ }
383
+
384
+ button?.addEventListener('click', () => toggleDropdown());
385
+
386
+ // Close on outside click
387
+ document.addEventListener('click', (e) => {
388
+ if (!picker.contains(e.target)) {
389
+ toggleDropdown(false);
390
+ }
391
+ });
392
+
393
+ // Close on Escape
394
+ document.addEventListener('keydown', (e) => {
395
+ if (e.key === 'Escape') {
396
+ toggleDropdown(false);
397
+ }
398
+ });
399
+ }
400
+
401
+ // =============================================================================
402
+ // Mobile Menu / Nav Overlay
403
+ // =============================================================================
404
+
405
+ function initMobileMenu() {
406
+ const menuToggle = document.querySelector('.menu-toggle');
407
+ const navOverlay = document.getElementById('nav-overlay');
408
+ const closeBtn = document.querySelector('.nav-overlay__close');
409
+ const backdrop = document.querySelector('.nav-overlay__backdrop');
410
+
411
+ if (!menuToggle || !navOverlay) return;
412
+
413
+ function openNavOverlay() {
414
+ navOverlay.setAttribute('aria-hidden', 'false');
415
+ menuToggle.setAttribute('aria-expanded', 'true');
416
+ document.body.style.overflow = 'hidden';
417
+ // Focus first link
418
+ setTimeout(() => {
419
+ navOverlay.querySelector('.nav-overlay__link')?.focus();
420
+ }, 100);
421
+ }
422
+
423
+ function closeNavOverlay() {
424
+ navOverlay.setAttribute('aria-hidden', 'true');
425
+ menuToggle.setAttribute('aria-expanded', 'false');
426
+ document.body.style.overflow = '';
427
+ menuToggle.focus();
428
+ }
429
+
430
+ menuToggle.addEventListener('click', openNavOverlay);
431
+ closeBtn?.addEventListener('click', closeNavOverlay);
432
+ backdrop?.addEventListener('click', closeNavOverlay);
433
+
434
+ // Close on Escape
435
+ document.addEventListener('keydown', (e) => {
436
+ if (e.key === 'Escape' && navOverlay.getAttribute('aria-hidden') === 'false') {
437
+ closeNavOverlay();
438
+ }
439
+ });
440
+
441
+ // Close when clicking a link
442
+ navOverlay.querySelectorAll('.nav-overlay__link').forEach(link => {
443
+ link.addEventListener('click', closeNavOverlay);
444
+ });
445
+ }
446
+
447
+ // =============================================================================
448
+ // Scroll to Top
449
+ // =============================================================================
450
+
451
+ function initScrollToTop() {
452
+ const btn = document.querySelector('.scroll-to-top');
453
+ if (!btn) return;
454
+
455
+ function updateVisibility() {
456
+ btn.classList.toggle('visible', window.scrollY > 300);
457
+ }
458
+
459
+ window.addEventListener('scroll', updateVisibility, { passive: true });
460
+ updateVisibility();
461
+
462
+ btn.addEventListener('click', () => {
463
+ window.scrollTo({ top: 0, behavior: 'smooth' });
464
+ });
465
+ }
466
+
467
+ // =============================================================================
468
+ // Active TOC Highlight
469
+ // =============================================================================
470
+
471
+ function initActiveTocHighlight() {
472
+ const tocLinks = document.querySelectorAll('.toc__link[href^="#"]');
473
+
474
+ if (!tocLinks.length) return;
475
+
476
+ // Build array of headings that have corresponding TOC links
477
+ const trackedHeadings = [];
478
+ tocLinks.forEach(link => {
479
+ const id = link.getAttribute('href').slice(1);
480
+ const heading = document.getElementById(id);
481
+ if (heading) {
482
+ trackedHeadings.push({ id, element: heading });
483
+ }
484
+ });
485
+
486
+ if (!trackedHeadings.length) return;
487
+
488
+ let currentActiveId = trackedHeadings[0].id;
489
+
490
+ function updateActiveLink() {
491
+ const scrollTop = window.scrollY + 120;
492
+
493
+ // Find the last tracked heading that's above the scroll position
494
+ let newActiveId = trackedHeadings[0].id; // Default to first
495
+
496
+ for (let i = trackedHeadings.length - 1; i >= 0; i--) {
497
+ if (trackedHeadings[i].element.offsetTop <= scrollTop) {
498
+ newActiveId = trackedHeadings[i].id;
499
+ break;
500
+ }
501
+ }
502
+
503
+ // Only update DOM if changed
504
+ if (newActiveId !== currentActiveId) {
505
+ currentActiveId = newActiveId;
506
+ tocLinks.forEach(link => {
507
+ const href = link.getAttribute('href');
508
+ link.classList.toggle('active', href === `#${currentActiveId}`);
509
+ });
510
+ }
511
+ }
512
+
513
+ window.addEventListener('scroll', updateActiveLink, { passive: true });
514
+ updateActiveLink();
515
+ }
516
+
517
+ // =============================================================================
518
+ // Anchor Copy to Clipboard
519
+ // =============================================================================
520
+
521
+ function initAnchorCopy() {
522
+ // Find all headings with anchors
523
+ const headings = document.querySelectorAll('h1:has(.zola-anchor), h2:has(.zola-anchor), h3:has(.zola-anchor), h4:has(.zola-anchor), h5:has(.zola-anchor), h6:has(.zola-anchor)');
524
+
525
+ headings.forEach(heading => {
526
+ const anchor = heading.querySelector('.zola-anchor');
527
+ if (!anchor) return;
528
+
529
+ let hideTimeout;
530
+
531
+ const copyUrl = async (e) => {
532
+ e.preventDefault();
533
+
534
+ const url = anchor.href;
535
+
536
+ try {
537
+ await navigator.clipboard.writeText(url);
538
+
539
+ // Get or create indicator
540
+ let indicator = heading.querySelector('.anchor-copied');
541
+ if (!indicator) {
542
+ indicator = document.createElement('span');
543
+ indicator.className = 'anchor-copied';
544
+ indicator.textContent = 'Copied!';
545
+ heading.appendChild(indicator);
546
+ }
547
+
548
+ // Clear any pending hide
549
+ clearTimeout(hideTimeout);
550
+
551
+ // Show indicator
552
+ requestAnimationFrame(() => {
553
+ indicator.classList.add('show');
554
+ });
555
+
556
+ // Hide after delay
557
+ hideTimeout = setTimeout(() => {
558
+ indicator.classList.remove('show');
559
+ }, 1500);
560
+
561
+ } catch (err) {
562
+ // Fallback: navigate to the anchor
563
+ window.location.href = url;
564
+ }
565
+ };
566
+
567
+ // Click on heading or anchor copies URL
568
+ heading.addEventListener('click', copyUrl);
569
+ });
570
+ }
571
+
572
+ // =============================================================================
573
+ // Page ToC Panel (Docs Mode - Right Sidebar)
574
+ // =============================================================================
575
+
576
+ function initPageTocPanel() {
577
+ const panel = document.getElementById('page-toc-panel');
578
+ const toggleBtn = document.querySelector('.page-toc-toggle');
579
+ const closeBtn = document.querySelector('.page-toc-panel__close');
580
+
581
+ if (!panel || !toggleBtn) return;
582
+
583
+ const STORAGE_KEY = 'page-toc-open';
584
+
585
+ function openPanel() {
586
+ panel.setAttribute('aria-hidden', 'false');
587
+ toggleBtn.setAttribute('aria-expanded', 'true');
588
+ localStorage.setItem(STORAGE_KEY, 'true');
589
+ }
590
+
591
+ function closePanel() {
592
+ panel.setAttribute('aria-hidden', 'true');
593
+ toggleBtn.setAttribute('aria-expanded', 'false');
594
+ localStorage.setItem(STORAGE_KEY, 'false');
595
+ }
596
+
597
+ function togglePanel() {
598
+ const isHidden = panel.getAttribute('aria-hidden') === 'true';
599
+ if (isHidden) {
600
+ openPanel();
601
+ } else {
602
+ closePanel();
603
+ }
604
+ }
605
+
606
+ // Restore state from localStorage (default to open on desktop)
607
+ const savedState = localStorage.getItem(STORAGE_KEY);
608
+ const isDesktop = window.innerWidth >= 1024;
609
+
610
+ if (savedState === 'true' || (savedState === null && isDesktop)) {
611
+ openPanel();
612
+ }
613
+
614
+ toggleBtn.addEventListener('click', togglePanel);
615
+ if (closeBtn) closeBtn.addEventListener('click', closePanel);
616
+
617
+ // Sync active state with scroll
618
+ initPanelActiveTocHighlight();
619
+ }
620
+
621
+ function initPanelActiveTocHighlight() {
622
+ const panelLinks = document.querySelectorAll('.page-toc-panel__link');
623
+
624
+ if (!panelLinks.length) return;
625
+
626
+ // Build array of headings that have corresponding panel links
627
+ const trackedHeadings = [];
628
+ panelLinks.forEach(link => {
629
+ const href = link.getAttribute('href');
630
+ if (href && href.startsWith('#')) {
631
+ const id = href.slice(1);
632
+ const heading = document.getElementById(id);
633
+ if (heading) {
634
+ trackedHeadings.push({ id, element: heading, link });
635
+ }
636
+ }
637
+ });
638
+
639
+ if (!trackedHeadings.length) return;
640
+
641
+ let currentActiveId = trackedHeadings[0].id;
642
+
643
+ function updateActiveLink() {
644
+ const scrollTop = window.scrollY + 120;
645
+ let newActiveId = trackedHeadings[0].id;
646
+
647
+ for (let i = trackedHeadings.length - 1; i >= 0; i--) {
648
+ if (trackedHeadings[i].element.offsetTop <= scrollTop) {
649
+ newActiveId = trackedHeadings[i].id;
650
+ break;
651
+ }
652
+ }
653
+
654
+ if (newActiveId !== currentActiveId) {
655
+ currentActiveId = newActiveId;
656
+ trackedHeadings.forEach(({ id, link }) => {
657
+ link.classList.toggle('active', id === currentActiveId);
658
+ });
659
+ }
660
+ }
661
+
662
+ window.addEventListener('scroll', updateActiveLink, { passive: true });
663
+ updateActiveLink();
664
+ }
665
+
666
+ // =============================================================================
667
+ // Scroll Progress Indicator
668
+ // =============================================================================
669
+
670
+ function initScrollProgress() {
671
+ const progressContainer = document.querySelector('.scroll-progress');
672
+ const progressBar = document.querySelector('.scroll-progress__bar');
673
+ const progressPercent = document.querySelector('.scroll-progress__percent');
674
+ const header = document.querySelector('.header');
675
+ const sidebar = document.querySelector('.sidebar');
676
+
677
+ if (!progressContainer || !progressBar) return;
678
+
679
+ // Update progress bar position based on header height and sidebar width
680
+ function updatePosition() {
681
+ if (header) {
682
+ const headerHeight = header.offsetHeight;
683
+ progressContainer.style.top = `${headerHeight}px`;
684
+ }
685
+
686
+ // Account for sidebar width on desktop when visible
687
+ if (sidebar && window.innerWidth >= 1024 && !sidebar.classList.contains('collapsed')) {
688
+ const sidebarWidth = sidebar.offsetWidth;
689
+ progressContainer.style.left = `${sidebarWidth}px`;
690
+ progressContainer.style.width = `calc(100vw - ${sidebarWidth}px)`;
691
+ } else {
692
+ progressContainer.style.left = '0';
693
+ progressContainer.style.width = '100vw';
694
+ }
695
+ }
696
+
697
+ function updateProgress() {
698
+ // Calculate scroll progress
699
+ const scrollTop = window.scrollY;
700
+ const docHeight = document.documentElement.scrollHeight - window.innerHeight;
701
+
702
+ // Avoid division by zero for short pages
703
+ if (docHeight <= 0) {
704
+ progressBar.style.width = '100%';
705
+ if (progressPercent) {
706
+ progressPercent.textContent = '100%';
707
+ progressPercent.style.left = '100%';
708
+ }
709
+ return;
710
+ }
711
+
712
+ const scrollPercent = Math.min(100, Math.round((scrollTop / docHeight) * 100));
713
+
714
+ // Update bar width
715
+ progressBar.style.width = `${scrollPercent}%`;
716
+
717
+ // Update percentage text and position it at the end of the bar
718
+ if (progressPercent) {
719
+ progressPercent.textContent = `${scrollPercent}%`;
720
+ progressPercent.style.left = `${scrollPercent}%`;
721
+ }
722
+
723
+ // Toggle active class for showing percentage (only after some scroll)
724
+ progressContainer.classList.toggle('scroll-progress--active', scrollTop > 50);
725
+ }
726
+
727
+ // Throttled scroll handler for performance
728
+ let ticking = false;
729
+ window.addEventListener('scroll', () => {
730
+ if (!ticking) {
731
+ requestAnimationFrame(() => {
732
+ updateProgress();
733
+ ticking = false;
734
+ });
735
+ ticking = true;
736
+ }
737
+ }, { passive: true });
738
+
739
+ // Update position on resize (header height may change)
740
+ window.addEventListener('resize', () => {
741
+ updatePosition();
742
+ }, { passive: true });
743
+
744
+ // Update position when sidebar is toggled
745
+ window.addEventListener('sidebar-toggle', () => {
746
+ updatePosition();
747
+ });
748
+
749
+ // Initial updates
750
+ updatePosition();
751
+ updateProgress();
752
+ }
753
+
754
+ // =============================================================================
755
+ // Initialize
756
+ // =============================================================================
757
+
758
+ function init() {
759
+ initSidebar();
760
+ initSidebarResize();
761
+ initTocOverlay();
762
+ initTocCollapse();
763
+ initKeyboardNav();
764
+ initVersionPicker();
765
+ initMobileMenu();
766
+ initScrollToTop();
767
+ initActiveTocHighlight();
768
+ initAnchorCopy();
769
+ initPageTocPanel();
770
+ initScrollProgress();
771
+ }
772
+
773
+ if (document.readyState === 'loading') {
774
+ document.addEventListener('DOMContentLoaded', init);
775
+ } else {
776
+ init();
777
+ }
778
+ })();