Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/diff/base-unified-diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,17 +135,20 @@ export abstract class BaseUnifiedDiffManager {
/**
* Accept all changes
*/
protected acceptAll(): void {
// simply accept the current state
public acceptAll(): void {
const sharedModel = this.getSharedModel();
this._originalSource = sharedModel.getSource();
this._newSource = this._originalSource;
this._deactivate();
}

/**
* Reject all changes
*/
protected rejectAll(): void {
public rejectAll(): void {
const sharedModel = this.getSharedModel();
sharedModel.setSource(this._originalSource);
this._newSource = this._originalSource;
this._deactivate();
}

Expand Down Expand Up @@ -184,9 +187,9 @@ export abstract class BaseUnifiedDiffManager {
protected showActionButtons: boolean;
protected acceptAllButton: ToolbarButton | null = null;
protected rejectAllButton: ToolbarButton | null = null;
private _originalSource: string;
private _newSource: string;
private _isInitialized: boolean;
private _isDisposed: boolean;
private _diffCompartment: Compartment;
public _originalSource: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this attribute need to be public?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes i think we need to use it as public as we are using _originalSource in hasPendingChanges() in unified-cell.ts.

}
27 changes: 19 additions & 8 deletions src/diff/unified-cell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,19 @@ export class UnifiedCellDiffManager extends BaseUnifiedDiffManager {
super(options);
this._cell = options.cell;
this._cellFooterTracker = options.cellFooterTracker;
this._originalSource = options.originalSource ?? '';
this.activate();
}

private static _activeDiffCount = 0;

/**
* Check if this cell still has pending changes
*/
public hasPendingChanges(): boolean {
return this._originalSource !== this._cell.model.sharedModel.getSource();
}

/**
* Get the shared model for source manipulation
*/
Expand Down Expand Up @@ -112,27 +120,30 @@ export class UnifiedCellDiffManager extends BaseUnifiedDiffManager {
return;
}

const cellId = this._cell.id;
if (!this.hasPendingChanges()) {
this.removeToolbarButtons();
return;
}

const cellId = this._cell.model.id;
const footer = this._cellFooterTracker.getFooter(cellId);
if (!footer) {
return;
}

this.acceptAllButton = new ToolbarButton({
icon: checkIcon,
label: this.trans.__('Accept All'),
tooltip: this.trans.__('Accept all chunks'),
label: this.trans.__('Accept'),
tooltip: this.trans.__('Accept changes in this cell'),
enabled: true,
className: 'jp-UnifiedDiff-acceptAll',
onClick: () => this.acceptAll()
});

this.rejectAllButton = new ToolbarButton({
icon: undoIcon,
label: this.trans.__('Reject All'),
tooltip: this.trans.__('Reject all chunks'),
label: this.trans.__('Reject'),
tooltip: this.trans.__('Reject changes in this cell'),
enabled: true,
className: 'jp-UnifiedDiff-rejectAll',
onClick: () => this.rejectAll()
});

Expand All @@ -155,7 +166,7 @@ export class UnifiedCellDiffManager extends BaseUnifiedDiffManager {
return;
}

const cellId = this._cell.id;
const cellId = this._cell.model.id;
const footer = this._cellFooterTracker.getFooter(cellId);
if (!footer) {
return;
Expand Down
92 changes: 92 additions & 0 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,29 @@ export function findCell(
return cell ?? null;
}

/**
* Registry for notebook-level diff managers
*/
const notebookDiffRegistry = new Map<string, UnifiedCellDiffManager[]>();

let registerCellManager = (
notebookId: string,
manager: UnifiedCellDiffManager
): void => {
if (!notebookDiffRegistry.has(notebookId)) {
notebookDiffRegistry.set(notebookId, []);
}
notebookDiffRegistry.get(notebookId)!.push(manager);
};

function getNotebookManagers(notebookId: string) {
return notebookDiffRegistry.get(notebookId) || [];
}

function clearNotebookManagers(notebookId: string) {
notebookDiffRegistry.delete(notebookId);
}

/**
* Split cell diff plugin - shows side-by-side comparison
*/
Expand Down Expand Up @@ -283,7 +306,76 @@ const unifiedCellDiffPlugin: JupyterFrontEndPlugin<void> = {
trans
});
cellDiffManagers.set(cell.id, manager);

registerCellManager(currentNotebook.id, manager);
}
});
notebookTracker.widgetAdded.connect((sender, notebookPanel) => {
const notebookId = notebookPanel.id;

let floatingPanel: HTMLElement | null = null;

function createFloatingPanel(): HTMLElement {
const panel = document.createElement('div');
panel.classList.add('jp-unified-diff-floating-panel');

const acceptButton = document.createElement('button');
acceptButton.classList.add('jp-merge-accept-button');
acceptButton.textContent = 'Accept All';
acceptButton.title = trans.__('Accept all changes in this notebook');
acceptButton.onclick = () => {
getNotebookManagers(notebookId).forEach(m => m.acceptAll());
updateFloatingPanel();
};

const rejectButton = document.createElement('button');
rejectButton.classList.add('jp-merge-reject-button');
rejectButton.textContent = 'Reject All';
rejectButton.title = trans.__('Reject all changes in this notebook');
rejectButton.onclick = () => {
getNotebookManagers(notebookId).forEach(m => m.rejectAll());
updateFloatingPanel();
};

panel.appendChild(acceptButton);
panel.appendChild(rejectButton);
return panel;
}

function updateFloatingPanel(): void {
const managers = getNotebookManagers(notebookId);
const anyPending = managers.some(m => m.hasPendingChanges());

if (!anyPending) {
if (floatingPanel && floatingPanel.parentElement) {
floatingPanel.parentElement.removeChild(floatingPanel);
}
floatingPanel = null;
return;
}

if (!floatingPanel) {
floatingPanel = createFloatingPanel();
notebookPanel.node.appendChild(floatingPanel);
}
}

notebookPanel.disposed.connect(() => {
clearNotebookManagers(notebookId);
if (floatingPanel && floatingPanel.parentElement) {
floatingPanel.parentElement.removeChild(floatingPanel);
}
floatingPanel = null;
});

notebookPanel.node.addEventListener('diff-updated', updateFloatingPanel);

const originalRegister = registerCellManager;
registerCellManager = (nid: string, manager: UnifiedCellDiffManager) => {
originalRegister(nid, manager);
const event = new Event('diff-updated');
notebookPanel.node.dispatchEvent(event);
};
});
}
};
Expand Down
44 changes: 44 additions & 0 deletions style/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,47 @@
background-color: var(--jp-layout-color3);
border-color: var(--jp-border-color1);
}

/* Floating unified-cell-diff control panel */
.jp-unified-diff-floating-panel {
position: absolute;
bottom: 16px;
right: 16px;
display: flex;
gap: 12px;
background: var(--jp-layout-color1);
border: 1px solid var(--jp-border-color2);
padding: 8px 12px;
box-shadow: 0 2px 8px #00000040;
z-index: 200;
opacity: 0.95;
transition: opacity 0.2s ease-in-out;
}

.jp-unified-diff-floating-panel .jp-merge-accept-button,
.jp-unified-diff-floating-panel .jp-merge-reject-button {
padding: 12px 10px;
font-size: 13px;
border-radius: 2px;
cursor: pointer;
color: white;
font-weight: inherit;
border: none;
transition: background-color 0.2s ease-in-out;
}

.jp-unified-diff-floating-panel .jp-merge-accept-button {
background-color: var(--jp-success-color1);
}

.jp-unified-diff-floating-panel .jp-merge-accept-button:hover {
background-color: var(--jp-success-color0);
}

.jp-unified-diff-floating-panel .jp-merge-reject-button {
background-color: var(--jp-error-color1);
}

.jp-unified-diff-floating-panel .jp-merge-reject-button:hover {
background-color: var(--jp-error-color0);
}
Loading