| ... | ... | @@ -183,12 +183,16 @@ export type ComposerCitationCommentRequest = { |
| 183 | 183 | value: string; |
| 184 | 184 | citationStart: number; |
| 185 | 185 | sourceAnchor: AssistantCitationSourceAnchor; |
| 186 | insertedSpaces: CitationInsertedSpaces; |
| 186 | 187 | }; |
| 187 | 188 | |
| 189 | // Spaces added around a freshly inserted citation, removed with it if its comment is cancelled. |
| 190 | type CitationInsertedSpaces = { before: boolean; after: boolean }; |
| 191 | |
| 188 | 192 | type OpenCitationComment = { |
| 189 | 193 | key: string; |
| 190 | 194 | sourceAnchor?: AssistantCitationSourceAnchor; |
| 191 | | removeOnCancel?: boolean; |
| 195 | removeOnCancel?: CitationInsertedSpaces; |
| 192 | 196 | }; |
| 193 | 197 | |
| 194 | 198 | const ComposerCitationCommentContext = createContext<{ |
| ... | ... | @@ -392,18 +396,20 @@ function ComposerCitationNodeView({ node, editor, getPos }: NodeViewProps) { |
| 392 | 396 | [editor, nodePos], |
| 393 | 397 | ); |
| 394 | 398 | |
| 399 | // Undo a fresh insertion: drop the chip and the spaces inserted with it. |
| 400 | const removeOnCancel = commentTarget?.removeOnCancel; |
| 395 | 401 | const onRemove = useCallback(() => { |
| 396 | | if (!editor.isEditable) return; |
| 402 | if (!editor.isEditable || !removeOnCancel) return; |
| 397 | 403 | const pos = nodePos(); |
| 398 | 404 | if (pos === null) return; |
| 399 | | const current = editor.state.doc.nodeAt(pos); |
| 405 | const { doc } = editor.state; |
| 406 | const current = doc.nodeAt(pos); |
| 400 | 407 | if (!current) return; |
| 401 | | editor |
| 402 | | .chain() |
| 403 | | .focus() |
| 404 | | .deleteRange({ from: pos, to: pos + current.nodeSize }) |
| 405 | | .run(); |
| 406 | | }, [editor, nodePos]); |
| 408 | const end = pos + current.nodeSize; |
| 409 | const from = removeOnCancel.before && doc.textBetween(pos - 1, pos) === " " ? pos - 1 : pos; |
| 410 | const to = removeOnCancel.after && doc.textBetween(end, end + 1) === " " ? end + 1 : end; |
| 411 | editor.chain().focus().deleteRange({ from, to }).run(); |
| 412 | }, [editor, nodePos, removeOnCancel]); |
| 407 | 413 | |
| 408 | 414 | // Put the caret right after the chip so Enter sends and typing continues the prompt. |
| 409 | 415 | const onRestoreFocus = useCallback(() => { |
| ... | ... | @@ -450,7 +456,7 @@ function ComposerCitationNodeView({ node, editor, getPos }: NodeViewProps) { |
| 450 | 456 | if (open && !editor.isEditable) return; |
| 451 | 457 | commentContext.onOpenChange(citeKey, open); |
| 452 | 458 | }, |
| 453 | | ...(commentTarget?.removeOnCancel ? { onCancel: onRemove } : {}), |
| 459 | ...(removeOnCancel ? { onCancel: onRemove } : {}), |
| 454 | 460 | onSave: onSaveComment, |
| 455 | 461 | onSaveAndSend: (comment) => { |
| 456 | 462 | if (!onSaveComment(comment)) return false; |
| ... | ... | @@ -1531,7 +1537,7 @@ function ComposerPromptEditorTiptapInner(props: ComposerPromptEditorProps) { |
| 1531 | 1537 | setOpenCitation({ |
| 1532 | 1538 | key: citeKey, |
| 1533 | 1539 | sourceAnchor: pendingCitation.sourceAnchor, |
| 1534 | | removeOnCancel: true, |
| 1540 | removeOnCancel: pendingCitation.insertedSpaces, |
| 1535 | 1541 | }); |
| 1536 | 1542 | } |
| 1537 | 1543 | } |
| ... | ... | @@ -1618,7 +1624,7 @@ function ComposerPromptEditorTiptapInner(props: ComposerPromptEditorProps) { |
| 1618 | 1624 | setOpenCitation({ |
| 1619 | 1625 | key: citeKey, |
| 1620 | 1626 | sourceAnchor: request.sourceAnchor, |
| 1621 | | removeOnCancel: true, |
| 1627 | removeOnCancel: request.insertedSpaces, |
| 1622 | 1628 | }); |
| 1623 | 1629 | } |
| 1624 | 1630 | }, |