@agimon-ai/doompi-author 0.0.1-alpha.2

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 (282) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +37 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +1 -0
  4. package/dist/adapters/authorApi.cjs +2 -0
  5. package/dist/adapters/authorApi.cjs.map +1 -0
  6. package/dist/adapters/authorBridgeApi.cjs +2 -0
  7. package/dist/adapters/authorBridgeApi.cjs.map +1 -0
  8. package/dist/adapters/authorDocumentApi.cjs +2 -0
  9. package/dist/adapters/authorDocumentApi.cjs.map +1 -0
  10. package/dist/adapters/pi/authorBridgeClient.cjs +2 -0
  11. package/dist/adapters/pi/authorBridgeClient.cjs.map +1 -0
  12. package/dist/adapters/pi/authorTools.cjs +2 -0
  13. package/dist/adapters/pi/authorTools.cjs.map +1 -0
  14. package/dist/adapters/pi/extension.cjs +2 -0
  15. package/dist/adapters/pi/extension.cjs.map +1 -0
  16. package/dist/adapters/structuredDocuments/common.cjs +2 -0
  17. package/dist/adapters/structuredDocuments/common.cjs.map +1 -0
  18. package/dist/adapters/structuredDocuments/csv.cjs +7 -0
  19. package/dist/adapters/structuredDocuments/csv.cjs.map +1 -0
  20. package/dist/adapters/structuredDocuments/index.cjs +2 -0
  21. package/dist/adapters/structuredDocuments/index.cjs.map +1 -0
  22. package/dist/adapters/structuredDocuments/markdownSlides.cjs +6 -0
  23. package/dist/adapters/structuredDocuments/markdownSlides.cjs.map +1 -0
  24. package/dist/adapters/structuredDocuments/ooxmlArchive.cjs +2 -0
  25. package/dist/adapters/structuredDocuments/ooxmlArchive.cjs.map +1 -0
  26. package/dist/adapters/structuredDocuments/pptx.cjs +2 -0
  27. package/dist/adapters/structuredDocuments/pptx.cjs.map +1 -0
  28. package/dist/adapters/structuredDocuments/xlsx.cjs +2 -0
  29. package/dist/adapters/structuredDocuments/xlsx.cjs.map +1 -0
  30. package/dist/adapters/structuredDocuments/xmlText.cjs +2 -0
  31. package/dist/adapters/structuredDocuments/xmlText.cjs.map +1 -0
  32. package/dist/adapters/webAuthorChannel.cjs +2 -0
  33. package/dist/adapters/webAuthorChannel.cjs.map +1 -0
  34. package/dist/commands/doomAuthorCommand.cjs +2 -0
  35. package/dist/commands/doomAuthorCommand.cjs.map +1 -0
  36. package/dist/container/index.cjs +2 -0
  37. package/dist/container/index.cjs.map +1 -0
  38. package/dist/extensions/pi.cjs +1 -0
  39. package/dist/extensions/pi.d.cts +2 -0
  40. package/dist/extensions/pi.d.mts +2 -0
  41. package/dist/extensions/pi.mjs +1 -0
  42. package/dist/index.cjs +1 -0
  43. package/dist/index.d.cts +21 -0
  44. package/dist/index.d.mts +21 -0
  45. package/dist/index.mjs +1 -0
  46. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/engine/helpers/union.d.cts +6 -0
  47. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/engine/helpers/union.d.cts.map +1 -0
  48. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/engine/helpers/union.d.mts +6 -0
  49. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/engine/helpers/union.d.mts.map +1 -0
  50. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/_optional.d.cts +7 -0
  51. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/_optional.d.cts.map +1 -0
  52. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/_optional.d.mts +7 -0
  53. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/_optional.d.mts.map +1 -0
  54. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/object.d.cts +12 -0
  55. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/object.d.cts.map +1 -0
  56. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/object.d.mts +12 -0
  57. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/object.d.mts.map +1 -0
  58. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/properties.d.cts +12 -0
  59. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/properties.d.cts.map +1 -0
  60. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/properties.d.mts +12 -0
  61. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/properties.d.mts.map +1 -0
  62. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/record.d.cts +9 -0
  63. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/record.d.cts.map +1 -0
  64. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/record.d.mts +9 -0
  65. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/record.d.mts.map +1 -0
  66. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/schema.d.cts +4 -0
  67. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/schema.d.cts.map +1 -0
  68. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/schema.d.mts +4 -0
  69. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/schema.d.mts.map +1 -0
  70. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/string.d.cts +9 -0
  71. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/string.d.cts.map +1 -0
  72. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/string.d.mts +9 -0
  73. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/string.d.mts.map +1 -0
  74. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/unknown.d.cts +8 -0
  75. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/unknown.d.cts.map +1 -0
  76. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/unknown.d.mts +8 -0
  77. package/dist/node_modules/.pnpm/typebox@1.3.25/node_modules/typebox/build/type/types/unknown.d.mts.map +1 -0
  78. package/dist/packages/minor/doompi-author/src/adapters/authorApi.d.cts +15 -0
  79. package/dist/packages/minor/doompi-author/src/adapters/authorApi.d.cts.map +1 -0
  80. package/dist/packages/minor/doompi-author/src/adapters/authorApi.d.mts +15 -0
  81. package/dist/packages/minor/doompi-author/src/adapters/authorApi.d.mts.map +1 -0
  82. package/dist/packages/minor/doompi-author/src/adapters/authorApi.mjs +2 -0
  83. package/dist/packages/minor/doompi-author/src/adapters/authorApi.mjs.map +1 -0
  84. package/dist/packages/minor/doompi-author/src/adapters/authorBridgeApi.d.cts +6 -0
  85. package/dist/packages/minor/doompi-author/src/adapters/authorBridgeApi.d.cts.map +1 -0
  86. package/dist/packages/minor/doompi-author/src/adapters/authorBridgeApi.d.mts +6 -0
  87. package/dist/packages/minor/doompi-author/src/adapters/authorBridgeApi.d.mts.map +1 -0
  88. package/dist/packages/minor/doompi-author/src/adapters/authorBridgeApi.mjs +2 -0
  89. package/dist/packages/minor/doompi-author/src/adapters/authorBridgeApi.mjs.map +1 -0
  90. package/dist/packages/minor/doompi-author/src/adapters/authorDocumentApi.d.cts +11 -0
  91. package/dist/packages/minor/doompi-author/src/adapters/authorDocumentApi.d.cts.map +1 -0
  92. package/dist/packages/minor/doompi-author/src/adapters/authorDocumentApi.d.mts +11 -0
  93. package/dist/packages/minor/doompi-author/src/adapters/authorDocumentApi.d.mts.map +1 -0
  94. package/dist/packages/minor/doompi-author/src/adapters/authorDocumentApi.mjs +2 -0
  95. package/dist/packages/minor/doompi-author/src/adapters/authorDocumentApi.mjs.map +1 -0
  96. package/dist/packages/minor/doompi-author/src/adapters/pi/authorBridgeClient.d.cts +18 -0
  97. package/dist/packages/minor/doompi-author/src/adapters/pi/authorBridgeClient.d.cts.map +1 -0
  98. package/dist/packages/minor/doompi-author/src/adapters/pi/authorBridgeClient.d.mts +18 -0
  99. package/dist/packages/minor/doompi-author/src/adapters/pi/authorBridgeClient.d.mts.map +1 -0
  100. package/dist/packages/minor/doompi-author/src/adapters/pi/authorBridgeClient.mjs +2 -0
  101. package/dist/packages/minor/doompi-author/src/adapters/pi/authorBridgeClient.mjs.map +1 -0
  102. package/dist/packages/minor/doompi-author/src/adapters/pi/authorTools.d.cts +9 -0
  103. package/dist/packages/minor/doompi-author/src/adapters/pi/authorTools.d.cts.map +1 -0
  104. package/dist/packages/minor/doompi-author/src/adapters/pi/authorTools.d.mts +9 -0
  105. package/dist/packages/minor/doompi-author/src/adapters/pi/authorTools.d.mts.map +1 -0
  106. package/dist/packages/minor/doompi-author/src/adapters/pi/authorTools.mjs +2 -0
  107. package/dist/packages/minor/doompi-author/src/adapters/pi/authorTools.mjs.map +1 -0
  108. package/dist/packages/minor/doompi-author/src/adapters/pi/extension.d.cts +19 -0
  109. package/dist/packages/minor/doompi-author/src/adapters/pi/extension.d.cts.map +1 -0
  110. package/dist/packages/minor/doompi-author/src/adapters/pi/extension.d.mts +19 -0
  111. package/dist/packages/minor/doompi-author/src/adapters/pi/extension.d.mts.map +1 -0
  112. package/dist/packages/minor/doompi-author/src/adapters/pi/extension.mjs +2 -0
  113. package/dist/packages/minor/doompi-author/src/adapters/pi/extension.mjs.map +1 -0
  114. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/common.mjs +2 -0
  115. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/common.mjs.map +1 -0
  116. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/csv.d.cts +7 -0
  117. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/csv.d.cts.map +1 -0
  118. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/csv.d.mts +7 -0
  119. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/csv.d.mts.map +1 -0
  120. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/csv.mjs +7 -0
  121. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/csv.mjs.map +1 -0
  122. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/index.d.cts +13 -0
  123. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/index.d.cts.map +1 -0
  124. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/index.d.mts +13 -0
  125. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/index.d.mts.map +1 -0
  126. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/index.mjs +2 -0
  127. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/index.mjs.map +1 -0
  128. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/markdownSlides.d.cts +7 -0
  129. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/markdownSlides.d.cts.map +1 -0
  130. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/markdownSlides.d.mts +7 -0
  131. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/markdownSlides.d.mts.map +1 -0
  132. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/markdownSlides.mjs +6 -0
  133. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/markdownSlides.mjs.map +1 -0
  134. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/ooxmlArchive.d.cts +13 -0
  135. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/ooxmlArchive.d.cts.map +1 -0
  136. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/ooxmlArchive.d.mts +13 -0
  137. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/ooxmlArchive.d.mts.map +1 -0
  138. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/ooxmlArchive.mjs +2 -0
  139. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/ooxmlArchive.mjs.map +1 -0
  140. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/pptx.d.cts +7 -0
  141. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/pptx.d.cts.map +1 -0
  142. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/pptx.d.mts +7 -0
  143. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/pptx.d.mts.map +1 -0
  144. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/pptx.mjs +2 -0
  145. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/pptx.mjs.map +1 -0
  146. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/xlsx.d.cts +7 -0
  147. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/xlsx.d.cts.map +1 -0
  148. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/xlsx.d.mts +7 -0
  149. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/xlsx.d.mts.map +1 -0
  150. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/xlsx.mjs +2 -0
  151. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/xlsx.mjs.map +1 -0
  152. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/xmlText.mjs +2 -0
  153. package/dist/packages/minor/doompi-author/src/adapters/structuredDocuments/xmlText.mjs.map +1 -0
  154. package/dist/packages/minor/doompi-author/src/adapters/webAuthorChannel.d.cts +6 -0
  155. package/dist/packages/minor/doompi-author/src/adapters/webAuthorChannel.d.cts.map +1 -0
  156. package/dist/packages/minor/doompi-author/src/adapters/webAuthorChannel.d.mts +6 -0
  157. package/dist/packages/minor/doompi-author/src/adapters/webAuthorChannel.d.mts.map +1 -0
  158. package/dist/packages/minor/doompi-author/src/adapters/webAuthorChannel.mjs +2 -0
  159. package/dist/packages/minor/doompi-author/src/adapters/webAuthorChannel.mjs.map +1 -0
  160. package/dist/packages/minor/doompi-author/src/commands/doomAuthorCommand.mjs +2 -0
  161. package/dist/packages/minor/doompi-author/src/commands/doomAuthorCommand.mjs.map +1 -0
  162. package/dist/packages/minor/doompi-author/src/container/index.d.cts +5 -0
  163. package/dist/packages/minor/doompi-author/src/container/index.d.cts.map +1 -0
  164. package/dist/packages/minor/doompi-author/src/container/index.d.mts +5 -0
  165. package/dist/packages/minor/doompi-author/src/container/index.d.mts.map +1 -0
  166. package/dist/packages/minor/doompi-author/src/container/index.mjs +2 -0
  167. package/dist/packages/minor/doompi-author/src/container/index.mjs.map +1 -0
  168. package/dist/packages/minor/doompi-author/src/schemas/authorTools.d.cts +16 -0
  169. package/dist/packages/minor/doompi-author/src/schemas/authorTools.d.cts.map +1 -0
  170. package/dist/packages/minor/doompi-author/src/schemas/authorTools.d.mts +16 -0
  171. package/dist/packages/minor/doompi-author/src/schemas/authorTools.d.mts.map +1 -0
  172. package/dist/packages/minor/doompi-author/src/schemas/authorTools.mjs +2 -0
  173. package/dist/packages/minor/doompi-author/src/schemas/authorTools.mjs.map +1 -0
  174. package/dist/packages/minor/doompi-author/src/services/authorBridgeState.d.cts +31 -0
  175. package/dist/packages/minor/doompi-author/src/services/authorBridgeState.d.cts.map +1 -0
  176. package/dist/packages/minor/doompi-author/src/services/authorBridgeState.d.mts +31 -0
  177. package/dist/packages/minor/doompi-author/src/services/authorBridgeState.d.mts.map +1 -0
  178. package/dist/packages/minor/doompi-author/src/services/authorBridgeState.mjs +2 -0
  179. package/dist/packages/minor/doompi-author/src/services/authorBridgeState.mjs.map +1 -0
  180. package/dist/packages/minor/doompi-author/src/services/authorCatalog.d.cts +10 -0
  181. package/dist/packages/minor/doompi-author/src/services/authorCatalog.d.cts.map +1 -0
  182. package/dist/packages/minor/doompi-author/src/services/authorCatalog.d.mts +10 -0
  183. package/dist/packages/minor/doompi-author/src/services/authorCatalog.d.mts.map +1 -0
  184. package/dist/packages/minor/doompi-author/src/services/authorMode.d.cts +19 -0
  185. package/dist/packages/minor/doompi-author/src/services/authorMode.d.cts.map +1 -0
  186. package/dist/packages/minor/doompi-author/src/services/authorMode.d.mts +19 -0
  187. package/dist/packages/minor/doompi-author/src/services/authorMode.d.mts.map +1 -0
  188. package/dist/packages/minor/doompi-author/src/services/authorMode.mjs +3 -0
  189. package/dist/packages/minor/doompi-author/src/services/authorMode.mjs.map +1 -0
  190. package/dist/packages/minor/doompi-author/src/services/extensionService.d.cts +7 -0
  191. package/dist/packages/minor/doompi-author/src/services/extensionService.d.cts.map +1 -0
  192. package/dist/packages/minor/doompi-author/src/services/extensionService.d.mts +7 -0
  193. package/dist/packages/minor/doompi-author/src/services/extensionService.d.mts.map +1 -0
  194. package/dist/packages/minor/doompi-author/src/services/extensionService.mjs +2 -0
  195. package/dist/packages/minor/doompi-author/src/services/extensionService.mjs.map +1 -0
  196. package/dist/packages/minor/doompi-author/src/types/author.d.cts +39 -0
  197. package/dist/packages/minor/doompi-author/src/types/author.d.cts.map +1 -0
  198. package/dist/packages/minor/doompi-author/src/types/author.d.mts +39 -0
  199. package/dist/packages/minor/doompi-author/src/types/author.d.mts.map +1 -0
  200. package/dist/packages/minor/doompi-author/src/types/author.mjs +2 -0
  201. package/dist/packages/minor/doompi-author/src/types/author.mjs.map +1 -0
  202. package/dist/packages/minor/doompi-author/src/types/authorApi.d.cts +23 -0
  203. package/dist/packages/minor/doompi-author/src/types/authorApi.d.cts.map +1 -0
  204. package/dist/packages/minor/doompi-author/src/types/authorApi.d.mts +23 -0
  205. package/dist/packages/minor/doompi-author/src/types/authorApi.d.mts.map +1 -0
  206. package/dist/packages/minor/doompi-author/src/types/authorApi.mjs +2 -0
  207. package/dist/packages/minor/doompi-author/src/types/authorApi.mjs.map +1 -0
  208. package/dist/packages/minor/doompi-author/src/types/extension.d.cts +16 -0
  209. package/dist/packages/minor/doompi-author/src/types/extension.d.cts.map +1 -0
  210. package/dist/packages/minor/doompi-author/src/types/extension.d.mts +16 -0
  211. package/dist/packages/minor/doompi-author/src/types/extension.d.mts.map +1 -0
  212. package/dist/packages/minor/doompi-author/src/types/structuredDocuments.d.cts +60 -0
  213. package/dist/packages/minor/doompi-author/src/types/structuredDocuments.d.cts.map +1 -0
  214. package/dist/packages/minor/doompi-author/src/types/structuredDocuments.d.mts +60 -0
  215. package/dist/packages/minor/doompi-author/src/types/structuredDocuments.d.mts.map +1 -0
  216. package/dist/packages/minor/doompi-author/src/types/webAuthor.d.cts +67 -0
  217. package/dist/packages/minor/doompi-author/src/types/webAuthor.d.cts.map +1 -0
  218. package/dist/packages/minor/doompi-author/src/types/webAuthor.d.mts +67 -0
  219. package/dist/packages/minor/doompi-author/src/types/webAuthor.d.mts.map +1 -0
  220. package/dist/packages/minor/doompi-author/src/types/webAuthor.mjs +2 -0
  221. package/dist/packages/minor/doompi-author/src/types/webAuthor.mjs.map +1 -0
  222. package/dist/schemas/authorTools.cjs +2 -0
  223. package/dist/schemas/authorTools.cjs.map +1 -0
  224. package/dist/services/authorBridgeState.cjs +2 -0
  225. package/dist/services/authorBridgeState.cjs.map +1 -0
  226. package/dist/services/authorMode.cjs +3 -0
  227. package/dist/services/authorMode.cjs.map +1 -0
  228. package/dist/services/extensionService.cjs +2 -0
  229. package/dist/services/extensionService.cjs.map +1 -0
  230. package/dist/sessionApi.cjs +1 -0
  231. package/dist/sessionApi.d.cts +7 -0
  232. package/dist/sessionApi.d.mts +7 -0
  233. package/dist/sessionApi.mjs +1 -0
  234. package/dist/types/author.cjs +2 -0
  235. package/dist/types/author.cjs.map +1 -0
  236. package/dist/types/authorApi.cjs +2 -0
  237. package/dist/types/authorApi.cjs.map +1 -0
  238. package/dist/types/webAuthor.cjs +2 -0
  239. package/dist/types/webAuthor.cjs.map +1 -0
  240. package/dist/webHub.cjs +1 -0
  241. package/dist/webHub.d.cts +2 -0
  242. package/dist/webHub.d.mts +2 -0
  243. package/dist/webHub.mjs +1 -0
  244. package/llms.txt +11 -0
  245. package/package.json +140 -0
  246. package/src/exports/webClient.ts +76 -0
  247. package/src/prompts/doompi-use-author/SKILL.md +17 -0
  248. package/src/types/author.ts +45 -0
  249. package/src/types/structuredDocuments.ts +63 -0
  250. package/src/types/webAuthor.ts +85 -0
  251. package/src/web/api/authorBroker.ts +14 -0
  252. package/src/web/api/authorBrowserBridge.ts +225 -0
  253. package/src/web/api/authorFiles.ts +219 -0
  254. package/src/web/api/authorMedia.ts +19 -0
  255. package/src/web/api/authorRuntime.ts +164 -0
  256. package/src/web/components/AuthorDocumentPanel.tsx +232 -0
  257. package/src/web/components/AuthorGridOverlay.tsx +81 -0
  258. package/src/web/components/AuthorMediaPreview.tsx +41 -0
  259. package/src/web/components/AuthorMediaView.tsx +356 -0
  260. package/src/web/components/AuthorPanel.tsx +105 -0
  261. package/src/web/components/AuthorRegionDrafts.tsx +102 -0
  262. package/src/web/components/AuthorRequestLog.tsx +179 -0
  263. package/src/web/components/AuthorStructuredView.tsx +121 -0
  264. package/src/web/components/AuthorTextView.tsx +97 -0
  265. package/src/web/components/AuthorToolPalette.tsx +103 -0
  266. package/src/web/components/AuthorVideoControls.tsx +111 -0
  267. package/src/web/components/DescribeAuthorToolsToolCard.tsx +50 -0
  268. package/src/web/components/OpenAuthoringFileToolCard.tsx +66 -0
  269. package/src/web/components/UseAuthorToolsToolCard.tsx +50 -0
  270. package/src/web/index.ts +68 -0
  271. package/src/web/lib/authorGrid.ts +124 -0
  272. package/src/web/lib/authorRegions.ts +30 -0
  273. package/src/web/lib/authorViewportTypes.ts +202 -0
  274. package/src/web/lib/describeAuthorToolsToolRender.ts +20 -0
  275. package/src/web/lib/useAuthorToolsToolRender.ts +20 -0
  276. package/src/web/stores/authorCapture.ts +322 -0
  277. package/src/web/stores/authorGridTools.ts +316 -0
  278. package/src/web/stores/authorProfiles.ts +130 -0
  279. package/src/web/stores/authorRequestLifecycle.ts +82 -0
  280. package/src/web/stores/authorStore.ts +60 -0
  281. package/src/web/stores/authorWorkspaceStore.ts +525 -0
  282. package/src/web/tsconfig.json +22 -0
