[content] src/extension.ts (typescript)
	const editor = vscode.window.activeTextEditor;
	if (!editor) { return; }
	disposePreviewDecorations();

	// Only preview the next text edit action (insert/delete/replace)
	const next = plan.find(a => a.kind === 'editInsert' || a.kind === 'editDelete' || a.kind === 'editReplace');
	if (!next) {
		previewVisible = false;
		vscode.commands.executeCommand('setContext', UI_CONTEXT_KEY, false);
		currentPlan = plan;
		return;
	}

	const oneLinePreview = (t: string) => {
		const idx = t.indexOf('
');
		const first = idx === -1 ? t : t.slice(0, idx) + ' …';
		return first.length > 80 ? first.slice(0, 77) + '…' : first;
	};

	if (next.kind === 'editInsert') {
		const pos = new vscode.Position(next.position[0], next.position[1]);
		decorationInsertType = vscode.window.createTextEditorDecorationType({
			after: {
				contentText: `${oneLinePreview(next.text)}`,
				color: new vscode.ThemeColor('charts.purple'),
				fontStyle: 'italic',
				fontWeight: '600',
			}
		});
		editor.setDecorations(decorationInsertType, [{ range: new vscode.Range(pos, pos) }]);
		currentInsertPreview = undefined;
	} else if (next.kind === 'editDelete') {
		const range = new vscode.Range(
			new vscode.Position(next.range.start[0], next.range.start[1]),
			new vscode.Position(next.range.end[0], next.range.end[1])
		);
		decorationDeleteType = vscode.window.createTextEditorDecorationType({
			backgroundColor: 'rgba(255, 60, 60, 0.18)',
			border: '1px solid rgba(255, 60, 60, 0.35)',
			textDecoration: 'line-through'
		});
		editor.setDecorations(decorationDeleteType, [{ range }]);
		currentInsertPreview = undefined;
	} else if (next.kind === 'editReplace') {
		const range = new vscode.Range(
			new vscode.Position(next.range.start[0], next.range.start[1]),
			new vscode.Position(next.range.end[0], next.range.end[1])
		);
		decorationReplaceType = vscode.window.createTextEditorDecorationType({
			backgroundColor: 'rgba(255,165,0,0.15)',
			border: '1px dashed rgba(255,165,0,0.45)',
		});
		editor.setDecorations(decorationReplaceType, [{ range }]);
		// Build overlays: original first line on the line above (strike-through), replacement shown after each target line
		const docLineCount = editor.document.lineCount;
		const replacementLines = next.text.split(/
?
/);
		const originalLines: string[] = [];
		for (let i = 0; i < replacementLines.length; i++) {
			const lineIdx = Math.min(next.range.start[0] + i, Math.max(0, docLineCount - 1));
			originalLines.push(editor.document.lineAt(lineIdx).text);
		}
		decorationReplaceOriginalType = vscode.window.createTextEditorDecorationType({
			before: {
				color: new vscode.ThemeColor('disabledForeground'),
				fontStyle: 'italic',
				textDecoration: 'line-through'
			}
		});
		decorationReplaceNewType = vscode.window.createTextEditorDecorationType({
			after: {
				color: new vscode.ThemeColor('charts.purple'),
				fontStyle: 'italic',
				fontWeight: '600'
			}
		});
		const originalDecorations: vscode.DecorationOptions[] = [];
		const newDecorations: vscode.DecorationOptions[] = [];
		// Original first line shown above the first replacement line
		{
			const firstOriginal = originalLines[0] ?? '';
			const overlayLine = Math.max(0, next.range.start[0] - 1);
			originalDecorations.push({
				range: new vscode.Range(overlayLine, 0, overlayLine, 0),
				renderOptions: {
					before: {
						contentText: firstOriginal
					}
				}
			});
		}
		for (let i = 0; i < replacementLines.length; i++) {
			const lineIdx = Math.min(next.range.start[0] + i, Math.max(0, docLineCount - 1));
			const lineRange = editor.document.lineAt(lineIdx).range;
			// Replacement line overlay (after at end-of-line)
			if (i < replacementLines.length) {
				newDecorations.push({
					range: lineRange,
					renderOptions: {
						after: {
							contentText: replacementLines[i]
						}
					}
				});
			}
		}
		editor.setDecorations(decorationReplaceOriginalType, originalDecorations);
		editor.setDecorations(decorationReplaceNewType, newDecorations);
		currentInsertPreview = undefined;
	}