or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

diff-editor.mdeditor.mdindex.mdloader.mdmonaco-hook.md

diff-editor.mddocs/

0

# Diff Editor

1

2

Side-by-side code comparison editor for visualizing differences between two versions of code. Perfect for code reviews, version comparisons, and merge conflict resolution.

3

4

## Capabilities

5

6

### DiffEditor Component

7

8

Monaco Diff Editor component that displays two code versions side-by-side with highlighted differences.

9

10

```typescript { .api }

11

/**

12

* Monaco Diff Editor React component for comparing two code versions

13

* @param props - Diff editor configuration and content

14

* @returns React component rendering Monaco Diff Editor

15

*/

16

declare const DiffEditor: React.ComponentType<DiffEditorProps>;

17

18

interface DiffEditorProps {

19

/** Original source (left side) value */

20

original?: string;

21

/** Modified source (right side) value */

22

modified?: string;

23

/** Language for both models */

24

language?: string;

25

/** Language for original model only */

26

originalLanguage?: string;

27

/** Language for modified model only */

28

modifiedLanguage?: string;

29

/** Path for the original model */

30

originalModelPath?: string;

31

/** Path for the modified model */

32

modifiedModelPath?: string;

33

/** Whether to keep original model when unmounting */

34

keepCurrentOriginalModel?: boolean;

35

/** Whether to keep modified model when unmounting */

36

keepCurrentModifiedModel?: boolean;

37

/** Theme for the monaco editor */

38

theme?: Theme | string;

39

/** Loading component shown before editor is mounted */

40

loading?: ReactNode;

41

/** Diff editor construction options */

42

options?: editor.IDiffEditorConstructionOptions;

43

/** Width of editor wrapper */

44

width?: number | string;

45

/** Height of editor wrapper */

46

height?: number | string;

47

/** CSS class name for editor container */

48

className?: string;

49

/** Props applied to the wrapper element */

50

wrapperProps?: object;

51

/** Called before diff editor is mounted */

52

beforeMount?: DiffBeforeMount;

53

/** Called when diff editor is mounted */

54

onMount?: DiffOnMount;

55

}

56

```

57

58

**Usage Examples:**

59

60

```typescript

61

import React from "react";

62

import { DiffEditor } from "@monaco-editor/react";

63

64

// Basic diff comparison

65

function BasicDiffEditor() {

66

const originalCode = `function hello() {

67

console.log("Hello");

68

}`;

69

70

const modifiedCode = `function hello(name) {

71

console.log("Hello " + name);

72

}`;

73

74

return (

75

<DiffEditor

76

height="400px"

77

language="javascript"

78

original={originalCode}

79

modified={modifiedCode}

80

/>

81

);

82

}

83

84

// Advanced diff with custom options

85

function AdvancedDiffEditor() {

86

const original = `const users = [

87

{ name: "Alice", age: 25 },

88

{ name: "Bob", age: 30 }

89

];`;

90

91

const modified = `const users = [

92

{ name: "Alice", age: 25, active: true },

93

{ name: "Bob", age: 30, active: false },

94

{ name: "Charlie", age: 35, active: true }

95

];`;

96

97

const options = {

98

readOnly: false,

99

renderSideBySide: true,

100

enableSplitViewResizing: true,

101

renderOverviewRuler: true,

102

ignoreTrimWhitespace: false,

103

};

104

105

return (

106

<DiffEditor

107

height="500px"

108

language="javascript"

109

original={original}

110

modified={modified}

111

options={options}

112

theme="vs-dark"

113

/>

114

);

115

}

116

117

// Multi-language diff

118

function MultiLanguageDiff() {

119

const tsCode = `interface User {

120

name: string;

121

age: number;

122

}`;

123

124

const jsCode = `/**

125

* @typedef {Object} User

126

* @property {string} name

127

* @property {number} age

128

*/`;

129

130

return (

131

<DiffEditor

132

height="300px"

133

originalLanguage="typescript"

134

modifiedLanguage="javascript"

135

original={tsCode}

136

modified={jsCode}

137

/>

138

);

139

}

140

```

141

142

### Diff Event Handlers

143

144

Event handlers for diff editor lifecycle management.

145

146

