Skip to content

Commit

Permalink
Implementing Collaborative Timeline Slider with Undo/Redo Functionali…
Browse files Browse the repository at this point in the history
…ty (#338)

* collaborative timeline slider added in the status bar

dependency conflicts

fixed timestamp order

fork handler added

connect fork

read only notebook connected (fork)

fork document in frontend

fork in the frontend + apply updates

added fork indication to the page

added fork indication to the page

distinction between fork and original document added to slider

adjustement in fork handler

file

add css slider

context menu option for opening file (fork) + timeline added

notebook condition added

file menu option to open timeline for notebook

alternative: add file menu option to open right widget

added timeline slider to status bar

slider added to status bar

status bar timeline slider with logic to switch between notebooks added

error handling in the backend handler

clean console

add isActive to the registerStatusItem

fix: internal server error fixed in updateTimelineforNotebook function

fork cnd added to timeline status bar plugin

undo manager refactoring

fork logic for 1 notebook.

fork logic implemented for multiple notebooks opened at the same time

ydoc.share

fork works with multiple notebooks + freezing notebook when sliding through timeline

notebook id

testing cad

undo manager tests

clearDoc method

remove is timelineFork open

rebase

build error resolved

using undo manager in the backend

undo/redo steps added

freeze document

updated to the new version of pycrdt, fixed problem with undostack

rebase

restore version btn

undo manager agnostic for different type of documents

restoring version

restoring version

restore btn : fix style

delete unused files

delete unused files

jupytercad

jupyter cad

jupytercad integration + rebase main

conflicts

conflicts

fixed console error: empty response.

icon visible only when data is not null

moving fetch timeline in slider component: on click of the history button

get format & content type from query params

get format & content type from query params: fix updating contenttype and format when switching between documents

remove sharedmodel from update content

support for documents inside folders/subfolders.

clean drive.ts

move test files in folder

delete unused dependency

return comments deleted by accident

fixes in jupyter-server-ydoc

delete test documents

add test-folder to gitignore

styling restore button

styling restore button

pre commit hooks

fixed pre commit issues

fixed pre commit issues

add license header to new files

pre commit hooks

python test: added jupytercad_core to CI/CD workflow

python test debug

python test debug

collaborative timeline slider added in the status bar

dependency conflicts

fixed timestamp order

fork handler added

connect fork

read only notebook connected (fork)

fork document in frontend

fork in the frontend + apply updates

added fork indication to the page

added fork indication to the page

distinction between fork and original document added to slider

adjustement in fork handler

file

add css slider

context menu option for opening file (fork) + timeline added

notebook condition added

file menu option to open timeline for notebook

alternative: add file menu option to open right widget

added timeline slider to status bar

slider added to status bar

status bar timeline slider with logic to switch between notebooks added

error handling in the backend handler

clean console

add isActive to the registerStatusItem

fix: internal server error fixed in updateTimelineforNotebook function

fork cnd added to timeline status bar plugin

undo manager refactoring

fork logic for 1 notebook.

fork logic implemented for multiple notebooks opened at the same time

ydoc.share

fork works with multiple notebooks + freezing notebook when sliding through timeline

notebook id

testing cad

undo manager tests

clearDoc method

remove is timelineFork open

rebase

build error resolved

using undo manager in the backend

undo/redo steps added

freeze document

updated to the new version of pycrdt, fixed problem with undostack

rebase

restore version btn

undo manager agnostic for different type of documents

restoring version

restoring version

restore btn : fix style

delete unused files

delete unused files

jupytercad

jupyter cad

jupytercad integration + rebase main

conflicts

conflicts

fixed console error: empty response.

icon visible only when data is not null

moving fetch timeline in slider component: on click of the history button

get format & content type from query params

get format & content type from query params: fix updating contenttype and format when switching between documents

remove sharedmodel from update content

support for documents inside folders/subfolders.

clean drive.ts

move test files in folder

delete unused dependency

return comments deleted by accident

fixes in jupyter-server-ydoc

delete test documents

add test-folder to gitignore

styling restore button

styling restore button

pre commit hooks

fixed pre commit issues

python test debug: test.yaml

python test debug: test.yaml

python test debug: test.yaml

changed order of dependencies in test.yml

removed jupytercad to test dependencies version

removed jupytercad to test dependencies version

pre commit

changed the way document types are imported in the backend

fixed yarn.lock after rebase

* added code changes as requested.

* fixed issue with opening timeline after closing notebook & generic type in handler

* slider set to undo stack updates, improved fork handling

* remove global variable keeping track of undo managers

remove global variable keeping track of undo managers

remove print

* reconnect & refetching timeline after restore.

* add loggin and error handling in handlers.

* removed unused code in restore endpoint.

* update pycrdt-websocket version requirement.

* update pycrdt-websocket version requirement.

* styling issues resolved.

* remove widget attachement to body.
  • Loading branch information
Meriem-BenIsmail authored Sep 4, 2024
1 parent 2ca6b0f commit 1492678
Show file tree
Hide file tree
Showing 16 changed files with 2,354 additions and 1,334 deletions.
101 changes: 98 additions & 3 deletions packages/docprovider-extension/src/filebrowser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ import {
JupyterFrontEndPlugin
} from '@jupyterlab/application';
import { Dialog, showDialog } from '@jupyterlab/apputils';
import { IDocumentWidget } from '@jupyterlab/docregistry';
import { DocumentWidget, IDocumentWidget } from '@jupyterlab/docregistry';
import { Widget } from '@lumino/widgets';
import {
FileBrowser,
IDefaultFileBrowser,
IFileBrowserFactory
} from '@jupyterlab/filebrowser';
import { IStatusBar } from '@jupyterlab/statusbar';

import { IEditorTracker } from '@jupyterlab/fileeditor';
import { ILogger, ILoggerRegistry } from '@jupyterlab/logconsole';
import { INotebookTracker } from '@jupyterlab/notebook';
Expand All @@ -28,17 +31,21 @@ import { YFile, YNotebook } from '@jupyter/ydoc';

import {
ICollaborativeDrive,
IForkProvider,
IGlobalAwareness,
TimelineWidget,
YDrive
} from '@jupyter/docprovider';
import { Awareness } from 'y-protocols/awareness';
import { URLExt } from '@jupyterlab/coreutils';

/**
* The command IDs used by the file browser plugin.
*/
namespace CommandIDs {
export const openPath = 'filebrowser:open-path';
}
const DOCUMENT_TIMELINE_URL = 'api/collaboration/timeline';

/**
* The default collaborative drive provider.
Expand Down Expand Up @@ -91,7 +98,7 @@ export const ynotebook: JupyterFrontEndPlugin<void> = {
optional: [ISettingRegistry],
activate: (
app: JupyterFrontEnd,
drive: ICollaborativeDrive,
drive: YDrive,
settingRegistry: ISettingRegistry | null
): void => {
let disableDocumentWideUndoRedo = true;
Expand Down Expand Up @@ -127,6 +134,93 @@ export const ynotebook: JupyterFrontEndPlugin<void> = {
);
}
};
/**
* A plugin to add a timeline slider status item to the status bar.
*/
export const statusBarTimeline: JupyterFrontEndPlugin<void> = {
id: '@jupyter/docprovider-extension:statusBarTimeline',
description: 'Plugin to add a timeline slider to the status bar',
autoStart: true,
requires: [IStatusBar, ICollaborativeDrive],
activate: async (
app: JupyterFrontEnd,
statusBar: IStatusBar,
drive: ICollaborativeDrive
): Promise<void> => {
function isYDrive(drive: YDrive | ICollaborativeDrive): drive is YDrive {
return 'getProviderForPath' in drive;
}
try {
let sliderItem: Widget | null = null;
let timelineWidget: TimelineWidget | null = null;

const updateTimelineForDocument = async (documentPath: string) => {
if (drive && isYDrive(drive)) {
// Dispose of the previous timelineWidget if it exists
if (timelineWidget) {
timelineWidget.dispose();
timelineWidget = null;
}

const provider = (await drive.getProviderForPath(
documentPath
)) as IForkProvider;
const fullPath = URLExt.join(
app.serviceManager.serverSettings.baseUrl,
DOCUMENT_TIMELINE_URL,
documentPath.split(':')[1]
);

timelineWidget = new TimelineWidget(
fullPath,
provider,
provider.contentType,
provider.format
);

const elt = document.getElementById('jp-slider-status-bar');
if (elt && !timelineWidget.isAttached) {
Widget.attach(timelineWidget, elt);
}
}
};

if (app.shell.currentChanged) {
app.shell.currentChanged.connect(async (_, args) => {
const currentWidget = args.newValue as DocumentWidget;
if (timelineWidget) {
// Dispose of the timelineWidget when the document is closed
timelineWidget.dispose();
timelineWidget = null;
}
if (currentWidget && 'context' in currentWidget) {
await updateTimelineForDocument(currentWidget.context.path);
}
});
}

if (statusBar) {
if (!sliderItem) {
sliderItem = new Widget();
sliderItem.addClass('jp-StatusBar-GroupItem');
sliderItem.addClass('jp-mod-highlighted');
sliderItem.id = 'jp-slider-status-bar';
statusBar.registerStatusItem('jp-slider-status-bar', {
item: sliderItem,
align: 'left',
rank: 4,
isActive: () => {
const currentWidget = app.shell.currentWidget;
return !!currentWidget && 'context' in currentWidget;
}
});
}
}
} catch (error) {
console.error('Failed to activate statusBarTimeline plugin:', error);
}
}
};

/**
* The default file browser factory provider.
Expand All @@ -144,7 +238,7 @@ export const defaultFileBrowser: JupyterFrontEndPlugin<IDefaultFileBrowser> = {
],
activate: async (
app: JupyterFrontEnd,
drive: ICollaborativeDrive,
drive: YDrive,
fileBrowserFactory: IFileBrowserFactory,
router: IRouter | null,
tree: JupyterFrontEnd.ITreeResolver | null,
Expand Down Expand Up @@ -292,6 +386,7 @@ namespace Private {
router.routed.disconnect(listener);

const paths = await tree?.paths;

if (paths?.file || paths?.browser) {
// Restore the model without populating it.
await browser.model.restore(browser.id, false);
Expand Down
6 changes: 4 additions & 2 deletions packages/docprovider-extension/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
yfile,
ynotebook,
defaultFileBrowser,
logger
logger,
statusBarTimeline
} from './filebrowser';
import { notebookCellExecutor } from './executor';

Expand All @@ -25,7 +26,8 @@ const plugins: JupyterFrontEndPlugin<any>[] = [
ynotebook,
defaultFileBrowser,
logger,
notebookCellExecutor
notebookCellExecutor,
statusBarTimeline
];

export default plugins;
50 changes: 50 additions & 0 deletions packages/docprovider/src/TimelineSlider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* -----------------------------------------------------------------------------
| Copyright (c) Jupyter Development Team.
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/

import { ReactWidget } from '@jupyterlab/apputils';
import { TimelineSliderComponent } from './component';
import * as React from 'react';
import { IForkProvider } from './ydrive';

export class TimelineWidget extends ReactWidget {
private apiURL: string;
private provider: IForkProvider;
private contentType: string;
private format: string;

constructor(
apiURL: string,
provider: IForkProvider,
contentType: string,
format: string
) {
super();
this.apiURL = apiURL;
this.provider = provider;
this.contentType = contentType;
this.format = format;
this.addClass('jp-timelineSliderWrapper');
}

render(): JSX.Element {
return (
<TimelineSliderComponent
key={this.apiURL}
apiURL={this.apiURL}
provider={this.provider}
contentType={this.contentType}
format={this.format}
/>
);
}
updateContent(apiURL: string, provider: IForkProvider): void {
this.apiURL = apiURL;
this.provider = provider;
this.contentType = this.provider.contentType;
this.format = this.provider.format;

this.update();
}
}
Loading

0 comments on commit 1492678

Please sign in to comment.