@shivaedev/work-board 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +4 -4
  3. package/assets/react.css +1 -1
  4. package/assets/react.js +52 -39
  5. package/dist/browser/views/remaining.d.ts +0 -240
  6. package/dist/browser/views/remaining.d.ts.map +1 -1
  7. package/dist/browser/views/remaining.js +0 -2
  8. package/dist/browser/views/remaining.js.map +1 -1
  9. package/dist/client/document/Mermaid.d.ts.map +1 -1
  10. package/dist/client/document/Mermaid.js +4 -10
  11. package/dist/client/document/Mermaid.js.map +1 -1
  12. package/dist/client/document/View.js +1 -1
  13. package/dist/client/document/View.js.map +1 -1
  14. package/dist/client/react/BoardShell.d.ts.map +1 -1
  15. package/dist/client/react/BoardShell.js +28 -23
  16. package/dist/client/react/BoardShell.js.map +1 -1
  17. package/dist/client/react/DocumentAttention.d.ts.map +1 -1
  18. package/dist/client/react/DocumentAttention.js +2 -3
  19. package/dist/client/react/DocumentAttention.js.map +1 -1
  20. package/dist/client/react/DocumentContext.d.ts.map +1 -1
  21. package/dist/client/react/DocumentContext.js +6 -4
  22. package/dist/client/react/DocumentContext.js.map +1 -1
  23. package/dist/client/react/LiveControls.d.ts.map +1 -1
  24. package/dist/client/react/LiveControls.js +1 -6
  25. package/dist/client/react/LiveControls.js.map +1 -1
  26. package/dist/client/react/PhoneNavigation.d.ts +3 -1
  27. package/dist/client/react/PhoneNavigation.d.ts.map +1 -1
  28. package/dist/client/react/PhoneNavigation.js +8 -13
  29. package/dist/client/react/PhoneNavigation.js.map +1 -1
  30. package/dist/client/react/ProjectionFrame.js +1 -1
  31. package/dist/client/react/ProjectionFrame.js.map +1 -1
  32. package/dist/client/react/ReadingLibrary.d.ts.map +1 -1
  33. package/dist/client/react/ReadingLibrary.js +2 -6
  34. package/dist/client/react/ReadingLibrary.js.map +1 -1
  35. package/dist/client/react/ReadingReadyContext.d.ts +6 -0
  36. package/dist/client/react/ReadingReadyContext.d.ts.map +1 -0
  37. package/dist/client/react/ReadingReadyContext.js +7 -0
  38. package/dist/client/react/ReadingReadyContext.js.map +1 -0
  39. package/dist/client/react/RemainingMain.d.ts.map +1 -1
  40. package/dist/client/react/RemainingMain.js +0 -3
  41. package/dist/client/react/RemainingMain.js.map +1 -1
  42. package/dist/client/react/ResultPage.d.ts.map +1 -1
  43. package/dist/client/react/ResultPage.js +2 -1
  44. package/dist/client/react/ResultPage.js.map +1 -1
  45. package/dist/client/react/SessionContext.d.ts +3 -0
  46. package/dist/client/react/SessionContext.d.ts.map +1 -0
  47. package/dist/client/react/SessionContext.js +3 -0
  48. package/dist/client/react/SessionContext.js.map +1 -0
  49. package/dist/client/react/ShellApi.d.ts +0 -1
  50. package/dist/client/react/ShellApi.d.ts.map +1 -1
  51. package/dist/client/react/SystemMain.d.ts.map +1 -1
  52. package/dist/client/react/SystemMain.js +3 -1
  53. package/dist/client/react/SystemMain.js.map +1 -1
  54. package/dist/client/react/ThemeContext.d.ts +2 -0
  55. package/dist/client/react/ThemeContext.d.ts.map +1 -0
  56. package/dist/client/react/ThemeContext.js +3 -0
  57. package/dist/client/react/ThemeContext.js.map +1 -0
  58. package/dist/client/react/TodayOptions.d.ts.map +1 -1
  59. package/dist/client/react/TodayOptions.js +2 -2
  60. package/dist/client/react/TodayOptions.js.map +1 -1
  61. package/dist/client/react/entry.js +1 -1
  62. package/dist/client/react/entry.js.map +1 -1
  63. package/dist/client/react/loadBoardPage.d.ts.map +1 -1
  64. package/dist/client/react/loadBoardPage.js +5 -13
  65. package/dist/client/react/loadBoardPage.js.map +1 -1
  66. package/dist/client/react/loadRemaining.d.ts +124 -364
  67. package/dist/client/react/loadRemaining.d.ts.map +1 -1
  68. package/dist/client/react/loadRemaining.js +0 -2
  69. package/dist/client/react/loadRemaining.js.map +1 -1
  70. package/dist/client/react/navigationLocation.d.ts +2 -0
  71. package/dist/client/react/navigationLocation.d.ts.map +1 -1
  72. package/dist/client/react/navigationLocation.js +10 -0
  73. package/dist/client/react/navigationLocation.js.map +1 -1
  74. package/dist/client/react/pageQueries.d.ts.map +1 -1
  75. package/dist/client/react/pageQueries.js +0 -3
  76. package/dist/client/react/pageQueries.js.map +1 -1
  77. package/dist/client/react/readerLink.js +1 -1
  78. package/dist/client/react/readerLink.js.map +1 -1
  79. package/dist/client/react/remainingRoute.d.ts +1 -1
  80. package/dist/client/react/remainingRoute.d.ts.map +1 -1
  81. package/dist/client/react/remainingRoute.js +0 -2
  82. package/dist/client/react/remainingRoute.js.map +1 -1
  83. package/dist/client/react/scheduleReading.d.ts.map +1 -1
  84. package/dist/client/react/scheduleReading.js +1 -10
  85. package/dist/client/react/scheduleReading.js.map +1 -1
  86. package/dist/client/react/useAppearance.d.ts +2 -1
  87. package/dist/client/react/useAppearance.d.ts.map +1 -1
  88. package/dist/client/react/useAppearance.js +1 -2
  89. package/dist/client/react/useAppearance.js.map +1 -1
  90. package/dist/client/react/useBoardNavigation.d.ts +1 -1
  91. package/dist/client/react/useBoardNavigation.d.ts.map +1 -1
  92. package/dist/client/react/useBoardNavigation.js +13 -17
  93. package/dist/client/react/useBoardNavigation.js.map +1 -1
  94. package/dist/client/react/useDiagnosticsPolling.d.ts +4 -1
  95. package/dist/client/react/useDiagnosticsPolling.d.ts.map +1 -1
  96. package/dist/client/react/useDiagnosticsPolling.js +9 -7
  97. package/dist/client/react/useDiagnosticsPolling.js.map +1 -1
  98. package/dist/client/react/useSidebar.d.ts.map +1 -1
  99. package/dist/client/react/useSidebar.js +5 -3
  100. package/dist/client/react/useSidebar.js.map +1 -1
  101. package/dist/render/{failed.d.ts → Failed.d.ts} +1 -1
  102. package/dist/render/Failed.d.ts.map +1 -0
  103. package/dist/render/{failed.js → Failed.js} +1 -1
  104. package/dist/render/Failed.js.map +1 -0
  105. package/dist/render/code.d.ts +2 -2
  106. package/dist/render/code.d.ts.map +1 -1
  107. package/dist/render/code.js +15 -13
  108. package/dist/render/code.js.map +1 -1
  109. package/dist/render/footnoteIds.d.ts +3 -0
  110. package/dist/render/footnoteIds.d.ts.map +1 -0
  111. package/dist/render/{footnotes.js → footnoteIds.js} +11 -9
  112. package/dist/render/footnoteIds.js.map +1 -0
  113. package/dist/responses/url.js +1 -1
  114. package/dist/responses/url.js.map +1 -1
  115. package/dist/rpc/PageUrl.d.ts.map +1 -1
  116. package/dist/rpc/PageUrl.js +0 -1
  117. package/dist/rpc/PageUrl.js.map +1 -1
  118. package/dist/rpc/contract.d.ts +25 -748
  119. package/dist/rpc/contract.d.ts.map +1 -1
  120. package/dist/rpc/contract.js +6 -9
  121. package/dist/rpc/contract.js.map +1 -1
  122. package/dist/rpc/handoffContract.d.ts +33 -33
  123. package/dist/server/{responses/service.d.ts → Responses.d.ts} +9 -21
  124. package/dist/server/Responses.d.ts.map +1 -0
  125. package/dist/server/{responses/service.js → Responses.js} +22 -22
  126. package/dist/server/Responses.js.map +1 -0
  127. package/dist/server/attention/inbox.d.ts +2 -0
  128. package/dist/server/attention/inbox.d.ts.map +1 -1
  129. package/dist/server/attention/inbox.js +1 -0
  130. package/dist/server/attention/inbox.js.map +1 -1
  131. package/dist/server/attention/projection.js +1 -1
  132. package/dist/server/attention/projection.js.map +1 -1
  133. package/dist/server/attention/requestInbox.d.ts +3 -0
  134. package/dist/server/attention/requestInbox.d.ts.map +1 -1
  135. package/dist/server/attention/requestInbox.js +1 -0
  136. package/dist/server/attention/requestInbox.js.map +1 -1
  137. package/dist/server/board.d.ts +1 -1
  138. package/dist/server/boardRuntime.d.ts +2 -2
  139. package/dist/server/boardRuntime.d.ts.map +1 -1
  140. package/dist/server/boardRuntime.js +12 -16
  141. package/dist/server/boardRuntime.js.map +1 -1
  142. package/dist/server/boardServices.d.ts +3 -4
  143. package/dist/server/boardServices.d.ts.map +1 -1
  144. package/dist/server/boardServices.js +5 -4
  145. package/dist/server/boardServices.js.map +1 -1
  146. package/dist/server/boardSocket.d.ts +1 -1
  147. package/dist/server/boardSocket.d.ts.map +1 -1
  148. package/dist/server/document/read.d.ts +4 -10
  149. package/dist/server/document/read.d.ts.map +1 -1
  150. package/dist/server/document/read.js +5 -5
  151. package/dist/server/document/read.js.map +1 -1
  152. package/dist/server/document/requests.d.ts.map +1 -1
  153. package/dist/server/document/requests.js +1 -2
  154. package/dist/server/document/requests.js.map +1 -1
  155. package/dist/server/handoffs/Service.d.ts +1 -2
  156. package/dist/server/handoffs/Service.d.ts.map +1 -1
  157. package/dist/server/handoffs/Service.js +14 -13
  158. package/dist/server/handoffs/Service.js.map +1 -1
  159. package/dist/server/http/assets.d.ts +0 -3
  160. package/dist/server/http/assets.d.ts.map +1 -1
  161. package/dist/server/http/assets.js +1 -7
  162. package/dist/server/http/assets.js.map +1 -1
  163. package/dist/server/http/boardRoutes.d.ts.map +1 -1
  164. package/dist/server/http/boardRoutes.js +3 -3
  165. package/dist/server/http/boardRoutes.js.map +1 -1
  166. package/dist/server/integration/responses.d.ts +207 -4
  167. package/dist/server/integration/responses.d.ts.map +1 -1
  168. package/dist/server/integration/responses.js +4 -2
  169. package/dist/server/integration/responses.js.map +1 -1
  170. package/dist/server/mcp/client.d.ts +1060 -1300
  171. package/dist/server/mcp/client.d.ts.map +1 -1
  172. package/dist/server/mcp/queries.d.ts +24 -744
  173. package/dist/server/mcp/queries.d.ts.map +1 -1
  174. package/dist/server/mcp/queries.js +3 -3
  175. package/dist/server/mcp/queries.js.map +1 -1
  176. package/dist/server/packageInfo.d.ts +1 -1
  177. package/dist/server/packageInfo.js +1 -1
  178. package/dist/server/render/Highlighter.d.ts +1 -1
  179. package/dist/server/render/Highlighter.js +1 -1
  180. package/dist/server/render/markdown.d.ts +1 -1
  181. package/dist/server/render/markdown.js +2 -2
  182. package/dist/server/render/markdown.js.map +1 -1
  183. package/dist/server/responses/readingActions.d.ts +10 -1
  184. package/dist/server/responses/readingActions.d.ts.map +1 -1
  185. package/dist/server/responses/readingActions.js +2 -4
  186. package/dist/server/responses/readingActions.js.map +1 -1
  187. package/dist/server/responses/registerQuestion.d.ts +2 -2
  188. package/dist/server/responses/registerQuestion.d.ts.map +1 -1
  189. package/dist/server/responses/registerQuestion.js +3 -3
  190. package/dist/server/responses/registerQuestion.js.map +1 -1
  191. package/dist/server/responses/retry.d.ts +2 -2
  192. package/dist/server/responses/retry.d.ts.map +1 -1
  193. package/dist/server/responses/retry.js +1 -1
  194. package/dist/server/responses/retry.js.map +1 -1
  195. package/dist/server/rpc/HandlerOptions.d.ts +0 -8
  196. package/dist/server/rpc/HandlerOptions.d.ts.map +1 -1
  197. package/dist/server/rpc/handlers.d.ts +1 -1
  198. package/dist/server/rpc/handlers.d.ts.map +1 -1
  199. package/dist/server/rpc/handlers.js +14 -13
  200. package/dist/server/rpc/handlers.js.map +1 -1
  201. package/dist/server/rpc/hints.d.ts +3 -5
  202. package/dist/server/rpc/hints.d.ts.map +1 -1
  203. package/dist/server/rpc/hints.js +5 -1
  204. package/dist/server/rpc/hints.js.map +1 -1
  205. package/dist/server/serve.d.ts +1 -1
  206. package/dist/server/views/fleetItems.d.ts +2 -2
  207. package/dist/server/views/fleetItems.d.ts.map +1 -1
  208. package/dist/server/views/fleetItems.js +8 -3
  209. package/dist/server/views/fleetItems.js.map +1 -1
  210. package/dist/server/views/projectionQueries.d.ts +5 -9
  211. package/dist/server/views/projectionQueries.d.ts.map +1 -1
  212. package/dist/server/views/projectionQueries.js +6 -6
  213. package/dist/server/views/projectionQueries.js.map +1 -1
  214. package/dist/server/views/remainingQueries.d.ts +4 -252
  215. package/dist/server/views/remainingQueries.d.ts.map +1 -1
  216. package/dist/server/views/remainingQueries.js +11 -19
  217. package/dist/server/views/remainingQueries.js.map +1 -1
  218. package/dist/server/workspace.d.ts +222 -0
  219. package/dist/server/workspace.d.ts.map +1 -0
  220. package/dist/server/workspace.js +25 -0
  221. package/dist/server/workspace.js.map +1 -0
  222. package/package.json +6 -37
  223. package/src/browser/views/remaining.ts +0 -3
  224. package/src/client/document/Mermaid.ts +4 -10
  225. package/src/client/document/View.ts +1 -1
  226. package/src/client/react/BoardShell.tsx +37 -24
  227. package/src/client/react/DocumentAttention.tsx +2 -3
  228. package/src/client/react/DocumentContext.tsx +6 -4
  229. package/src/client/react/LiveControls.tsx +1 -14
  230. package/src/client/react/PhoneNavigation.tsx +20 -15
  231. package/src/client/react/ProjectionFrame.tsx +1 -1
  232. package/src/client/react/ReadingLibrary.tsx +2 -6
  233. package/src/client/react/ReadingReadyContext.ts +11 -0
  234. package/src/client/react/RemainingMain.tsx +0 -10
  235. package/src/client/react/ResultPage.tsx +2 -2
  236. package/src/client/react/SessionContext.ts +4 -0
  237. package/src/client/react/ShellApi.ts +0 -1
  238. package/src/client/react/SystemMain.tsx +3 -1
  239. package/src/client/react/ThemeContext.ts +3 -0
  240. package/src/client/react/TodayOptions.tsx +2 -2
  241. package/src/client/react/entry.tsx +1 -1
  242. package/src/client/react/loadBoardPage.ts +5 -14
  243. package/src/client/react/loadRemaining.ts +0 -2
  244. package/src/client/react/navigationLocation.ts +12 -0
  245. package/src/client/react/pageQueries.ts +0 -3
  246. package/src/client/react/readerLink.ts +1 -1
  247. package/src/client/react/remainingRoute.ts +0 -2
  248. package/src/client/react/scheduleReading.ts +1 -10
  249. package/src/client/react/useAppearance.ts +2 -3
  250. package/src/client/react/useBoardNavigation.ts +13 -18
  251. package/src/client/react/useDiagnosticsPolling.ts +14 -7
  252. package/src/client/react/useSidebar.ts +5 -3
  253. package/src/render/code.ts +3 -4
  254. package/src/render/{footnotes.ts → footnoteIds.ts} +12 -10
  255. package/src/responses/url.ts +1 -1
  256. package/src/rpc/PageUrl.ts +0 -1
  257. package/src/rpc/contract.ts +6 -9
  258. package/src/server/{responses/service.ts → Responses.ts} +21 -29
  259. package/src/server/attention/inbox.ts +1 -0
  260. package/src/server/attention/projection.ts +1 -1
  261. package/src/server/attention/requestInbox.ts +1 -0
  262. package/src/server/board.ts +1 -1
  263. package/src/server/boardRuntime.ts +20 -25
  264. package/src/server/boardServices.ts +7 -7
  265. package/src/server/document/read.ts +6 -14
  266. package/src/server/document/requests.ts +1 -2
  267. package/src/server/handoffs/Service.ts +16 -19
  268. package/src/server/http/assets.ts +1 -7
  269. package/src/server/http/boardRoutes.ts +3 -3
  270. package/src/server/integration/responses.ts +5 -3
  271. package/src/server/mcp/queries.ts +3 -3
  272. package/src/server/packageInfo.ts +1 -1
  273. package/src/server/render/Highlighter.ts +1 -1
  274. package/src/server/render/markdown.ts +2 -2
  275. package/src/server/responses/readingActions.ts +3 -3
  276. package/src/server/responses/registerQuestion.ts +5 -5
  277. package/src/server/responses/retry.ts +3 -3
  278. package/src/server/rpc/HandlerOptions.ts +0 -8
  279. package/src/server/rpc/handlers.ts +17 -15
  280. package/src/server/rpc/hints.ts +6 -3
  281. package/src/server/views/fleetItems.ts +8 -4
  282. package/src/server/views/projectionQueries.ts +6 -7
  283. package/src/server/views/remainingQueries.ts +11 -31
  284. package/src/server/workspace.ts +41 -0
  285. package/assets/native.js +0 -37
  286. package/dist/client/entry.d.ts +0 -3
  287. package/dist/client/entry.d.ts.map +0 -1
  288. package/dist/client/entry.js +0 -3
  289. package/dist/client/entry.js.map +0 -1
  290. package/dist/client/react/RespondPage.d.ts +0 -5
  291. package/dist/client/react/RespondPage.d.ts.map +0 -1
  292. package/dist/client/react/RespondPage.js +0 -5
  293. package/dist/client/react/RespondPage.js.map +0 -1
  294. package/dist/render/failed.d.ts.map +0 -1
  295. package/dist/render/failed.js.map +0 -1
  296. package/dist/render/footnotes.d.ts +0 -3
  297. package/dist/render/footnotes.d.ts.map +0 -1
  298. package/dist/render/footnotes.js.map +0 -1
  299. package/dist/server/responses/service.d.ts.map +0 -1
  300. package/dist/server/responses/service.js.map +0 -1
  301. package/src/client/entry.ts +0 -3
  302. package/src/client/react/RespondPage.tsx +0 -8
  303. /package/src/render/{failed.ts → Failed.ts} +0 -0
