@devaccel-labs/app-comments-toolbar 1.1.0 → 1.3.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 (214) hide show
  1. package/README.md +148 -30
  2. package/dist/react.js +417 -64
  3. package/dist/server.js +342 -14
  4. package/dist/types/capture/api.d.ts +30 -0
  5. package/dist/types/capture/editor.d.ts +10 -0
  6. package/dist/types/capture/ignore.d.ts +19 -0
  7. package/dist/types/capture/index.d.ts +1 -0
  8. package/dist/types/capture/picker.d.ts +9 -0
  9. package/dist/types/capture/picture.d.ts +29 -0
  10. package/dist/types/capture/surface.d.ts +39 -0
  11. package/dist/types/core/index.d.ts +2 -2
  12. package/dist/types/core/resolve.d.ts +2 -1
  13. package/dist/types/core/types.d.ts +11 -0
  14. package/dist/types/debug-context.d.ts +7 -0
  15. package/dist/types/dictation.d.ts +9 -0
  16. package/dist/types/embed/controller.d.ts +61 -0
  17. package/dist/types/embed/index.d.ts +1 -0
  18. package/dist/types/embed/options.d.ts +35 -0
  19. package/dist/types/help/index.d.ts +1 -0
  20. package/dist/types/help/panel-index.d.ts +1 -0
  21. package/dist/types/help/panel-styles.d.ts +1 -0
  22. package/dist/types/help/panel.d.ts +4 -0
  23. package/dist/types/help/pill-styles.d.ts +11 -0
  24. package/dist/types/help/pill.d.ts +44 -0
  25. package/dist/types/help/react.d.ts +12 -0
  26. package/dist/types/help/requests.d.ts +2 -0
  27. package/dist/types/help/types.d.ts +119 -0
  28. package/dist/types/help/wire.d.ts +61 -0
  29. package/dist/types/native-wire.d.ts +63 -0
  30. package/dist/types/native.d.ts +43 -0
  31. package/dist/types/react.d.ts +7 -0
  32. package/dist/types/replay.d.ts +6 -0
  33. package/dist/types/runtime/api.d.ts +250 -0
  34. package/dist/types/runtime/attachment-model.d.ts +16 -0
  35. package/dist/types/runtime/attachments.d.ts +43 -7
  36. package/dist/types/runtime/boot.d.ts +39 -0
  37. package/dist/types/runtime/browser-label.d.ts +5 -0
  38. package/dist/types/runtime/build-id.d.ts +1 -0
  39. package/dist/types/runtime/capture-ui.d.ts +45 -21
  40. package/dist/types/runtime/clarity-model.d.ts +260 -0
  41. package/dist/types/runtime/clarity-ui.d.ts +62 -0
  42. package/dist/types/runtime/client.d.ts +6 -197
  43. package/dist/types/runtime/composer-size.d.ts +30 -0
  44. package/dist/types/runtime/dashboard.d.ts +17 -2
  45. package/dist/types/runtime/debug-context-core.d.ts +88 -0
  46. package/dist/types/runtime/debug-context-host.d.ts +39 -0
  47. package/dist/types/runtime/debug-context-model.d.ts +352 -0
  48. package/dist/types/runtime/debug-context-redact.d.ts +48 -0
  49. package/dist/types/runtime/debug-context.d.ts +36 -0
  50. package/dist/types/runtime/details-model.d.ts +7 -0
  51. package/dist/types/runtime/details.d.ts +7 -5
  52. package/dist/types/runtime/dictation-engine.d.ts +98 -0
  53. package/dist/types/runtime/dictation-model.d.ts +28 -0
  54. package/dist/types/runtime/dictation-ui.d.ts +56 -0
  55. package/dist/types/runtime/dictation.d.ts +66 -0
  56. package/dist/types/runtime/draft-dock.d.ts +32 -0
  57. package/dist/types/runtime/draft-model.d.ts +104 -0
  58. package/dist/types/runtime/draft-store.d.ts +15 -0
  59. package/dist/types/runtime/edits.d.ts +1 -1
  60. package/dist/types/runtime/font.d.ts +16 -0
  61. package/dist/types/runtime/format.d.ts +30 -1
  62. package/dist/types/runtime/host-watch.d.ts +20 -0
  63. package/dist/types/runtime/icons.d.ts +16 -2
  64. package/dist/types/runtime/index.d.ts +2 -0
  65. package/dist/types/runtime/ink.d.ts +8 -0
  66. package/dist/types/runtime/kind-model.d.ts +14 -0
  67. package/dist/types/runtime/live-model.d.ts +25 -0
  68. package/dist/types/runtime/loader.d.ts +5 -3
  69. package/dist/types/runtime/markup-editor.d.ts +54 -0
  70. package/dist/types/runtime/markup-model.d.ts +126 -0
  71. package/dist/types/runtime/markup.d.ts +69 -0
  72. package/dist/types/runtime/mention-parse.d.ts +18 -0
  73. package/dist/types/runtime/mentions.d.ts +4 -4
  74. package/dist/types/runtime/page-background.d.ts +9 -0
  75. package/dist/types/runtime/page-image.d.ts +64 -11
  76. package/dist/types/runtime/placement.d.ts +6 -3
  77. package/dist/types/runtime/record.d.ts +18 -2
  78. package/dist/types/runtime/related.d.ts +1 -1
  79. package/dist/types/runtime/replay-model.d.ts +80 -0
  80. package/dist/types/runtime/replay.d.ts +5 -38
  81. package/dist/types/runtime/screen.d.ts +47 -0
  82. package/dist/types/runtime/screenshot.d.ts +60 -8
  83. package/dist/types/runtime/session-source.d.ts +45 -0
  84. package/dist/types/runtime/session.d.ts +20 -18
  85. package/dist/types/runtime/shield.d.ts +9 -0
  86. package/dist/types/runtime/shot.d.ts +45 -0
  87. package/dist/types/runtime/sketch.d.ts +26 -0
  88. package/dist/types/runtime/styles.d.ts +1 -1
  89. package/dist/types/runtime/sync.d.ts +1 -0
  90. package/dist/types/runtime/thread-model.d.ts +18 -0
  91. package/dist/types/runtime/ticket-form-model.d.ts +57 -0
  92. package/dist/types/runtime/ticket-form.d.ts +9 -0
  93. package/dist/types/runtime/toolbar.d.ts +37 -1
  94. package/dist/types/runtime/ui-scale.d.ts +17 -0
  95. package/dist/types/runtime/update.d.ts +32 -8
  96. package/dist/types/runtime/use-debug-context.d.ts +27 -0
  97. package/dist/types/server/index.d.ts +22 -0
  98. package/dist/types/server/support.d.ts +20 -0
  99. package/dist/types/server.d.ts +2 -0
  100. package/dist/types/vite/plugin.d.ts +15 -0
  101. package/dist/vite.js +121 -41
  102. package/package.json +24 -11
  103. package/src/capture/api.ts +128 -0
  104. package/src/capture/editor.tsx +178 -0
  105. package/src/capture/ignore.ts +38 -0
  106. package/src/capture/index.ts +27 -0
  107. package/src/capture/picker.tsx +171 -0
  108. package/src/capture/picture.ts +90 -0
  109. package/src/capture/surface.tsx +185 -0
  110. package/src/compiler/annotate.ts +202 -0
  111. package/src/compiler/babel.d.ts +11 -0
  112. package/src/compiler/enabled.ts +55 -0
  113. package/src/compiler/index.ts +2 -0
  114. package/src/core/index.ts +8 -0
  115. package/src/core/resolve.ts +307 -0
  116. package/src/core/types.ts +58 -0
  117. package/src/debug-context.ts +47 -0
  118. package/src/dictation.ts +17 -0
  119. package/src/embed/controller.ts +215 -0
  120. package/src/embed/index.ts +68 -0
  121. package/src/embed/options.ts +100 -0
  122. package/src/help/index.ts +38 -0
  123. package/src/help/panel-index.ts +17 -0
  124. package/src/help/panel-styles.ts +123 -0
  125. package/src/help/panel.ts +840 -0
  126. package/src/help/pill-styles.ts +75 -0
  127. package/src/help/pill.ts +338 -0
  128. package/src/help/react.tsx +143 -0
  129. package/src/help/requests.ts +111 -0
  130. package/src/help/types.ts +124 -0
  131. package/src/help/wire.ts +55 -0
  132. package/src/native-wire.ts +124 -0
  133. package/src/native.ts +282 -0
  134. package/src/react.ts +27 -0
  135. package/src/replay.ts +13 -0
  136. package/src/runtime/api.ts +531 -0
  137. package/src/runtime/attachment-model.ts +42 -0
  138. package/src/runtime/attachments.tsx +1260 -0
  139. package/src/runtime/boot.ts +95 -0
  140. package/src/runtime/browser-label.ts +31 -0
  141. package/src/runtime/build-id.ts +10 -0
  142. package/src/runtime/capture-ui.tsx +306 -0
  143. package/src/runtime/capture.ts +508 -0
  144. package/src/runtime/clarity-model.ts +634 -0
  145. package/src/runtime/clarity-ui.tsx +454 -0
  146. package/src/runtime/client.ts +151 -0
  147. package/src/runtime/composer-size.tsx +146 -0
  148. package/src/runtime/dashboard.tsx +1130 -0
  149. package/src/runtime/debug-context-core.ts +681 -0
  150. package/src/runtime/debug-context-host.ts +56 -0
  151. package/src/runtime/debug-context-model.ts +1319 -0
  152. package/src/runtime/debug-context-redact.ts +259 -0
  153. package/src/runtime/debug-context.ts +613 -0
  154. package/src/runtime/details-model.ts +30 -0
  155. package/src/runtime/details.tsx +395 -0
  156. package/src/runtime/dictation-engine.ts +616 -0
  157. package/src/runtime/dictation-model.ts +79 -0
  158. package/src/runtime/dictation-ui.tsx +363 -0
  159. package/src/runtime/dictation.ts +271 -0
  160. package/src/runtime/dock.ts +108 -0
  161. package/src/runtime/draft-dock.tsx +124 -0
  162. package/src/runtime/draft-model.ts +251 -0
  163. package/src/runtime/draft-store.ts +149 -0
  164. package/src/runtime/edits.tsx +165 -0
  165. package/src/runtime/font.ts +42 -0
  166. package/src/runtime/format.ts +129 -0
  167. package/src/runtime/freshness.ts +23 -0
  168. package/src/runtime/handoff.ts +76 -0
  169. package/src/runtime/host-watch.ts +64 -0
  170. package/src/runtime/icons.tsx +295 -0
  171. package/src/runtime/idle.ts +20 -0
  172. package/src/runtime/index.ts +11 -0
  173. package/src/runtime/ink.ts +6 -0
  174. package/src/runtime/kind-model.ts +20 -0
  175. package/src/runtime/live-model.ts +32 -0
  176. package/src/runtime/loader.tsx +87 -0
  177. package/src/runtime/markup-editor.tsx +1333 -0
  178. package/src/runtime/markup-model.ts +485 -0
  179. package/src/runtime/markup.ts +382 -0
  180. package/src/runtime/members.ts +58 -0
  181. package/src/runtime/mention-parse.ts +56 -0
  182. package/src/runtime/mentions.tsx +205 -0
  183. package/src/runtime/page-background.ts +51 -0
  184. package/src/runtime/page-image.ts +1592 -0
  185. package/src/runtime/placement.ts +336 -0
  186. package/src/runtime/record.ts +402 -0
  187. package/src/runtime/redact.ts +30 -0
  188. package/src/runtime/related.ts +34 -0
  189. package/src/runtime/render-page.ts +2 -0
  190. package/src/runtime/replay-model.ts +232 -0
  191. package/src/runtime/replay.ts +173 -0
  192. package/src/runtime/runtime-version.ts +7 -0
  193. package/src/runtime/runtime.tsx +51 -0
  194. package/src/runtime/screen.ts +182 -0
  195. package/src/runtime/screenshot.ts +260 -0
  196. package/src/runtime/session-source.ts +112 -0
  197. package/src/runtime/session.ts +108 -0
  198. package/src/runtime/shield.ts +34 -0
  199. package/src/runtime/shot.tsx +422 -0
  200. package/src/runtime/sketch.ts +139 -0
  201. package/src/runtime/styles.ts +1541 -0
  202. package/src/runtime/sync.ts +300 -0
  203. package/src/runtime/thread-model.ts +71 -0
  204. package/src/runtime/ticket-form-model.ts +128 -0
  205. package/src/runtime/ticket-form.tsx +342 -0
  206. package/src/runtime/toolbar.tsx +4427 -0
  207. package/src/runtime/ui-scale.ts +75 -0
  208. package/src/runtime/update.ts +98 -0
  209. package/src/runtime/use-debug-context.ts +81 -0
  210. package/src/server/index.ts +133 -0
  211. package/src/server/support.ts +499 -0
  212. package/src/server.ts +4 -0
  213. package/src/vite/plugin.ts +102 -0
  214. package/src/vite.ts +1 -0
