зеркало из https://github.com/microsoft/gather.git
UI: Fixed marker deletion bug, updated gather icons
This commit is contained in:
Родитель
60cb46c4b5
Коммит
b969a7fa0a
|
@ -9,6 +9,9 @@ Download the extension with one command:
|
|||
jupyter labextension install gather
|
||||
```
|
||||
|
||||
If you are seeing installation errors, make sure that npm
|
||||
and Jupyter Lab are up-to-date.
|
||||
|
||||
Read the docs [here](https://microsoft.github.io/gather).
|
||||
And read our academic paper on the design of the tool
|
||||
[here](https://people.eecs.berkeley.edu/~andrewhead/pdf/notebooks.pdf).
|
||||
|
@ -47,6 +50,10 @@ will continue to live after the tests finish running---it
|
|||
will recompile and re-run the tests whenever the test code
|
||||
changes. Type Ctrl+C to abort the command at any time.
|
||||
|
||||
Note that running tests with this command may interfere with
|
||||
you opening Chrome browsers. If that happens, cancel the
|
||||
command, open Chrome, and then restart the command.
|
||||
|
||||
To debug the tests, call:
|
||||
|
||||
```bash
|
||||
|
|
|
@ -436,7 +436,6 @@ class CallNamesListener implements ast.IWalkListener {
|
|||
this._statement = statement;
|
||||
}
|
||||
|
||||
// TODO: Include the level of each name...
|
||||
onEnterNode(node: ast.ISyntaxNode, type: string, ancestors: ast.ISyntaxNode[]) {
|
||||
if (type == ast.CALL) {
|
||||
let callNode = node as ast.ICall;
|
||||
|
@ -530,7 +529,6 @@ class TargetsDefListener implements ast.IWalkListener {
|
|||
this._statement = statement;
|
||||
}
|
||||
|
||||
// TODO: Include the level of each name...
|
||||
onEnterNode(node: ast.ISyntaxNode, type: string, ancestors: ast.ISyntaxNode[]) {
|
||||
if (type == ast.NAME) {
|
||||
let level = ReferenceType.DEFINITION;
|
||||
|
|
|
@ -34,14 +34,10 @@ export class Clipboard {
|
|||
return this._copied;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(andrewhead): selected outputs should be passed as arguments to this function too.
|
||||
*/
|
||||
copy(slice: SlicedExecution) {
|
||||
copy(slice: SlicedExecution, outputSelections?: OutputSelection[]) {
|
||||
const JUPYTER_CELL_MIME = 'application/vnd.jupyter.cells';
|
||||
if (slice) {
|
||||
// let cellJson = sliceToCellJson(slice, this._gatherModel.selectedOutputs.concat());
|
||||
let cellJson = getCellsJsonForSlice(slice, []);
|
||||
let cellJson = getCellsJsonForSlice(slice, outputSelections);
|
||||
const clipboard = JupyterClipboard.getInstance();
|
||||
clipboard.clear();
|
||||
clipboard.setData(JUPYTER_CELL_MIME, cellJson);
|
||||
|
@ -75,9 +71,6 @@ export class ScriptOpener {
|
|||
}
|
||||
|
||||
openScriptForSlice(slice: SlicedExecution) {
|
||||
/*
|
||||
* TODO(andrewhead): give the document a context-sensitive name, say the name of the result.
|
||||
*/
|
||||
this._documentManager.newUntitled({ ext: 'py' }).then(model => {
|
||||
let kernelSpec = _createKernelSpecForCurrentWidget(this._notebooks);
|
||||
let editor = this._documentManager.open(model.path, undefined, kernelSpec) as IDocumentWidget<FileEditor>;
|
||||
|
@ -107,9 +100,6 @@ export class NotebookOpener {
|
|||
}
|
||||
|
||||
openNotebookForSlice(slice: SlicedExecution, outputSelections?: OutputSelection[]) {
|
||||
/*
|
||||
* TODO(andrewhead): give the document a context-sensitive name, say the name of the result.
|
||||
*/
|
||||
this._documentManager.newUntitled({ ext: 'ipynb' }).then(model => {
|
||||
let kernelSpec = _createKernelSpecForCurrentWidget(this._notebooks);
|
||||
const widget = this._documentManager.open(model.path, undefined, kernelSpec) as NotebookPanel;
|
||||
|
|
|
@ -13,7 +13,7 @@ import { ExecutionLogSlicer } from '../analysis/slice/log-slicer';
|
|||
import { GatherController, GatherModel, GatherState, SliceSelection } from '../model';
|
||||
import { GatherModelRegistry, getGatherModelForActiveNotebook } from '../model/gather-registry';
|
||||
import { NotifactionExtension as NotificationExtension } from '../overlay/notification';
|
||||
import { ResultsHighlighter } from '../overlay/result-highlighter';
|
||||
import { CellChangeListener } from '../overlay/cell-listener';
|
||||
import { initToolbar } from '../overlay/toolbar';
|
||||
import { MarkerManager } from '../overlay/variable-markers';
|
||||
import { loadHistory as loadHistory } from '../persistence/load';
|
||||
|
@ -21,6 +21,7 @@ import { storeHistory } from '../persistence/store';
|
|||
import { initLogger, log } from '../util/log';
|
||||
import { ExecutionLogger } from './execution-logger';
|
||||
import { Clipboard } from './gather-actions';
|
||||
import { RevisionBrowser } from '../overlay/revision-browser';
|
||||
|
||||
|
||||
const extension: JupyterLabPlugin<void> = {
|
||||
|
@ -37,8 +38,9 @@ const extension: JupyterLabPlugin<void> = {
|
|||
*/
|
||||
export class CodeGatheringExtension implements DocumentRegistry.IWidgetExtension<NotebookPanel, INotebookModel> {
|
||||
|
||||
constructor(documentManager: DocumentManager, notebooks: INotebookTracker,
|
||||
constructor(app: JupyterLab, documentManager: DocumentManager, notebooks: INotebookTracker,
|
||||
gatherModelRegistry: GatherModelRegistry) {
|
||||
this._app = app;
|
||||
this._documentManager = documentManager;
|
||||
this._notebooks = notebooks;
|
||||
this._gatherModelRegistry = gatherModelRegistry;
|
||||
|
@ -59,10 +61,10 @@ export class CodeGatheringExtension implements DocumentRegistry.IWidgetExtension
|
|||
* update the UI automatically as we populate the log.
|
||||
*/
|
||||
this._toolbarWidgets = initToolbar(notebook, gatherModel, this);
|
||||
let markerManager = new MarkerManager(gatherModel, notebook);
|
||||
new ResultsHighlighter(gatherModel, notebook, markerManager);
|
||||
new MarkerManager(gatherModel, notebook);
|
||||
new CellChangeListener(gatherModel, notebook);
|
||||
new GatherController(gatherModel, this._documentManager, this._notebooks);
|
||||
|
||||
|
||||
this._gatherModelRegistry.addGatherModel(notebookModel, gatherModel);
|
||||
new ExecutionLogger(notebook, gatherModel);
|
||||
saveHistoryOnNotebookSave(notebook, gatherModel);
|
||||
|
@ -108,7 +110,15 @@ export class CodeGatheringExtension implements DocumentRegistry.IWidgetExtension
|
|||
}
|
||||
}
|
||||
|
||||
gatherRevisions() {
|
||||
let gatherModel = getGatherModelForActiveNotebook(this._notebooks, this._gatherModelRegistry);
|
||||
let revisionBrowser = new RevisionBrowser(gatherModel);
|
||||
this._app.shell.addToMainArea(revisionBrowser);
|
||||
this._app.shell.activateById(revisionBrowser.id);
|
||||
}
|
||||
|
||||
private _toolbarWidgets: Widget[];
|
||||
private _app: JupyterLab;
|
||||
private _documentManager: DocumentManager;
|
||||
private _notebooks: INotebookTracker;
|
||||
private _gatherModelRegistry: GatherModelRegistry;
|
||||
|
@ -130,11 +140,11 @@ function activateExtension(app: JupyterLab, palette: ICommandPalette, notebooks:
|
|||
const notificationExtension = new NotificationExtension();
|
||||
app.docRegistry.addWidgetExtension('Notebook', notificationExtension);
|
||||
Clipboard.getInstance().copied.connect(() => {
|
||||
notificationExtension.showMessage("Copied cells to clipboard.");
|
||||
notificationExtension.showMessage("Copied cells to clipboard. Type 'V' to paste.");
|
||||
});
|
||||
|
||||
let gatherModelRegistry = new GatherModelRegistry();
|
||||
let codeGatheringExtension = new CodeGatheringExtension(documentManager, notebooks, gatherModelRegistry);
|
||||
let codeGatheringExtension = new CodeGatheringExtension(app, documentManager, notebooks, gatherModelRegistry);
|
||||
app.docRegistry.addWidgetExtension('Notebook', codeGatheringExtension);
|
||||
|
||||
function addCommand(command: string, label: string, execute: (options?: JSONObject) => void) {
|
||||
|
@ -154,36 +164,12 @@ function activateExtension(app: JupyterLab, palette: ICommandPalette, notebooks:
|
|||
codeGatheringExtension.gatherToScript();
|
||||
});
|
||||
|
||||
// TODO: re-enable this as an optional feature.
|
||||
/*
|
||||
addCommand('gather:gatherFromHistory', 'Compare previous versions of this result', () => {
|
||||
|
||||
const panel = notebooks.currentWidget;
|
||||
if (panel && panel.content && panel.content.activeCell.model.type === 'code') {
|
||||
const activeCell = panel.content.activeCell;
|
||||
let slicer = executionLogger.executionSlicer;
|
||||
let cellModel = activeCell.model as ICodeCellModel;
|
||||
let slicedExecutions = slicer.sliceAllExecutions(new LabCell(cellModel));
|
||||
// TODO: Update this with a real gather-model and real output renderer.
|
||||
let historyModel = buildHistoryModel<IOutputModel>(new GatherModel(), activeCell.model.id, slicedExecutions);
|
||||
|
||||
let widget = new HistoryViewer({
|
||||
model: historyModel,
|
||||
outputRenderer: { render: () => null }
|
||||
});
|
||||
|
||||
if (!widget.isAttached) {
|
||||
app.shell.addToMainArea(widget);
|
||||
}
|
||||
app.shell.activateById(widget.id);
|
||||
}
|
||||
codeGatheringExtension.gatherRevisions();
|
||||
});
|
||||
*/
|
||||
|
||||
// settingRegistry.set("gather:plugin", "gatheringConfig", { email: "andrewhead@berkeley.edu" });
|
||||
|
||||
initLogger(settingRegistry);
|
||||
console.log('Code gathering tools have been activated.');
|
||||
console.log('Gathering tools have been activated.');
|
||||
}
|
||||
|
||||
export default extension;
|
||||
|
|
|
@ -35,7 +35,7 @@ export class GatherController implements IGatherObserver {
|
|||
let mergedSlice = slices[0].merge(...slices.slice(1));
|
||||
if (newState == GatherState.GATHER_TO_CLIPBOARD) {
|
||||
log("Gathering to clipboard", { slice: mergedSlice });
|
||||
this._cellClipboard.copy(mergedSlice);
|
||||
this._cellClipboard.copy(mergedSlice, [...model.selectedOutputs]);
|
||||
} else if (newState == GatherState.GATHER_TO_NOTEBOOK) {
|
||||
log("Gathering to notebook", { slice: mergedSlice });
|
||||
if (this._notebookOpener !== undefined) {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
import { CodeCellModel, ICellModel } from "@jupyterlab/cells";
|
||||
import { NotebookPanel } from "@jupyterlab/notebook";
|
||||
import { IObservableList } from "@jupyterlab/observables";
|
||||
import { GatherModel } from "../model";
|
||||
import { LabCell } from "../model/cell";
|
||||
|
||||
/**
|
||||
* Listens to cell executions and edits.
|
||||
*/
|
||||
export class CellChangeListener {
|
||||
|
||||
private _gatherModel: GatherModel;
|
||||
|
||||
constructor(gatherModel: GatherModel, panel: NotebookPanel) {
|
||||
this._gatherModel = gatherModel;
|
||||
|
||||
for (let i = 0; i < panel.content.model.cells.length; i++) {
|
||||
this.registerCell(panel.content.model.cells.get(i));
|
||||
}
|
||||
|
||||
panel.content.model.cells.changed.connect((_, change) => this.registerAddedCells(change), this);
|
||||
}
|
||||
|
||||
private registerCell(cell: ICellModel) {
|
||||
if (cell.type !== 'code') { return; }
|
||||
/**
|
||||
* A cell will be considered edited whenever any of its contents changed, including
|
||||
* execution count, metadata, outputs, text, etc.
|
||||
*/
|
||||
cell.contentChanged.connect((changedCell, _) => {
|
||||
if (changedCell instanceof CodeCellModel) {
|
||||
this._gatherModel.lastEditedCell = new LabCell(changedCell);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public registerAddedCells(cellListChange: IObservableList.IChangedArgs<ICellModel>): void {
|
||||
if (cellListChange.type === 'add' || cellListChange.type === 'remove') {
|
||||
const cellModel = cellListChange.newValues[0] as ICellModel;
|
||||
if (cellListChange.type === 'add') {
|
||||
this.registerCell(cellModel);
|
||||
} else if (cellListChange.type === 'remove') {
|
||||
if (cellModel instanceof CodeCellModel) {
|
||||
this._gatherModel.lastDeletedCell = new LabCell(cellModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
import { IClientSession } from "@jupyterlab/apputils";
|
||||
import { CodeCellModel, ICellModel } from "@jupyterlab/cells";
|
||||
import { Notebook, NotebookPanel } from "@jupyterlab/notebook";
|
||||
import { IObservableList } from "@jupyterlab/observables";
|
||||
import { MarkerManager } from "./variable-markers";
|
||||
import { GatherModel } from "../model";
|
||||
import { LabCell } from "../model/cell";
|
||||
|
||||
/**
|
||||
* Highlights gatherable entities.
|
||||
*/
|
||||
export class ResultsHighlighter {
|
||||
|
||||
private _markerManager: MarkerManager;
|
||||
private _gatherModel: GatherModel;
|
||||
|
||||
constructor(gatherModel: GatherModel, panel: NotebookPanel, markerManager: MarkerManager) {
|
||||
this._markerManager = markerManager;
|
||||
this._gatherModel = gatherModel;
|
||||
|
||||
panel.content.model.cells.changed.connect(
|
||||
(cells, value) =>
|
||||
this.onCellsChanged(panel.content, panel.session, cells, value),
|
||||
this);
|
||||
|
||||
document.body.addEventListener("mouseup", (event: MouseEvent) => {
|
||||
this._markerManager.handleClick(event);
|
||||
});
|
||||
}
|
||||
|
||||
public onCellsChanged(
|
||||
notebook: Notebook,
|
||||
_: IClientSession,
|
||||
__: IObservableList<ICellModel>,
|
||||
cellListChange: IObservableList.IChangedArgs<ICellModel>
|
||||
): void {
|
||||
if (cellListChange.type === 'add') {
|
||||
const cellModel = cellListChange.newValues[0] as ICellModel;
|
||||
if (cellModel.type !== 'code') { return; }
|
||||
|
||||
// When a cell is added, register for its state changes.
|
||||
cellModel.contentChanged.connect((changedCell, args) => {
|
||||
// TODO(andrewhead): check that this change is due to a user's text edit in the cell.
|
||||
if (changedCell instanceof CodeCellModel) {
|
||||
this._gatherModel.lastEditedCell = new LabCell(changedCell);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (cellListChange.type === 'remove') {
|
||||
const cellModel = cellListChange.newValues[0] as ICellModel;
|
||||
if (cellModel instanceof CodeCellModel) {
|
||||
this._gatherModel.lastDeletedCell = new LabCell(cellModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@ export function initToolbar(notebook: NotebookPanel, gatherModel: GatherModel,
|
|||
let labelLayout = gatherLabelWidget.layout = new PanelLayout();
|
||||
let gatherLabel = new Widget({ node: document.createElement("span") });
|
||||
gatherLabel.addClass('jp-GatherLabel');
|
||||
gatherLabel.node.textContent = "Gather to";
|
||||
gatherLabel.node.textContent = "Gather to:";
|
||||
labelLayout.addWidget(gatherLabel);
|
||||
notebook.toolbar.insertItem(insertIndex, "gatherLabel", gatherLabelWidget);
|
||||
return gatherLabelWidget;
|
||||
|
@ -55,6 +55,11 @@ export function initToolbar(notebook: NotebookPanel, gatherModel: GatherModel,
|
|||
let buttons = [
|
||||
new GatherToClipboardButton(gatherModel, codeGatheringExtension.gatherToClipboard.bind(codeGatheringExtension)),
|
||||
new GatherToNotebookButton(gatherModel, codeGatheringExtension.gatherToNotebook.bind(codeGatheringExtension)),
|
||||
/*
|
||||
* The following line adds a button for gathering code to a script.
|
||||
*/
|
||||
// new GatherToScriptButton(gatherModel, codeGatheringExtension.gatherToScript.bind(codeGatheringExtension)),
|
||||
new GatherRevisionsButton(gatherModel, codeGatheringExtension.gatherRevisions.bind(codeGatheringExtension)),
|
||||
new ClearButton(gatherModel)
|
||||
];
|
||||
for (let button of buttons) {
|
||||
|
@ -124,9 +129,9 @@ export class GatherToClipboardButton extends GatherButton {
|
|||
constructor(gatherModel: GatherModel, callback: () => void) {
|
||||
super("gatherToClipboard", gatherModel, {
|
||||
className: "jp-Toolbar-gathertoclipboardbutton",
|
||||
iconClassName: "jp-CopyIcon jp-Icon jp-Icon-16",
|
||||
tooltip: "Gather code to clipboard",
|
||||
label: "Clipboard",
|
||||
iconClassName: "jp-CellsIcon jp-Icon jp-Icon-16",
|
||||
tooltip: "Gather cells code to clipboard",
|
||||
label: "Cells",
|
||||
onClick: () => { this.onClick() }
|
||||
});
|
||||
this._callback = callback;
|
||||
|
@ -174,6 +179,77 @@ export class GatherToNotebookButton extends GatherButton {
|
|||
private _callback: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A button to gather code to a new notebook.
|
||||
*/
|
||||
export class GatherToScriptButton extends GatherButton {
|
||||
|
||||
constructor(gatherModel: GatherModel, callback: () => void) {
|
||||
super("gatherToScript", gatherModel, {
|
||||
className: "jp-Toolbar-gathertoscriptbutton",
|
||||
iconClassName: "jp-TextEditorIcon jp-Icon jp-Icon-16",
|
||||
tooltip: "Gather code to new script",
|
||||
label: "Script",
|
||||
onClick: () => { this.onClick() }
|
||||
});
|
||||
this._callback = callback;
|
||||
}
|
||||
|
||||
onClick() {
|
||||
if (this._gatherModel.selectedSlices.length >= 1) {
|
||||
log("Button: Clicked gather to script");
|
||||
this._callback();
|
||||
} else {
|
||||
log("Button: Clicked gather to script without selections");
|
||||
window.alert("Before you gather, click on one of the blue variable names, or one of the outputs with a blue border.");
|
||||
}
|
||||
}
|
||||
|
||||
private _callback: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A button to gather code to a new notebook.
|
||||
*/
|
||||
export class GatherRevisionsButton extends GatherButton {
|
||||
|
||||
constructor(gatherModel: GatherModel, callback: () => void) {
|
||||
super("gatherRevisions", gatherModel, {
|
||||
className: "jp-Toolbar-gatherrevisionsbutton",
|
||||
iconClassName: "jp-HistoryIcon jp-Icon jp-Icon-16",
|
||||
tooltip: "Gather revisions of this cell",
|
||||
label: "Version Browser",
|
||||
onClick: () => { this.onClick() }
|
||||
});
|
||||
this._callback = callback;
|
||||
}
|
||||
|
||||
protected _updateDisabled() {
|
||||
if (this._gatherModel.selectedSlices.length == 1) {
|
||||
this.addClass(HIGHLIGHTED_BUTTON_CLASS);
|
||||
this.removeClass(this.DISABLED_CLASS_NAME);
|
||||
} else {
|
||||
this.removeClass(HIGHLIGHTED_BUTTON_CLASS);
|
||||
this.addClass(this.DISABLED_CLASS_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
onClick() {
|
||||
if (this._gatherModel.selectedSlices.length == 1) {
|
||||
log("Button: Clicked gather to history with a selection");
|
||||
this._callback();
|
||||
} else if (this._gatherModel.selectedSlices.length == 0) {
|
||||
log("Button: Clicked gather to history without any selections");
|
||||
window.alert("Before bringing up a history, first click on one of the blue variables, or one of the outputs with a blue border.");
|
||||
} else if (this._gatherModel.selectedSlices.length > 1) {
|
||||
log("Button: Clicked gather to history with too many selections");
|
||||
window.alert("You cannot bring up a history if more than one blue variable name or output has been selected. Make sure only one variable or output is selectedß");
|
||||
}
|
||||
}
|
||||
|
||||
private _callback: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A button to clear the gathering selections.
|
||||
*/
|
||||
|
|
|
@ -24,8 +24,10 @@ export class DisplayData extends Widget {
|
|||
let layout = (this.layout = new PanelLayout());
|
||||
|
||||
// Code borrowed from OutputArea extension.
|
||||
// TODO(andrewhead): support other types of display data.
|
||||
// TODO(andrewhead): change second argument (preferSafe) based on display data field.
|
||||
/*
|
||||
* TODO(andrewhead): support other types of display data.
|
||||
* TODO(andrewhead): change second argument (preferSafe) based on display data field.
|
||||
*/
|
||||
if (nbformat.isExecuteResult(model) || nbformat.isDisplayData(model)) {
|
||||
let mimeType = rendermime.preferredMimeType(model.data, "ensure");
|
||||
let output = rendermime.createRenderer(mimeType);
|
||||
|
|
|
@ -43,10 +43,10 @@ export function buildHistoryModel(
|
|||
let recentCellVersion = latestCellVersions[cell.persistentId];
|
||||
let latestText: string = "";
|
||||
if (recentCellVersion) {
|
||||
latestText = recentCellVersion.textSlice;
|
||||
latestText = recentCellVersion.textSliceLines;
|
||||
}
|
||||
|
||||
let thisVersionText: string = cellSlice.textSlice;
|
||||
let thisVersionText: string = cellSlice.textSliceLines;
|
||||
let diff = computeTextDiff(latestText, thisVersionText);
|
||||
|
||||
let slicedCell: SlicedCellModel = new SlicedCellModel({
|
||||
|
|
|
@ -11,10 +11,6 @@ const HISTORY_VIEWER_ICON_CLASS = 'jp-HistoryViewerIcon';
|
|||
|
||||
const REFERENCE_VERSION_CLASS = 'jp-HistoryViewer-referenceversion';
|
||||
|
||||
const OLDER_VERSIONS_CLASS = 'jp-HistoryViewer-olderversions';
|
||||
|
||||
const OLDER_VERSIONS_CONTENTS_CLASS = 'jp-HistoryViewer-olderversions-revisions';
|
||||
|
||||
/**
|
||||
* A widget for showing the history of a result and how it was produced.
|
||||
*/
|
||||
|
@ -33,40 +29,30 @@ export class HistoryViewer extends Widget {
|
|||
this.title.closable = true;
|
||||
|
||||
this._model = options.model;
|
||||
// let rendermime = (this.rendermime = options.rendermime);
|
||||
|
||||
// Add revisions from most recent to oldest.
|
||||
let layout = (this.layout = new PanelLayout());
|
||||
|
||||
// Add pane for reference (most recent) version.
|
||||
let referenceVersion = new Widget({ node: document.createElement("div") });
|
||||
referenceVersion.addClass(REFERENCE_VERSION_CLASS);
|
||||
referenceVersion.layout = new PanelLayout();
|
||||
const now = new Date();
|
||||
(referenceVersion.layout as PanelLayout).addWidget(new Revision({
|
||||
let referenceVersion = new Revision({
|
||||
model: this._model.revisions[this._model.revisions.length - 1],
|
||||
outputRenderer: options.outputRenderer,
|
||||
now: now
|
||||
}));
|
||||
});
|
||||
referenceVersion.addClass(REFERENCE_VERSION_CLASS);
|
||||
layout.addWidget(referenceVersion);
|
||||
|
||||
// Add pane for older versions.
|
||||
if (this._model.revisions.length > 1) {
|
||||
let olderVersions = new Widget({ node: document.createElement("div") });
|
||||
olderVersions.addClass(OLDER_VERSIONS_CLASS);
|
||||
let olderVersionsContents = new Widget({ node: document.createElement("div") });
|
||||
olderVersionsContents.addClass(OLDER_VERSIONS_CONTENTS_CLASS);
|
||||
olderVersions.node.appendChild(olderVersionsContents.node);
|
||||
olderVersionsContents.layout = new PanelLayout();
|
||||
for (let i = this._model.revisions.length - 2; i >= 0; i--) {
|
||||
let revisionModel = this._model.revisions[i];
|
||||
(olderVersionsContents.layout as PanelLayout).addWidget(new Revision({
|
||||
layout.addWidget(new Revision({
|
||||
model: revisionModel,
|
||||
outputRenderer: options.outputRenderer,
|
||||
now: now
|
||||
}));
|
||||
}
|
||||
layout.addWidget(olderVersions);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,11 +63,6 @@ export class HistoryViewer extends Widget {
|
|||
return this._model;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rendermime instance used by the widget.
|
||||
*/
|
||||
// readonly rendermime: RenderMimeRegistry;
|
||||
|
||||
/**
|
||||
* Dispose of the resources used by the widget.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><path d="M22,3.375c0,-0.747 -0.607,-1.354 -1.354,-1.354l-17.292,0c-0.747,0 -1.354,0.607 -1.354,1.354l0,0.921c0,0.748 0.607,1.354 1.354,1.354l17.292,0c0.747,0 1.354,-0.606 1.354,-1.354l0,-0.921Z" style="fill:none;stroke:#5a5a5a;stroke-width:2px;"/><path d="M22,10.354c0,-1.2 -0.975,-2.175 -2.176,-2.175l-15.648,0c-1.201,0 -2.176,0.975 -2.176,2.175l0,1.481c0,1.2 0.975,2.175 2.176,2.175l15.648,0c1.201,0 2.176,-0.975 2.176,-2.175l0,-1.481Z" style="fill:none;stroke:#5a5a5a;stroke-width:2px;"/><path d="M22,18.584c0,-1.129 -0.916,-2.045 -2.045,-2.045l-15.91,0c-1.129,0 -2.045,0.916 -2.045,2.045l0,1.392c0,1.128 0.916,2.045 2.045,2.045l15.91,0c1.129,0 2.045,-0.917 2.045,-2.045l0,-1.392Z" style="fill:none;stroke:#5a5a5a;stroke-width:2px;"/></svg>
|
После Ширина: | Высота: | Размер: 1.2 KiB |
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg fill="#616161" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<circle cx="12" cy="12" r="2"/>
|
||||
<g>
|
||||
<circle cx="5" cy="12" r="2"/>
|
||||
<circle cx="19" cy="12" r="2"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 320 B |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><rect x="11.62" y="4.913" width="0.873" height="8" style="fill:none;stroke:#5a5a5a;stroke-width:2px;"/><rect x="6.826" y="12.037" width="5" height="0.873" style="fill:none;stroke:#5a5a5a;stroke-width:2px;"/><path d="M12.057,21.664c-5.303,0 -9.608,-4.305 -9.608,-9.607c0,-5.303 4.305,-9.608 9.608,-9.608c5.302,0 9.607,4.305 9.607,9.608" style="fill:none;stroke:#5a5a5a;stroke-width:2px;"/><path d="M20.075,14.949l0,6.059l-6.059,0l0,-6.059m8.603,-2.544l-11.147,0l0,11.086l11.086,0l0.061,-11.086Z" style="fill:#ef6c00;fill-rule:nonzero;"/><path d="M18.966,19.899l-1.886,-1.502l-1.955,1.502l0,-3.841l3.841,0l0,3.841Z" style="fill:#ef6c00;fill-rule:nonzero;"/></svg>
|
После Ширина: | Высота: | Размер: 1.1 KiB |
Двоичный файл не отображается.
|
@ -53,14 +53,18 @@ span.jp-GatherLabel {
|
|||
background-image: var(--jp-icon-book);
|
||||
}
|
||||
|
||||
.jp-CopyIcon {
|
||||
background-image: var(--jp-icon-copy);
|
||||
.jp-CellsIcon {
|
||||
background-image: url('icons/cells.svg');
|
||||
}
|
||||
|
||||
.jp-CloseIcon {
|
||||
background-image: var(--jp-icon-close);
|
||||
}
|
||||
|
||||
.jp-HistoryIcon {
|
||||
background-image: url('icons/history.svg');
|
||||
}
|
||||
|
||||
/**
|
||||
* Clues for gatherable text.
|
||||
*/
|
||||
|
@ -155,36 +159,26 @@ div.CodeMirror-linebackground.jp-InputArea-editor-dependencyline {
|
|||
.jp-HistoryViewer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-x: scroll;
|
||||
white-space: nowrap;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.jp-HistoryViewer .jp-HistoryViewer-referenceversion,
|
||||
.jp-HistoryViewer .jp-HistoryViewer-olderversions {
|
||||
.jp-HistoryViewer .jp-Revision {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.jp-HistoryViewer-referenceversion {
|
||||
max-width: 500px;
|
||||
height: 98%;
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
.jp-HistoryViewer-olderversions {
|
||||
height: 99%;
|
||||
width: 65%;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.jp-HistoryViewer-olderversions-revisions {
|
||||
height: 99%;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.jp-Revision {
|
||||
display: inline-block;
|
||||
height: 99%;
|
||||
vertical-align: top;
|
||||
background-color: #eee;
|
||||
|
@ -206,15 +200,6 @@ div.CodeMirror-linebackground.jp-InputArea-editor-dependencyline {
|
|||
flex-flow: column;
|
||||
}
|
||||
|
||||
.jp-HistoryViewer-referenceversion .jp-Revision {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.jp-HistoryViewer-olderversions .jp-Revision {
|
||||
max-width: 500px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.jp-HistoryViewer-referenceversion .jp-Revision-header {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
@ -222,7 +207,6 @@ div.CodeMirror-linebackground.jp-InputArea-editor-dependencyline {
|
|||
.jp-Revision-header {
|
||||
flex: 0 0 auto;
|
||||
background-color: white;
|
||||
/* border: 1px solid black; */
|
||||
font-size: medium;
|
||||
padding-left: 1ex;
|
||||
padding-right: 1ex;
|
||||
|
@ -284,7 +268,6 @@ div.CodeMirror-linebackground.jp-InputArea-editor-dependencyline {
|
|||
|
||||
.jp-DiffedCell-editor-aftertext.jp-DiffedCell-editor-changedtext {
|
||||
font-weight: bold;
|
||||
/* background-color: #ffe189; */
|
||||
}
|
||||
|
||||
.jp-DiffedCell-editor-beforebackground {
|
||||
|
@ -292,7 +275,6 @@ div.CodeMirror-linebackground.jp-InputArea-editor-dependencyline {
|
|||
}
|
||||
|
||||
.jp-DiffedCell-editor-beforetext {
|
||||
/* background-color: #d8f7ff; */
|
||||
font-style: italic;
|
||||
color: #6d6d6d;
|
||||
}
|
||||
|
@ -304,6 +286,10 @@ div.CodeMirror-linebackground.jp-InputArea-editor-dependencyline {
|
|||
}
|
||||
|
||||
/* Revision cell outputs */
|
||||
.jp-Notebook-revisionbrowser {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.jp-Notebook-revisionbrowser-output div.output_area {
|
||||
max-height: 5000px;
|
||||
overflow-y: auto;
|
||||
|
@ -314,20 +300,8 @@ div.CodeMirror-linebackground.jp-InputArea-editor-dependencyline {
|
|||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
/* border-right: 1px solid black; */
|
||||
/* border-left: 1px solid black; */
|
||||
}
|
||||
|
||||
/*
|
||||
.jp-Notebook-revisionbrowser-output div.output_area:last-child div.output_subarea {
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
|
||||
.jp-Notebook-revisionbrowser-output div.output_area:first-child div.output_subarea {
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
*/
|
||||
|
||||
.jp-Notebook-revisionbrowser-output .output_area:first-child {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче