@@ -27,6 +27,14 @@ export interface PromptEditorProps extends PromptEditorKeyPolicy {
2727 placeholder ?: string
2828 /** Focuses the editor (caret at end) on mount. */
2929 autoFocus ?: boolean
30+ /**
31+ * Renders the editor as a non-editable display surface: the textarea becomes
32+ * `readOnly` (so the chip overlay still paints `@`-mention / `/`-skill chips
33+ * and the text stays selectable/copyable) and the caret-anchored resource and
34+ * skill menus are not mounted. Use for read-only records — e.g. a finished
35+ * scheduled task — where the prompt should render with chips but not be edited.
36+ */
37+ readOnly ?: boolean
3038 /**
3139 * Layout/sizing only — a height cap (`max-h-[200px]`) or fill (`flex-1`)
3240 * for the scroll container. The text chrome is owned by the editor.
@@ -56,6 +64,7 @@ export function PromptEditor({
5664 editor,
5765 placeholder,
5866 autoFocus = false ,
67+ readOnly = false ,
5968 className,
6069 'aria-label' : ariaLabel ,
6170 onSubmit,
@@ -73,22 +82,24 @@ export function PromptEditor({
7382 } , [ value , textareaRef ] )
7483
7584 useEffect ( ( ) => {
76- if ( autoFocus ) editor . focusAtEnd ( )
85+ if ( autoFocus && ! readOnly ) editor . focusAtEnd ( )
7786 // eslint-disable-next-line react-hooks/exhaustive-deps -- mount-only focus
7887 } , [ ] )
7988
8089 /**
8190 * Clicking the editor's empty regions (padding, space below the last line)
8291 * focuses the textarea; clicks on the textarea itself keep native caret
83- * placement.
92+ * placement. No-op in read-only mode: the surface is display-only, so a
93+ * padding click should not pull focus onto the non-editable textarea.
8494 */
8595 const handleSurfaceClick = useCallback (
8696 ( e : React . MouseEvent < HTMLDivElement > ) => {
97+ if ( readOnly ) return
8798 if ( e . target === textareaRef . current ) return
8899 if ( ( e . target as HTMLElement ) . closest ( 'button' ) ) return
89100 textareaRef . current ?. focus ( )
90101 } ,
91- [ textareaRef ]
102+ [ readOnly , textareaRef ]
92103 )
93104
94105 const overlayContent = useMemo ( ( ) => {
@@ -167,38 +178,45 @@ export function PromptEditor({
167178 < textarea
168179 ref = { textareaRef }
169180 value = { value }
170- onChange = { editor . handleInputChange }
171- onKeyDown = { ( e ) => editor . handleKeyDown ( e , { onSubmit, onArrowUpOnEmpty } ) }
172- onPaste = { editor . handlePaste }
181+ readOnly = { readOnly }
182+ onChange = { readOnly ? undefined : editor . handleInputChange }
183+ onKeyDown = {
184+ readOnly ? undefined : ( e ) => editor . handleKeyDown ( e , { onSubmit, onArrowUpOnEmpty } )
185+ }
186+ onPaste = { readOnly ? undefined : editor . handlePaste }
173187 onCopy = { editor . handleCopy }
174- onCut = { editor . handleCut }
175- onSelect = { editor . handleSelectAdjust }
176- onMouseUp = { editor . handleSelectAdjust }
188+ onCut = { readOnly ? undefined : editor . handleCut }
189+ onSelect = { readOnly ? undefined : editor . handleSelectAdjust }
190+ onMouseUp = { readOnly ? undefined : editor . handleSelectAdjust }
177191 placeholder = { placeholder }
178192 aria-label = { ariaLabel }
179193 rows = { 1 }
180- className = { TEXTAREA_BASE_CLASSES }
194+ className = { cn ( TEXTAREA_BASE_CLASSES , readOnly && 'cursor-default caret-transparent' ) }
181195 />
182196 </ div >
183197
184- < PlusMenuDropdown
185- ref = { editor . plusMenuRef }
186- availableResources = { editor . availableResources }
187- onResourceSelect = { editor . insertResource }
188- onClose = { editor . handlePlusMenuClose }
189- textareaRef = { editor . textareaRef }
190- pendingCursorRef = { editor . pendingCursorRef }
191- mentionQuery = { editor . mentionQuery ?? undefined }
192- />
193- < SkillsMenuDropdown
194- ref = { editor . skillsMenuRef }
195- skills = { editor . skills }
196- onSkillSelect = { editor . handleSkillSelect }
197- onClose = { editor . handleSkillsMenuClose }
198- textareaRef = { editor . textareaRef }
199- pendingCursorRef = { editor . pendingCursorRef }
200- slashQuery = { editor . slashQuery ?? undefined }
201- />
198+ { ! readOnly && (
199+ < >
200+ < PlusMenuDropdown
201+ ref = { editor . plusMenuRef }
202+ availableResources = { editor . availableResources }
203+ onResourceSelect = { editor . insertResource }
204+ onClose = { editor . handlePlusMenuClose }
205+ textareaRef = { editor . textareaRef }
206+ pendingCursorRef = { editor . pendingCursorRef }
207+ mentionQuery = { editor . mentionQuery ?? undefined }
208+ />
209+ < SkillsMenuDropdown
210+ ref = { editor . skillsMenuRef }
211+ skills = { editor . skills }
212+ onSkillSelect = { editor . handleSkillSelect }
213+ onClose = { editor . handleSkillsMenuClose }
214+ textareaRef = { editor . textareaRef }
215+ pendingCursorRef = { editor . pendingCursorRef }
216+ slashQuery = { editor . slashQuery ?? undefined }
217+ />
218+ </ >
219+ ) }
202220 </ div >
203221 )
204222}
0 commit comments