@@ -0,0 +1,260 @@
1
+ /**
2
+ * The clarity check as the composer runs it (docs/adr/0002-clarity-check.md): what Send does before a new
3
+ * comment is posted, what the person is shown when a reader would still have to ask something, and what
4
+ * the comment says when it goes. Every decision is here; `./clarity-ui.tsx` draws it. DOM-free.
5
+ *
6
+ * The check is never the reason a comment is lost or late: no mode, mode `off`, a failed or slow request,
7
+ * or an answer this file cannot read all post the comment exactly as it would have gone without the check.
8
+ *
9
+ * The wire types and bounds mirror `packages/contract/src/clarity.ts` and `limits.ts`; the toolbar bundle
10
+ * does not import the contract package. `tests/clarity-model.spec.ts` holds the two in step.
11
+ *
12
+ * Native clients run the same decisions: `../native.ts` exports this file to `@devaccel-labs/app-comments-native`.
13
+ */
14
+ import type { TicketDraft } from "./ticket-form-model";
15
+ export type ClarityMode = "off" | "advise" | "require";
16
+ export type ClarityGap = "expected" | "actual" | "steps" | "which" | "scope" | "done" | "other";
17
+ /** Who would have to ask: the person making the change, or whoever checks it afterwards. */
18
+ export type ClarityAudience = "developer" | "tester";
19
+ export interface ClarityQuestion {
20
+ readonly id: string;
21
+ readonly gap: ClarityGap;
22
+ /** Absent on a report from before questions said who asks: read it as the developer's. */
23
+ readonly audience?: ClarityAudience;
24
+ /** The question, in one sentence. */
25
+ readonly ask: string;
26
+ /** Why a reader needs it, in a few words. */
27
+ readonly why: string;
28
+ }
29
+ export interface ClarityAnswer {
30
+ readonly questionId: string;
31
+ readonly ask: string;
32
+ readonly answer: string;
33
+ }
34
+ export interface ClarityAnswerCheck {
35
+ readonly questionId: string;
36
+ readonly status: "answered" | "partial" | "unrelated";
37
+ readonly note?: string;
38
+ }
39
+ /** The part of the service's report the composer reads. */
40
+ export interface ClarityReport {
41
+ readonly verdict: "clear" | "needs_detail" | "unchecked";
42
+ readonly understood: string;
43
+ /** What the check took as given instead of asking. Absent when it took nothing, or predates the field. */
44
+ readonly assumed?: readonly string[];
45
+ readonly questions: readonly ClarityQuestion[];
46
+ readonly answers: readonly ClarityAnswerCheck[];
47
+ }
48
+ /** The body of `POST /v1/clarity`. */
49
+ export interface ClarityDraft {
50
+ readonly body: string;
51
+ readonly url: string;
52
+ readonly route: string;
53
+ readonly target: string;
54
+ readonly attachmentIds: readonly string[];
55
+ /** As a new comment is posted with them: the role, custom field values by id, a tester's ticket answers. */
56
+ readonly role?: string | null;
57
+ readonly fields?: Readonly<Record<string, string>>;
58
+ readonly ticket?: TicketDraft;
59
+ readonly asked?: readonly ClarityQuestion[];
60
+ readonly answers?: readonly ClarityAnswer[];
61
+ }
62
+ export declare const CLARITY_MAX_QUESTIONS = 3;
63
+ export declare const CLARITY_MAX_ASSUMED = 3;
64
+ export declare const CLARITY_MAX_ROUNDS = 2;
65
+ export declare const CLARITY_ANSWER_MAX_LENGTH = 2000;
66
+ export declare const COMMENT_MESSAGE_MAX_LENGTH = 4000;
67
+ /** How long Send waits for the check. The service gives its model eight seconds; past nine the comment goes. */
68
+ export declare const CLARITY_WAIT_MS = 9000;
69
+ /** Every word the check puts on screen or into a comment. About the reader, never about the writer. */
70
+ export declare const CLARITY_WORDS: {
71
+ /** The panel's name, for a screen reader. */
72
+ readonly region: "More detail";
73
+ readonly understood: "What a reader takes from this:";
74
+ /** Over what the check took as given, and the line under it. The console's inbox uses the same words. */
75
+ readonly assumed: "Taken as given";
76
+ readonly assumedNote: "Not asked about. Shown so a wrong one can be put right.";
77
+ /** The lead when every question is a developer's; `whoWouldAsk` names the reader otherwise. */
78
+ readonly lead: "A developer would still have to ask:";
79
+ readonly placeholder: "Add your answer…";
80
+ readonly hint: "Add a little more so nobody has to guess.";
81
+ /** After the second look: nothing holds the comment any more. */
82
+ readonly free: "You can send now. Add more if it helps.";
83
+ /** `require`, first look, Send pressed with nothing added. */
84
+ readonly stay: "Answer a question or add to the comment, then send.";
85
+ readonly sendAsIs: "Send as is";
86
+ readonly working: "One moment…";
87
+ /** Beside a question whose answer the service said falls short without saying how. */
88
+ readonly more: "A reader would still need a little more here.";
89
+ readonly full: "The comment has no room for more.";
90
+ /** The line the answers sit under in the posted comment. */
91
+ readonly detail: "More detail:";
92
+ readonly question: "Q. ";
93
+ readonly answer: "A. ";
94
+ };
95
+ /**
96
+ * The environment's mode as `GET /v1/threads` carries it in `clarity`. Null when the field is missing or is
97
+ * not a mode: a service that predates the check, which is never asked.
98
+ */
99
+ export declare function readClarityMode(value: unknown): ClarityMode | null;
100
+ /** Whether a new comment is checked before it is posted. */
101
+ export declare function clarityChecks(mode: ClarityMode | null): mode is "advise" | "require";
102
+ /**
103
+ * What `POST /v1/clarity` answered, or null when it is not an answer this build can act on. Null posts the
104
+ * comment: an unreadable answer is the check failing, and the check fails open.
105
+ */
106
+ export declare function readClarityResponse(payload: unknown): {
107
+ readonly mode: ClarityMode;
108
+ readonly report: ClarityReport;
109
+ } | null;
110
+ /** How one check ended: with the service's answer, or without one. */
111
+ export type ClarityOutcome = {
112
+ readonly kind: "answered";
113
+ readonly mode: ClarityMode;
114
+ readonly report: ClarityReport;
115
+ } | {
116
+ readonly kind: "failed";
117
+ };
118
+ /**
119
+ * Runs one check and never throws. A rejection (a 404 from a service without the route, any other status,
120
+ * the network), an answer that cannot be read, or no answer within `waitMs` are all `failed`. The wait is
121
+ * raced, not only aborted, so a request that ignores its signal still lets Send go on time.
122
+ */
123
+ export declare function askClarity(run: (signal: AbortSignal) => Promise<unknown>, waitMs?: number): Promise<ClarityOutcome>;
124
+ /** The draft at the moment Send was pressed. */
125
+ export interface ClarityFacts {
126
+ readonly body: string;
127
+ /** The page the comment is filed against. */
128
+ readonly url: string;
129
+ readonly route: string;
130
+ /** What it is attached to, in words. */
131
+ readonly target: string;
132
+ /** Its uploaded files, by attachment id. */
133
+ readonly attachmentIds: readonly string[];
134
+ /**
135
+ * What it will be posted with besides its words (the role, custom fields and a tester's ticket answers),
136
+ * exactly as the post sends them, so the check does not ask what one of them already says.
137
+ */
138
+ readonly posted?: Pick<ClarityDraft, "role" | "fields" | "ticket">;
139
+ /**
140
+ * Its files by their key in the draft. A key outlives a reload (an attachment id does not: a resumed
141
+ * draft uploads its files again), so this is what says whether the files changed.
142
+ */
143
+ readonly fileKeys: readonly string[];
144
+ }
145
+ /**
146
+ * A draft the check is holding: a reader would still have to ask something. Kept with the draft in storage
147
+ * (`./draft-model.ts`), so a reload or a minimise brings the questions and the typed answers back.
148
+ */
149
+ export interface ClarityHold {
150
+ readonly v: 1;
151
+ /** Looks the service has taken at this draft that left a question open. */
152
+ readonly round: 1 | 2;
153
+ /** The environment's mode when it last looked. */
154
+ readonly mode: "advise" | "require";
155
+ readonly understood: string;
156
+ /** What the last look took as given. Absent when it took nothing. */
157
+ readonly assumed?: readonly string[];
158
+ /** Every question asked of this draft, most important first. */
159
+ readonly questions: readonly ClarityQuestion[];
160
+ /** The ones still open, by id. */
161
+ readonly open: readonly string[];
162
+ /** What was typed under each question, by id. An answer that closed its question is still here. */
163
+ readonly answers: Readonly<Record<string, string>>;
164
+ /** What the last look said is still needed, by question id. */
165
+ readonly notes: Readonly<Record<string, string>>;
166
+ /** The draft as it was last looked at (`draftMark`). */
167
+ readonly checked: string;
168
+ }
169
+ /** The draft's own content: its words and its files. Answers are not part of it. */
170
+ export declare function draftMark(facts: Pick<ClarityFacts, "body" | "fileKeys">): string;
171
+ /** The draft and its answers together: what a look that let the comment through was shown. */
172
+ export declare function passMark(facts: Pick<ClarityFacts, "body" | "fileKeys">, hold: ClarityHold | null): string;
173
+ /** The questions still open, in the order they were asked. */
174
+ export declare function openQuestions(hold: ClarityHold): ClarityQuestion[];
175
+ /** `hold` with `text` typed under one of its questions. */
176
+ export declare function withAnswer(hold: ClarityHold, questionId: string, text: string): ClarityHold;
177
+ /**
178
+ * Whether the comment may be sent with questions still open. In `advise` always; in `require` only once the
179
+ * service has looked twice, so the first Send with open questions cannot be skipped and the second always can.
180
+ */
181
+ export declare function canSendAsIs(hold: ClarityHold): boolean;
182
+ /** "send" is the composer's own Send; "as-is" is the panel's "Send as is". */
183
+ export type ClaritySend = "send" | "as-is";
184
+ export type ClarityStep =
185
+ /** Post now. */
186
+ {
187
+ readonly kind: "post";
188
+ }
189
+ /** Ask the service first. */
190
+ | {
191
+ readonly kind: "check";
192
+ readonly request: ClarityDraft;
193
+ }
194
+ /** `require`, first look, and nothing was added: the questions stay. */
195
+ | {
196
+ readonly kind: "stay";
197
+ };
198
+ /**
199
+ * What a press of Send does to a new comment.
200
+ *
201
+ * - No mode (a service without the check) or `off`: post, and nothing is asked.
202
+ * - A draft the service already let through, unchanged since: post (a failed post is retried without asking again).
203
+ * - Not looked at yet: ask.
204
+ * - Held, and skipping is allowed and was chosen, or the service has looked twice: post.
205
+ * - Held after the first look: answers go to be judged against their questions; with none, an edited draft is
206
+ * looked at afresh; with nothing added, `advise` posts and `require` stays.
207
+ */
208
+ export declare function clarityStep(input: {
209
+ readonly mode: ClarityMode | null;
210
+ readonly hold: ClarityHold | null;
211
+ readonly draft: ClarityFacts;
212
+ readonly send: ClaritySend;
213
+ /** `passMark` of the draft the service last let through, if it has. */
214
+ readonly passed?: string | null;
215
+ }): ClarityStep;
216
+ /**
217
+ * The draft's hold after a check, or null when the comment goes: the check failed, the mode is `off`, the
218
+ * verdict is `clear` or `unchecked`, or it left nothing to ask. A second look at answers opens nothing new:
219
+ * only questions that were already asked can stay open, whatever the service sends.
220
+ */
221
+ export declare function clarityAfterCheck(hold: ClarityHold | null, draft: ClarityFacts, request: ClarityDraft, outcome: ClarityOutcome): ClarityHold | null;
222
+ /**
223
+ * The text the comment is posted with: its own words, then every answered question under "More detail:", as
224
+ * plain text (the same lines the service's `detailText` writes for an inbox). With no answers it is `body`,
225
+ * untouched.
226
+ *
227
+ * It never passes `COMMENT_MESSAGE_MAX_LENGTH`, and the comment's own words are never cut to make room: an
228
+ * answer that does not fit whole is shortened and ends in "…", and one with no room left (and any after it)
229
+ * is left out. A comment already at the limit is posted as it is, with no answers.
230
+ */
231
+ export declare function clarityBody(body: string, hold: ClarityHold | null): string;
232
+ /**
233
+ * How long the answer under `questionId` may be, so that what is typed is what is posted: the smaller of the
234
+ * service's cap for one answer and what is left of the comment's length after its words and the other answers.
235
+ */
236
+ export declare function answerRoom(body: string, hold: ClarityHold, questionId: string): number;
237
+ /**
238
+ * What a screen reader is told when questions appear, change or stay. `who` is the reader the lead names
239
+ * (`whoWouldAsk`); the native package's panel does not pass it yet and keeps "A developer".
240
+ */
241
+ export declare function clarityAnnouncement(hold: ClarityHold | null, stayed: boolean, who?: string): string;
242
+ /** Who would ask `question`. A question from before questions said so is the developer's. */
243
+ export declare function questionAudience(question: ClarityQuestion): ClarityAudience;
244
+ /** The reader who would ask, as the subject of a sentence. The console's inbox uses the same words. */
245
+ export declare function clarityReader(audience: ClarityAudience): string;
246
+ /** `questions` by who would ask them, the developer's first, leaving out a reader who has none. */
247
+ export declare function clarityGroups(questions: readonly ClarityQuestion[]): Array<{
248
+ readonly audience: ClarityAudience;
249
+ readonly questions: ClarityQuestion[];
250
+ }>;
251
+ /**
252
+ * Who would ask the open questions, as the subject of "… would still have to ask": the one reader when they
253
+ * are all one reader's, "A reader" when a developer and whoever tests the fix both have some.
254
+ */
255
+ export declare function whoWouldAsk(questions: readonly ClarityQuestion[]): string;
256
+ /**
257
+ * `value` as a hold, or null when it is not one this build can show. Storage holds whatever an earlier build
258
+ * wrote; a hold that does not read whole is dropped, and the draft it came with is resumed without it.
259
+ */
260
+ export declare function readClarityHold(value: unknown): ClarityHold | null;
@@ -0,0 +1,62 @@
1
+ import { type KeyboardEvent as ReactKeyboardEvent } from "react";
2
+ import { type ClarityFacts, type ClarityHold, type ClarityMode, type ClaritySend } from "./clarity-model.js";
3
+ import type { DictationDeps } from "./dictation-engine.js";
4
+ import type { Session } from "./session.js";
5
+ /** What Send does next: post this text, or wait (the questions are on screen). */
6
+ export type ClaritySent = {
7
+ readonly post: true;
8
+ readonly body: string;
9
+ } | {
10
+ readonly post: false;
11
+ };
12
+ export type ClarityControl = ReturnType<typeof useClarity>;
13
+ /**
14
+ * The clarity check for one composer (`./clarity-model.ts` decides; this holds the state). `send` is what the
15
+ * composer's submit asks before it posts a new comment. It always resolves, within the model's wait, and
16
+ * never rejects: when the check can't answer, the comment is posted as it would have been without it.
17
+ */
18
+ export declare function useClarity(session: Session | null, member: string | null, mode: ClarityMode | null): {
19
+ hold: ClarityHold | null;
20
+ /** The service is being asked: Send shows it is working. */
21
+ checking: boolean;
22
+ stayed: boolean;
23
+ arrived: number;
24
+ /** An answer is still being dictated: sending now would lose words. */
25
+ dictating: boolean;
26
+ send: (facts: ClarityFacts, how: ClaritySend) => Promise<ClaritySent>;
27
+ takeSend: () => ClaritySend;
28
+ chooseAsIs: () => void;
29
+ answer: (questionId: string, text: string) => void;
30
+ noteDictating: (questionId: string, on: boolean) => void;
31
+ reset: () => void;
32
+ restore: (found: ClarityHold | null) => void;
33
+ };
34
+ type AnswerKeys = (event: ReactKeyboardEvent<HTMLTextAreaElement>) => void;
35
+ type Voice = {
36
+ readonly transcribe: DictationDeps["transcribe"];
37
+ /** People who may be named, so they are spelled right. */
38
+ readonly names: readonly string[];
39
+ readonly onNote: (note: string) => void;
40
+ /** Dictation can't start now (a post or a screen recording is running). */
41
+ readonly off: boolean;
42
+ };
43
+ /**
44
+ * What the composer shows under the comment while the check holds it: what a reader takes from the comment,
45
+ * what the check took as given, each open question with why a reader needs it and its own answer box, grouped
46
+ * by who would ask when a developer and whoever tests the fix both would, and "Send as is" when skipping is
47
+ * allowed. Focus goes to the first question when they arrive, and a polite live region says they are there.
48
+ *
49
+ * Renders nothing at all where the service has no check, so nothing about those composers changes.
50
+ */
51
+ export declare function ClarityQuestions({ clarity, mode, body, busy, voice, onKeyDown, }: {
52
+ clarity: ClarityControl;
53
+ mode: ClarityMode | null;
54
+ /** The comment's text as it stands, for how much room each answer has. */
55
+ body: string;
56
+ /** A post or a check is running. */
57
+ busy: boolean;
58
+ voice: Voice;
59
+ /** The comment box's own key rules (Cmd+Enter sends), so the answer boxes follow them. */
60
+ onKeyDown: AnswerKeys;
61
+ }): import("react/jsx-runtime").JSX.Element | null;
62
+ export {};
@@ -1,141 +1,15 @@
1
- import type { Anchor } from "../core/index.js";
1
+ /**
2
+ * The comment service client for the web toolbar: the DOM-free API (`./api.ts`) plus what only a browser page
3
+ * has — `localStorage` member tokens, and the sign-in window's relay back to this page.
4
+ */
5
+ import { type MsfHandoff } from "./api.js";
2
6
  import { type Session } from "./session.js";