```typescript { .api }

147

/**

148

* Called when diff editor is mounted

149

* @param editor - The standalone diff editor instance

150

* @param monaco - The monaco editor namespace

151

*/

152

type DiffOnMount = (editor: editor.IStandaloneDiffEditor, monaco: Monaco) => void;

153

154

/**

155

* Called before diff editor is mounted

156

* @param monaco - The monaco editor namespace

157

*/

158

type DiffBeforeMount = (monaco: Monaco) => void;

159

160

type MonacoDiffEditor = editor.IStandaloneDiffEditor;

161

```

162

163

**Event Handler Examples:**

164

165

```typescript

166

import { DiffEditor } from "@monaco-editor/react";

167

168

function DiffWithHandlers() {

169

const handleBeforeMount = (monaco) => {

170

// Configure monaco before diff editor creation

171

monaco.editor.defineTheme('diffTheme', {

172

base: 'vs',

173

inherit: true,

174

rules: [],

175

colors: {

176

'diffEditor.insertedTextBackground': '#90EE9050',

177

'diffEditor.removedTextBackground': '#FF634750',

178

}

179

});

180

};

181

182

const handleMount = (diffEditor, monaco) => {

183

// Access both original and modified editors

184

const originalEditor = diffEditor.getOriginalEditor();

185

const modifiedEditor = diffEditor.getModifiedEditor();

186

187

// Configure diff editor behavior

188

diffEditor.updateOptions({

189

renderSideBySide: true,

190

enableSplitViewResizing: true,

191

});

192

193

// Focus on modified editor

194

modifiedEditor.focus();

195

196

// Add navigation commands

197

diffEditor.addCommand(monaco.KeyMod.Alt | monaco.KeyCode.DownArrow, () => {

198

diffEditor.goToNextDiff();

199

});

200

201

diffEditor.addCommand(monaco.KeyMod.Alt | monaco.KeyCode.UpArrow, () => {

202

diffEditor.goToPreviousDiff();

203

});

204

};

205

206

return (

207

<DiffEditor

208

height="400px"

209

language="javascript"

210

original="const a = 1;"

211

modified="const a = 2;"

212

beforeMount={handleBeforeMount}

213

onMount={handleMount}

214

theme="diffTheme"

215

/>

216

);

217

}

218

```

219

220

### Diff Editor Configuration

221

222

Configuration options specific to the diff editor functionality.

223

224

```typescript { .api }

225

// Diff editor specific options

226

interface DiffEditorOptions {

227

/** Monaco diff editor construction options */

228

options?: editor.IDiffEditorConstructionOptions;

229

}

230

231

// Content configuration for diff comparison

232

interface DiffContentProps {

233

/** Original (left) content */

234

original?: string;

235

/** Modified (right) content */

236

modified?: string;

237

/** Language for both sides */

238

language?: string;

239

/** Separate language for original side */

240

originalLanguage?: string;

241

/** Separate language for modified side */

242

modifiedLanguage?: string;

243

}

244

245

// Model paths for diff editor

246

interface DiffModelProps {

247

/** Path/URI for original model */

248

originalModelPath?: string;

249

/** Path/URI for modified model */

250

modifiedModelPath?: string;

251

/** Keep original model when unmounting */

252

keepCurrentOriginalModel?: boolean;

253

/** Keep modified model when unmounting */

254

keepCurrentModifiedModel?: boolean;

255

}

256

```

257

258

**Configuration Examples:**

259

260

```typescript

261

import { DiffEditor } from "@monaco-editor/react";

262

263

// Customized diff editor

264

function CustomDiffEditor() {

265

const diffOptions = {

266

// Display options

267

renderSideBySide: true, // Side-by-side vs inline

268

renderMarginRevertIcon: true, // Show revert icons

269

renderOverviewRuler: true, // Show overview ruler

270

renderIndicators: true, // Show change indicators

271

272

// Interaction options

273

enableSplitViewResizing: true, // Allow resizing split

274

readOnly: false, // Allow editing

275

originalEditable: false, // Original side read-only

276

277

// Diff algorithm options

278

ignoreTrimWhitespace: true, // Ignore whitespace at line ends

279

renderWhitespace: 'selection', // Show whitespace

280

diffWordWrap: 'inherit', // Word wrapping

281

282

// Navigation

283

automaticLayout: true, // Auto-resize

284

scrollBeyondLastLine: false,

285

};

286

287

return (

288

<DiffEditor

289

height="600px"

290

width="100%"

291

language="typescript"

292

theme="vs-dark"

293

options={diffOptions}

294

original="// Original code"

295

modified="// Modified code"

296

/>

297

);

298

}

299

300

// File-based diff with model paths

301

function FileDiffEditor() {

302

return (

303

<DiffEditor

304

height="500px"

305

language="javascript"

306

originalModelPath="file:///original/main.js"

307

modifiedModelPath="file:///modified/main.js"

308

original={`function calculate(a, b) {

309

return a + b;

310

}`}

311

modified={`function calculate(a, b) {

312

// Added validation

313

if (typeof a !== 'number' || typeof b !== 'number') {

314

throw new Error('Arguments must be numbers');

315

}

316

return a + b;

317

}`}

318

keepCurrentOriginalModel={true}

319

keepCurrentModifiedModel={true}

320

/>

321

);

322

}

323

```

