| ... | ... | @@ -56,6 +56,13 @@ class SelectionNode { |
| 56 | 56 | get nodeType() { |
| 57 | 57 | return this.tagName === "#text" ? 3 : 1; |
| 58 | 58 | } |
| 59 | get ownerDocument() { |
| 60 | // `user-select: none` inherits, like Chromium's computed style. |
| 61 | const getComputedStyle = (element: SelectionNode) => ({ |
| 62 | userSelect: element.closest("[data-select-none]") ? "none" : "auto", |
| 63 | }); |
| 64 | return { defaultView: { getComputedStyle } }; |
| 65 | } |
| 59 | 66 | get length() { |
| 60 | 67 | return this.data.length; |
| 61 | 68 | } |
| ... | ... | @@ -259,6 +266,22 @@ describe("captureAssistantTextSelection", () => { |
| 259 | 266 | expect(capture(source, nativeSelection([quote, 0], [timestampText, 1]))).toBeNull(); |
| 260 | 267 | }); |
| 261 | 268 | |
| 269 | it("captures a triple-clicked response that ends before a tool row and the next response", () => { |
| 270 | const quote = textNode("Recording your answers in the session note:"); |
| 271 | const source = assistantSource(new SelectionNode("P").append(quote)); |
| 272 | const nextParagraph = new SelectionNode("P").append(textNode("1. What it means.")); |
| 273 | const viewport = new SelectionNode("MAIN").append( |
| 274 | source, |
| 275 | new SelectionNode("DIV", "", { "data-select-none": "" }).append(textNode("echo ok")), |
| 276 | new SelectionNode("H3", "", { "data-select-none": "" }).append(textNode("T3 Code")), |
| 277 | assistantSource(nextParagraph), |
| 278 | ); |
| 279 | |
| 280 | expect(capture(viewport, nativeSelection([quote, 0], [nextParagraph, 0]))?.selector).toEqual( |
| 281 | selector(quote.data), |
| 282 | ); |
| 283 | }); |
| 284 | |
| 262 | 285 | it("does not attribute an empty starting endpoint to the previous response", () => { |
| 263 | 286 | const previous = textNode("Previous response."); |
| 264 | 287 | const quote = textNode("Selected response."); |