3
- export type Mentioned = {
4
- id: string;
5
- name: string;
6
- };
7
- /** Where a page screenshot was taken. */
8
- export type Capture = {
9
- url: string;
10
- viewport?: {
11
- width: number;
12
- height: number;
13
- };
14
- browser?: string;
15
- };
16
- export type Attachment = {
17
- id: string;
18
- name: string;
19
- contentType: string;
20
- size: number;
21
- url: string;
22
- capture?: Capture;
23
- };
24
- /** An earlier version of a message. `createdAt` is when that version was written. */
25
- export type Revision = {
26
- body: string;
27
- mentions?: Mentioned[];
28
- createdAt: string;
29
- };
30
- export type CommentMessage = {
31
- id: string;
32
- body: string;
33
- author: string;
34
- /** Missing on messages written before author ids were stored. */
35
- authorId?: string | null;
36
- createdAt: string;
37
- editedAt?: string | null;
38
- mentions?: Mentioned[];
39
- attachments?: Attachment[];
40
- /** Earlier versions, oldest first. The current body is the version after the last one. */
41
- revisions?: Revision[];
42
- };
43
- export type Member = {
44
- id: string;
45
- name: string;
46
- email: string;
47
- };
48
- export type Delivery = {
49
- emailed: Mentioned[];
50
- skipped: Array<Mentioned & {
51
- reason: "rate_limited" | "muted" | "disabled" | string;
52
- }>;
53
- };
54
- export type Profile = {
55
- name: string;
56
- email: string | null;
57
- mentionEmails: boolean;
58
- };
59
- export type PostExtras = {
60
- mentions?: string[];
61
- screenshot?: string | null;
62
- attachments?: string[];
63
- };
64
- export type FieldDef = {
65
- id: string;
66
- label: string;
67
- };
68
- export type FieldValue = FieldDef & {
69
- value: string;
70
- };
71
- export type Settings = {
72
- roles: {
73
- enabled: boolean;
74
- options: string[];
75
- };
76
- fields: {
77
- enabled: boolean;
78
- items: FieldDef[];
79
- };
80
- };
81
- /** Role and custom field values for a new or edited comment, keyed by field id. */
82
- export type Details = {
83
- role: string | null;
84
- fields: Record<string, string>;
85
- };
86
- export type Thread = {
87
- id: string;
88
- /** The environment the comment was left in. Differs from the session's for comments from a linked environment. */
89
- environment?: string;
90
- route: string;
91
- /** The page the comment was left on, including query and hash. */
92
- url: string;
93
- role: string | null;
94
- fields: FieldValue[];
95
- anchor: Anchor;
96
- status: "open" | "resolved";
97
- createdBy: string;
98
- createdAt: string;
99
- updatedAt: string;
100
- messages: CommentMessage[];
101
- /** Set when the team turned this comment into a support ticket in MyStatusFlow. */
102
- ticket?: TicketBadge;
103
- };
104
- /** A comment's support ticket, as the commenter sees it: number and status only. */
105
- export type TicketBadge = {
106
- number: string;
107
- status: string;
108
- label: string;
109
- };
110
- export declare class ApiError extends Error {
111
- readonly status: number;
112
- readonly code: string;
113
- constructor(status: number, code: string, message: string);
114
- }
115
- export declare function storageKey(session: Pick<Session, "apiUrl" | "project" | "environment">): string;
116
- /** The team passphrase, kept on this device after unlocking with it so it can be shared from the dashboard. */
117
- export declare function passphraseKey(session: Pick<Session, "apiUrl" | "project" | "environment">): string;
7
+ export * from "./api.js";
118
8
  /**
119
9
  * When the comment service moves to a new URL, the saved sign-in is under the old URL's key. Copy the newest current
120
10
  * one for this project and environment to the new key, and leave the old key in place so going back also works.
121
11
  */
