This solution relies on putting a TagBox
with a custom tag around the part to be replaced, reading the cell and replacing the tag, then writing it back. Personally I've always felt that the need to read the entire cell and write it all again seems kind of clunky, but I don't know of a better way to do this.
MakeBoxes[replaceMarker[a_]MakeBoxes[replacementMarker[a_,tag_],StandardForm]^:=TagBox[MakeBoxes[a],"replacementTag"]tag]
replaceMark[rule_,nb_:EvaluationNotebook[],which_:All,cell_:EvaluationCell]:=(
SelectionMove[nb,which,cell];
NotebookWrite[EvaluationNotebook[],NotebookRead[EvaluationNotebook[]]/.rule])
Row[{"don't"Not replace"replaced",
replaceMarker[replacementMarker[
Panel[Column[{Style["This is a panel",Bold],Button["Press me!",
SelectionMove[EvaluationNotebook[],All,EvaluationCell];
NotebookWrite[EvaluationNotebook[],
NotebookRead[EvaluationNotebook[]]/.TagBox[_replaceMark[TagBox[_,"replacementTag"] :>MakeBoxes["\"> MakeBoxes["\" Now I'm just text. \""]\""]]
]]]}]]
],"replacementTag"]
,"don't"Not replace"replaced"}]
Important note! The action cannot be undone, so if used for example to allow dynamic reformatting of data or similar, be aware that mistakes can end up deleting the old contents.