@@ -0,0 +1,219 @@
1
+ import { sealedTransport } from '@agimon-ai/doompi-web-security/browser';
2
+ import type {
3
+ CsvDialect,
4
+ DocumentFragment,
5
+ DocumentOperation,
6
+ DocumentPreflightReport,
7
+ ParsedStructuredDocument,
8
+ StructuredDocumentFormat,
9
+ } from '../../types/structuredDocuments.ts';
10
+ import type { AuthorDocumentInput, AuthorDocumentKind } from '../lib/authorViewportTypes.ts';
11
+
12
+ const IMAGE_EXTENSIONS = new Set(['avif', 'bmp', 'gif', 'jpeg', 'jpg', 'png', 'svg', 'webp']);
13
+ const VIDEO_EXTENSIONS = new Set(['m4v', 'mov', 'mp4', 'webm']);
14
+
15
+ function structuredFormat(path: string): StructuredDocumentFormat | undefined {
16
+ const lower = path.toLowerCase();
17
+ if (lower.endsWith('.slides.md')) return 'markdown-slides';
18
+ if (lower.endsWith('.csv')) return 'csv';
19
+ if (lower.endsWith('.pptx')) return 'pptx';
20
+ if (lower.endsWith('.xlsx')) return 'xlsx';
21
+ return undefined;
22
+ }
23
+
24
+ export function authorKindForPath(path: string): AuthorDocumentKind {
25
+ const format = structuredFormat(path);
26
+ if (format === 'markdown-slides') return 'slides';
27
+ if (format !== undefined) return format;
28
+ const extension = path.split('.').at(-1)?.toLowerCase() ?? '';
29
+ if (extension === 'md') return 'markdown';
30
+ if (IMAGE_EXTENSIONS.has(extension)) return 'image';
31
+ if (VIDEO_EXTENSIONS.has(extension)) return 'video';
32
+ if (extension === 'pdf') return 'pdf';
33
+ if (['txt', 'json', 'yaml', 'yml', 'xml', 'html', 'css', 'js', 'ts', 'tsx', 'jsx'].includes(extension)) return 'text';
34
+ return 'opaque';
35
+ }
36
+
37
+ export function authorSessionFileUrl(sessionId: string, path: string): string {
38
+ return `/api/sessions/${encodeURIComponent(sessionId)}/file?path=${encodeURIComponent(path)}`;
39
+ }
40
+
41
+ function authorDocumentApiUrl(sessionId: string, operation: 'open' | 'preflight' | 'serialize'): string {
42
+ return `/api/plugin/author/documents/${operation}?session=${encodeURIComponent(sessionId)}`;
43
+ }
44
+
45
+ async function jsonRequest<T>(url: string, body: Record<string, unknown>, signal?: AbortSignal): Promise<T> {
46
+ const response = await sealedTransport.fetch(url, {
47
+ method: 'POST',
48
+ headers: { 'Content-Type': 'application/json' },
49
+ body: JSON.stringify(body),
50
+ signal,
51
+ });
52
+ const value = (await response.json()) as T & { error?: unknown };
53
+ if (!response.ok)
54
+ throw new Error(typeof value.error === 'string' ? value.error : `Author document API failed (${response.status})`);
55
+ return value;
56
+ }
57
+
58
+ export async function loadAuthorDocument(
59
+ sessionId: string,
60
+ path: string,
61
+ signal?: AbortSignal,
62
+ ): Promise<AuthorDocumentInput> {
63
+ const kind = authorKindForPath(path);
64
+ const url = authorSessionFileUrl(sessionId, path);
65
+ const response = await sealedTransport.fetch(url, { signal });
66
+ if (!response.ok) throw new Error(`Author could not open ${path} (${response.status})`);
67
+ const sourceSha256 = response.headers.get('X-File-SHA256') ?? undefined;
68
+ const format = structuredFormat(path);
69
+ if (format !== undefined) {
70
+ const parsed = await jsonRequest<ParsedStructuredDocument>(
71
+ authorDocumentApiUrl(sessionId, 'open'),
72
+ { path, format },
73
+ signal,
74
+ );
75
+ const parsedDigest = parsed.manifest?.sourceDigest;
76
+ if (sourceSha256 !== undefined && parsedDigest !== undefined && sourceSha256 !== parsedDigest) {
77
+ throw new Error('Document source changed while opening. Reopen the document.');
78
+ }
79
+ const csvDialect = format === 'csv' ? csvDialectFromMetadata(parsed.manifest?.metadata) : undefined;
80
+ return {
81
+ path,
82
+ kind,
83
+ title: path.split('/').at(-1) ?? path,
84
+ structuredFormat: format,
85
+ fragments: parsed.fragments,
86
+ ...(csvDialect === undefined ? {} : { csvDialect }),
87
+ originalFragments: parsed.fragments.map((fragment) => ({ ...fragment })),
88
+ ...(sourceSha256 === undefined ? {} : { sourceSha256 }),
89
+ };
90
+ }
91
+ const mediaUrl = kind === 'image' || kind === 'video' || kind === 'pdf' || kind === 'opaque' ? url : undefined;
92
+ const content = kind === 'text' || kind === 'markdown' ? await response.text() : undefined;
93
+ return {
94
+ path,
95
+ kind,
96
+ title: path.split('/').at(-1) ?? path,
97
+ ...(content === undefined ? {} : { content }),
98
+ ...(mediaUrl === undefined ? {} : { mediaUrl }),
99
+ ...(sourceSha256 === undefined ? {} : { sourceSha256 }),
100
+ };
101
+ }
102
+
103
+ function operationsFor(document: AuthorDocumentInput): DocumentOperation[] {
104
+ const originals = new Map((document.originalFragments ?? []).map((fragment) => [fragment.id, fragment.text]));
105
+ return (document.fragments ?? [])
106
+ .filter((fragment) => !fragment.readOnly && originals.get(fragment.id) !== fragment.text)
107
+ .map((fragment) => ({ fragmentId: fragment.id, replacement: fragment.text }));
108
+ }
109
+
110
+ function decodeBase64(value: string): Uint8Array {
111
+ if (!/^(?:[A-Za-z\d+/]{4})*(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?$/.test(value))
112
+ throw new Error('Invalid serialized document bytes.');
113
+ const binary = atob(value);
114
+ return Uint8Array.from(binary, (character) => character.charCodeAt(0));
115
+ }
116
+
117
+ function imageMimeType(path: string): 'image/png' | 'image/jpeg' | 'image/webp' | undefined {
118
+ const extension = path.split('.').at(-1)?.toLowerCase();
119
+ if (extension === 'png') return 'image/png';
120
+ if (extension === 'jpg' || extension === 'jpeg') return 'image/jpeg';
121
+ if (extension === 'webp') return 'image/webp';
122
+ return undefined;
123
+ }
124
+
125
+ async function croppedImageBytes(documentInput: AuthorDocumentInput, signal?: AbortSignal): Promise<Blob> {
126
+ const { crop, mediaUrl } = documentInput;
127
+ if (crop === undefined || mediaUrl === undefined) throw new Error('This Author image has no crop source.');
128
+ const mimeType = imageMimeType(documentInput.path);
129
+ if (mimeType === undefined) throw new Error('This image format cannot preserve its encoding after crop.');
130
+ const response = await sealedTransport.fetch(mediaUrl, { signal, cache: 'no-store' });
131
+ if (!response.ok) throw new Error(`Author could not read the image before saving (${response.status})`);
132
+ const bitmap = await createImageBitmap(await response.blob());
133
+ try {
134
+ const sourceX = Math.round(crop.x * bitmap.width);
135
+ const sourceY = Math.round(crop.y * bitmap.height);
136
+ const sourceWidth = Math.max(1, Math.round(crop.width * bitmap.width));
137
+ const sourceHeight = Math.max(1, Math.round(crop.height * bitmap.height));
138
+ const canvas = window.document.createElement('canvas');
139
+ canvas.width = sourceWidth;
140
+ canvas.height = sourceHeight;
141
+ const context = canvas.getContext('2d');
142
+ if (context === null) throw new Error('Canvas is unavailable for image crop.');
143
+ context.drawImage(bitmap, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, sourceWidth, sourceHeight);
144
+ const blob = await new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, mimeType));
145
+ if (blob === null || blob.type !== mimeType)
146
+ throw new Error('The browser cannot encode this cropped image format.');
147
+ return blob;
148
+ } finally {
149
+ bitmap.close();
150
+ }
151
+ }
152
+
153
+ async function bytesToSave(sessionId: string, document: AuthorDocumentInput, signal?: AbortSignal): Promise<BodyInit> {
154
+ if (document.kind === 'text' || document.kind === 'markdown') return document.content ?? '';
155
+ if (document.kind === 'image' && document.crop !== undefined) return await croppedImageBytes(document, signal);
156
+ if (document.structuredFormat === undefined) throw new Error('This Author view cannot be saved.');
157
+ const operations = operationsFor(document);
158
+ const request = {
159
+ path: document.path,
160
+ format: document.structuredFormat,
161
+ operations,
162
+ ...(document.csvDialect === undefined ? {} : { csvDialect: document.csvDialect }),
163
+ };
164
+ const preflight = await jsonRequest<DocumentPreflightReport>(
165
+ authorDocumentApiUrl(sessionId, 'preflight'),
166
+ request,
167
+ signal,
168
+ );
169
+ if (preflight.sourceDigest !== undefined && preflight.sourceDigest !== document.sourceSha256) {
170
+ throw new Error('Document source changed before saving. Reopen the document.');
171
+ }
172
+ if (!preflight.accepted)
173
+ throw new Error(preflight.issues.map((issue) => issue.message).join('\n') || 'Author preflight rejected the edit.');
174
+ const serialized = await jsonRequest<{ bytes: string; encoding: string }>(
175
+ authorDocumentApiUrl(sessionId, 'serialize'),
176
+ { ...request, preflightDigest: preflight.digest },
177
+ signal,
178
+ );
179
+ if (serialized.encoding !== 'base64') throw new Error('Author serialize returned an unsupported encoding.');
180
+ const bytes = decodeBase64(serialized.bytes);
181
+ return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;
182
+ }
183
+
184
+ export async function saveAuthorDocument(
185
+ sessionId: string,
186
+ document: AuthorDocumentInput,
187
+ signal?: AbortSignal,
188
+ ): Promise<string> {
189
+ if (document.sourceSha256 === undefined) throw new Error('This Author view has no source digest.');
190
+ const response = await sealedTransport.fetch(authorSessionFileUrl(sessionId, document.path), {
191
+ method: 'PUT',
192
+ headers: { 'Content-Type': 'application/octet-stream', 'X-Expected-SHA256': document.sourceSha256 },
193
+ body: await bytesToSave(sessionId, document, signal),
194
+ signal,
195
+ });
196
+ if (!response.ok) throw new Error(`Author save failed (${response.status})`);
197
+ const sha256 = response.headers.get('X-File-SHA256');
198
+ if (sha256 === null || !/^[a-f0-9]{64}$/.test(sha256)) throw new Error('Author save returned an invalid digest.');
199
+ return sha256;
200
+ }
201
+
202
+ function csvDialectFromMetadata(
203
+ metadata: Record<string, string | number | boolean> | undefined,
204
+ ): CsvDialect | undefined {
205
+ if (metadata === undefined) return undefined;
206
+ const { delimiter, quote, recordDelimiter } = metadata;
207
+ if (
208
+ (delimiter !== ',' && delimiter !== ';' && delimiter !== '\t' && delimiter !== '|') ||
209
+ (quote !== '"' && quote !== "'") ||
210
+ (recordDelimiter !== '\n' && recordDelimiter !== '\r\n')
211
+ ) {
212
+ throw new Error('Author document API returned an invalid CSV dialect.');
213
+ }
214
+ return { delimiter, quote, recordDelimiter };
215
+ }
216
+
217
+ export function editableStructuredFragments(document: AuthorDocumentInput): readonly DocumentFragment[] {
218
+ return document.fragments ?? [];
219
+ }
@@ -0,0 +1,19 @@
1
+ import { sealedTransport } from '@agimon-ai/doompi-web-security/browser';
2
+
3
+ /** Native media elements cannot perform the remote session's encrypted HTTP exchange. */
4
+ export async function loadAuthorMedia(source: string, signal: AbortSignal): Promise<{ url: string; dispose(): void }> {
5
+ const response = await sealedTransport.fetch(source, { signal, cache: 'no-store' });
6
+ if (!response.ok) throw new Error(`Author media could not be loaded (${response.status}).`);
7
+ const blob = await response.blob();
8
+ signal.throwIfAborted();
9
+ const url = URL.createObjectURL(blob);
10
+ let disposed = false;
11
+ return {
12
+ url,
13
+ dispose() {
14
+ if (disposed) return;
15
+ disposed = true;
16
+ URL.revokeObjectURL(url);
17
+ },
18
+ };
19
+ }
@@ -0,0 +1,164 @@
1
+ import type {
2
+ ModelContext,
3
+ ModelContextAbortSignal,
4
+ ModelContextToolDescriptor,
5
+ WebPluginRuntime,
6
+ } from '@agimon-ai/doompi-web-contracts';
7
+ import type { AuthorTrustedProfile, AuthorTrustedTool } from '../lib/authorViewportTypes.ts';
8
+
9
+ export const AUTHOR_RUNTIME_BINDING_IDS = {
10
+ text: 'author.viewport.text',
11
+ media: 'author.viewport.media',
12
+ } as const;
13
+
14
+ interface ActiveCatalog {
15
+ generation: number;
16
+ controller: AbortController;
17
+ profiles: ReadonlyMap<string, AuthorTrustedProfile>;
18
+ }
19
+
20
+ function assertProfile(profile: AuthorTrustedProfile): void {
21
+ if (profile.id.trim() === '') throw new Error('Author profile id must not be empty');
22
+ if (profile.tools.length > 15) throw new Error('Author profiles may expose at most 15 tools');
23
+ const names = new Set<string>();
24
+ for (const tool of profile.tools) {
25
+ if (!/^[a-z][a-z0-9_]{0,29}$/.test(tool.name)) throw new Error(`Invalid Author tool name: ${tool.name}`);
26
+ if (names.has(tool.name)) throw new Error(`Duplicate Author tool name: ${tool.name}`);
27
+ names.add(tool.name);
28
+ }
29
+ }
30
+
31
+ function descriptorMatches(actual: ModelContextToolDescriptor, expected: AuthorTrustedTool): boolean {
32
+ return (
33
+ actual.name === expected.name &&
34
+ actual.description === expected.description &&
35
+ JSON.stringify(actual.inputSchema) === JSON.stringify(expected.inputSchema)
36
+ );
37
+ }
38
+
39
+ function staleError(): Error {
40
+ return new Error('Author viewport catalog is stale');
41
+ }
42
+
43
+ function invocationSignal(
44
+ catalogSignal: AbortSignal,
45
+ requestSignal: ModelContextAbortSignal,
46
+ ): { signal: AbortSignal; release: () => void } {
47
+ const controller = new AbortController();
48
+ const abortCatalog = (): void => controller.abort(catalogSignal.reason);
49
+ const abortRequest = (): void => controller.abort(requestSignal.reason);
50
+ if (catalogSignal.aborted) abortCatalog();
51
+ else if (requestSignal.aborted) abortRequest();
52
+ else {
53
+ catalogSignal.addEventListener('abort', abortCatalog, { once: true });
54
+ requestSignal.addEventListener('abort', abortRequest, { once: true });
55
+ }
56
+ return {
57
+ signal: controller.signal,
58
+ release: () => {
59
+ catalogSignal.removeEventListener('abort', abortCatalog);
60
+ requestSignal.removeEventListener('abort', abortRequest);
61
+ },
62
+ };
63
+ }
64
+
65
+ /** Owns page-lifetime WebMCP registrations. Profiles are replaced as one abortable catalog. */
66
+ export class AuthorRuntime {
67
+ readonly #runtime: WebPluginRuntime;
68
+ #context: ModelContext | undefined;
69
+ #active: ActiveCatalog | undefined;
70
+ #pending: ActiveCatalog | undefined;
71
+ #generation = 0;
72
+ #disposed = false;
73
+
74
+ constructor(runtime: WebPluginRuntime) {
75
+ this.#runtime = runtime;
76
+ }
77
+
78
+ async replaceProfiles(profiles: readonly AuthorTrustedProfile[]): Promise<() => void> {
79
+ if (this.#disposed) throw new Error('Author runtime is disposed');
80
+ for (const profile of profiles) assertProfile(profile);
81
+ const profileMap = new Map(profiles.map((profile) => [profile.id, profile]));
82
+ const tools = profiles.flatMap((profile) => profile.tools);
83
+ if (new Set(tools.map((tool) => tool.name)).size !== tools.length) {
84
+ throw new Error('Author tool names must be unique across trusted profiles');
85
+ }
86
+
87
+ const context = this.#context ?? (await this.#runtime.acquireModelContext?.())?.modelContext;
88
+ if (context === undefined) throw new Error('Model Context is unavailable');
89
+ this.#context = context;
90
+ const generation = ++this.#generation;
91
+ const controller = new AbortController();
92
+ const candidate: ActiveCatalog = { generation, controller, profiles: profileMap };
93
+ const inactive = staleError();
94
+ this.#pending?.controller.abort(inactive);
95
+ this.#active?.controller.abort(inactive);
96
+ this.#pending = candidate;
97
+ this.#active = undefined;
98
+ const assertCurrent = (): void => {
99
+ if (controller.signal.aborted || this.#disposed || this.#active?.generation !== generation) throw staleError();
100
+ };
101
+
102
+ try {
103
+ await Promise.all(
104
+ tools.map(async (tool) =>
105
+ context.registerTool(
106
+ {
107
+ name: tool.name,
108
+ description: tool.description,
109
+ inputSchema: tool.inputSchema,
110
+ execute: async (input, options) => {
111
+ assertCurrent();
112
+ const parsed: unknown = JSON.parse(input);
113
+ assertCurrent();
114
+ const invocation = invocationSignal(controller.signal, options.signal);
115
+ try {
116
+ const result = await tool.execute(parsed, invocation.signal);
117
+ assertCurrent();
118
+ return result;
119
+ } finally {
120
+ invocation.release();
121
+ }
122
+ },
123
+ },
124
+ { signal: controller.signal },
125
+ ),
126
+ ),
127
+ );
128
+ const registered = await context.getTools();
129
+ if (!tools.every((tool) => registered.some((descriptor) => descriptorMatches(descriptor, tool)))) {
130
+ throw new Error('Model Context did not confirm the Author catalog');
131
+ }
132
+ if (this.#disposed || generation !== this.#generation || this.#pending !== candidate) throw staleError();
133
+ this.#pending = undefined;
134
+ this.#active = candidate;
135
+ } catch (error) {
136
+ controller.abort(error);
137
+ if (this.#pending === candidate) this.#pending = undefined;
138
+ throw error;
139
+ }
140
+
141
+ return () => {
142
+ if (this.#active?.generation !== generation) return;
143
+ controller.abort(staleError());
144
+ this.#active = undefined;
145
+ this.#generation += 1;
146
+ };
147
+ }
148
+
149
+ async execute(name: string, input: unknown, signal: AbortSignal): Promise<unknown> {
150
+ const context = this.#context;
151
+ if (context === undefined || this.#active === undefined) throw new Error('Author catalog is not registered');
152
+ return context.executeTool(name, JSON.stringify(input), { signal });
153
+ }
154
+
155
+ dispose(): void {
156
+ if (this.#disposed) return;
157
+ this.#disposed = true;
158
+ this.#generation += 1;
159
+ this.#pending?.controller.abort(new Error('Author runtime disposed'));
160
+ this.#active?.controller.abort(new Error('Author runtime disposed'));
161
+ this.#pending = undefined;
162
+ this.#active = undefined;
163
+ }
164
+ }
@@ -0,0 +1,232 @@
1
+ import { Button } from '@agimon-ai/doompi-web-components';
2
+ import { AuthorGridOverlay, autonomousVoiceGridVisible } from './AuthorGridOverlay.tsx';
3
+ import { AuthorMediaView } from './AuthorMediaView.tsx';
4
+ import { AuthorTextView } from './AuthorTextView.tsx';
5
+ import type { FileLinkSource, TransientTab, WebPluginSlotProps } from '@agimon-ai/doompi-web-contracts';
6
+ import { useStore } from '@tanstack/react-store';
7
+ import { useEffect, useRef, useState } from 'react';
8
+ import { focusAuthorViewport } from '../api/authorBrowserBridge.ts';
9
+ import { loadAuthorDocument, saveAuthorDocument } from '../api/authorFiles.ts';
10
+ import { authorProfilesForDocument } from '../stores/authorProfiles.ts';
11
+ import type { AuthorDisplayedRegion } from '../lib/authorViewportTypes.ts';
12
+ import {
13
+ authorDocumentKey,
14
+ authorWorkspace,
15
+ completeAuthorSave,
16
+ failAuthorSave,
17
+ focusAuthorDocument,
18
+ normalizeAuthorPath,
19
+ putAuthorDocument,
20
+ releaseAuthorDocumentFocus,
21
+ requestAuthorSave,
22
+ syncAuthorDocumentFocus,
23
+ type AuthorSessionWorkspace,
24
+ } from '../stores/authorWorkspaceStore.ts';
25
+ import { AuthorStructuredView } from './AuthorStructuredView.tsx';
26
+ interface AuthorDocumentPanelProps extends WebPluginSlotProps {
27
+ path: string;
28
+ }
29
+
30
+ function tabId(path: string): string {
31
+ let hash = 2166136261;
32
+ for (const char of normalizeAuthorPath(path)) hash = Math.imul(hash ^ char.charCodeAt(0), 16777619);
33
+ return `author-file-${(hash >>> 0).toString(36)}`;
34
+ }
35
+
36
+ export function displayedAuthorRegions(
37
+ workspace: AuthorSessionWorkspace | undefined,
38
+ ): readonly AuthorDisplayedRegion[] {
39
+ const draftRegions = workspace?.regions ?? [];
40
+ if (draftRegions.length > 0) return draftRegions.map((region, index) => ({ ordinal: index + 1, region }));
41
+ const activeRequest = workspace?.requests.findLast(
42
+ (request) => request.status === 'REQUESTED' || request.status === 'CHANGING',
43
+ );
44
+ return (activeRequest?.pendingRegions ?? []).map((region) => ({
45
+ ordinal: Math.max(1, (activeRequest?.regions.findIndex((original) => original.id === region.id) ?? 0) + 1),
46
+ region,
47
+ }));
48
+ }
49
+ export function AuthorDocumentPanel(props: AuthorDocumentPanelProps) {
50
+ if (!props.activeMinorModes?.includes('author')) {
51
+ const fallback = props.fileTabFor(props.path);
52
+ if (fallback !== undefined && fallback.id !== tabId(props.path)) {
53
+ return <fallback.panel {...props} />;
54
+ }
55
+ return (
56
+ <p data-testid="author-mode-inactive" className="p-3 text-xs text-doom-dim">
57
+ No file viewer is available for this path.
58
+ </p>
59
+ );
60
+ }
61
+ return <ActiveAuthorDocumentPanel {...props} />;
62
+ }
63
+
64
+ function ActiveAuthorDocumentPanel({ path, sessionId, statuses }: AuthorDocumentPanelProps) {
65
+ const document = useStore(authorWorkspace.store, (state) =>
66
+ sessionId === null ? undefined : state.documents[authorDocumentKey(sessionId, path)],
67
+ );
68
+ const focusGeneration = useRef<number | undefined>(undefined);
69
+ const workspace = useStore(authorWorkspace.store, (state) =>
70
+ sessionId === null ? undefined : state.sessions[sessionId],
71
+ );
72
+ const activeTool = workspace?.activeTool ?? 'select';
73
+ const [markdownPreview, setMarkdownPreview] = useState(true);
74
+ const [status, setStatus] = useState<string | undefined>();
75
+ const kind = document?.kind;
76
+ useEffect(() => {
77
+ if (document !== undefined || sessionId === null) return;
78
+ const controller = new AbortController();
79
+ setStatus('loading');
80
+ void loadAuthorDocument(sessionId, path, controller.signal)
81
+ .then((loaded) => {
82
+ if (controller.signal.aborted) return;
83
+ putAuthorDocument(sessionId, loaded);
84
+ setStatus(undefined);
85
+ })
86
+ .catch((error: unknown) => {
87
+ if (!controller.signal.aborted) setStatus(error instanceof Error ? error.message : String(error));
88
+ });
89
+ return () => controller.abort();
90
+ }, [document, path, sessionId]);
91
+ useEffect(() => {
92
+ if (document === undefined || sessionId === null) return;
93
+ const generation = focusAuthorDocument(sessionId, path, document.version, document.sourceSha256);
94
+ focusGeneration.current = generation;
95
+ return () => releaseAuthorDocumentFocus(sessionId, generation);
96
+ }, [kind, document?.sourceSha256, path, sessionId]);
97
+ useEffect(() => {
98
+ const generation = focusGeneration.current;
99
+ if (document === undefined || sessionId === null || generation === undefined) return;
100
+ syncAuthorDocumentFocus(sessionId, generation, document.version, document.sourceSha256);
101
+ }, [document?.sourceSha256, document?.version, sessionId]);
102
+ useEffect(() => {
103
+ if (kind === undefined || sessionId === null) return;
104
+ const profiles = authorProfilesForDocument(sessionId, path, kind);
105
+ let release: (() => void) | undefined;
106
+ let cancelled = false;
107
+ void focusAuthorViewport(sessionId, profiles).then((nextRelease) => {
108
+ if (cancelled) nextRelease();
109
+ else release = nextRelease;
110
+ });
111
+ return () => {
112
+ cancelled = true;
113
+ release?.();
114
+ };
115
+ }, [kind, path, sessionId]);
116
+ if (document === undefined || sessionId === null) {
117
+ return <p className="p-4 text-[11px] text-doom-faint">{status ?? 'Loading document...'}</p>;
118
+ }
119
+
120
+ const save = async (): Promise<void> => {
121
+ const savedVersion = document.version;
122
+ const savedFragments = document.fragments;
123
+ requestAuthorSave(sessionId, path);
124
+ setStatus('saving');
125
+ try {
126
+ const sha256 = await saveAuthorDocument(sessionId, document);
127
+ completeAuthorSave(sessionId, path, sha256, savedVersion, savedFragments);
128
+ setStatus('saved');
129
+ } catch (error) {
130
+ failAuthorSave(sessionId, path, savedVersion);
131
+ setStatus(error instanceof Error ? error.message : String(error));
132
+ }
133
+ };
134
+ const gridVisible = autonomousVoiceGridVisible(statuses);
135
+ const displayedRegions = displayedAuthorRegions(workspace);
136
+ const textPanel = (
137
+ <AuthorTextView
138
+ sessionId={sessionId}
139
+ document={document}
140
+ preview={document.kind === 'markdown' && markdownPreview && activeTool === 'select' && !gridVisible}
141
+ displayedRegions={displayedRegions}
142
+ />
143
+ );
144
+
145
+ return (
146
+ <section data-testid="author-document" className="flex min-h-0 flex-1 flex-col">
147
+ <header className="flex items-center gap-2 border-b border-doom-border px-4 py-2">
148
+ <strong className="min-w-0 flex-1 truncate text-sm text-doom-hi">{document.title ?? document.path}</strong>
149
+ {document.kind === 'markdown' ? (
150
+ <Button
151
+ size="xs"
152
+ variant="ghost"
153
+ data-testid="author-markdown-toggle"
154
+ onClick={() => setMarkdownPreview(!markdownPreview)}
155
+ >
156
+ {markdownPreview ? 'edit' : 'preview'}
157
+ </Button>
158
+ ) : null}
159
+ {document.kind === 'video' ? (
160
+ <span className="text-sm text-doom-dim">Video feedback</span>
161
+ ) : (
162
+ <Button
163
+ className="min-h-11 px-4 text-sm"
164
+ variant="outline"
165
+ data-testid="author-save"
166
+ disabled={document.revisions.length === 0 || document.savingVersion !== undefined}
167
+ onClick={() => void save()}
168
+ >
169
+ Save
170
+ </Button>
171
+ )}
172
+ </header>
173
+ {status === undefined ? null : <output className="px-4 py-1 text-[10px] text-doom-faint">{status}</output>}
174
+ <div className="relative flex min-h-0 flex-1 overflow-hidden">
175
+ {document.kind === 'text' || document.kind === 'markdown' ? (
176
+ textPanel
177
+ ) : document.structuredFormat !== undefined ? (
178
+ <AuthorStructuredView sessionId={sessionId} document={document} displayedRegions={displayedRegions} />
179
+ ) : (
180
+ <AuthorMediaView
181
+ key={document.path}
182
+ sessionId={sessionId}
183
+ document={document}
184
+ activeTool={activeTool}
185
+ displayedRegions={displayedRegions}
186
+ pendingCandidate={workspace?.candidate !== undefined}
187
+ seekRequest={workspace?.videoSeekRequest}
188
+ />
189
+ )}
190
+ <AuthorGridOverlay sessionId={sessionId} document={document} visible={gridVisible} />
191
+ </div>
192
+ </section>
193
+ );
194
+ }
195
+
196
+ export function authorFileTab(path: string): TransientTab {
197
+ const normalized = normalizeAuthorPath(path);
198
+ return {
199
+ id: tabId(normalized),
200
+ label: normalized.split('/').at(-1) ?? normalized,
201
+ retainComposer: true,
202
+ panel: (props) => <AuthorDocumentPanel {...props} path={normalized} />,
203
+ };
204
+ }
205
+
206
+ export const authorFileLinks: FileLinkSource = {
207
+ requiredMinorMode: 'author',
208
+ subscribe(listener) {
209
+ const subscription = authorWorkspace.store.subscribe(listener);
210
+ return () => subscription.unsubscribe();
211
+ },
212
+ fingerprint(sessionId) {
213
+ if (sessionId === null) return '';
214
+ const prefix = `${sessionId}\n`;
215
+ return Object.keys(authorWorkspace.store.state.documents)
216
+ .filter((key) => key.startsWith(prefix))
217
+ .sort()
218
+ .join('\n');
219
+ },
220
+ resolve(sessionId, path) {
221
+ if (sessionId === null) return undefined;
222
+ const normalized = normalizeAuthorPath(path.replace(/:\d+(?::\d+)?$/, ''));
223
+ return authorWorkspace.store.state.documents[authorDocumentKey(sessionId, normalized)] === undefined
224
+ ? undefined
225
+ : authorFileTab(normalized);
226
+ },
227
+ openPath(sessionId, path) {
228
+ if (sessionId === null) return undefined;
229
+ const normalized = normalizeAuthorPath(path);
230
+ return normalized === '' ? undefined : authorFileTab(normalized);
231
+ },
232
+ };