122
12
  export declare function adoptStoredMember(session: Pick<Session, "apiUrl" | "project" | "environment">, storage?: Storage): void;
123
- export declare function memberIsCurrent(token: string): boolean;
124
- /** Sign in with the team passphrase, or with an invite link token instead. */
125
- export declare function unlock(session: Session, displayName: string, email: string, secret: {
126
- passphrase: string;
127
- } | {
128
- invite: string;
129
- }): Promise<string>;
130
- /** How people can sign in here. Services that predate MyStatusFlow sign-in answer 404: passphrase only. */
131
- export type SignInOptions = {
132
- passphrase: boolean;
133
- msf: {
134
- authorizeUrl: string;
135
- label: string;
136
- } | null;
137
- };
138
- export declare function signInOptions(session: Session): Promise<SignInOptions>;
139
13
  /**
140
14
  * An OAuth 2.0 authorization request with PKCE (S256). The verifier never leaves this page. The sign-in window
141
15
  * comes back to `returnTo` (this page, on this origin) with the code, so a code only ever reaches the app it
@@ -146,19 +20,6 @@ export declare function msfAuthorization(authorizeUrl: string, returnTo: string)
146
20
  state: string;
147
21
  verifier: string;
148
22
  }>;
149
- /** Trades the authorization code for a member token. */
150
- export declare function finishMsfSignIn(session: Session, input: {
151
- state: string;
152
- verifier: string;
153
- code: string;
154
- }): Promise<string>;
155
- /** What the sign-in window hands back to the app (a code, or an error such as access_denied). */
156
- export type MsfHandoff = {
157
- state: string;
158
- code?: string;
159
- error?: string;
160
- at: number;
161
- };
162
23
  /**
163
24
  * The sign-in window ends on this app's page with the result in the address hash (when it couldn't message the
164
25
  * page that opened it). Pass it to that page through same-origin storage and a broadcast, and clean the address.
@@ -166,55 +27,3 @@ export type MsfHandoff = {
166
27
  export declare function relayMsfHandoff(): boolean;
167
28
  /** Calls `onHandoff` when a sign-in window hands a result back, by message, broadcast or storage. */