@@ -1,29 +1,20 @@
1
- import { Clock, Context, Effect, FileSystem, Path, Schema, Semaphore } from "effect";
1
+ import { Clock, Effect, FileSystem, Path, Schema, Semaphore } from "effect";
2
2
  import { defineService } from "@shivaedev/effect-service/define-service.ts";
3
3
  import { Conflict, Forbidden, PreconditionFailed, ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
4
4
  import { rpcBoundary } from "@shivaedev/platform/rpc-server/boundary.ts";
5
5
  import type { DraftInput } from "#browser/responses/schema.ts";
6
- import type { Changes } from "#server/files/changes.ts";
7
- import type { Snapshot } from "#server/search/snapshot.ts";
8
- import { awaitResponse } from "./await.ts";
9
- import { responseHistory } from "./history.ts";
10
- import { locate as locateQuestion } from "./locate.ts";
11
- import { makeResponse } from "./make.ts";
12
- import { publish } from "./publish.ts";
13
- import { readResponses } from "./read.ts";
14
- import { responseMarkdown } from "./records.ts";
15
- import { registerQuestion } from "./registerQuestion.ts";
16
- import { retryResponse } from "./retry.ts";
17
- import { validatedInput } from "./validatedInput.ts";
18
- import { validateSupersedes } from "./validateSupersedes.ts";
19
-
20
- export interface ResponseOptions {
21
- readonly changes: Changes;
22
- readonly enabled: boolean;
23
- readonly index: Effect.Effect<Snapshot, unknown>;
24
- readonly root: string;
25
- }
26
- export class ResponseConfig extends Context.Service<ResponseConfig, ResponseOptions>()("WorkBoard/ResponseConfig") {}
6
+ import { awaitResponse } from "#server/responses/await.ts";
7
+ import { responseHistory } from "#server/responses/history.ts";
8
+ import { locate as locateQuestion } from "#server/responses/locate.ts";
9
+ import { makeResponse } from "#server/responses/make.ts";
10
+ import { publish } from "#server/responses/publish.ts";
11
+ import { readResponses } from "#server/responses/read.ts";
12
+ import { responseMarkdown } from "#server/responses/records.ts";
13
+ import { registerQuestion } from "#server/responses/registerQuestion.ts";
14
+ import { retryResponse } from "#server/responses/retry.ts";
15
+ import { validatedInput } from "#server/responses/validatedInput.ts";
16
+ import { validateSupersedes } from "#server/responses/validateSupersedes.ts";
17
+ import { Workspace } from "#server/workspace.ts";
27
18
 
28
19
  function invalidResponse(cause: unknown): Conflict {
29
20
  return Schema.is(Conflict)(cause)
@@ -34,19 +25,20 @@ function invalidResponse(cause: unknown): Conflict {
34
25
  export const Responses = defineService({
35
26
  id: "WorkBoard/Responses",
36
27
  initialize: Effect.gen(function* () {
37
- return { fs: yield* FileSystem.FileSystem, lock: yield* Semaphore.make(1), options: yield* ResponseConfig };
28
+ const workspace = yield* Workspace;
29
+ return { fs: yield* FileSystem.FileSystem, lock: yield* Semaphore.make(1), options: yield* workspace.settings(), workspace };
38
30
  }),
39
- methods: ({ options, fs, lock }) => {
40
- const snapshot = options.index.pipe(
31
+ methods: ({ options, fs, lock, workspace }) => {
32
+ const snapshot = workspace.index().pipe(
41
33
  rpcBoundary(ServiceUnavailable, {
42
34
  message: "The workspace could not be indexed.",
43
35
  unexpected: (message) => new ServiceUnavailable({ message }),
44
36
  }),
45
37
  );
46
- const enabled = options.enabled
38
+ const enabled = options.writable
47
39
  ? Effect.void
48
40
  : Effect.fail(new Forbidden({ message: "Start with --responses to explicitly enable local question/response writes." }));
49
- const locate = locateQuestion(options.root, options.changes.realRoot, snapshot, fs);
41
+ const locate = locateQuestion(options.root, options.realRoot, snapshot, fs);
50
42
  const read = readResponses(snapshot);
51
43
  const register = registerQuestion(options, snapshot, locate, read);
52
44
  function record(draft: DraftInput) {
@@ -80,7 +72,7 @@ export const Responses = defineService({
80
72
  question,
81
73
  result.responses.reduce((time, saved) => Math.max(time, saved.response.recordedAt + 1), yield* Clock.currentTimeMillis),
82
74
  );
83
- yield* publish(options.root, options.changes.realRoot, response.id, responseMarkdown(response)).pipe(
75
+ yield* publish(options.root, options.realRoot, response.id, responseMarkdown(response)).pipe(
84
76
  Effect.ensuring(options.changes.refresh ?? Effect.void),
85
77
  );
86
78
  return response;
@@ -107,6 +99,6 @@ export const Responses = defineService({
107
99
  register: (input: Parameters<typeof register>[0]) => lock.withPermit(register(input)),
108
100
  };
109
101
  },
110
- requires: [ResponseConfig, FileSystem.FileSystem, Path.Path],
102
+ requires: [Workspace, FileSystem.FileSystem, Path.Path],
111
103
  });
112
104
  export type Responses = typeof Responses.Identifier;
@@ -86,6 +86,7 @@ export function inboxItems(snapshot: Snapshot) {
86
86
  id: id ?? document.file,
87
87
  problem,
88
88
  reason,
89
+ request: undefined,
89
90
  title: titles.get(document.file) ?? document.file,
90
91
  unblocks: [id ?? ""],
91
92
  },
@@ -12,7 +12,7 @@ export function attentionProjection(snapshot: Snapshot): InboxProjection {
12
12
  return cached;
13
13
  }
14
14
  const requests = requestInbox(snapshot);
15
- function project({ document, unblocks: _unblocks, ...item }: ReturnType<typeof inboxItems>[number]) {
15
+ function project({ document, request: _request, unblocks: _unblocks, ...item }: ReturnType<typeof inboxItems>[number]) {
16
16
  return { ...item, file: document.file };
17
17
  }
18
18
  const you = inboxItems(snapshot).map(project);
@@ -28,6 +28,7 @@ export function requestInbox(snapshot: Snapshot) {
28
28
  modified: snapshot.modified.get(entry.document.file),
29
29
  problem: undefined,
30
30
  reason: entry.request.managed ? `Managed decision: ${entry.request.reason}` : entry.request.reason,
31
+ request: entry.request.id,
31
32
  responseFrom: entry.request.responseFrom,
32
33
  title: (target ? titles.get(target.file) : undefined) ?? entry.title,
33
34
  unblocks: entry.request.unblocks,
@@ -3,7 +3,7 @@ import { HttpRouter } from "effect/http";
3
3
  import { RpcSerialization, RpcServer } from "effect/rpc";
4
4
  import type { InternalServerError } from "@shivaedev/platform/errors/taxonomy.ts";
5
5
  import { localRpcGuard } from "@shivaedev/platform/rpc-server/local.ts";
6
- import type { RenderFailed } from "#render/failed.ts";
6
+ import type { RenderFailed } from "#render/Failed.ts";
7
7
  import { workRpcs } from "#rpc/contract.ts";
8
8
  import { boardRuntime } from "#server/boardRuntime.ts";
9
9
  import type { HomeMissing } from "#server/files/home.ts";
@@ -1,16 +1,15 @@
1
1
  import { Context, Effect, FileSystem, Layer, Path, type PlatformError } from "effect";
2
- import { DiagnosticsSources } from "#diagnostics/source.ts";
2
+ import { DiagnosticsConfig, DiagnosticsSources } from "#diagnostics/source.ts";
3
3
  import type { BoardOptions } from "#server/board.ts";
4
4
  import { boardServices } from "#server/boardServices.ts";
5
5
  import { daemonDiagnostics } from "#server/diagnostics/daemon.ts";
6
- import { readDocument } from "#server/document/read.ts";
7
6
  import { watchChanges } from "#server/files/changes.ts";
8
7
  import { homeIn } from "#server/files/home.ts";
9
8
  import { checkBrowserAssets, mermaidRoot } from "#server/http/assets.ts";
10
9
  import { Highlighter } from "#server/render/Highlighter.ts";
11
10
  import type { HandlerOptions } from "#server/rpc/HandlerOptions.ts";
12
- import { handlers } from "#server/rpc/handlers.ts";
13
11
  import { type Snapshot, searchSnapshot } from "#server/search/snapshot.ts";
12
+ import { Workspace, WorkspaceConfig } from "#server/workspace.ts";
14
13
 
15
14
  export const boardRuntime = Effect.fn("WorkBoard.boardRuntime")(function* (options: BoardOptions, server?: HandlerOptions["server"]) {
16
15
  const home = yield* homeIn(options.root, options.home);
@@ -28,30 +27,26 @@ export const boardRuntime = Effect.fn("WorkBoard.boardRuntime")(function* (optio
28
27
  );
29
28
  const provided = yield* Effect.serviceOption(DiagnosticsSources);
30
29
  const services = yield* Layer.build(
31
- boardServices(
32
- { changes, enabled: options.responses === true, index: Effect.provideContext(index, platform), root: options.root },
33
- provided._tag === "Some" ? { _tag: "Sources", sources: provided.value } : { _tag: "Read", read: daemonDiagnostics(options.fleetPort) },
34
- ),
35
- );
36
- const context = Context.merge(platform, services);
37
- const rpcHandlers = yield* handlers({
38
- ...(options.fleetPort === undefined ? {} : { fleetPort: options.fleetPort }),
39
- changes,
40
- document: (url) =>
41
- Effect.provideContext(
42
- readDocument({
30
+ boardServices({
31
+ ...(options.fleetPort === undefined ? {} : { fleetPort: options.fleetPort }),
32
+ ...(server === undefined ? {} : { server }),
33
+ }).pipe(
34
+ Layer.provideMerge(Workspace.layer),
35
+ Layer.provide([
36
+ Layer.succeed(WorkspaceConfig, {
43
37
  changes,
44
38
  home,
45
- index: Effect.provideContext(index, context),
39
+ index: Effect.provideContext(index, platform),
46
40
  root: options.root,
47
41
  writable: options.responses === true,
48
- })(url),
49
- context,
50
- ),
51
- home,
52
- index: Effect.provideContext(index, context),
53
- ...(server === undefined ? {} : { server }),
54
- writable: options.responses === true,
55
- }).pipe(Effect.provideContext(context));
56
- return { changes, context, home, index, mermaid, rpcHandlers };
42
+ }),
43
+ Layer.succeed(
44
+ DiagnosticsConfig,
45
+ provided._tag === "Some" ? { _tag: "Sources", sources: provided.value } : { _tag: "Read", read: daemonDiagnostics(options.fleetPort) },
46
+ ),
47
+ ]),
48
+ ),
49
+ );
50
+ const context = Context.merge(platform, services);
51
+ return { changes, context, home, index, mermaid, rpcHandlers: Layer.succeedContext(services) };
57
52
  });
@@ -1,10 +1,10 @@
1
- import { type Context, Layer } from "effect";
2
- import { Diagnostics, DiagnosticsConfig } from "#diagnostics/source.ts";
1
+ import { Layer } from "effect";
2
+ import { Diagnostics } from "#diagnostics/source.ts";
3
3
  import { HandoffsService } from "#server/handoffs/Service.ts";
4
- import { ResponseConfig, type ResponseOptions, Responses } from "#server/responses/service.ts";
4
+ import { Responses } from "#server/Responses.ts";
5
+ import type { HandlerOptions } from "#server/rpc/HandlerOptions.ts";
6
+ import { handlers } from "#server/rpc/handlers.ts";
5
7
 
6
- export function boardServices(options: ResponseOptions, diagnostics: Context.Service.Shape<typeof DiagnosticsConfig>) {
7
- return Layer.mergeAll(Responses.layer, HandoffsService.layer, Diagnostics.layer).pipe(
8
- Layer.provide(Layer.merge(Layer.succeed(ResponseConfig, options), Layer.succeed(DiagnosticsConfig, diagnostics))),
9
- );
8
+ export function boardServices(options: HandlerOptions = {}) {
9
+ return Layer.unwrap(handlers(options)).pipe(Layer.provideMerge(Layer.mergeAll(Responses.layer, HandoffsService.layer, Diagnostics.layer)));
10
10
  }
@@ -2,24 +2,17 @@ import { Effect, FileSystem } from "effect";
2
2
  import { Conflict, NotFound, ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
3
3
  import type { Document } from "#browser/document/schema.ts";
4
4
  import { currentDocument } from "#server/currentDocument.ts";
5
- import type { Changes } from "#server/files/changes.ts";
6
5
  import type { MarkdownFile } from "#server/files/list.ts";
7
6
  import { markdownPath } from "#server/files/markdownPath.ts";
7
+ import { locate } from "#server/responses/locate.ts";
8
8
  import { readingActions } from "#server/responses/readingActions.ts";
9
9
  import type { Snapshot } from "#server/search/snapshot.ts";
10
+ import type { WorkspaceSettings } from "#server/workspace.ts";
10
11
  import { answerBody } from "./answerBody.ts";
11
12
  import { readingContext } from "./readingContext.ts";
12
13
  import { documentRequests } from "./requests.ts";
13
14
  import { documentTargets } from "./targets.ts";
14
15
 
15
- export interface DocumentOptions {
16
- readonly changes: Changes;
17
- readonly home: string | undefined;
18
- readonly index: Effect.Effect<Snapshot, unknown>;
19
- readonly root: string;
20
- readonly writable: boolean;
21
- }
22
-
23
16
  const selected = Effect.fn("WorkBoard.selected")(function* (
24
17
  url: string,
25
18
  snapshot: Snapshot,
@@ -45,12 +38,11 @@ const selected = Effect.fn("WorkBoard.selected")(function* (
45
38
  return { file, item };
46
39
  });
47
40
 
48
- export function readDocument({ changes, home, index, root, writable }: DocumentOptions) {
49
- return Effect.fn("WorkBoard.document")(function* (url: string) {
50
- const snapshot = yield* index;
41
+ export function readDocument({ changes, home, root, realRoot, writable }: WorkspaceSettings) {
42
+ return Effect.fn("WorkBoard.document")(function* (url: string, snapshot: Snapshot) {
51
43
  const files = yield* changes.files;
52
44
  const { file, item } = yield* selected(url, snapshot, files, home);
53
- const real = yield* markdownPath(root, changes.realRoot, file.path);
45
+ const real = yield* markdownPath(root, realRoot, file.path);
54
46
  if (!real) {
55
47
  return yield* Effect.fail(new NotFound({ message: "The document is missing." }));
56
48
  }
@@ -65,7 +57,7 @@ export function readDocument({ changes, home, index, root, writable }: DocumentO
65
57
  if (item && identity !== item) {
66
58
  return yield* Effect.fail(new NotFound({ message: "The document is missing." }));
67
59
  }
68
- const questionActions = yield* readingActions(document, source, writable, identity);
60
+ const questionActions = yield* readingActions(document, source, writable, identity, locate(root, realRoot, Effect.succeed(snapshot), fs));
69
61
  const titles = new Map(current.snapshot.entries.filter((entry) => entry.kind === "document").map((entry) => [entry.file, entry.title]));
70
62
  const reading = readingContext(document, model, file.path === home, titles);
71
63
  const targets = documentTargets(
@@ -1,6 +1,5 @@
1
1
  import { ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
2
2
  import type { DocumentRequest } from "#browser/document/schema.ts";
3
- import { responseUrl } from "#responses/url.ts";
4
3
  import { authoredQuestion, openItem } from "#server/attention/inbox.ts";
5
4
  import { requestInbox } from "#server/attention/requestInbox.ts";
6
5
  import { attentionRequests } from "#server/attention/requests.ts";
@@ -84,6 +83,6 @@ function requestOpenInSource(document: MetadataDocument, id: string): boolean {
84
83
 
85
84
  function activeSource(document: MetadataDocument, snapshot: Snapshot, item: string, id: string) {
86
85
  const managed = document.parsed.fields.attention?.find((entry) => entry.id === id)?.managed;
87
- const eligible = !managed || requestInbox(snapshot).items.some((entry) => entry.href === responseUrl(item, id));
86
+ const eligible = !managed || requestInbox(snapshot).items.some((entry) => entry.document.file === document.file && entry.request === id);
88
87
  return eligible ? requestSource(snapshot, item, id) : undefined;
89
88
  }
@@ -9,26 +9,27 @@ import { handoffFrom, handoffMarkdown, handoffPrompt, matchesHandoff } from "#se
9
9
  import { handoffSource } from "#server/handoffs/source.ts";
10
10
  import { publish } from "#server/responses/publish.ts";
11
11
  import { revisionOf } from "#server/responses/records.ts";
12
- import { ResponseConfig } from "#server/responses/service.ts";
12
+ import { Workspace } from "#server/workspace.ts";
13
13
 
14
14
  export const HandoffsService = defineService({
15
15
  id: "WorkBoard/HandoffsService",
16
16
  initialize: Effect.gen(function* () {
17
- return { fs: yield* FileSystem.FileSystem, lock: yield* Semaphore.make(1), options: yield* ResponseConfig };
17
+ const workspace = yield* Workspace;
18
+ return { fs: yield* FileSystem.FileSystem, lock: yield* Semaphore.make(1), options: yield* workspace.settings(), workspace };
18
19
  }),
19
- methods: ({ options, fs, lock }) => {
20
- const snapshot = options.index.pipe(
20
+ methods: ({ options, fs, lock, workspace }) => {
21
+ const snapshot = workspace.index().pipe(
21
22
  rpcBoundary(ServiceUnavailable, {
22
23
  message: "The workspace could not be indexed.",
23
24
  unexpected: (message) => new ServiceUnavailable({ message }),
24
25
  }),
25
26
  );
26
- const locate = handoffSource(options.root, options.changes.realRoot, snapshot, fs);
27
+ const locate = handoffSource(options.root, options.realRoot, snapshot, fs);
27
28
  const read = Effect.fn("WorkBoard.read")(function* (item: string) {
28
29
  const data = yield* snapshot;
29
30
  const unknown = [...data.unavailable];
30
31
  const records = data.documents.flatMap((document) => {
31
- const record = handoffFrom(document, options.changes.realRoot);
32
+ const record = handoffFrom(document, options.realRoot);
32
33
  if (
33
34
  (record && data.model.ids.get(record.id)?.length !== 1)
34
35
  || (document.parsed.fields.kind === "handoff" && !record)
@@ -45,7 +46,7 @@ export const HandoffsService = defineService({
45
46
  if (!record || record.file !== `handoffs/${input.id}.md` || !matchesHandoff(record, input) || revisionOf(record.context) !== input.revision) {
46
47
  return yield* Effect.fail(new Conflict({ message: "This handoff identity moved, is duplicated or records different direction." }));
47
48
  }
48
- const path = yield* markdownPath(options.root, options.changes.realRoot, record.file);
49
+ const path = yield* markdownPath(options.root, options.realRoot, record.file);
49
50
  if (!path) {
50
51
  return yield* Effect.fail(new NotFound({ message: "The prepared handoff moved or disappeared." }));
51
52
  }
@@ -55,20 +56,20 @@ export const HandoffsService = defineService({
55
56
  unexpected: (message) => new ServiceUnavailable({ message }),
56
57
  }),
57
58
  );
58
- yield* publish(options.root, options.changes.realRoot, record.id, raw, "handoffs");
59
+ yield* publish(options.root, options.realRoot, record.id, raw, "handoffs");
59
60
  return record;
60
61
  });
61
62
 
62
63
  function prepare(input: HandoffInput) {
63
64
  return lock.withPermit(
64
65
  Effect.gen(function* () {
65
- if (!options.enabled) {
66
+ if (!options.writable) {
66
67
  return yield* Effect.fail(new Forbidden({ message: "Restart with --responses to enable local response/handoff writes." }));
67
68
  }
68
69
  const data = yield* snapshot;
69
70
  const existing = data.model.ids.get(input.id);
70
71
  if (existing?.length) {
71
- const record = existing.length === 1 ? handoffFrom(existing[0], options.changes.realRoot) : undefined;
72
+ const record = existing.length === 1 ? handoffFrom(existing[0], options.realRoot) : undefined;
72
73
  return yield* reconcile(record, input);
73
74
  }
74
75
  const current = yield* locate(input.item);
@@ -91,21 +92,17 @@ export const HandoffsService = defineService({
91
92
  state: "requested",
92
93
  },
93
94
  id: input.id,
94
- prompt: handoffPrompt(join(options.changes.realRoot, file)),
95
+ prompt: handoffPrompt(join(options.realRoot, file)),
95
96
  };
96
- yield* publish(
97
- options.root,
98
- options.changes.realRoot,
99
- record.id,
100
- handoffMarkdown(record.id, record.handoff, record.context),
101
- "handoffs",
102
- ).pipe(Effect.ensuring(options.changes.refresh ?? Effect.void));
97
+ yield* publish(options.root, options.realRoot, record.id, handoffMarkdown(record.id, record.handoff, record.context), "handoffs").pipe(
98
+ Effect.ensuring(options.changes.refresh ?? Effect.void),
99
+ );
103
100
  return record;
104
101
  }),
105
102
  );
106
103
  }
107
104
  return { locate, prepare, read };
108
105
  },
109
- requires: [ResponseConfig, FileSystem.FileSystem, Path.Path],
106
+ requires: [Workspace, FileSystem.FileSystem, Path.Path],
110
107
  });
111
108
  export type HandoffsService = typeof HandoffsService.Identifier;
@@ -35,7 +35,6 @@ export function mermaidFile(root: string) {
35
35
 
36
36
  export const MERMAID_ROUTE: HttpRouter.PathInput = `${MERMAID}*`;
37
37
 
38
- export const NATIVE_ROUTE: HttpRouter.PathInput = "/_board/native.js";
39
38
  function missingBundle(file: string) {
40
39
  return `Work Board browser bundle assets/${file} is missing. Run pnpm --filter @shivaedev/work-board build:browser from the repository root to build it.`;
41
40
  }
@@ -57,16 +56,11 @@ const checkAssets = Effect.fn("WorkBoard.checkAssets")(function* (files: readonl
57
56
  }
58
57
  });
59
58
 
60
- export function checkNativeAsset() {
61
- return checkAssets(["native.js"]);
62
- }
63
59
  export function checkBrowserAssets() {
64
- return checkAssets(["native.js", "react.js", "react.css"]);
60
+ return checkAssets(["react.js", "react.css"]);
65
61
  }
66
- export const nativeAsset = bundledAsset("native.js", "text/javascript");
67
62
 
68
63
  const BUNDLED_ASSETS: ReadonlyArray<readonly [HttpRouter.PathInput, ReturnType<typeof bundledAsset>]> = [
69
- [NATIVE_ROUTE, nativeAsset],
70
64
  ["/_board/react.js", bundledAsset("react.js", "text/javascript")],
71
65
  ["/_board/react.css", bundledAsset("react.css", "text/css")],
72
66
  ];
@@ -2,13 +2,13 @@ import { Effect, type FileSystem, type Path, type Scope } from "effect";
2
2
  import { HttpRouter, type HttpServerRequest, HttpServerResponse } from "effect/http";
3
3
  import { loopbackOnly } from "@shivaedev/platform/rpc-server/local.ts";
4
4
  import type { Diagnostics } from "#diagnostics/source.ts";
5
+ import { responseUrl } from "#responses/url.ts";
5
6
  import type { BoardOptions } from "#server/board.ts";
6
7
  import type { boardRuntime } from "#server/boardRuntime.ts";
7
8
  import type { HandoffsService } from "#server/handoffs/Service.ts";
8
- import { identityUrl } from "#server/metadata/links.ts";
9
9
  import { shell } from "#server/page/shell.ts";
10
+ import { Responses } from "#server/Responses.ts";
10
11
  import type { Highlighter } from "#server/render/Highlighter.ts";
11
- import { Responses } from "#server/responses/service.ts";
12
12
  import { ASSET_HANDLERS, MERMAID_ROUTE, mermaidFile } from "./assets.ts";
13
13
  import { attachment } from "./attachment.ts";
14
14
  import { identity } from "./identity.ts";
@@ -47,7 +47,7 @@ export function boardRoutes(options: BoardOptions, runtime: Effect.Success<Retur
47
47
  const params = new URL(request.url, "http://127.0.0.1").searchParams;
48
48
  const service = yield* Responses;
49
49
  const preview = yield* service.locate(params.get("item") ?? "", params.get("request") ?? "");
50
- return HttpServerResponse.redirect(`${identityUrl(preview.item)}#answer-card-${encodeURIComponent(preview.request)}`);
50
+ return HttpServerResponse.redirect(responseUrl(preview.item, preview.request));
51
51
  }).pipe(Effect.catch(() => Effect.succeed(respond("Response unavailable", "text/html", 409)))),
52
52
  );
53
53
  for (const view of ["system", "start", "handoff", "result", "library", "goals", "work"] as const) {
@@ -5,13 +5,14 @@ import {
5
5
  RecordedResponse as RecordedResponseSchema,
6
6
  } from "#browser/responses/schema.ts";
7
7
  import { currentResponses } from "#responses/current.ts";
8
+ import { Responses } from "#server/Responses.ts";
8
9
  import { makeResponse } from "#server/responses/make.ts";
9
10
  import { publish } from "#server/responses/publish.ts";
10
11
  import { readResponses } from "#server/responses/read.ts";
11
12
  import { questionFrom, questionId, questionMarkdown, responseMarkdown, revisionOf } from "#server/responses/records.ts";
12
- import { type ResponseOptions as InternalResponseOptions, ResponseConfig, Responses } from "#server/responses/service.ts";
13
+ import { type WorkspaceOptions as InternalWorkspaceOptions, Workspace, WorkspaceConfig } from "#server/workspace.ts";
13
14
 
14
- export type ResponseOptions = InternalResponseOptions;
15
+ export type WorkspaceOptions = InternalWorkspaceOptions;
15
16
  export type DraftInput = typeof DraftInputSchema.Type;
16
17
  export type Question = typeof QuestionSchema.Type;
17
18
  export type Reading = typeof ReadingSchema.Type;
@@ -28,9 +29,10 @@ export const responses = {
28
29
  questionMarkdown,
29
30
  Reading: ReadingSchema,
30
31
  RecordedResponse: RecordedResponseSchema,
31
- ResponseConfig,
32
32
  Responses,
33
33
  readResponses,
34
34
  responseMarkdown,
35
35
  revisionOf,
36
+ Workspace,
37
+ WorkspaceConfig,
36
38
  } as const;
@@ -2,6 +2,7 @@ import { Clock, Effect } from "effect";
2
2
  import { cardStatus } from "#browser/views/now.ts";
3
3
  import type { InboxProjection } from "#browser/views/schema.ts";
4
4
  import { fileUrl } from "#files/url.ts";
5
+ import { responseUrl } from "#responses/url.ts";
5
6
  import { identityUrl } from "#server/metadata/links.ts";
6
7
  import type { BoardClient } from "./client.ts";
7
8
 
@@ -11,12 +12,11 @@ export const needsYou = Effect.fn("BoardMcp.needsYou")(function* (client: BoardC
11
12
  const you = yield* Effect.forEach(inbox.you, (row) =>
12
13
  Effect.gen(function* () {
13
14
  const document = yield* client("work-board.document", { url: fileUrl(row.file) });
14
- const request = new URL(row.href, "http://localhost").searchParams.get("request");
15
- const decision = document.requests.find((entry) => entry.id === request);
15
+ const decision = document.requests.find((entry) => responseUrl(document.metadata.id ?? "", entry.id) === row.href);
16
16
  return {
17
17
  ...row,
18
18
  ageMs: row.modified === undefined ? null : Math.max(0, now - row.modified),
19
- options: (request ? decision?.options : document.metadata.options) ?? [],
19
+ options: (decision ? decision.options : document.metadata.options) ?? [],
20
20
  prompts: decision?.prompts ?? [],
21
21
  promptUnavailable: decision?.promptUnavailable,
22
22
  recommended: decision?.recommended ?? document.metadata.recommended,
@@ -1,2 +1,2 @@
1
1
  export const NAME = "work-board";
2
- export const VERSION = "0.15.0";
2
+ export const VERSION = "0.17.0";
@@ -2,7 +2,7 @@ import { Effect } from "effect";
2
2
  import { createHighlighter } from "shiki";
3
3
  import { defineService } from "@shivaedev/effect-service/define-service.ts";
4
4
  import { codeBlocks, THEMES } from "#render/code.ts";
5
- import { RenderFailed } from "#render/failed.ts";
5
+ import { RenderFailed } from "#render/Failed.ts";
6
6
 
7
7
  export const Highlighter = defineService({
8
8
  id: "WorkBoard/Highlighter",
@@ -7,8 +7,8 @@ import type { PluggableList } from "unified";
7
7
  import type { documentHeadings } from "#render/documentHeadings.ts";
8
8
  import { documentLinks } from "#render/documentLinks.ts";
9
9
  import { documentPlugins } from "#render/documentPlugins.ts";
10
- import { RenderFailed } from "#render/failed.ts";
11
- import { footnoteIds } from "#render/footnotes.ts";
10
+ import { RenderFailed } from "#render/Failed.ts";
11
+ import { footnoteIds } from "#render/footnoteIds.ts";
12
12
  import { Highlighter } from "./Highlighter.ts";
13
13
 
14
14
  export interface RenderOptions {
@@ -3,7 +3,7 @@ import { BadRequest } from "@shivaedev/platform/errors/taxonomy.ts";
3
3
  import { responseUrl } from "#responses/url.ts";
4
4
  import { authoredQuestion } from "#server/attention/inbox.ts";
5
5
  import type { MetadataDocument } from "#server/metadata/model.ts";
6
- import { Responses } from "./service.ts";
6
+ import type { locate } from "./locate.ts";
7
7
  import { questionTemplate } from "./template.ts";
8
8
 
9
9
  export const readingActions = Effect.fn("WorkBoard.readingActions")(function* (
@@ -11,16 +11,16 @@ export const readingActions = Effect.fn("WorkBoard.readingActions")(function* (
11
11
  source: string,
12
12
  enabled: boolean,
13
13
  identity: string | undefined,
14
+ locateQuestion: ReturnType<typeof locate>,
14
15
  ) {
15
16
  if (!(enabled && identity)) {
16
17
  return undefined;
17
18
  }
18
- const service = yield* Responses;
19
19
  const item = identity;
20
20
  const requests = authoredQuestion(document) ? ["answer"] : (document.parsed.fields.attention ?? []).map((request) => request.id);
21
21
  const actions = new Map<string, string>();
22
22
  for (const request of requests) {
23
- const question = yield* service.locate(item, request).pipe(Effect.option);
23
+ const question = yield* locateQuestion(item, request).pipe(Effect.option);
24
24
  if (question._tag === "Some" && question.value.context === source) {
25
25
  const template = yield* Effect.try({
26
26
  catch: (cause) => new BadRequest({ message: cause instanceof Error ? cause.message : "Review the question template and retry." }),
@@ -2,21 +2,21 @@ import { Clock, Effect } from "effect";
2
2
  import { Conflict, Forbidden, PreconditionFailed, type ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
3
3
  import type { Question } from "#browser/responses/schema.ts";
4
4
  import type { Snapshot } from "#server/search/snapshot.ts";
5
+ import type { WorkspaceSettings } from "#server/workspace.ts";
5
6
  import type { locate as locateQuestion } from "./locate.ts";
6
7
  import { publish } from "./publish.ts";
7
8
  import type { readResponses } from "./read.ts";
8
9
  import { questionMarkdown } from "./records.ts";
9
- import type { ResponseOptions } from "./service.ts";
10
10
 
11
11
  const HOURS_48 = 48 * 60 * 60 * 1000;
12
12
  export function registerQuestion(
13
- options: ResponseOptions,
13
+ options: WorkspaceSettings,
14
14
  snapshot: Effect.Effect<Snapshot, ServiceUnavailable>,
15
15
  locate: ReturnType<typeof locateQuestion>,
16
16
  read: ReturnType<typeof readResponses>,
17
17
  ) {
18
18
  return Effect.fn("WorkBoard.registerQuestion")(function* (input: { readonly item: string; readonly request: string; readonly revision: string }) {
19
- if (!options.enabled) {
19
+ if (!options.writable) {
20
20
  return yield* Effect.fail(new Forbidden({ message: "Start with --responses to explicitly enable local question/response writes." }));
21
21
  }
22
22
  const preview = yield* locate(input.item, input.request);
@@ -32,7 +32,7 @@ export function registerQuestion(
32
32
  );
33
33
  }
34
34
  const saved = (yield* read(preview.id)).question;
35
- yield* publish(options.root, options.changes.realRoot, saved.id, questionMarkdown(saved));
35
+ yield* publish(options.root, options.realRoot, saved.id, questionMarkdown(saved));
36
36
  return saved;
37
37
  }
38
38
  const now = yield* Clock.currentTimeMillis;
@@ -49,7 +49,7 @@ export function registerQuestion(
49
49
  source: preview.source,
50
50
  },
51
51
  };
52
- yield* publish(options.root, options.changes.realRoot, question.id, questionMarkdown(question)).pipe(
52
+ yield* publish(options.root, options.realRoot, question.id, questionMarkdown(question)).pipe(
53
53
  Effect.ensuring(options.changes.refresh ?? Effect.void),
54
54
  );
55
55
  return question;
@@ -3,15 +3,15 @@ import { Conflict } from "@shivaedev/platform/errors/taxonomy.ts";
3
3
  import type { DraftInput, RecordedResponse } from "#browser/responses/schema.ts";
4
4
  import type { ResponseRejection } from "#rpc/rejections.ts";
5
5
  import type { Snapshot } from "#server/search/snapshot.ts";
6
+ import type { WorkspaceSettings } from "#server/workspace.ts";
6
7
  import { publish } from "./publish.ts";
7
8
  import { matchesResponse, responseMarkdown } from "./records.ts";
8
- import type { ResponseOptions } from "./service.ts";
9
9
 
10
10
  export const retryResponse = Effect.fn("WorkBoard.retryResponse")(function* (
11
11
  existing: RecordedResponse,
12
12
  input: DraftInput,
13
13
  snapshot: Effect.Effect<Snapshot, ResponseRejection>,
14
- options: ResponseOptions,
14
+ options: WorkspaceSettings,
15
15
  ) {
16
16
  if (!matchesResponse(existing, input)) {
17
17
  return yield* Effect.fail(new Conflict({ message: "The response identity already records different content." }));
@@ -19,5 +19,5 @@ export const retryResponse = Effect.fn("WorkBoard.retryResponse")(function* (
19
19
  if ((yield* snapshot).model.ids.get(input.id)?.[0]?.file !== `responses/${input.id}.md`) {
20
20
  return yield* Effect.fail(new Conflict({ message: "The saved response moved. Read its existing record instead of retrying a write." }));
21
21
  }
22
- yield* publish(options.root, options.changes.realRoot, existing.id, responseMarkdown(existing));
22
+ yield* publish(options.root, options.realRoot, existing.id, responseMarkdown(existing));
23
23
  });
@@ -1,16 +1,8 @@
1
1
  import type { Effect } from "effect";
2
2
  import type { ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
3
- import type { Document } from "#browser/document/schema.ts";
4
3
  import type { Server } from "#rpc/Server.ts";
5
- import type { Changes } from "#server/files/changes.ts";
6
- import type { Snapshot } from "#server/search/snapshot.ts";
7
4
 
8
5
  export interface HandlerOptions {
9
- readonly changes: Changes;
10
- readonly document?: (url: string) => Effect.Effect<Document, unknown>;
11
6
  readonly fleetPort?: number | null;
12
- readonly home: string | undefined;
13
- readonly index: Effect.Effect<Snapshot, unknown>;
14
7
  readonly server?: Effect.Effect<typeof Server.Type, ServiceUnavailable>;
15
- readonly writable?: boolean;
16
8
  }