@shivaedev/work-board 0.16.0 → 0.17.1

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 (331) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +7 -7
  3. package/assets/react.css +1 -1
  4. package/assets/react.js +53 -39
  5. package/dist/browser/responses/cardContext.d.ts +8 -1
  6. package/dist/browser/responses/cardContext.d.ts.map +1 -1
  7. package/dist/browser/responses/cardContext.js +14 -6
  8. package/dist/browser/responses/cardContext.js.map +1 -1
  9. package/dist/browser/views/remaining.d.ts +0 -240
  10. package/dist/browser/views/remaining.d.ts.map +1 -1
  11. package/dist/browser/views/remaining.js +0 -2
  12. package/dist/browser/views/remaining.js.map +1 -1
  13. package/dist/browser/views/schema.d.ts +11 -0
  14. package/dist/browser/views/schema.d.ts.map +1 -1
  15. package/dist/browser/views/schema.js +1 -0
  16. package/dist/browser/views/schema.js.map +1 -1
  17. package/dist/client/formStatus.d.ts +1 -1
  18. package/dist/client/formStatus.d.ts.map +1 -1
  19. package/dist/client/formStatus.js +2 -2
  20. package/dist/client/formStatus.js.map +1 -1
  21. package/dist/client/react/AnswerContext.d.ts.map +1 -1
  22. package/dist/client/react/AnswerContext.js +5 -2
  23. package/dist/client/react/AnswerContext.js.map +1 -1
  24. package/dist/client/react/AnswerForm.d.ts.map +1 -1
  25. package/dist/client/react/AnswerForm.js +6 -2
  26. package/dist/client/react/AnswerForm.js.map +1 -1
  27. package/dist/client/react/AnswerMarkdown.d.ts +6 -0
  28. package/dist/client/react/AnswerMarkdown.d.ts.map +1 -0
  29. package/dist/client/react/AnswerMarkdown.js +7 -0
  30. package/dist/client/react/AnswerMarkdown.js.map +1 -0
  31. package/dist/client/react/AnswerMore.d.ts.map +1 -1
  32. package/dist/client/react/AnswerMore.js +2 -1
  33. package/dist/client/react/AnswerMore.js.map +1 -1
  34. package/dist/client/react/AnswerPanel.d.ts.map +1 -1
  35. package/dist/client/react/AnswerPanel.js +3 -2
  36. package/dist/client/react/AnswerPanel.js.map +1 -1
  37. package/dist/client/react/AnswerPrompts.d.ts.map +1 -1
  38. package/dist/client/react/AnswerPrompts.js +16 -10
  39. package/dist/client/react/AnswerPrompts.js.map +1 -1
  40. package/dist/client/react/BoardShell.js +1 -1
  41. package/dist/client/react/BoardShell.js.map +1 -1
  42. package/dist/client/react/DocumentAttention.d.ts.map +1 -1
  43. package/dist/client/react/DocumentAttention.js +2 -3
  44. package/dist/client/react/DocumentAttention.js.map +1 -1
  45. package/dist/client/react/DocumentMain.d.ts.map +1 -1
  46. package/dist/client/react/DocumentMain.js +1 -1
  47. package/dist/client/react/DocumentMain.js.map +1 -1
  48. package/dist/client/react/GoalPage.js +1 -1
  49. package/dist/client/react/GoalPage.js.map +1 -1
  50. package/dist/client/react/GoalProgressHeader.js +1 -1
  51. package/dist/client/react/GoalProgressHeader.js.map +1 -1
  52. package/dist/client/react/LibraryMain.d.ts.map +1 -1
  53. package/dist/client/react/LibraryMain.js +1 -1
  54. package/dist/client/react/LibraryMain.js.map +1 -1
  55. package/dist/client/react/LibraryPage.js +1 -1
  56. package/dist/client/react/LibraryPage.js.map +1 -1
  57. package/dist/client/react/ProjectionFrame.js +1 -1
  58. package/dist/client/react/ProjectionFrame.js.map +1 -1
  59. package/dist/client/react/ProjectionMain.d.ts.map +1 -1
  60. package/dist/client/react/ProjectionMain.js +1 -1
  61. package/dist/client/react/ProjectionMain.js.map +1 -1
  62. package/dist/client/react/PromptOption.d.ts.map +1 -1
  63. package/dist/client/react/PromptOption.js +7 -5
  64. package/dist/client/react/PromptOption.js.map +1 -1
  65. package/dist/client/react/RemainingMain.d.ts.map +1 -1
  66. package/dist/client/react/RemainingMain.js +1 -4
  67. package/dist/client/react/RemainingMain.js.map +1 -1
  68. package/dist/client/react/ResponseHistory.d.ts.map +1 -1
  69. package/dist/client/react/ResponseHistory.js +11 -5
  70. package/dist/client/react/ResponseHistory.js.map +1 -1
  71. package/dist/client/react/ResultPage.d.ts.map +1 -1
  72. package/dist/client/react/ResultPage.js +2 -1
  73. package/dist/client/react/ResultPage.js.map +1 -1
  74. package/dist/client/react/ShellApi.d.ts +0 -1
  75. package/dist/client/react/ShellApi.d.ts.map +1 -1
  76. package/dist/client/react/SystemMain.d.ts.map +1 -1
  77. package/dist/client/react/SystemMain.js +1 -1
  78. package/dist/client/react/SystemMain.js.map +1 -1
  79. package/dist/client/react/TodayNeeds.d.ts +5 -0
  80. package/dist/client/react/TodayNeeds.d.ts.map +1 -1
  81. package/dist/client/react/TodayNeeds.js +6 -3
  82. package/dist/client/react/TodayNeeds.js.map +1 -1
  83. package/dist/client/react/TodayOptionButton.d.ts +15 -0
  84. package/dist/client/react/TodayOptionButton.d.ts.map +1 -0
  85. package/dist/client/react/TodayOptionButton.js +10 -0
  86. package/dist/client/react/TodayOptionButton.js.map +1 -0
  87. package/dist/client/react/TodayOptions.d.ts.map +1 -1
  88. package/dist/client/react/TodayOptions.js +13 -11
  89. package/dist/client/react/TodayOptions.js.map +1 -1
  90. package/dist/client/react/TodayPage.d.ts.map +1 -1
  91. package/dist/client/react/TodayPage.js +7 -4
  92. package/dist/client/react/TodayPage.js.map +1 -1
  93. package/dist/client/react/activeRead.js +1 -1
  94. package/dist/client/react/activeRead.js.map +1 -1
  95. package/dist/client/react/entry.js +1 -1
  96. package/dist/client/react/entry.js.map +1 -1
  97. package/dist/client/react/loadBoardPage.d.ts.map +1 -1
  98. package/dist/client/react/loadBoardPage.js +6 -14
  99. package/dist/client/react/loadBoardPage.js.map +1 -1
  100. package/dist/client/react/loadProjection.d.ts.map +1 -1
  101. package/dist/client/react/loadProjection.js +1 -1
  102. package/dist/client/react/loadProjection.js.map +1 -1
  103. package/dist/client/react/loadRemaining.d.ts +17 -257
  104. package/dist/client/react/loadRemaining.d.ts.map +1 -1
  105. package/dist/client/react/loadRemaining.js +1 -3
  106. package/dist/client/react/loadRemaining.js.map +1 -1
  107. package/dist/client/react/pageQueries.d.ts.map +1 -1
  108. package/dist/client/react/pageQueries.js +1 -4
  109. package/dist/client/react/pageQueries.js.map +1 -1
  110. package/dist/client/react/readerLink.js +1 -1
  111. package/dist/client/react/readerLink.js.map +1 -1
  112. package/dist/client/react/remainingRoute.d.ts +1 -1
  113. package/dist/client/react/remainingRoute.d.ts.map +1 -1
  114. package/dist/client/react/remainingRoute.js +0 -2
  115. package/dist/client/react/remainingRoute.js.map +1 -1
  116. package/dist/client/react/resolvedDocumentFailure.d.ts.map +1 -0
  117. package/dist/client/react/resolvedDocumentFailure.js.map +1 -0
  118. package/dist/client/react/routeQuery.d.ts.map +1 -0
  119. package/dist/client/react/routeQuery.js.map +1 -0
  120. package/dist/client/react/useResponseForm.d.ts.map +1 -1
  121. package/dist/client/react/useResponseForm.js +2 -2
  122. package/dist/client/react/useResponseForm.js.map +1 -1
  123. package/dist/client/responses/summary.d.ts +1 -0
  124. package/dist/client/responses/summary.d.ts.map +1 -1
  125. package/dist/client/responses/summary.js +12 -1
  126. package/dist/client/responses/summary.js.map +1 -1
  127. package/dist/responses/url.js +1 -1
  128. package/dist/responses/url.js.map +1 -1
  129. package/dist/rpc/PageUrl.d.ts.map +1 -1
  130. package/dist/rpc/PageUrl.js +0 -1
  131. package/dist/rpc/PageUrl.js.map +1 -1
  132. package/dist/rpc/contract.d.ts +2195 -2885
  133. package/dist/rpc/contract.d.ts.map +1 -1
  134. package/dist/rpc/contract.js +6 -9
  135. package/dist/rpc/contract.js.map +1 -1
  136. package/dist/rpc/responseContract.d.ts +56 -56
  137. package/dist/server/{responses/service.d.ts → Responses.d.ts} +9 -21
  138. package/dist/server/Responses.d.ts.map +1 -0
  139. package/dist/server/{responses/service.js → Responses.js} +22 -22
  140. package/dist/server/Responses.js.map +1 -0
  141. package/dist/server/attention/inbox.d.ts +15 -2
  142. package/dist/server/attention/inbox.d.ts.map +1 -1
  143. package/dist/server/attention/inbox.js +44 -26
  144. package/dist/server/attention/inbox.js.map +1 -1
  145. package/dist/server/attention/managedRequestReceipt.d.ts +10 -0
  146. package/dist/server/attention/managedRequestReceipt.d.ts.map +1 -1
  147. package/dist/server/attention/managedRequestReceipt.js +29 -10
  148. package/dist/server/attention/managedRequestReceipt.js.map +1 -1
  149. package/dist/server/attention/projection.d.ts.map +1 -1
  150. package/dist/server/attention/projection.js +3 -2
  151. package/dist/server/attention/projection.js.map +1 -1
  152. package/dist/server/attention/requestInbox.d.ts +6 -3
  153. package/dist/server/attention/requestInbox.d.ts.map +1 -1
  154. package/dist/server/attention/requestInbox.js +3 -2
  155. package/dist/server/attention/requestInbox.js.map +1 -1
  156. package/dist/server/attention/requests.d.ts +1 -0
  157. package/dist/server/attention/requests.d.ts.map +1 -1
  158. package/dist/server/attention/requests.js +7 -3
  159. package/dist/server/attention/requests.js.map +1 -1
  160. package/dist/server/boardRuntime.d.ts +2 -2
  161. package/dist/server/boardRuntime.d.ts.map +1 -1
  162. package/dist/server/boardRuntime.js +12 -16
  163. package/dist/server/boardRuntime.js.map +1 -1
  164. package/dist/server/boardServices.d.ts +3 -4
  165. package/dist/server/boardServices.d.ts.map +1 -1
  166. package/dist/server/boardServices.js +5 -4
  167. package/dist/server/boardServices.js.map +1 -1
  168. package/dist/server/boardSocket.d.ts +1 -1
  169. package/dist/server/boardSocket.d.ts.map +1 -1
  170. package/dist/server/document/read.d.ts +4 -10
  171. package/dist/server/document/read.d.ts.map +1 -1
  172. package/dist/server/document/read.js +5 -5
  173. package/dist/server/document/read.js.map +1 -1
  174. package/dist/server/document/requests.d.ts.map +1 -1
  175. package/dist/server/document/requests.js +1 -2
  176. package/dist/server/document/requests.js.map +1 -1
  177. package/dist/server/handoffs/Service.d.ts +1 -2
  178. package/dist/server/handoffs/Service.d.ts.map +1 -1
  179. package/dist/server/handoffs/Service.js +14 -13
  180. package/dist/server/handoffs/Service.js.map +1 -1
  181. package/dist/server/http/assets.d.ts +0 -3
  182. package/dist/server/http/assets.d.ts.map +1 -1
  183. package/dist/server/http/assets.js +1 -7
  184. package/dist/server/http/assets.js.map +1 -1
  185. package/dist/server/http/boardRoutes.d.ts.map +1 -1
  186. package/dist/server/http/boardRoutes.js +3 -3
  187. package/dist/server/http/boardRoutes.js.map +1 -1
  188. package/dist/server/integration/responses.d.ts +207 -4
  189. package/dist/server/integration/responses.d.ts.map +1 -1
  190. package/dist/server/integration/responses.js +4 -2
  191. package/dist/server/integration/responses.js.map +1 -1
  192. package/dist/server/mcp/client.d.ts +869 -1098
  193. package/dist/server/mcp/client.d.ts.map +1 -1
  194. package/dist/server/mcp/queries.d.ts +47 -734
  195. package/dist/server/mcp/queries.d.ts.map +1 -1
  196. package/dist/server/mcp/queries.js +3 -3
  197. package/dist/server/mcp/queries.js.map +1 -1
  198. package/dist/server/packageInfo.d.ts +1 -1
  199. package/dist/server/packageInfo.js +1 -1
  200. package/dist/server/responses/readingActions.d.ts +10 -1
  201. package/dist/server/responses/readingActions.d.ts.map +1 -1
  202. package/dist/server/responses/readingActions.js +2 -4
  203. package/dist/server/responses/readingActions.js.map +1 -1
  204. package/dist/server/responses/registerQuestion.d.ts +2 -2
  205. package/dist/server/responses/registerQuestion.d.ts.map +1 -1
  206. package/dist/server/responses/registerQuestion.js +3 -3
  207. package/dist/server/responses/registerQuestion.js.map +1 -1
  208. package/dist/server/responses/retry.d.ts +2 -2
  209. package/dist/server/responses/retry.d.ts.map +1 -1
  210. package/dist/server/responses/retry.js +1 -1
  211. package/dist/server/responses/retry.js.map +1 -1
  212. package/dist/server/rpc/HandlerOptions.d.ts +0 -8
  213. package/dist/server/rpc/HandlerOptions.d.ts.map +1 -1
  214. package/dist/server/rpc/handlers.d.ts +1 -1
  215. package/dist/server/rpc/handlers.d.ts.map +1 -1
  216. package/dist/server/rpc/handlers.js +14 -13
  217. package/dist/server/rpc/handlers.js.map +1 -1
  218. package/dist/server/rpc/hints.d.ts +3 -5
  219. package/dist/server/rpc/hints.d.ts.map +1 -1
  220. package/dist/server/rpc/hints.js +5 -1
  221. package/dist/server/rpc/hints.js.map +1 -1
  222. package/dist/server/views/fleetItems.d.ts +2 -2
  223. package/dist/server/views/fleetItems.d.ts.map +1 -1
  224. package/dist/server/views/fleetItems.js +8 -3
  225. package/dist/server/views/fleetItems.js.map +1 -1
  226. package/dist/server/views/projectionQueries.d.ts +16 -9
  227. package/dist/server/views/projectionQueries.d.ts.map +1 -1
  228. package/dist/server/views/projectionQueries.js +6 -6
  229. package/dist/server/views/projectionQueries.js.map +1 -1
  230. package/dist/server/views/remainingQueries.d.ts +4 -252
  231. package/dist/server/views/remainingQueries.d.ts.map +1 -1
  232. package/dist/server/views/remainingQueries.js +11 -19
  233. package/dist/server/views/remainingQueries.js.map +1 -1
  234. package/dist/server/workspace.d.ts +222 -0
  235. package/dist/server/workspace.d.ts.map +1 -0
  236. package/dist/server/workspace.js +25 -0
  237. package/dist/server/workspace.js.map +1 -0
  238. package/docs/delivery/README.md +2 -2
  239. package/package.json +5 -5
  240. package/src/browser/responses/cardContext.ts +15 -6
  241. package/src/browser/views/remaining.ts +0 -3
  242. package/src/browser/views/schema.ts +1 -0
  243. package/src/client/formStatus.ts +2 -2
  244. package/src/client/react/AnswerContext.tsx +9 -4
  245. package/src/client/react/AnswerForm.tsx +25 -6
  246. package/src/client/react/AnswerMarkdown.tsx +15 -0
  247. package/src/client/react/AnswerMore.tsx +58 -46
  248. package/src/client/react/AnswerPanel.tsx +14 -8
  249. package/src/client/react/AnswerPrompts.tsx +33 -18
  250. package/src/client/react/BoardShell.tsx +1 -1
  251. package/src/client/react/DocumentAttention.tsx +2 -3
  252. package/src/client/react/DocumentMain.tsx +1 -1
  253. package/src/client/react/GoalPage.tsx +1 -1
  254. package/src/client/react/GoalProgressHeader.tsx +1 -1
  255. package/src/client/react/LibraryMain.tsx +1 -1
  256. package/src/client/react/LibraryPage.tsx +1 -1
  257. package/src/client/react/ProjectionFrame.tsx +1 -1
  258. package/src/client/react/ProjectionMain.tsx +1 -1
  259. package/src/client/react/PromptOption.tsx +39 -24
  260. package/src/client/react/RemainingMain.tsx +1 -11
  261. package/src/client/react/ResponseHistory.tsx +53 -14
  262. package/src/client/react/ResultPage.tsx +2 -2
  263. package/src/client/react/ShellApi.ts +0 -1
  264. package/src/client/react/SystemMain.tsx +1 -1
  265. package/src/client/react/TodayNeeds.tsx +21 -6
  266. package/src/client/react/TodayOptionButton.tsx +37 -0
  267. package/src/client/react/TodayOptions.tsx +38 -30
  268. package/src/client/react/TodayPage.tsx +18 -3
  269. package/src/client/react/activeRead.ts +1 -1
  270. package/src/client/react/entry.css +0 -6
  271. package/src/client/react/entry.tsx +1 -1
  272. package/src/client/react/loadBoardPage.ts +6 -15
  273. package/src/client/react/loadProjection.ts +1 -1
  274. package/src/client/react/loadRemaining.ts +1 -3
  275. package/src/client/react/pageQueries.ts +1 -4
  276. package/src/client/react/readerLink.ts +1 -1
  277. package/src/client/react/remainingRoute.ts +0 -2
  278. package/src/client/react/useResponseForm.ts +4 -2
  279. package/src/client/responses/summary.ts +13 -1
  280. package/src/responses/url.ts +1 -1
  281. package/src/rpc/PageUrl.ts +0 -1
  282. package/src/rpc/contract.ts +6 -9
  283. package/src/server/{responses/service.ts → Responses.ts} +21 -29
  284. package/src/server/attention/inbox.ts +51 -26
  285. package/src/server/attention/managedRequestReceipt.ts +41 -10
  286. package/src/server/attention/projection.ts +5 -2
  287. package/src/server/attention/requestInbox.ts +3 -2
  288. package/src/server/attention/requests.ts +7 -2
  289. package/src/server/boardRuntime.ts +20 -25
  290. package/src/server/boardServices.ts +7 -7
  291. package/src/server/document/read.ts +6 -14
  292. package/src/server/document/requests.ts +1 -2
  293. package/src/server/handoffs/Service.ts +16 -19
  294. package/src/server/http/assets.ts +1 -7
  295. package/src/server/http/boardRoutes.ts +3 -3
  296. package/src/server/integration/responses.ts +5 -3
  297. package/src/server/mcp/queries.ts +3 -3
  298. package/src/server/packageInfo.ts +1 -1
  299. package/src/server/responses/readingActions.ts +3 -3
  300. package/src/server/responses/registerQuestion.ts +5 -5
  301. package/src/server/responses/retry.ts +3 -3
  302. package/src/server/rpc/HandlerOptions.ts +0 -8
  303. package/src/server/rpc/handlers.ts +17 -15
  304. package/src/server/rpc/hints.ts +6 -3
  305. package/src/server/views/fleetItems.ts +8 -4
  306. package/src/server/views/projectionQueries.ts +6 -7
  307. package/src/server/views/remainingQueries.ts +11 -31
  308. package/src/server/workspace.ts +41 -0
  309. package/assets/native.js +0 -37
  310. package/dist/client/entry.d.ts +0 -3
  311. package/dist/client/entry.d.ts.map +0 -1
  312. package/dist/client/entry.js +0 -3
  313. package/dist/client/entry.js.map +0 -1
  314. package/dist/client/page/resolvedDocumentFailure.d.ts.map +0 -1
  315. package/dist/client/page/resolvedDocumentFailure.js.map +0 -1
  316. package/dist/client/page/routeQuery.d.ts.map +0 -1
  317. package/dist/client/page/routeQuery.js.map +0 -1
  318. package/dist/client/react/RespondPage.d.ts +0 -5
  319. package/dist/client/react/RespondPage.d.ts.map +0 -1
  320. package/dist/client/react/RespondPage.js +0 -5
  321. package/dist/client/react/RespondPage.js.map +0 -1
  322. package/dist/server/responses/service.d.ts.map +0 -1
  323. package/dist/server/responses/service.js.map +0 -1
  324. package/src/client/entry.ts +0 -3
  325. package/src/client/react/RespondPage.tsx +0 -8
  326. /package/dist/client/{page → react}/resolvedDocumentFailure.d.ts +0 -0
  327. /package/dist/client/{page → react}/resolvedDocumentFailure.js +0 -0
  328. /package/dist/client/{page → react}/routeQuery.d.ts +0 -0
  329. /package/dist/client/{page → react}/routeQuery.js +0 -0
  330. /package/src/client/{page → react}/resolvedDocumentFailure.ts +0 -0
  331. /package/src/client/{page → react}/routeQuery.ts +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../src/server/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAsB,MAAM,QAAQ,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,6CAA6C,CAAC;AAE5E,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAgBxD,MAAM,OAAO,eAAgB,SAAQ,OAAO,CAAC,OAAO,EAAqC,CAAC,2BAA2B,CAAC;CAAG;AAEzH,MAAM,CAAC,MAAM,SAAS,GAAG,aAAa,CAAC;IACtC,EAAE,EAAE,qBAAqB;IACzB,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC/B,OAAO,KAAK,CAAC,CAAC,eAAe,CAAC;IAC/B,CAAC,CAAC;IACF,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,EAAE,EAAE;QAClC,MAAM,QAAQ,GAAG,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACpE,MAAM,IAAI,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACpC,SAAS,QAAQ,CAAC,GAAW;YAC5B,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;QACjE,CAAC;QACD,OAAO;YACN,QAAQ;YACR,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK;YAClB,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;SACxC,CAAC;IACH,CAAC;IACD,QAAQ,EAAE,CAAC,eAAe,EAAE,UAAU,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC;CAC7D,CAAC,CAAC"}