168
29
  export declare function listenForMsfHandoff(apiOrigin: string, onHandoff: (handoff: MsfHandoff) => void): () => void;
169
- export declare function createInvite(session: Session, memberToken: string): Promise<{
170
- invite: string;
171
- invitedBy: string;
172
- expiresAt: string;
173
- }>;
174
- export declare function getProfile(session: Session, memberToken: string): Promise<Profile>;
175
- export declare function updateProfile(session: Session, memberToken: string, patch: {
176
- displayName?: string;
177
- email?: string;
178
- mentionEmails?: boolean;
179
- }): Promise<Profile & {
180
- token: string;
181
- }>;
182
- export declare function uploadAttachment(session: Session, memberToken: string, file: Blob, name: string, environment?: string, capture?: Capture): Promise<Attachment>;
183
- export declare function listMembers(session: Session, memberToken: string, environment?: string): Promise<Member[]>;
184
- /** "view": this person may read but not comment. Older services don't send it, which means "edit". */
185
- export type Access = "edit" | "view";
186
- export declare function listThreads(session: Session, memberToken: string): Promise<{
187
- threads: Thread[];
188
- settings: Settings;
189
- environment?: string | null;
190
- linked?: string[];
191
- access?: Access;
192
- }>;
193
- /** A socket that says "changed" whenever comments change in this environment or one it links to. */
194
- export declare function openLive(session: Session, memberToken: string): WebSocket;
195
- export declare function saveSettings(session: Session, memberToken: string, settings: Settings): Promise<Settings>;
196
- export declare function createThread(session: Session, memberToken: string, anchor: Anchor, body: string, extras?: PostExtras & Partial<Details> & {
197
- url?: string;
198
- }): Promise<{
199
- thread: Thread;
200
- delivery?: Delivery;
201
- }>;
202
- export declare function replyToThread(session: Session, memberToken: string, threadId: string, body: string, extras?: PostExtras): Promise<{
203
- message: CommentMessage;
204
- delivery?: Delivery;
205
- }>;
206
- export declare function updateThread(session: Session, memberToken: string, threadId: string, patch: {
207
- status?: "open" | "resolved";
208
- anchor?: Anchor;
209
- url?: string;
210
- } & Partial<Details>): Promise<{
211
- id: string;
212
- }>;
213
- export declare function editMessage(session: Session, memberToken: string, threadId: string, messageId: string, body: string, mentions: string[]): Promise<{
214
- message: {
215
- id: string;
216
- body: string;
217
- editedAt: string | null;
218
- };
219
- delivery: Delivery | null;
220
- }>;
@@ -0,0 +1,30 @@
1
+ /** The composer's size the person dragged it to. Kept in the toolbar prefs, so every new comment opens at it. */
2
+ export type ComposerSize = {
3
+ width: number;
4
+ height: number;
5
+ };
6
+ /** The composer's own width; it never gets narrower than it is by default. */
7
+ export declare const COMPOSER_MIN_WIDTH = 240;
8
+ export declare const COMPOSER_MIN_HEIGHT = 100;
9
+ export declare const COMPOSER_MAX_WIDTH = 720;
10
+ export declare function clampComposerSize(size: ComposerSize, room: {
11
+ width: number;
12
+ height: number;
13
+ }): ComposerSize;
14
+ /** A saved size, or nothing when the stored value is missing or malformed. */
15
+ export declare function readComposerSize(value: unknown): ComposerSize | undefined;
16
+ /**
17
+ * The composer's resize handle, in its bottom-right corner. Dragging right widens it and dragging down makes it
18
+ * taller (the top stays put); arrow keys do the same in steps; a double-click goes back to the default.
19
+ */
20
+ export declare function ResizeGrip(props: {
21
+ room: () => {
22
+ width: number;
23
+ height: number;
24
+ };
25
+ /** Every step of a drag. */
26
+ onResize: (size: ComposerSize) => void;
27
+ /** The drag ended (or a key was pressed) at `size`: save it. Not called for a press that didn't move. */
28
+ onEnd: (size: ComposerSize) => void;
29
+ onReset: () => void;
30
+ }): import("react/jsx-runtime").JSX.Element;
@@ -1,14 +1,24 @@
1
1
  import { type ReactNode } from "react";
