@@ -96,6 +141,7 @@ export default function CallHistoryRow({ call, expanded, onToggleExpand }: Props
Response
{JSON.stringify(call.response ?? {}, null, 2)}
+ {responseDiff &&
}
)}
diff --git a/src/index.css b/src/index.css
index 67d5bf4..372d4bd 100644
--- a/src/index.css
+++ b/src/index.css
@@ -2807,6 +2807,87 @@ code,
overflow-x: auto;
}
+.response-diff {
+ margin-top: 12px;
+ padding: 12px;
+ border: 1px solid var(--line);
+ border-radius: 6px;
+ background: var(--surface-soft);
+}
+
+.response-diff h4 {
+ margin: 0 0 10px 0;
+}
+
+.response-diff__empty {
+ margin: 0;
+ color: var(--muted);
+ font-size: 0.8125rem;
+}
+
+.response-diff__list {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.response-diff__row {
+ display: grid;
+ grid-template-columns: 72px minmax(120px, 1fr) minmax(0, 1.2fr) minmax(0, 1.2fr);
+ gap: 10px;
+ align-items: start;
+ padding: 10px;
+ border: 1px solid var(--line);
+ border-left-width: 4px;
+ border-radius: 6px;
+ background: var(--surface);
+ font-size: 0.8125rem;
+}
+
+.response-diff__row--added {
+ border-left-color: var(--success);
+}
+
+.response-diff__row--removed {
+ border-left-color: var(--danger);
+}
+
+.response-diff__row--changed {
+ border-left-color: var(--warning);
+}
+
+.response-diff__kind {
+ font-weight: 600;
+ text-transform: capitalize;
+}
+
+.response-diff__path {
+ color: var(--accent);
+ overflow-wrap: anywhere;
+}
+
+.response-diff__value {
+ display: grid;
+ gap: 4px;
+ min-width: 0;
+}
+
+.response-diff__value-label {
+ color: var(--muted);
+ font-size: 0.75rem;
+ font-weight: 600;
+ text-transform: uppercase;
+}
+
+.response-diff__value code {
+ color: var(--text);
+ overflow-wrap: anywhere;
+ white-space: pre-wrap;
+}
+
.integration-guide-section h2 {
margin: 0 0 16px 0;
}
@@ -2997,6 +3078,10 @@ code,
margin-right: 8px;
}
+ .response-diff__row {
+ grid-template-columns: 1fr;
+ }
+
.section-header {
flex-direction: column;
align-items: flex-start;
diff --git a/src/utils/diff.test.ts b/src/utils/diff.test.ts
new file mode 100644
index 0000000..8a66ca6
--- /dev/null
+++ b/src/utils/diff.test.ts
@@ -0,0 +1,32 @@
+import { describe, expect, it } from 'vitest';
+import { diffValues } from './diff';
+
+describe('diffValues', () => {
+ it('reports changed primitive values by path', () => {
+ expect(diffValues({ name: 'Alice' }, { name: 'Bob' })).toEqual([
+ { path: 'name', kind: 'changed', before: 'Alice', after: 'Bob' },
+ ]);
+ });
+
+ it('reports nested added and removed values', () => {
+ expect(
+ diffValues(
+ { user: { name: 'Alice', age: 30 } },
+ { user: { name: 'Alice', plan: 'pro' } },
+ ),
+ ).toEqual([
+ { path: 'user.age', kind: 'removed', before: 30 },
+ { path: 'user.plan', kind: 'added', after: 'pro' },
+ ]);
+ });
+
+ it('formats changed array items with indexed paths', () => {
+ expect(diffValues({ items: ['starter', 'basic'] }, { items: ['starter', 'pro'] })).toEqual([
+ { path: 'items[1]', kind: 'changed', before: 'basic', after: 'pro' },
+ ]);
+ });
+
+ it('returns no entries for equivalent values', () => {
+ expect(diffValues({ ok: true, data: [1, 2] }, { ok: true, data: [1, 2] })).toEqual([]);
+ });
+});
diff --git a/src/utils/diff.ts b/src/utils/diff.ts
new file mode 100644
index 0000000..fd909a1
--- /dev/null
+++ b/src/utils/diff.ts
@@ -0,0 +1,88 @@
+export type DiffKind = "added" | "removed" | "changed";
+
+export type DiffEntry = {
+ path: string;
+ kind: DiffKind;
+ before?: unknown;
+ after?: unknown;
+};
+
+type FlatValueMap = Map