@@ -36,8 +36,8 @@ The roadmap records whether each decision is settled and what it means for a ste
36
36
  | [Board parser](https://github.com/ShivaeDev/platform/blob/main/packages/work-board/src/server/render/board.ts), [Markdown renderer](https://github.com/ShivaeDev/platform/blob/main/packages/work-board/src/server/render/markdown.ts) | Source interpretation and semantic reading | Heading boards, GFM, references and readable fallback |
37
37
  | [Page route](https://github.com/ShivaeDev/platform/blob/main/packages/work-board/src/server/http/page.ts), [router](https://github.com/ShivaeDev/platform/blob/main/packages/work-board/src/server/board.ts) | Locations, projections and HTTP composition | Loopback boundary, embedding and Effect scope/errors |
38
38
  | [Listing](https://github.com/ShivaeDev/platform/blob/main/packages/work-board/src/server/files/list.ts), [watcher](https://github.com/ShivaeDev/platform/blob/main/packages/work-board/src/server/files/changes.ts) | File discovery and freshness | Root policy, rename/delete handling and recovery |
39
- | [Native client](https://github.com/ShivaeDev/platform/blob/main/packages/work-board/src/client/client.ts), [DOM swap](https://github.com/ShivaeDev/platform/blob/main/packages/work-board/src/client/page/scripts.ts) | Scoped reads and retained reading state | Shared Effect contracts/live primitives, clear failures and reconciliation |
40
- | [Assets](https://github.com/ShivaeDev/platform/blob/main/packages/work-board/src/server/http/assets.ts), [diagrams](https://github.com/ShivaeDev/platform/blob/main/packages/work-board/src/client/page/scripts.ts) | Local visual reading | Bounded access and locally loaded renderers |
39
+ | [Native client](https://github.com/ShivaeDev/platform/blob/main/packages/work-board/src/client/client.ts), [React navigation](https://github.com/ShivaeDev/platform/blob/main/packages/work-board/src/client/react/useBoardNavigation.ts) | Scoped reads and retained reading state | Shared Effect contracts/live primitives, clear failures and reconciliation |
40
+ | [Assets](https://github.com/ShivaeDev/platform/blob/main/packages/work-board/src/server/http/assets.ts), [diagrams](https://github.com/ShivaeDev/platform/blob/main/packages/work-board/src/client/document/Mermaid.ts) | Local visual reading | Bounded access and locally loaded renderers |
41
41
 
42
42
  Keep CLI and embedding working together. Static mockup markup and fictional data are review aids, not the production data model. A new framework, persistent store or adapter must earn its cost through an agreed requirement.
43
43
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/work-board",
3
- "version": "0.16.0",
3
+ "version": "0.17.1",
4
4
  "description": "Serve a folder of markdown files as a live local page that updates in place when a file changes",
5
5
  "homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/work-board#readme",
6
6
  "bugs": {
@@ -411,7 +411,7 @@
411
411
  "dependencies": {
412
412
  "@shivaedev/effect-contract": "0.5.1",
413
413
  "@shivaedev/effect-service": "0.3.0",
414
- "@shivaedev/platform": "0.14.3",
414
+ "@shivaedev/platform": "0.15.0",
415
415
  "mdast-util-directive": "3.1.1",
416
416
  "mermaid": "12.1.0",
417
417
  "react": "19.3.0",
@@ -433,8 +433,8 @@
433
433
  "@shivaedev/effect-form": "0.4.0",
434
434
  "@shivaedev/effect-react": "0.4.1",
435
435
  "@shivaedev/effect-test": "0.5.0",
436
- "@shivaedev/quality": "0.23.0",
437
- "@shivaedev/test-story": "0.6.0",
436
+ "@shivaedev/quality": "0.24.0",
437
+ "@shivaedev/test-story": "0.7.0",
438
438
  "@tailwindcss/vite": "4.3.3",
439
439
  "@tanstack/react-router": "1.170.42",
440
440
  "@types/hast": "3.0.5",
@@ -470,7 +470,7 @@
470
470
  },
471
471
  "scripts": {
472
472
  "build": "node --eval \"import('node:fs').then(({ rmSync }) => rmSync('dist', { force: true, recursive: true }))\" && tsc --project tsconfig.build.json && pnpm build:browser",
473
- "build:browser": "esbuild src/client/entry.ts --bundle --platform=browser --format=esm --conditions=source --minify --banner:js='/* @shivaedev/work-board native browser bundle */' --outfile=assets/native.js && esbuild src/server/responses/agent-cli.ts --bundle --platform=node --format=esm --conditions=source --minify --outfile=assets/agent.js && vite build",
473
+ "build:browser": "esbuild src/server/responses/agent-cli.ts --bundle --platform=node --format=esm --conditions=source --minify --outfile=assets/agent.js && vite build",
474
474
  "ready": "pnpm typecheck && pnpm test && pnpm build",
475
475
  "test": "pnpm test:prepare && vitest run",
476
476
  "test:prepare": "pnpm build:browser",
@@ -10,15 +10,19 @@ export function cardContext(source: string) {
10
10
  return { links, recommendation };
11
11
  }
12
12
 
13
- export function optionLabel(id: string, source: string, reason: string) {
14
- const heading = /^#{1,6}\s+(?<title>.+)$/mu.exec(source)?.groups?.title;
15
- if (heading) {
16
- return heading;
13
+ export function optionText(id: string, source: string, reason: string) {
14
+ const heading = promptHeading(source);
15
+ if (heading.title) {
16
+ return { description: heading.rest, label: heading.title };
17
+ }
18
+ const lead = /^\*\*(?<label>[^*]+)\*\*\s*(?:[—–:-]\s*)?/u.exec(source.trim());
19
+ if (lead?.groups?.label) {
20
+ return { description: source.trim().slice(lead[0].length).trim(), label: lead.groups.label.trim() };
17
21
  }
18
22
  if (id === "retry" && /reviewed pr is ready to merge/iu.test(reason)) {
19
- return "Request changes (retry)";
23
+ return { description: source.trim(), label: "Request changes (retry)" };
20
24
  }
21
- return id.charAt(0).toUpperCase() + id.slice(1).replaceAll("-", " ");
25
+ return { description: source.trim(), label: id.charAt(0).toUpperCase() + id.slice(1).replaceAll("-", " ") };
22
26
  }
23
27
 
24
28
  function section(source: string, title: string) {
@@ -48,3 +52,8 @@ function section(source: string, title: string) {
48
52
  function nextFence(current: string | undefined, marker: string) {
49
53
  return current?.startsWith(marker.charAt(0)) && marker.length >= current.length ? undefined : marker;
50
54
  }
55
+
56
+ export function promptHeading(source: string) {
57
+ const heading = /^#{1,6}\s+(?<title>.+)$/mu.exec(source);
58
+ return { rest: heading ? source.replace(heading[0], "").trim() : source.trim(), title: heading?.groups?.title?.trim() };
59
+ }
@@ -1,5 +1,4 @@
1
1
  import { Schema } from "effect";
2
- import { Document, DocumentRequest } from "#browser/document/schema.ts";
3
2
  import { HandoffPreview, HandoffReading } from "#browser/handoffs/schema.ts";
4
3
  import { DiagnosticsSnapshot } from "#diagnostics/daemonContract.ts";
5
4
 
@@ -11,8 +10,6 @@ export const StartProjection = Schema.Struct({
11
10
  templates: Schema.Array(Schema.Struct({ file: Schema.String, label: Schema.String, source: Schema.String })),
12
11
  });
13
12
  export type StartProjection = typeof StartProjection.Type;
14
- export const RespondProjection = Schema.Struct({ document: Document, request: DocumentRequest });
15
- export type RespondProjection = typeof RespondProjection.Type;
16
13
  export const HandoffProjection = Schema.Struct({
17
14
  background: Schema.String,
18
15
  goal: Schema.String,
@@ -40,6 +40,7 @@ export const InboxProjection = Schema.Struct({
40
40
  complete: Schema.Boolean,
41
41
  issues: Schema.Array(Schema.Struct({ file: Schema.String, line: Schema.Number, message: Schema.String })),
42
42
  items: Schema.Array(InboxItem),
43
+ later: Schema.Array(InboxItem),
43
44
  parked: Schema.Array(InboxItem),
44
45
  resolved: Schema.Array(InboxItem),
45
46
  sourceIssues: Schema.Boolean,
@@ -30,6 +30,6 @@ export function formStatus<A>(
30
30
  };
31
31
  }
32
32
 
33
- export function responseSaved(id: string, dirty: boolean) {
34
- return `Saved: responses/${id}.md. This records feedback, not acceptance.${dirty ? " Your current draft remains unsaved." : ""}`;
33
+ export function responseSaved(id: string, dirty: boolean, choice = "") {
34
+ return `Saved: your answer${choice ? ` “${choice}”` : ""} is recorded. The agent reads it from responses/${id}.md the next time it checks. This records your answer, not acceptance of the work.${dirty ? " Your current draft remains unsaved." : ""}`;
35
35
  }
@@ -1,25 +1,30 @@
1
+ import { ExternalLink } from "lucide-react";
1
2
  import { cardContext } from "#browser/responses/cardContext.ts";
2
3
  import type { QuestionPreview } from "#browser/responses/schema.ts";
3
- import { ResponseMarkdown } from "./ResponseMarkdown.tsx";
4
+ import { buttonVariants } from "#client/react/components/ui/buttonVariants.ts";
5
+ import { cn } from "#client/react/lib/cn.ts";
6
+ import { AnswerMarkdown } from "./AnswerMarkdown.tsx";
4
7
 
5
8
  export function AnswerContext({ preview }: { readonly preview: QuestionPreview }) {
6
9
  const context = cardContext(preview.context);
7
10
  return (
8
11
  <>
9
12
  {Boolean(context.recommendation) && (
10
- <div className="answer-recommendation">
11
- <ResponseMarkdown file={preview.source} source={context.recommendation} />
13
+ <div className="rounded-lg bg-muted/60 px-4 py-3 text-sm">
14
+ <p className="mb-1 font-medium text-muted-foreground text-xs uppercase tracking-wide">Recommendation</p>
15
+ <AnswerMarkdown className="answer-recommendation" file={preview.source} source={context.recommendation} />
12
16
  </div>
13
17
  )}
14
18
  {context.links.length > 0 && (
15
19
  <div className="answer-links flex flex-wrap gap-2">
16
20
  {context.links.map((link) => (
17
21
  <a
18
- className={`answer-link ${link.primary ? "bg-primary text-primary-foreground" : "border border-border"} inline-flex items-center rounded-md px-4 py-2 font-medium`}
22
+ className={cn(buttonVariants({ size: "sm", variant: link.primary ? "default" : "outline" }), "answer-link")}
19
23
  href={link.url}
20
24
  key={link.url}
21
25
  >
22
26
  {link.label}
27
+ <ExternalLink aria-hidden="true" />
23
28
  </a>
24
29
  ))}
25
30
  </div>
@@ -1,5 +1,8 @@
1
+ import { CheckCircle2 } from "lucide-react";
1
2
  import type { Document, DocumentRequest } from "#browser/document/schema.ts";
2
3
  import type { QuestionPreview } from "#browser/responses/schema.ts";
4
+ import { Button } from "#client/react/components/ui/Button.tsx";
5
+ import { cn } from "#client/react/lib/cn.ts";
3
6
  import { AnswerMore } from "./AnswerMore.tsx";
4
7
  import { AnswerPrompts } from "./AnswerPrompts.tsx";
5
8
  import { FormField } from "./FormField.tsx";
@@ -7,6 +10,8 @@ import { shellIds } from "./shellIds.ts";
7
10
  import { useFormFocus } from "./useFormFocus.ts";
8
11
  import { useResponseForm } from "./useResponseForm.ts";
9
12
 
13
+ const READ_ONLY = "This Board is read-only. Start it with --responses to record answers.";
14
+
10
15
  interface AnswerProps {
11
16
  readonly document: Document;
12
17
  readonly preview: QuestionPreview;
@@ -38,6 +43,7 @@ function AnswerSession({ document: data, request, preview }: AnswerProps) {
38
43
  return (
39
44
  <form
40
45
  {...focus}
46
+ className="space-y-4"
41
47
  data-item={preview.item}
42
48
  data-question={preview.id}
43
49
  data-request={preview.request}
@@ -76,13 +82,26 @@ function AnswerSession({ document: data, request, preview }: AnswerProps) {
76
82
  showErrors={state.attempted}
77
83
  />
78
84
  {prompts.length === 0 && body}
79
- <button disabled={!data.writable || state.created.saving} id={id(shellIds.responseSubmit)} type="submit">
80
- Record
81
- </button>
82
- <AnswerMore body={prompts.length > 0 ? body : undefined} id={id} request={request} state={state} />
83
- <p data-outcome={state.outcome} id={id(shellIds.responseStatus)} role="status">
84
- {data.writable ? writableStatus : `${state.status} Read-only. Restart with --responses to enable answers.`.trim()}
85
+ <div className="flex flex-wrap items-center gap-3">
86
+ <Button disabled={!data.writable || state.created.saving} id={id(shellIds.responseSubmit)} type="submit">
87
+ Record answer
88
+ </Button>
89
+ </div>
90
+ <p
91
+ className={cn(
92
+ "flex items-start gap-2 text-sm empty:hidden",
93
+ !data.writable && "text-muted-foreground",
94
+ state.outcome === "saved" && "rounded-md border border-done/40 bg-done/10 px-3 py-2",
95
+ (state.outcome === "rejected" || state.outcome === "uncertain") && "rounded-md border border-destructive/40 px-3 py-2 text-destructive",
96
+ )}
97
+ data-outcome={state.outcome}
98
+ id={id(shellIds.responseStatus)}
99
+ role="status"
100
+ >
101
+ {state.outcome === "saved" && <CheckCircle2 aria-hidden="true" className="mt-0.5 size-4 shrink-0 text-done" />}
102
+ {data.writable ? writableStatus : `${state.status} ${READ_ONLY}`.trim()}
85
103
  </p>
104
+ <AnswerMore body={prompts.length > 0 ? body : undefined} id={id} request={request} state={state} />
86
105
  </form>
87
106
  );
88
107
  }
@@ -0,0 +1,15 @@
1
+ import { cn } from "#client/react/lib/cn.ts";
2
+ import { ResponseMarkdown } from "./ResponseMarkdown.tsx";
3
+
4
+ export function AnswerMarkdown({ className, file, source }: { readonly className?: string; readonly file: string; readonly source: string }) {
5
+ return (
6
+ <div
7
+ className={cn(
8
+ "[&_a]:underline [&_a]:underline-offset-4 [&_code]:font-mono [&_code]:text-[0.9em] [&_li]:my-1 [&_ol]:list-decimal [&_ol]:pl-5 [&_p+p]:mt-2 [&_ul]:list-disc [&_ul]:pl-5",
9
+ className,
10
+ )}
11
+ >
12
+ <ResponseMarkdown file={file} source={source} />
13
+ </div>
14
+ );
15
+ }
@@ -1,5 +1,6 @@
1
1
  import type { ReactNode } from "react";
2
2
  import type { DocumentRequest } from "#browser/document/schema.ts";
3
+ import { Button } from "#client/react/components/ui/Button.tsx";
3
4
  import { FormField } from "./FormField.tsx";
4
5
  import { shellIds } from "./shellIds.ts";
5
6
  import type { useResponseForm } from "./useResponseForm.ts";
@@ -16,52 +17,63 @@ export function AnswerMore({
16
17
  readonly state: ReturnType<typeof useResponseForm>;
17
18
  }) {
18
19
  return (
19
- <details data-key="response-details">
20
- <summary>More</summary>
21
- <FormField
22
- autoComplete="nickname"
23
- form={state.form}
24
- label="Local author label"
25
- maxLength={200}
26
- name="author"
27
- required
28
- showErrors={state.attempted}
29
- />
30
- <FormField form={state.form} label="Response kind" name="type">
31
- <option value="answer">Answer (including no)</option>
32
- <option value="clarify">Ask back / clarify</option>
33
- <option value="not_now">Not now</option>
34
- </FormField>
35
- {body}
36
- <FormField form={state.form} label="Supersedes an earlier response (optional)" name="supersedes">
37
- <option value="">Adds feedback; does not supersede</option>
38
- {request.responses?.map((record) => (
39
- <option key={record.id} value={record.id}>
40
- {record.id} · {record.response.author} · {record.response.reviewedRevision.slice(0, 12)}
41
- </option>
42
- ))}
43
- </FormField>
44
- <pre hidden={!state.recovery} id={id(shellIds.responseDraftRecovery)}>
45
- {state.recovery}
46
- </pre>
47
- <p>
48
- Drafts stay in this browser for seven days after the last edit, with a 256 KiB limit per draft. Saved responses are Markdown files in
49
- responses/.
50
- </p>
51
- <button
52
- disabled={typeof window === "undefined" || state.created.saving}
53
- id={id(shellIds.responsePreview)}
54
- onClick={state.showPreview}
55
- type="button"
56
- >
57
- Preview Markdown
58
- </button>
59
- <pre hidden={!state.preview} id={id(shellIds.responsePreviewContent)}>
60
- {state.preview}
61
- </pre>
62
- <button disabled={typeof window === "undefined" || state.created.saving} id={id(shellIds.responseClear)} onClick={state.clear} type="button">
63
- Clear draft
64
- </button>
20
+ <details className="group rounded-lg border px-4 py-2 text-sm [&[open]]:pb-4" data-key="response-details">
21
+ <summary className="cursor-pointer select-none py-1 text-muted-foreground hover:text-foreground">More options</summary>
22
+ <div className="mt-3 grid gap-4">
23
+ <FormField
24
+ autoComplete="nickname"
25
+ form={state.form}
26
+ label="Local author label"
27
+ maxLength={200}
28
+ name="author"
29
+ required
30
+ showErrors={state.attempted}
31
+ />
32
+ <FormField form={state.form} label="Response kind" name="type">
33
+ <option value="answer">Answer (including no)</option>
34
+ <option value="clarify">Ask back / clarify</option>
35
+ <option value="not_now">Not now</option>
36
+ </FormField>
37
+ {body}
38
+ <FormField form={state.form} label="Supersedes an earlier response (optional)" name="supersedes">
39
+ <option value="">Adds feedback; does not supersede</option>
40
+ {request.responses?.map((record) => (
41
+ <option key={record.id} value={record.id}>
42
+ {record.id} · {record.response.author} · {record.response.reviewedRevision.slice(0, 12)}
43
+ </option>
44
+ ))}
45
+ </FormField>
46
+ <pre className="whitespace-pre-wrap rounded-md bg-muted p-3 text-xs" hidden={!state.recovery} id={id(shellIds.responseDraftRecovery)}>
47
+ {state.recovery}
48
+ </pre>
49
+ <p className="text-muted-foreground text-xs">
50
+ Drafts stay in this browser for seven days after the last edit, with a 256 KiB limit per draft. Saved responses are Markdown files in
51
+ responses/.
52
+ </p>
53
+ <div className="flex flex-wrap gap-2">
54
+ <Button
55
+ disabled={typeof window === "undefined" || state.created.saving}
56
+ id={id(shellIds.responsePreview)}
57
+ onClick={state.showPreview}
58
+ size="sm"
59
+ variant="outline"
60
+ >
61
+ Preview Markdown
62
+ </Button>
63
+ <Button
64
+ disabled={typeof window === "undefined" || state.created.saving}
65
+ id={id(shellIds.responseClear)}
66
+ onClick={state.clear}
67
+ size="sm"
68
+ variant="ghost"
69
+ >
70
+ Clear draft
71
+ </Button>
72
+ </div>
73
+ <pre className="whitespace-pre-wrap rounded-md bg-muted p-3 text-xs" hidden={!state.preview} id={id(shellIds.responsePreviewContent)}>
74
+ {state.preview}
75
+ </pre>
76
+ </div>
65
77
  </details>
66
78
  );
67
79
  }
@@ -1,9 +1,10 @@
1
+ import { MessageCircleQuestion } from "lucide-react";
1
2
  import type { Document, DocumentRequest } from "#browser/document/schema.ts";
2
3
  import { reasonMarkdown } from "#browser/responses/reasonMarkdown.ts";
3
4
  import { AnswerContext } from "./AnswerContext.tsx";
4
5
  import { AnswerForm } from "./AnswerForm.tsx";
6
+ import { AnswerMarkdown } from "./AnswerMarkdown.tsx";
5
7
  import { ResponseHistory } from "./ResponseHistory.tsx";
6
- import { ResponseMarkdown } from "./ResponseMarkdown.tsx";
7
8
 
8
9
  export function AnswerPanel({ document: data }: { readonly document: Document }) {
9
10
  const requests = data.requests.filter(
@@ -30,23 +31,28 @@ function AnswerCard({ document: data, request }: { readonly document: Document;
30
31
  return (
31
32
  <section
32
33
  aria-labelledby={`answer-title-${request.id}`}
33
- className="response-panel rounded-lg border border-border bg-card p-4"
34
+ className="response-panel max-w-[72ch] space-y-5 rounded-xl border bg-card p-5 text-card-foreground shadow-sm"
34
35
  id={`answer-card-${request.id}`}
35
36
  tabIndex={-1}
36
37
  >
37
- <h2 id={`answer-title-${request.id}`}>{cardTitle(request, closed)}</h2>
38
+ <h2 className="flex items-center gap-2 font-medium text-muted-foreground text-sm" id={`answer-title-${request.id}`}>
39
+ {request.open ? <MessageCircleQuestion aria-hidden="true" className="size-4 text-needs-you" /> : null}
40
+ {cardTitle(request, closed)}
41
+ </h2>
38
42
  {!(request.open || unavailable) && (
39
- <p role="status">
43
+ <p className="text-muted-foreground text-sm" role="status">
40
44
  {closed ? "This question is closed. Recorded answers remain below." : "Answer unavailable. Check the source diagnostics."}
41
45
  </p>
42
46
  )}
43
- {Boolean(unavailable) && <p role="status">{unavailable}</p>}
47
+ {Boolean(unavailable) && (
48
+ <p className="rounded-md border border-destructive/40 px-3 py-2 text-sm" role="status">
49
+ {unavailable}
50
+ </p>
51
+ )}
44
52
  {request.open && preview !== undefined && (
45
53
  <>
54
+ <AnswerMarkdown className="answer-reason font-medium text-base" file={source.file} source={reasonMarkdown(preview.reason)} />
46
55
  <AnswerContext preview={preview} />
47
- <div className="answer-reason">
48
- <ResponseMarkdown file={source.file} source={reasonMarkdown(preview.reason)} />
49
- </div>
50
56
  </>
51
57
  )}
52
58
  {request.open && preview && !unavailable && <AnswerForm document={source} preview={preview} request={request} />}
@@ -1,10 +1,11 @@
1
1
  import { useAtomValue } from "@effect/atom-react";
2
2
  import { useId, useLayoutEffect, useRef } from "react";
3
3
  import type { Document } from "#browser/document/schema.ts";
4
+ import { promptHeading } from "#browser/responses/cardContext.ts";
4
5
  import type { Prompt } from "#browser/responses/Prompt.ts";
5
6
  import type { PromptAnswer } from "#browser/responses/schema.ts";
7
+ import { AnswerMarkdown } from "./AnswerMarkdown.tsx";
6
8
  import { PromptOption } from "./PromptOption.tsx";
7
- import { ResponseMarkdown } from "./ResponseMarkdown.tsx";
8
9
  import type { useResponseForm } from "./useResponseForm.ts";
9
10
 
10
11
  type Model = ReturnType<typeof useResponseForm>["form"];
@@ -36,12 +37,9 @@ function PromptField({
36
37
  useLayoutEffect(() => ref.current?.setCustomValidity(error ?? ""), [error]);
37
38
  const choices = document.metadata.options;
38
39
 
39
- let guidance = "Answer in your own words.";
40
- if (choices) {
41
- guidance = "Choose an option or write another answer.";
42
- } else if (prompt.mode !== "text") {
43
- guidance = `${prompt.mode === "one" ? "Choose one option" : "Choose any combination"}, or answer in your own words.`;
44
- }
40
+ const guidance = promptGuidance(prompt.mode, choices !== undefined);
41
+ const heading = promptHeading(prompt.source);
42
+ const title = heading.title ?? `Question ${index + 1}`;
45
43
  function change(next: PromptAnswer) {
46
44
  onEdit(index, next);
47
45
  }
@@ -57,14 +55,14 @@ function PromptField({
57
55
  }
58
56
  let message = error;
59
57
  if (error && answer.selected.length === 0 && !answer.text.trim()) {
60
- message = `Unanswered: Question ${index + 1} · ${prompt.id}. Choose an option or answer in your own words.`;
58
+ message = `Unanswered: “${title}”. Choose an option or answer in your own words.`;
61
59
  }
62
60
  return (
63
- <fieldset data-mode={prompt.mode} data-prompt={prompt.id} data-source={prompt.source}>
64
- <legend>{choices ? "Your answer" : `Question ${index + 1} · ${prompt.id}`}</legend>
65
- {choices === undefined && <ResponseMarkdown file={document.file} source={prompt.source} />}
66
- <p>{guidance} Nothing is selected for you.</p>
67
- <div className={choices ? "question-choices" : undefined}>
61
+ <fieldset className="m-0 min-w-0 space-y-3 border-0 p-0" data-mode={prompt.mode} data-prompt={prompt.id} data-source={prompt.source}>
62
+ <legend className="mb-1 p-0 font-semibold text-base">{choices === undefined ? title : "Your answer"}</legend>
63
+ {choices === undefined && heading.rest ? <AnswerMarkdown className="text-sm" file={document.file} source={heading.rest} /> : null}
64
+ <p className="text-muted-foreground text-sm">{guidance}</p>
65
+ <div className={`grid gap-2 ${choices ? "question-choices" : ""}`}>
68
66
  {prompt.options.map((option, choice) => (
69
67
  <PromptOption
70
68
  checked={answer.selected.includes(option.id)}
@@ -78,26 +76,43 @@ function PromptField({
78
76
  ))}
79
77
  </div>
80
78
  {prompt.mode !== "text" && (
81
- <button data-clear-choices onClick={() => change({ ...answer, selected: [] })} type="button">
79
+ <button
80
+ className="text-muted-foreground text-xs underline-offset-4 hover:underline disabled:opacity-50"
81
+ data-clear-choices
82
+ disabled={answer.selected.length === 0}
83
+ onClick={() => change({ ...answer, selected: [] })}
84
+ type="button"
85
+ >
82
86
  Clear selections
83
87
  </button>
84
88
  )}
85
- <label>
86
- {choices ? "Another answer / additional text" : "Additional text / another answer"}
89
+ <label className="grid gap-2 text-sm">
90
+ <span>{prompt.mode === "text" ? "Your answer" : "Add a note or answer in your own words (optional)"}</span>
87
91
  <textarea
88
92
  aria-describedby={`${errorId}-error`}
89
93
  aria-invalid={Boolean(error)}
94
+ className="w-full rounded-md border bg-background px-3 py-2 text-sm aria-invalid:border-destructive"
90
95
  data-answer-text
91
96
  maxLength={8192}
92
97
  onInput={(event) => change({ ...answer, text: event.currentTarget.value })}
93
98
  ref={ref}
94
- rows={choices ? 4 : 3}
99
+ rows={choices || prompt.mode === "text" ? 4 : 2}
95
100
  value={answer.text}
96
101
  />
97
- <span className="response-field-error" hidden={!error} id={`${errorId}-error`}>
102
+ <span className="response-field-error text-destructive text-xs" hidden={!error} id={`${errorId}-error`}>
98
103
  {message}
99
104
  </span>
100
105
  </label>
101
106
  </fieldset>
102
107
  );
103
108
  }
109
+
110
+ function promptGuidance(mode: Prompt["mode"], authored: boolean) {
111
+ if (authored) {
112
+ return "Choose an option or write another answer. Nothing is selected for you.";
113
+ }
114
+ if (mode === "text") {
115
+ return "Answer in your own words.";
116
+ }
117
+ return `${mode === "one" ? "Choose one option" : "Choose any combination"}, or answer in your own words. Nothing is selected for you.`;
118
+ }
@@ -4,7 +4,6 @@ import type * as AtomRegistry from "effect/reactivity/AtomRegistry";
4
4
  import { useCallback, useContext, useEffect, useState } from "react";
5
5
  import { useQuery } from "@shivaedev/effect-react/result-state.ts";
6
6
  import type { BrowserClient } from "#client/client.ts";
7
- import { navigationQuery } from "#client/page/routeQuery.ts";
8
7
  import { AgeTicker } from "./Age.tsx";
9
8
  import { FleetMain } from "./FleetMain.tsx";
10
9
  import { FormContext } from "./FormContext.ts";
@@ -15,6 +14,7 @@ import { emptyMainLocation, type MainLocation } from "./mainLocation.ts";
15
14
  import { PhoneNavigation } from "./PhoneNavigation.tsx";
16
15
  import { ReadingLibrary } from "./ReadingLibrary.tsx";
17
16
  import { ReadingReadyContext, readingReady } from "./ReadingReadyContext.ts";
17
+ import { navigationQuery } from "./routeQuery.ts";
18
18
  import { SearchDialog } from "./SearchDialog.tsx";
19
19
  import { SessionContext } from "./SessionContext.ts";
20
20
  import type { ShellApi } from "./ShellApi.ts";
@@ -1,5 +1,6 @@
1
1
  import type { Document } from "#browser/document/schema.ts";
2
2
  import { fileUrl } from "#files/url.ts";
3
+ import { responseUrl } from "#responses/url.ts";
3
4
  import { keyed } from "./keyed.ts";
4
5
  import { Reference } from "./Reference.tsx";
5
6
  export function DocumentAttention({ document }: { readonly document: Document }) {
@@ -28,9 +29,7 @@ function Request({ document, index }: { readonly document: Document; readonly in
28
29
  document.metadata.attention?.filter((entry) => entry.id === request.id).length === 1
29
30
  ? `attention-request-${request.id}`
30
31
  : `attention-record-${index}`;
31
- const respond = document.requests.some((entry) => entry.id === request.id && entry.open)
32
- ? `#answer-card-${request.id}`
33
- : `/_board/respond?item=${encodeURIComponent(document.metadata.id ?? "")}&request=${encodeURIComponent(request.id)}`;
32
+ const respond = responseUrl(document.metadata.id ?? "", request.id);
34
33
  const targets = request.unblocks.map((target, targetIndex) => (
35
34
  <span key={target}>
36
35
  {targetIndex > 0 && ", "}
@@ -2,11 +2,11 @@ import { Cause, Option } from "effect";
2
2
  import { useEffect, useState } from "react";
3
3
  import { useQuery } from "@shivaedev/effect-react/result-state.ts";
4
4
  import type { Document } from "#browser/document/schema.ts";
5
- import { documentQuery } from "#client/page/routeQuery.ts";
6
5
  import { DocumentPage } from "./DocumentPage.tsx";
7
6
  import type { MainLocation } from "./mainLocation.ts";
8
7
  import { ProjectionMain } from "./ProjectionMain.tsx";
9
8
  import { RemainingMain } from "./RemainingMain.tsx";
9
+ import { documentQuery } from "./routeQuery.ts";
10
10
  import type { ShellApi } from "./ShellApi.ts";
11
11
  import { shellIds } from "./shellIds.ts";
12
12
  import { useDocumentPage } from "./useDocumentPage.ts";
@@ -1,10 +1,10 @@
1
1
  import { Option } from "effect";
2
2
  import { useQuery } from "@shivaedev/effect-react/result-state.ts";
3
3
  import type { Document } from "#browser/document/schema.ts";
4
- import { routeQueryOptions } from "#client/page/routeQuery.ts";
5
4
  import { GoalCriteria } from "./GoalCriteria.tsx";
6
5
  import { GoalWorkTable } from "./GoalWorkTable.tsx";
7
6
  import { targetsGoal } from "./goalWork.ts";
7
+ import { routeQueryOptions } from "./routeQuery.ts";
8
8
  import type { ShellApi } from "./ShellApi.ts";
9
9
 
10
10
  export function GoalPage({ api, document }: { readonly api: ShellApi; readonly document: Document }) {
@@ -2,8 +2,8 @@ import { Option } from "effect";
2
2
  import { useId } from "react";
3
3
  import { useQuery } from "@shivaedev/effect-react/result-state.ts";
4
4
  import type { Document } from "#browser/document/schema.ts";
5
- import { routeQueryOptions } from "#client/page/routeQuery.ts";
6
5
  import { GoalProgressSummary } from "./GoalProgressSummary.tsx";
6
+ import { routeQueryOptions } from "./routeQuery.ts";
7
7
  import type { ShellApi } from "./ShellApi.ts";
8
8
 
9
9
  export function GoalProgressHeader({ api, document }: { readonly api: ShellApi; readonly document: Document }) {
@@ -1,7 +1,7 @@
1
- import { routeQueryOptions } from "#client/page/routeQuery.ts";
2
1
  import { LibraryPage } from "./LibraryPage.tsx";
3
2
  import type { MainLocation } from "./mainLocation.ts";
4
3
  import { ProjectionFrame } from "./ProjectionFrame.tsx";
4
+ import { routeQueryOptions } from "./routeQuery.ts";
5
5
  import type { ShellApi } from "./ShellApi.ts";
6
6
  export function LibraryMain({
7
7
  api,
@@ -1,8 +1,8 @@
1
1
  import { Option } from "effect";
2
2
  import { useState } from "react";
3
3
  import { useQuery } from "@shivaedev/effect-react/result-state.ts";
4
- import { routeQueryOptions } from "#client/page/routeQuery.ts";
5
4
  import { LibraryContents } from "./LibraryContents.tsx";
5
+ import { routeQueryOptions } from "./routeQuery.ts";
6
6
  import type { ShellApi } from "./ShellApi.ts";
7
7
 
8
8
  export function LibraryPage({ api, url = "/_board/library" }: { readonly api: ShellApi; readonly url?: string }) {
@@ -130,7 +130,7 @@ function projectionView(route: ProjectionRoute | RemainingRoute) {
130
130
  if (route === "board") {
131
131
  return "";
132
132
  }
133
- return route === "respond" ? "response" : route;
133
+ return route;
134
134
  }
135
135
 
136
136
  function failureStatus(missing: boolean, conflict: boolean) {
@@ -1,9 +1,9 @@
1
1
  import { documentSourceId } from "#client/document/layout.ts";
2
- import { routeQueryOptions } from "#client/page/routeQuery.ts";
3
2
  import { HomeBoard } from "./HomeBoard.tsx";
4
3
  import type { MainLocation } from "./mainLocation.ts";
5
4
  import { ProjectionFrame } from "./ProjectionFrame.tsx";
6
5
  import type { ProjectionRoute } from "./projectionRoute.ts";
6
+ import { routeQueryOptions } from "./routeQuery.ts";
7
7
  import type { ShellApi } from "./ShellApi.ts";
8
8
  import { TodayPage } from "./TodayPage.tsx";
9
9
  import { WorkPage } from "./WorkPage.tsx";