2
2
  import type { Member, Thread } from "./client.js";
3
3
  import { type PlacedStatus } from "./format.js";
4
+ import type { Here } from "./screen.js";
4
5
  import type { Session } from "./session.js";
5
6
  type TableRow = {
6
7
  thread: Thread;
7
8
  status: PlacedStatus;
8
9
  };
9
- export declare function Dashboard({ rows, route, environment, memberName, memberId, roles, members, session, memberToken, envFilter, profile, settingsPane, onClose, onOpen, onRefresh, readOnly, }: {
10
+ /** The dashboard's sections. "inbox" is the Comments section. */
11
+ export type DashboardScreen = "insights" | "inbox" | "profile" | "settings";
12
+ type Screen = DashboardScreen;
13
+ /** Asks an open or opening dashboard to show this thread in Comments. A new `at` asks again. */
14
+ export type DashboardFocus = {
15
+ threadId: string;
16
+ at: number;
17
+ };
18
+ export declare function Dashboard({ rows, here, environment, memberName, memberId, roles, members, session, memberToken, envFilter, profile, settingsPane, onClose, onOpen, onReattach, onRefresh, screen, onScreen, focus, readOnly, }: {
10
19
  rows: TableRow[];
11
- route: string;
20
+ /** The screen the page is on: "This page" is its threads. */
21
+ here: Here;
12
22
  environment: string;
13
23
  memberName: string;
14
24
  memberId?: string;
@@ -21,7 +31,12 @@ export declare function Dashboard({ rows, route, environment, memberName, member
21
31
  settingsPane: ReactNode;
22
32
  onClose: () => void;
23
33
  onOpen: (row: TableRow) => void;
34
+ /** Re-pins a comment from this page that lost its element. */
35
+ onReattach?: (row: TableRow) => void;
24
36
  onRefresh: () => Promise<unknown> | unknown;
37
+ screen: Screen;
38
+ onScreen: (screen: Screen) => void;
39
+ focus?: DashboardFocus | null;
25
40
  /** View-only access: no replies and no status changes. */
26
41
  readOnly?: boolean;
27
42
  }): import("react/jsx-runtime").JSX.Element;