@fungi.computer/inkcap 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 (356) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -2
  3. package/backend/document-activity.ts +77 -0
  4. package/backend/documents-changed.ts +31 -0
  5. package/backend/gills.ts +55 -0
  6. package/backend/index.ts +52 -0
  7. package/backend/methods.ts +216 -0
  8. package/backend/test/cloudflare-workers.fixture.ts +1 -0
  9. package/backend/test/document-marker.test.ts +128 -0
  10. package/backend/test/gills.test.ts +156 -0
  11. package/backend/test/methods.test.ts +456 -0
  12. package/backend/test/present.test.ts +25 -0
  13. package/backend/tsconfig.json +14 -0
  14. package/backend/vitest.config.ts +12 -0
  15. package/dist/assets/Anton-5.3.0-latin-Byf51wtH.woff2 +0 -0
  16. package/dist/assets/BricolageGrotesque-VF-latin-Cre6nC2_.woff2 +0 -0
  17. package/dist/assets/DepartureMono-Regular-2BZob_Zz.woff2 +0 -0
  18. package/dist/assets/FileTab-B9q_XVEC.js +2078 -0
  19. package/dist/assets/Fraunces-Italic-VF-latin-D2JnYqd-.woff2 +0 -0
  20. package/dist/assets/Fraunces-VF-latin-CFFu7zhK.woff2 +0 -0
  21. package/dist/assets/GrenzeGotisch-VF-latin-BhUVw2FV.woff2 +0 -0
  22. package/dist/assets/InkcapFileTree-CdYnx2Jy.js +1 -0
  23. package/dist/assets/InstrumentSans-Italic-VF-latin-BlcgWI5J.woff2 +0 -0
  24. package/dist/assets/InstrumentSans-VF-latin-BbzFLZTg.woff2 +0 -0
  25. package/dist/assets/MapleMono-VF-CCDrizxw.woff2 +0 -0
  26. package/dist/assets/NothingYouCouldDo-Regular-B7VfwEBS.woff2 +0 -0
  27. package/dist/assets/Nunito-VF-8c6a9bb-cyrillic-BsWUdSU9.woff2 +0 -0
  28. package/dist/assets/Nunito-VF-8c6a9bb-cyrillic-ext-Cb1j0nGJ.woff2 +0 -0
  29. package/dist/assets/Nunito-VF-8c6a9bb-latin-BYs3GYNf.woff2 +0 -0
  30. package/dist/assets/Nunito-VF-8c6a9bb-latin-ext-0RvtKGDy.woff2 +0 -0
  31. package/dist/assets/Nunito-VF-8c6a9bb-vietnamese-cS2dauUA.woff2 +0 -0
  32. package/dist/assets/abap-CLvhMVsD.js +1 -0
  33. package/dist/assets/actionscript-3-Cs6JJXT5.js +1 -0
  34. package/dist/assets/ada-C5qYipkI.js +1 -0
  35. package/dist/assets/ahk-CA9gdOdF.js +1 -0
  36. package/dist/assets/ahk2-_6rzW9BS.js +1 -0
  37. package/dist/assets/andromeeda-vGVdxbeo.js +1 -0
  38. package/dist/assets/angular-html-B8mrqUL5.js +1 -0
  39. package/dist/assets/angular-ts-Bzix210W.js +1 -0
  40. package/dist/assets/apache-U0d_L8uA.js +1 -0
  41. package/dist/assets/apex-KFGFyKwG.js +1 -0
  42. package/dist/assets/apl-Dhin1L29.js +1 -0
  43. package/dist/assets/applescript-CCn79oCD.js +1 -0
  44. package/dist/assets/ara-4CJ0cIlV.js +1 -0
  45. package/dist/assets/asciidoc-SCjQUq34.js +1 -0
  46. package/dist/assets/asm-Cmm7eHzH.js +1 -0
  47. package/dist/assets/astro-B-4NNgOP.js +1 -0
  48. package/dist/assets/aurora-x-CDeNXAV0.js +1 -0
  49. package/dist/assets/awk-BWXHIvNe.js +1 -0
  50. package/dist/assets/ayu-dark-DluEY0Gj.js +1 -0
  51. package/dist/assets/ayu-light-C3h-C4tm.js +1 -0
  52. package/dist/assets/ayu-mirage-Bqwy1Gya.js +1 -0
  53. package/dist/assets/ballerina-B7ZEbQpA.js +1 -0
  54. package/dist/assets/bat-qdxAdBWQ.js +1 -0
  55. package/dist/assets/beancount-D-usSTwE.js +1 -0
  56. package/dist/assets/berry-DKpUyyne.js +1 -0
  57. package/dist/assets/bibtex-Ci_nEsc7.js +1 -0
  58. package/dist/assets/bicep-CUHmPFLl.js +1 -0
  59. package/dist/assets/bird2-CYIUgYqB.js +1 -0
  60. package/dist/assets/blade-C6Gedziy.js +1 -0
  61. package/dist/assets/bsl-_VjlB9mv.js +1 -0
  62. package/dist/assets/c-iCihek07.js +1 -0
  63. package/dist/assets/c3-D0apG41V.js +1 -0
  64. package/dist/assets/cadence-CQ2zXKGN.js +1 -0
  65. package/dist/assets/cairo-DLTphjLi.js +1 -0
  66. package/dist/assets/catppuccin-frappe-C9Q5zVZY.js +1 -0
  67. package/dist/assets/catppuccin-latte-CtsyrtPq.js +1 -0
  68. package/dist/assets/catppuccin-macchiato-R8nAf1vR.js +1 -0
  69. package/dist/assets/catppuccin-mocha-DjuaAJKi.js +1 -0
  70. package/dist/assets/chapel-DmtgEXO_.js +1 -0
  71. package/dist/assets/clarity-CtfBBspg.js +1 -0
  72. package/dist/assets/clojure-DqKBuwfJ.js +1 -0
  73. package/dist/assets/cmake-Bj61d0ZC.js +1 -0
  74. package/dist/assets/cobol-Cw2-KvLW.js +1 -0
  75. package/dist/assets/codeowners-C8r90Shi.js +1 -0
  76. package/dist/assets/codeql-oeQT6MSM.js +1 -0
  77. package/dist/assets/coffee-CiiSLhB6.js +1 -0
  78. package/dist/assets/common-lisp-Cv5bFMCO.js +1 -0
  79. package/dist/assets/coq-DuBRPxYg.js +1 -0
  80. package/dist/assets/cpp-71xkOQct.js +1 -0
  81. package/dist/assets/crystal-DGPAtLeY.js +1 -0
  82. package/dist/assets/csharp-oqKa8noW.js +1 -0
  83. package/dist/assets/css-CNuuLpMC.js +1 -0
  84. package/dist/assets/csv-Dx-8-gkx.js +1 -0
  85. package/dist/assets/cue-CE9AQfxI.js +1 -0
  86. package/dist/assets/cypher-ClKdZ_lG.js +1 -0
  87. package/dist/assets/d-qD-0Kul2.js +1 -0
  88. package/dist/assets/dark-plus-Cs2F2srj.js +1 -0
  89. package/dist/assets/dart-CnvKMtbv.js +1 -0
  90. package/dist/assets/dax-BkyTk9wS.js +1 -0
  91. package/dist/assets/desktop-Dlh5hvp9.js +1 -0
  92. package/dist/assets/dialog-CWwO4mBD.js +71 -0
  93. package/dist/assets/diff-woXpYk--.js +1 -0
  94. package/dist/assets/docker-IyjqRm3v.js +1 -0
  95. package/dist/assets/dotenv-_5a1GRtc.js +1 -0
  96. package/dist/assets/dracula-BHWKrbxM.js +1 -0
  97. package/dist/assets/dracula-soft-5eyTD99u.js +1 -0
  98. package/dist/assets/dream-maker-DW3nJb8Q.js +1 -0
  99. package/dist/assets/edge-DCY0a-ow.js +1 -0
  100. package/dist/assets/elixir-DP304Xio.js +1 -0
  101. package/dist/assets/elm-o4NJxfws.js +1 -0
  102. package/dist/assets/emacs-lisp-c_oH4hRZ.js +1 -0
  103. package/dist/assets/erb-C7MPl934.js +1 -0
  104. package/dist/assets/erlang-Cphh6RMH.js +1 -0
  105. package/dist/assets/everforest-dark-sB-x3p7T.js +1 -0
  106. package/dist/assets/everforest-light-Df2xbC6M.js +1 -0
  107. package/dist/assets/fennel-DQxkIbk2.js +1 -0
  108. package/dist/assets/fish-BJitypiv.js +1 -0
  109. package/dist/assets/fluent-C03EYrpw.js +1 -0
  110. package/dist/assets/fortran-fixed-form-DEKoE2YW.js +1 -0
  111. package/dist/assets/fortran-free-form-CYNrtFtB.js +1 -0
  112. package/dist/assets/fsharp-D13ZGOAj.js +1 -0
  113. package/dist/assets/gdresource-xMC7jr7x.js +1 -0
  114. package/dist/assets/gdscript-DYvRD3hg.js +1 -0
  115. package/dist/assets/gdshader-CBce3t8t.js +1 -0
  116. package/dist/assets/genie-CV2tkWYe.js +1 -0
  117. package/dist/assets/gherkin-DExj1W_8.js +1 -0
  118. package/dist/assets/git-commit-BSykSTBG.js +1 -0
  119. package/dist/assets/git-rebase-BDPpS8w6.js +1 -0
  120. package/dist/assets/github-dark-C-LZuMrd.js +1 -0
  121. package/dist/assets/github-dark-default-DXG-b-1a.js +1 -0
  122. package/dist/assets/github-dark-dimmed-Bx1FflLF.js +1 -0
  123. package/dist/assets/github-dark-high-contrast-B_tTalzw.js +1 -0
  124. package/dist/assets/github-light-EUqPIrTm.js +1 -0
  125. package/dist/assets/github-light-default-BXViO-2h.js +1 -0
  126. package/dist/assets/github-light-high-contrast-B68TUdTA.js +1 -0
  127. package/dist/assets/gleam-CSRkHgEL.js +1 -0
  128. package/dist/assets/glimmer-js-BONyzAsu.js +1 -0
  129. package/dist/assets/glimmer-ts-f9c0GCOe.js +1 -0
  130. package/dist/assets/glsl-D5MA2a_X.js +1 -0
  131. package/dist/assets/gn-ilITqXS6.js +1 -0
  132. package/dist/assets/gnuplot-7GGW24-e.js +1 -0
  133. package/dist/assets/go-rLFTqkRN.js +1 -0
  134. package/dist/assets/graphql-Ctjuxyvl.js +1 -0
  135. package/dist/assets/groovy-CacY0gHj.js +1 -0
  136. package/dist/assets/gruvbox-dark-hard-C820rvS2.js +1 -0
  137. package/dist/assets/gruvbox-dark-medium-BPjhmG05.js +1 -0
  138. package/dist/assets/gruvbox-dark-soft-MrdJrrXF.js +1 -0
  139. package/dist/assets/gruvbox-light-hard-BC_s9l72.js +1 -0
  140. package/dist/assets/gruvbox-light-medium-BAWPOn9u.js +1 -0
  141. package/dist/assets/gruvbox-light-soft-BSMLrYjP.js +1 -0
  142. package/dist/assets/hack-Dh_ZLRbs.js +1 -0
  143. package/dist/assets/haml-CwEpbK1d.js +1 -0
  144. package/dist/assets/handlebars-ryMP1rBR.js +1 -0
  145. package/dist/assets/haskell-D8IpX4py.js +1 -0
  146. package/dist/assets/haxe-C8oCnwJJ.js +1 -0
  147. package/dist/assets/hcl-Dh228itO.js +1 -0
  148. package/dist/assets/hjson-CxZEssPk.js +1 -0
  149. package/dist/assets/hlsl-Cvrh5tZx.js +1 -0
  150. package/dist/assets/horizon-CE9ld1lL.js +1 -0
  151. package/dist/assets/horizon-bright-DSNQnXHK.js +1 -0
  152. package/dist/assets/houston-CsvMBhTu.js +1 -0
  153. package/dist/assets/html-XwrE-1r6.js +1 -0
  154. package/dist/assets/html-derivative-i4STDFnl.js +1 -0
  155. package/dist/assets/http-kmH8AyfE.js +1 -0
  156. package/dist/assets/hurl-DCZHXKU4.js +1 -0
  157. package/dist/assets/hxml-Dcm88srW.js +1 -0
  158. package/dist/assets/hy-CZbG8q4J.js +1 -0
  159. package/dist/assets/imba-DsUTQ-LC.js +1 -0
  160. package/dist/assets/index-Iod2z-L_.js +1279 -0
  161. package/dist/assets/index-nLv8PMRJ.css +2 -0
  162. package/dist/assets/ini-B5eOa1yu.js +1 -0
  163. package/dist/assets/java-DdHNQ9hk.js +1 -0
  164. package/dist/assets/javascript-obGGvMPs.js +1 -0
  165. package/dist/assets/jinja-B-tLrNsM.js +1 -0
  166. package/dist/assets/jison-CBjhYtSs.js +1 -0
  167. package/dist/assets/json-DAgxKhMr.js +1 -0
  168. package/dist/assets/json5-BR5RXkoi.js +1 -0
  169. package/dist/assets/jsonc-CYpm1nAK.js +1 -0
  170. package/dist/assets/jsonl-CmCQp5Yx.js +1 -0
  171. package/dist/assets/jsonnet-CJTPZ8u_.js +1 -0
  172. package/dist/assets/jssm-DXw9l8Rf.js +1 -0
  173. package/dist/assets/jsx-gTgmArGx.js +1 -0
  174. package/dist/assets/julia-CXJVy74_.js +1 -0
  175. package/dist/assets/just-B9Xn_Dp4.js +1 -0
  176. package/dist/assets/kanagawa-dragon-Bozd7klA.js +1 -0
  177. package/dist/assets/kanagawa-lotus-BmuOG9ZW.js +1 -0
  178. package/dist/assets/kanagawa-wave-BML5mF2J.js +1 -0
  179. package/dist/assets/kdl-CsD5j6eV.js +1 -0
  180. package/dist/assets/kotlin-DhhofPvG.js +1 -0
  181. package/dist/assets/kusto-C7mF5XQf.js +1 -0
  182. package/dist/assets/laserwave-C_8bwKvT.js +1 -0
  183. package/dist/assets/latex-o9DRzaEe.js +1 -0
  184. package/dist/assets/lean-CewbzKMR.js +1 -0
  185. package/dist/assets/less-DVTAwKKz.js +1 -0
  186. package/dist/assets/light-plus-DVQuIRkW.js +1 -0
  187. package/dist/assets/liquid-G0MH4h_m.js +1 -0
  188. package/dist/assets/llvm-DqHOMS-s.js +1 -0
  189. package/dist/assets/log-BNLmms1o.js +1 -0
  190. package/dist/assets/logo-Cluzi2Zq.js +1 -0
  191. package/dist/assets/lua-FONpmTUP.js +1 -0
  192. package/dist/assets/luau-C2pBCSgt.js +1 -0
  193. package/dist/assets/make-Dixweg8N.js +1 -0
  194. package/dist/assets/markdown-BYOwaDjH.js +1 -0
  195. package/dist/assets/marko-DV5GrDmo.js +1 -0
  196. package/dist/assets/material-theme-Bm3Qr25_.js +1 -0
  197. package/dist/assets/material-theme-darker-2IIEA8gg.js +1 -0
  198. package/dist/assets/material-theme-lighter-uhdI0v04.js +1 -0
  199. package/dist/assets/material-theme-ocean-CHQ94UKr.js +1 -0
  200. package/dist/assets/material-theme-palenight-B5W6OYN7.js +1 -0
  201. package/dist/assets/matlab-D7qyCx1q.js +1 -0
  202. package/dist/assets/mdc-BKfPFPZw.js +1 -0
  203. package/dist/assets/mdx-DQZ5AkYe.js +1 -0
  204. package/dist/assets/mermaid-BxpfTIzt.js +1 -0
  205. package/dist/assets/min-dark-BSWPekZh.js +1 -0
  206. package/dist/assets/min-light-DDpmG2fV.js +1 -0
  207. package/dist/assets/mipsasm-BMqwQI7S.js +1 -0
  208. package/dist/assets/mojo-fg40_ntQ.js +1 -0
  209. package/dist/assets/monokai-B_bV9PQ9.js +1 -0
  210. package/dist/assets/moonbit-DgjMoLWr.js +1 -0
  211. package/dist/assets/move-BYnGhZoM.js +1 -0
  212. package/dist/assets/narrat-_X_XdTYD.js +1 -0
  213. package/dist/assets/nextflow-BJtWHP5T.js +1 -0
  214. package/dist/assets/nextflow-groovy-DJMQeKeT.js +1 -0
  215. package/dist/assets/nginx-BTMw9COX.js +1 -0
  216. package/dist/assets/night-owl-DhmEMT88.js +1 -0
  217. package/dist/assets/night-owl-light-eJ-hLW7d.js +1 -0
  218. package/dist/assets/nim-B6yJ_yaC.js +1 -0
  219. package/dist/assets/nix-IvuFDN5E.js +1 -0
  220. package/dist/assets/nord-Cb4Vim4T.js +1 -0
  221. package/dist/assets/nsis-CjNLYCLg.js +1 -0
  222. package/dist/assets/nushell-DHYfO66s.js +1 -0
  223. package/dist/assets/objective-c-D1A_Heim.js +1 -0
  224. package/dist/assets/objective-cpp-BsSzOQcm.js +1 -0
  225. package/dist/assets/ocaml-O90oeIOV.js +1 -0
  226. package/dist/assets/odin-B1RWQWA5.js +1 -0
  227. package/dist/assets/one-dark-pro-CLwyXe_n.js +1 -0
  228. package/dist/assets/one-light-D7Lr4KcI.js +1 -0
  229. package/dist/assets/openscad-BUDT5pXO.js +1 -0
  230. package/dist/assets/org-Ddi8Gs6E.js +1 -0
  231. package/dist/assets/pascal-4ZHwLPI5.js +1 -0
  232. package/dist/assets/perl-DEgXgjuC.js +1 -0
  233. package/dist/assets/php-B9UPq-vz.js +1 -0
  234. package/dist/assets/pierre-dark-CpLgRqie.js +1 -0
  235. package/dist/assets/pierre-dark-protanopia-deuteranopia-B35FxJx-.js +1 -0
  236. package/dist/assets/pierre-dark-soft-kZQmAZld.js +1 -0
  237. package/dist/assets/pierre-dark-tritanopia-CpjhbsIL.js +1 -0
  238. package/dist/assets/pierre-dark-vibrant-CpQYzh95.js +1 -0
  239. package/dist/assets/pierre-light-CoaEpmwp.js +1 -0
  240. package/dist/assets/pierre-light-protanopia-deuteranopia-0fSaH845.js +1 -0
  241. package/dist/assets/pierre-light-soft-lWLdNTOI.js +1 -0
  242. package/dist/assets/pierre-light-tritanopia-CEbqgOJL.js +1 -0
  243. package/dist/assets/pierre-light-vibrant-D80Fkn33.js +1 -0
  244. package/dist/assets/pkl-ot-7Btpt.js +1 -0
  245. package/dist/assets/plastic-DQwYfKfQ.js +1 -0
  246. package/dist/assets/plsql-DGHpHOYJ.js +1 -0
  247. package/dist/assets/po-BiJDBrnU.js +1 -0
  248. package/dist/assets/poimandres-DRFjx7u4.js +1 -0
  249. package/dist/assets/polar-C7UOKdEL.js +1 -0
  250. package/dist/assets/postcss-BXeXVLqQ.js +1 -0
  251. package/dist/assets/powerquery-DNMTfnFr.js +1 -0
  252. package/dist/assets/powershell-CCSX75yM.js +1 -0
  253. package/dist/assets/prisma-DMgNhnYS.js +1 -0
  254. package/dist/assets/prolog-iXnhIJG7.js +1 -0
  255. package/dist/assets/proto-DB4EqR-F.js +1 -0
  256. package/dist/assets/pug-DkgDHZci.js +1 -0
  257. package/dist/assets/puppet-CDv2pdJW.js +1 -0
  258. package/dist/assets/purescript-9MfHhQsQ.js +1 -0
  259. package/dist/assets/python-gzcpVVnB.js +1 -0
  260. package/dist/assets/qml-_a-QhfPD.js +1 -0
  261. package/dist/assets/qmldir-DCQb3MpD.js +1 -0
  262. package/dist/assets/qss-Fe1Jh2GI.js +1 -0
  263. package/dist/assets/r-BHcO8Vjq.js +1 -0
  264. package/dist/assets/racket-DcIDlBhZ.js +1 -0
  265. package/dist/assets/raku-B3gFvitq.js +1 -0
  266. package/dist/assets/razor-BMJ0eI3p.js +1 -0
  267. package/dist/assets/rbs-C0MpOrj4.js +1 -0
  268. package/dist/assets/red-DX1BQOs8.js +1 -0
  269. package/dist/assets/reg-CRGYupPL.js +1 -0
  270. package/dist/assets/regexp-DR20uMnK.js +1 -0
  271. package/dist/assets/rel-BtDbiS_P.js +1 -0
  272. package/dist/assets/riscv-Ckw8ddFX.js +1 -0
  273. package/dist/assets/rolldown-runtime-hePW80VL.js +1 -0
  274. package/dist/assets/ron-VUp2lXgN.js +1 -0
  275. package/dist/assets/rose-pine-aMsaMD1D.js +1 -0
  276. package/dist/assets/rose-pine-dawn-8PwUyIlP.js +1 -0
  277. package/dist/assets/rose-pine-moon-DO-qXg-I.js +1 -0
  278. package/dist/assets/rosmsg-CAekHB0j.js +1 -0
  279. package/dist/assets/rst-BQCbfb9V.js +1 -0
  280. package/dist/assets/ruby-B8JcIHv0.js +1 -0
  281. package/dist/assets/rust-Cfkwpbl8.js +1 -0
  282. package/dist/assets/sas-CBpMWA2M.js +1 -0
  283. package/dist/assets/sass-DXrisJhu.js +1 -0
  284. package/dist/assets/scala-BiFt_7mu.js +1 -0
  285. package/dist/assets/scheme-DQCgrYNe.js +1 -0
  286. package/dist/assets/scss-YesllqfO.js +1 -0
  287. package/dist/assets/sdbl-bTVj8UrX.js +1 -0
  288. package/dist/assets/shaderlab-TOUzSsQk.js +1 -0
  289. package/dist/assets/shellscript-Da78Ox-3.js +1 -0
  290. package/dist/assets/shellsession-CVsQGYfZ.js +1 -0
  291. package/dist/assets/slack-dark-DnToyrRv.js +1 -0
  292. package/dist/assets/slack-ochin-B2OO5cIa.js +1 -0
  293. package/dist/assets/smalltalk-BHpQb5nM.js +1 -0
  294. package/dist/assets/smithy-Dgaz7aur.js +1 -0
  295. package/dist/assets/snazzy-light-4G7pJPwS.js +1 -0
  296. package/dist/assets/solarized-dark-UdsmMDCL.js +1 -0
  297. package/dist/assets/solarized-light-C1IUL_tW.js +1 -0
  298. package/dist/assets/solidity-Bj7E1mRn.js +1 -0
  299. package/dist/assets/soy-DEd-qcad.js +1 -0
  300. package/dist/assets/sparql-D_iOobhT.js +1 -0
  301. package/dist/assets/splunk-BC2Px7Mm.js +1 -0
  302. package/dist/assets/sql-DdD7vxS4.js +1 -0
  303. package/dist/assets/ssh-config-BgfXC-Er.js +1 -0
  304. package/dist/assets/stata-CMv7m7Gi.js +1 -0
  305. package/dist/assets/stylus-B6D30XZt.js +1 -0
  306. package/dist/assets/surrealql-BZoq2lXR.js +1 -0
  307. package/dist/assets/svelte-Bj3Hmc9F.js +1 -0
  308. package/dist/assets/swift-CyEgAFGc.js +1 -0
  309. package/dist/assets/synthwave-84-nFMaYfgc.js +1 -0
  310. package/dist/assets/system-verilog-Dh5ihWbf.js +1 -0
  311. package/dist/assets/systemd-BxMlprV5.js +1 -0
  312. package/dist/assets/talonscript-CohzipZa.js +1 -0
  313. package/dist/assets/tasl-DMoTqEGO.js +1 -0
  314. package/dist/assets/tcl-CZd0xW_V.js +1 -0
  315. package/dist/assets/templ-TjxbArHn.js +1 -0
  316. package/dist/assets/terraform-DswuEJGm.js +1 -0
  317. package/dist/assets/tex-BK-H2Wn4.js +1 -0
  318. package/dist/assets/tokyo-night-oM2G3aXe.js +1 -0
  319. package/dist/assets/toml-CcmNWLt0.js +1 -0
  320. package/dist/assets/ts-tags-Cc5do7zu.js +1 -0
  321. package/dist/assets/tsv-sltzmVWM.js +1 -0
  322. package/dist/assets/tsx-CUOciP0b.js +1 -0
  323. package/dist/assets/turtle-ByJddavk.js +1 -0
  324. package/dist/assets/twig-BVSnuTf-.js +1 -0
  325. package/dist/assets/typescript-BydbNFcO.js +1 -0
  326. package/dist/assets/typespec-BRdr0IET.js +1 -0
  327. package/dist/assets/typst-C40W_gC6.js +1 -0
  328. package/dist/assets/useForm-CuiUTLkb.js +1 -0
  329. package/dist/assets/v-DBj4K9d_.js +1 -0
  330. package/dist/assets/vala-zf12oZj6.js +1 -0
  331. package/dist/assets/vb-DOwzaHZ0.js +1 -0
  332. package/dist/assets/verilog-B2gwz-sJ.js +1 -0
  333. package/dist/assets/vesper-DdrHHSXu.js +1 -0
  334. package/dist/assets/vhdl-BroJfC0k.js +1 -0
  335. package/dist/assets/viml-DvXPmvsu.js +1 -0
  336. package/dist/assets/vitesse-black-fwtXNY1n.js +1 -0
  337. package/dist/assets/vitesse-dark-BZCL-v6S.js +1 -0
  338. package/dist/assets/vitesse-light-VbXTXTou.js +1 -0
  339. package/dist/assets/vue-BNQF7r6F.js +1 -0
  340. package/dist/assets/vue-html-DkKkQnZL.js +1 -0
  341. package/dist/assets/vue-vine-DFaKYU4X.js +1 -0
  342. package/dist/assets/vyper-CgoNMtux.js +1 -0
  343. package/dist/assets/wasm-BnjxR4X6.js +1 -0
  344. package/dist/assets/wasm-ByWQv1Qj.js +1 -0
  345. package/dist/assets/wenyan-C8pVoKbM.js +1 -0
  346. package/dist/assets/wgsl-BsKzXJz4.js +1 -0
  347. package/dist/assets/wikitext-ClFFjSW2.js +1 -0
  348. package/dist/assets/wit-DdvCle-K.js +1 -0
  349. package/dist/assets/wolfram-DLL8P-h_.js +1 -0
  350. package/dist/assets/xml-BlnrEAEy.js +1 -0
  351. package/dist/assets/xsl-BOPS8gLt.js +1 -0
  352. package/dist/assets/yaml-Du_6cdrH.js +1 -0
  353. package/dist/assets/zenscript-BnlCZFoB.js +1 -0
  354. package/dist/assets/zig-CMLA9XwU.js +1 -0
  355. package/dist/index.html +15 -0
  356. package/package.json +225 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jamstack Consulting LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,107 @@