324

325

## Common Use Cases

326

327

### Code Review Workflow

328

329

```typescript

330

import React, { useState } from "react";

331

import { DiffEditor } from "@monaco-editor/react";

332

333

function CodeReviewDiff() {

334

const [currentFile, setCurrentFile] = useState(0);

335

336

const changedFiles = [

337

{

338

filename: "src/utils.js",

339

original: `export function formatDate(date) {

340

return date.toLocaleDateString();

341

}`,

342

modified: `export function formatDate(date) {

343

if (!date || !(date instanceof Date)) {

344

throw new Error('Invalid date provided');

345

}

346

return date.toLocaleDateString();

347

}`

348

},

349

{

350

filename: "src/api.js",

351

original: `async function fetchUser(id) {

352

const response = await fetch(\`/api/users/\${id}\`);

353

return response.json();

354

}`,

355

modified: `async function fetchUser(id) {

356

const response = await fetch(\`/api/users/\${id}\`);

357

if (!response.ok) {

358

throw new Error(\`Failed to fetch user: \${response.status}\`);

359

}

360

return response.json();

361

}`

362

}

363

];

364

365

return (

366

<div>

367

<div style={{ marginBottom: 10 }}>

368

{changedFiles.map((file, index) => (

369

<button

370

key={index}

371

onClick={() => setCurrentFile(index)}

372

style={{

373

marginRight: 10,

374

fontWeight: currentFile === index ? 'bold' : 'normal',

375

background: currentFile === index ? '#007acc' : '#f0f0f0',

376

color: currentFile === index ? 'white' : 'black',

377

border: 'none',

378

padding: '8px 12px',

379

cursor: 'pointer'

380

}}

381

>

382

{file.filename}

383

</button>

384

))}

385

</div>

386

387

<DiffEditor

388

height="500px"

389

language="javascript"

390

original={changedFiles[currentFile].original}

391

modified={changedFiles[currentFile].modified}

392

options={{

393

renderSideBySide: true,

394

renderMarginRevertIcon: false,

395

readOnly: true,

396

}}

397

/>

398

</div>

399

);

400

}

401

```

402

403

### Version Comparison

404

405

```typescript

406

function VersionComparisonDiff() {

407

const [versions] = useState([

408

{ version: "1.0.0", code: "const API_URL = 'http://localhost';" },

409

{ version: "1.1.0", code: "const API_URL = process.env.API_URL || 'http://localhost';" },

410

{ version: "2.0.0", code: "const API_URL = process.env.REACT_APP_API_URL || 'https://api.example.com';" }

411

]);

412

413

const [leftVersion, setLeftVersion] = useState(0);

414

const [rightVersion, setRightVersion] = useState(1);

415

416

return (

417

<div>

418

<div style={{ marginBottom: 10 }}>

419

<label>Original:

420

<select value={leftVersion} onChange={(e) => setLeftVersion(+e.target.value)}>

421

{versions.map((v, i) => (

422

<option key={i} value={i}>{v.version}</option>

423

))}

424

</select>

425

</label>

426

<label style={{ marginLeft: 20 }}>Modified:

427

<select value={rightVersion} onChange={(e) => setRightVersion(+e.target.value)}>

428

{versions.map((v, i) => (

429

<option key={i} value={i}>{v.version}</option>

430

))}

431

</select>

432

</label>

433

</div>

434

435

<DiffEditor

436

height="300px"

437

language="javascript"

438

original={versions[leftVersion].code}

439

modified={versions[rightVersion].code}

440

/>

441

</div>

442

);

443

}

444

```