1
- # Temporary Holding Version
1
+ # Inkcap (`@fungi.computer/inkcap`)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Inkcap edits the files on your Computer: a file explorer, tabs and a code editor
4
+ in the browser.
5
+
6
+ ![Inkcap with the koi-pond project open in the explorer and src/fish.ts in the editor beside README.md](docs/media/editor.png)
7
+
8
+ Inkcap is one of the starter Apps of
9
+ [Oyster](https://github.com/fungi-computer/oyster), the open App host that runs
10
+ on your own Cloudflare account. On Oyster, the Computer is a Linux workspace in
11
+ a Cloudflare Container, and Inkcap reads and writes its `/workspace`.
12
+
13
+ ## What you can do
14
+
15
+ - Browse the Computer's folders and open files in tabs.
16
+ - Edit text with syntax highlighting, and save with **Ctrl+S** / **⌘S**.
17
+ - Preview images (PNG, JPEG, GIF, WebP, SVG and AVIF) and video (MP4 and WebM).
18
+ - Turn on **Preview Markdown** for any `.md` file.
19
+ - Create files and folders, **Save file as** another path, or download a draft
20
+ without writing it.
21
+ - Resolve conflicts: when a file changed on the Computer after you opened it,
22
+ choose **Keep mine**, **Reload theirs** or **Compare** before anything is
23
+ overwritten.
24
+
25
+ Closing a tab or the App with unsaved edits asks whether to save, discard or
26
+ cancel. On a phone, **Open file explorer** brings the explorer over the editor.
27
+
28
+ <img alt="Inkcap on a phone in the light theme, with no file open and a New draft button" src="docs/media/new-draft-phone.png" width="300">
29
+
30
+ ## Get it
31
+
32
+ Inkcap installs with the other starter Apps. On a deployed Oyster (see the
33
+ [Oyster quick start](https://github.com/fungi-computer/oyster#quick-start-deploy-your-own-oyster)),
34
+ run:
35
+
36
+ ```sh
37
+ export OYSTER_URL=https://oyster.<your-subdomain>.workers.dev
38
+ export OYSTER_OWNER_KEY=<your owner key>
39
+ npx oyster install-apps
40
+ ```
41
+
42
+ The installer clones this repository and the six others, seeds each on your
43
+ Oyster with `git push`, builds them on your Computer and installs them. Sign in
44
+ at `$OYSTER_URL/login` with the owner key and open Inkcap from the Tourtois
45
+ desktop. Run `install-apps` again to update; each App keeps its data.
46
+
47
+ ## Change it
48
+
49
+ Every installed App is also a template you can fork on your own Oyster. Fork
50
+ Inkcap into a Bonsai repository and clone it with ordinary `git`:
51
+
52
+ ```sh
53
+ curl -X POST -H "Authorization: Bearer $OYSTER_OWNER_KEY" -H 'content-type: application/json' \
54
+ -d '{"appId":"app_inkcap","name":"my-inkcap"}' \
55
+ $OYSTER_URL/apps/bonsai/methods/apps.fork
56
+ git -c http.extraHeader="Authorization: Bearer $OYSTER_OWNER_KEY" \
57
+ clone $OYSTER_URL/git/bonsai/my-inkcap.git
58
+ ```
59
+
60
+ The reply names your fork's App id and repository. Push your edits, open the
61
+ repository in Bonsai and choose **Settings**, then **Build & install** with that
62
+ App id and your commit as `git:<sha>`. Install the Release at `inkcap` to
63
+ replace the running Inkcap, or at a new id to keep both.
64
+
65
+ To work from GitHub instead, fork this repository and point the installer at
66
+ your forks with `--repositories <base URL>` and `--ref <branch or tag>`. The
67
+ installer reads all seven starter Apps from that base, so fork them under one
68
+ account.
69
+
70
+ ## Agent tools
71
+
72
+ Agents call Inkcap through Oyster's MCP endpoint, or at
73
+ `POST $OYSTER_URL/apps/inkcap/methods/<name>` with a key; see
74
+ [Git, keys and MCP](https://github.com/fungi-computer/oyster#git-keys-and-mcp).
75
+
76
+ | Method | What it does |
77
+ | ---------------------------------------------- | ----------------------------------------------------------------------------- |
78
+ | `listFiles({path?, limit?, offset?})` | Reads one page of a directory, up to 256 entries. |
79
+ | `readFile({path, byteOffset?, byteLength?})` | Reads up to 128 KiB of a file as base64. |
80
+ | `writeFile({path, content, expectedContent?})` | Writes UTF-8 text, optionally only if the file still holds `expectedContent`. |
81
+ | `documents.recent` | Lists the documents most recently opened and edited in Inkcap. |
82
+
83
+ Paths are absolute and cannot contain `..`. A read-only attachment cannot write,
84
+ and the host picks the Computer: no argument selects one.
85
+
86
+ ## Build and check
87
+
88
+ Inkcap is a React App built with Vite. The file tree and editor come from
89
+ `@pierre/trees` and `@pierre/diffs`, and the controls from
90
+ [Caps](https://github.com/fungi-computer/caps).
91
+
92
+ ```sh
93
+ pnpm install
94
+ pnpm build
95
+ pnpm typecheck
96
+ pnpm test
97
+ ```
98
+
99
+ [src/InkcapApp.tsx](src/InkcapApp.tsx) owns the tabs, the selected Computer and
100
+ the close checks. [src/useFileDocument.ts](src/useFileDocument.ts) owns each
101
+ draft, its saved baseline and conflict recovery. The agent tools are in
102
+ [backend/methods.ts](backend/methods.ts), and [package.json](package.json)
103
+ declares the permissions the App asks for.
104
+
105
+ ## License
106
+
107
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,77 @@
1
+ import * as z from "zod";
2
+ import {
3
+ documentActivitySchema,
4
+ documentIds,
5
+ documentsRecentSchema,
6
+ } from "../src/document-activity-contract.js";
7
+ import {
8
+ createReadCopies,
9
+ type ReadCopySql,
10
+ } from "@fungi.computer/agar/read-copy";
11
+
12
+ /** Bounded owner SQLite rows for each actor's recent Computer documents. */
13
+ export type DocumentActivityStore = ReturnType<typeof createReadCopies>;
14
+ export function createDocumentActivityStore(
15
+ sql: ReadCopySql,
16
+ ): DocumentActivityStore {
17
+ return createReadCopies(sql, {
18
+ version: 1,
19
+ maxRows: 256,
20
+ // One row holds up to 100 entries whose paths reach 4,096 characters.
21
+ maxValueBytes: 4 * 1024 * 1024,
22
+ // Activity rows store no blobs; the store's grammar requires positive bounds.
23
+ maxBlobBytes: 1,
24
+ maxBlobTotalBytes: 1,
25
+ });
26
+ }
27
+
28
+ const stateSchema = z.strictObject({
29
+ sequence: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
30
+ rows: z.array(documentActivitySchema).max(100),
31
+ });
32
+ /** Member/Agent/key identity is supplied only by the admitting host. */
33
+ export function documentActivity(
34
+ activity: DocumentActivityStore,
35
+ actorId: string,
36
+ computerId: string,
37
+ ) {
38
+ const store = activity.open("document-activity-v1", stateSchema);
39
+ const key = JSON.stringify([actorId, computerId]);
40
+ function state() {
41
+ const saved = store.read(key);
42
+ return saved.kind === "ready" ? saved.value : { sequence: 0, rows: [] };
43
+ }
44
+ return {
45
+ record(path: string, activity: "opened" | "edited") {
46
+ const previous = state();
47
+ const row = documentActivitySchema.parse({
48
+ id:
49
+ previous.rows.find((item) => item.path === path)?.id ??
50
+ documentIds.document.generate(),
51
+ computerId,
52
+ path,
53
+ activity,
54
+ occurredAt: Date.now(),
55
+ sequence: previous.sequence + 1,
56
+ });
57
+ store.write(key, null, {
58
+ sequence: row.sequence,
59
+ rows: [
60
+ row,
61
+ ...previous.rows.filter((item) => item.path !== path),
62
+ ].slice(0, 100),
63
+ });
64
+ return row.sequence;
65
+ },
66
+ recent(limit: number, after?: { before: number }) {
67
+ const rows = state().rows.filter(
68
+ (row) => after === undefined || row.sequence < after.before,
69
+ );
70
+ const page = rows.slice(0, limit);
71
+ return documentsRecentSchema.parse({
72
+ rows: page,
73
+ next: rows.length > limit ? { before: page.at(-1)?.sequence } : null,
74
+ });
75
+ },
76
+ };
77
+ }
@@ -0,0 +1,31 @@
1
+ import * as S from "effect/Schema";
2
+ import {
3
+ AppStorageError,
4
+ appStorageReadResultSchema,
5
+ appStorageMutationResultSchema,
6
+ type AppStorageWire,
7
+ } from "@fungi.computer/agar/storage";
8
+ import { documentsChangedKey } from "../src/document-activity-contract.js";
9
+
10
+ /** Publish a scoped invalidation only after the real activity row was saved. */
11
+ export async function markDocumentsChanged(
12
+ storage: Pick<AppStorageWire, "read" | "write">,
13
+ computerId: string,
14
+ sequence: number,
15
+ ) {
16
+ const current = S.decodeUnknownSync(appStorageReadResultSchema, {
17
+ onExcessProperty: "error",
18
+ })(await storage.read(documentsChangedKey));
19
+ if (current.kind === "rejected") throw new AppStorageError(current.code);
20
+ const result = S.decodeUnknownSync(appStorageMutationResultSchema, {
21
+ onExcessProperty: "error",
22
+ })(
23
+ await storage.write({
24
+ key: documentsChangedKey,
25
+ scope: "member",
26
+ basedOn: current.value.member?.revision ?? null,
27
+ value: { computerId, sequence },
28
+ }),
29
+ );
30
+ if (result.kind === "rejected") throw new AppStorageError(result.code);
31
+ }
@@ -0,0 +1,55 @@
1
+ import { defineAppGill, type AppBackendGill } from "@fungi.computer/agar/gills";
2
+ import {
3
+ documentActivityRefusalSchema,
4
+ documentsChangedKey,
5
+ documentsRecentInputSchema,
6
+ documentsRecentSchema,
7
+ } from "../src/document-activity-contract.js";
8
+
9
+ /** The source pages saved actor/Computer activity; rendering never probes files. */
10
+ export const recentFilesGill = defineAppGill({
11
+ id: "recent-files",
12
+ title: "Recent Files",
13
+ source: "documents.recent",
14
+ limit: 10,
15
+ sizes: ["M", "L"],
16
+ params: [
17
+ { key: "computer", label: "Computer", kind: "computer", required: true },
18
+ ],
19
+ watch: { storage: documentsChangedKey },
20
+ input: ({ limit, after }): ReturnType<AppBackendGill["input"]> => {
21
+ const cursor: unknown = after === undefined ? undefined : JSON.parse(after);
22
+ const input = documentsRecentInputSchema.parse(
23
+ cursor === undefined ? { limit } : { limit, after: cursor },
24
+ );
25
+ return input.after === undefined
26
+ ? { limit: input.limit }
27
+ : { limit: input.limit, after: input.after };
28
+ },
29
+ rows: (output) => {
30
+ const identity = { viewId: "recent-files", title: "Recent Files" };
31
+ if (documentActivityRefusalSchema.safeParse(output).success)
32
+ return {
33
+ ...identity,
34
+ state: "error",
35
+ rows: [],
36
+ error: "Choose a Computer and allow Inkcap to read its files.",
37
+ recovery: { kind: "configure", param: "computer" },
38
+ };
39
+ const page = documentsRecentSchema.parse(output);
40
+ if (page.rows.length === 0)
41
+ return { ...identity, state: "empty", rows: [] };
42
+ const rows = page.rows.map((record) => {
43
+ const name = record.path.split("/").at(-1);
44
+ return {
45
+ id: record.id,
46
+ title: name === undefined || name.length === 0 ? record.path : name,
47
+ detail: `${record.activity === "edited" ? "Edited" : "Opened"} · ${record.path}`,
48
+ open: { params: [{ key: "path", value: record.path }] },
49
+ };
50
+ });
51
+ return page.next === null
52
+ ? { ...identity, state: "full", rows }
53
+ : { ...identity, state: "full", rows, next: JSON.stringify(page.next) };
54
+ },
55
+ });
@@ -0,0 +1,52 @@
1
+ import { markDocumentsChanged } from "./documents-changed.js";
2
+ import * as Redacted from "effect/Redacted";
3
+ import { DurableObject } from "cloudflare:workers";
4
+ import * as z from "zod";
5
+ import {
6
+ appBackendActorHeader,
7
+ appBackendComputerHeader,
8
+ } from "@fungi.computer/agar/backend";
9
+ import { createDocumentActivityStore } from "./document-activity.js";
10
+ import { appBackend, type InkcapBackendContext } from "./methods.js";
11
+
12
+ const requestSchema = z.strictObject({
13
+ method: z.enum(["documents.opened", "documents.edited", "documents.recent"]),
14
+ input: z.json(),
15
+ });
16
+ class InkcapFacet extends DurableObject<InkcapBackendContext["env"]> {
17
+ declare public readonly env: InkcapBackendContext["env"];
18
+ readonly activity;
19
+ constructor(ctx: DurableObjectState, env: InkcapBackendContext["env"]) {
20
+ super(ctx, env);
21
+ this.activity = createDocumentActivityStore(ctx.storage.sql);
22
+ }
23
+ readonly documentsChanged = (
24
+ computerId: string,
25
+ sequence: number,
26
+ ): Promise<void> =>
27
+ markDocumentsChanged(this.env.APP_STORAGE, computerId, sequence);
28
+ override async fetch(request: Request): Promise<Response> {
29
+ if (request.method !== "PUT")
30
+ return new Response(null, { status: 405, headers: { Allow: "PUT" } });
31
+ const memberId = request.headers.get(appBackendActorHeader);
32
+ if (!memberId)
33
+ return Response.json({ error: "App actor unavailable" }, { status: 403 });
34
+ try {
35
+ const input = requestSchema.parse(await request.json());
36
+ return Response.json(
37
+ await appBackend.call(input.method, input.input, this, {
38
+ actor: { kind: "member", memberId },
39
+ computerId: request.headers.get(appBackendComputerHeader),
40
+ }),
41
+ );
42
+ } catch (cause) {
43
+ console.error("Inkcap action failed", Redacted.make(cause));
44
+ // Never reflect provider errors, remote addresses or headers into the UI.
45
+ return Response.json(
46
+ { error: "Inkcap could not complete this action" },
47
+ { status: 400 },
48
+ );
49
+ }
50
+ }
51
+ }
52
+ export const Inkcap = appBackend.implement(InkcapFacet);
@@ -0,0 +1,216 @@
1
+ import { resultRows } from "@fungi.computer/agar/method-presentation";
2
+ import { recentFilesGill } from "./gills.js";
3
+ import {
4
+ documentIdentitySchema,
5
+ documentsRecentInputSchema,
6
+ documentsRecentSchema,
7
+ documentRecordedSchema,
8
+ documentActivityRefusalSchema,
9
+ } from "../src/document-activity-contract.js";
10
+ import {
11
+ documentActivity,
12
+ type DocumentActivityStore,
13
+ } from "./document-activity.js";
14
+ import * as z from "zod";
15
+ import { filesNavigateIntent } from "@fungi.computer/agar/roles/files";
16
+ import type { AppMethodCallContext } from "@fungi.computer/puffball/app-method-runtime";
17
+ import {
18
+ defineAppBackend,
19
+ defineAppMethod,
20
+ } from "@fungi.computer/agar/backend-definition";
21
+ import type { WhistleJsonValue } from "@fungi.computer/whistle";
22
+ import type { AppStorageWire } from "@fungi.computer/agar/storage";
23
+ import {
24
+ pathSchema,
25
+ listInputSchema,
26
+ listOutputSchema,
27
+ readBytesInputSchema,
28
+ writeOutputSchema,
29
+ } from "@fungi.computer/computer/operations";
30
+ export type InkcapBackendContext = {
31
+ activity: DocumentActivityStore;
32
+ documentsChanged(computerId: string, sequence: number): Promise<void>;
33
+ env: {
34
+ APP_STORAGE: Pick<AppStorageWire, "read" | "write">;
35
+ COMPUTER?: {
36
+ list?(input: z.output<typeof listInput>): Promise<WhistleJsonValue>;
37
+ read?(input: z.output<typeof readInput>): Promise<WhistleJsonValue>;
38
+ write?(input: z.output<typeof writeInput>): Promise<WhistleJsonValue>;
39
+ };
40
+ };
41
+ };
42
+ const computerPath = pathSchema.refine(
43
+ (value) => !value.split("/").includes(".."),
44
+ "Parent path segments are not allowed",
45
+ );
46
+ const listInput = listInputSchema.extend({ path: computerPath.default("/") });
47
+ const readInput = readBytesInputSchema.extend({
48
+ path: computerPath,
49
+ byteOffset: z
50
+ .number()
51
+ .int()
52
+ .nonnegative()
53
+ .max(Number.MAX_SAFE_INTEGER)
54
+ .default(0),
55
+ byteLength: z
56
+ .number()
57
+ .int()
58
+ .min(1)
59
+ .max(128 * 1024)
60
+ .default(128 * 1024),
61
+ });
62
+ const text = z.string().max(48 * 1024);
63
+ const writeInput = z
64
+ .strictObject({
65
+ path: computerPath,
66
+ content: text,
67
+ expectedContent: text.optional(),
68
+ })
69
+ .refine(
70
+ (value) =>
71
+ new TextEncoder().encode(value.content).byteLength +
72
+ new TextEncoder().encode(value.expectedContent ?? "").byteLength <=
73
+ 48 * 1024,
74
+ "Combined content exceeds 48 KiB",
75
+ );
76
+ const readOutput = z.strictObject({
77
+ path: computerPath,
78
+ byteOffset: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
79
+ content: z.base64().max(174764),
80
+ encoding: z.literal("base64"),
81
+ truncated: z.boolean(),
82
+ });
83
+ const refusal = z.union([
84
+ z.strictObject({
85
+ error: z.enum([
86
+ "not_granted",
87
+ "revoked",
88
+ "grantor_lost_authority",
89
+ "unavailable",
90
+ "invalid",
91
+ "invalid_path",
92
+ "too_large",
93
+ "conflict",
94
+ "not_found",
95
+ ]),
96
+ }),
97
+ z.strictObject({
98
+ error: z.literal("attachment_budget_exceeded"),
99
+ kind: z.literal("write"),
100
+ retryAfterMs: z.number().int().min(1).max(86400000),
101
+ }),
102
+ ]);
103
+ const listReply = z.union([listOutputSchema, refusal]);
104
+ const readReply = z.union([readOutput, refusal]);
105
+ const writeReply = z.union([writeOutputSchema, refusal]);
106
+ async function recordDocument(
107
+ input: z.output<typeof documentIdentitySchema>,
108
+ context: InkcapBackendContext,
109
+ caller: AppMethodCallContext,
110
+ activity: "opened" | "edited",
111
+ ) {
112
+ if (caller.computerId === null || caller.computerId !== input.computerId)
113
+ return { error: "not_granted" as const };
114
+ const sequence = documentActivity(
115
+ context.activity,
116
+ JSON.stringify(caller.actor),
117
+ caller.computerId,
118
+ ).record(input.path, activity);
119
+ await context.documentsChanged(caller.computerId, sequence);
120
+ return { recorded: true as const };
121
+ }
122
+ export const appBackend = defineAppBackend({
123
+ gills: [recentFilesGill],
124
+ methods: {
125
+ "documents.opened": defineAppMethod({
126
+ description: "Record a successfully opened foreground Computer document",
127
+ effect: "write",
128
+ requires: ["computer.readFile", "app.storage"],
129
+ input: documentIdentitySchema,
130
+ output: z.union([documentRecordedSchema, documentActivityRefusalSchema]),
131
+ handler: (input, context: InkcapBackendContext, caller) =>
132
+ recordDocument(input, context, caller, "opened"),
133
+ }),
134
+ "documents.edited": defineAppMethod({
135
+ description: "Record an accepted Computer document save",
136
+ effect: "write",
137
+ requires: ["computer.writeFile", "app.storage"],
138
+ input: documentIdentitySchema,
139
+ output: z.union([documentRecordedSchema, documentActivityRefusalSchema]),
140
+ handler: (input, context: InkcapBackendContext, caller) =>
141
+ recordDocument(input, context, caller, "edited"),
142
+ }),
143
+ "documents.recent": defineAppMethod({
144
+ description:
145
+ "Read one saved recent-documents page for the actor and admitted Computer; no provider calls",
146
+ effect: "read",
147
+ requires: ["computer.readFile"],
148
+ input: documentsRecentInputSchema,
149
+ output: z.union([documentsRecentSchema, documentActivityRefusalSchema]),
150
+ handler: (input, context: InkcapBackendContext, caller) =>
151
+ caller.computerId === null
152
+ ? { error: "not_granted" as const }
153
+ : documentActivity(
154
+ context.activity,
155
+ JSON.stringify(caller.actor),
156
+ caller.computerId,
157
+ ).recent(input.limit, input.after),
158
+ }),
159
+ listFiles: defineAppMethod({
160
+ description:
161
+ "List one bounded directory page on the attached Computer. Continue with nextOffset; no Computer selector is accepted.",
162
+ effect: "read",
163
+ requires: ["computer.list"],
164
+ input: listInput,
165
+ output: listReply,
166
+ handler: async (input, context: InkcapBackendContext, caller) =>
167
+ caller.computerId === null || !context.env.COMPUTER?.list
168
+ ? refusal.parse({ error: "not_granted" })
169
+ : listReply.parse(await context.env.COMPUTER.list(input)),
170
+ }),
171
+ readFile: defineAppMethod({
172
+ description:
173
+ "Read up to 128 KiB of a file on the attached Computer as base64. Use byteOffset for the next chunk.",
174
+ effect: "read",
175
+ requires: ["computer.readFile"],
176
+ input: readInput,
177
+ output: readReply,
178
+ handler: async (input, context: InkcapBackendContext, caller) =>
179
+ caller.computerId === null || !context.env.COMPUTER?.read
180
+ ? refusal.parse({ error: "not_granted" })
181
+ : readReply.parse(await context.env.COMPUTER.read(input)),
182
+ }),
183
+ writeFile: defineAppMethod({
184
+ description:
185
+ "Write UTF-8 text on the attached Computer. Supply expectedContent for a compare-and-swap; combined content is limited to 48 KiB. The host owns attribution and the attachment write budget.",
186
+ effect: "write",
187
+ requires: ["computer.writeFile"],
188
+ input: writeInput,
189
+ output: writeReply,
190
+ present: {
191
+ rows: resultRows,
192
+ verb: "wrote",
193
+ row: ({ output }) =>
194
+ "error" in output
195
+ ? {
196
+ id: "refused",
197
+ title: "Inkcap could not write the file",
198
+ detail: output.error,
199
+ tone: "error",
200
+ }
201
+ : {
202
+ id: output.path,
203
+ title: `Inkcap wrote ${output.path.split("/").at(-1) ?? output.path}`,
204
+ detail: `${output.path} · ${String(output.bytes)} bytes`,
205
+ open: {
206
+ params: filesNavigateIntent({ path: output.path }).params,
207
+ },
208
+ },
209
+ },
210
+ handler: async (input, context: InkcapBackendContext, caller) =>
211
+ caller.computerId === null || !context.env.COMPUTER?.write
212
+ ? refusal.parse({ error: "not_granted" })
213
+ : writeReply.parse(await context.env.COMPUTER.write(input)),
214
+ }),
215
+ },
216
+ });
@@ -0,0 +1 @@
1
+ export { DurableObject } from "../../../../packages/shiitake/test/fixtures/cloudflare-workers.fixture.js";