Skip to content
Merged
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: 11 additions & 0 deletions .releaserc.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,20 @@
{
"path": "*.vsix",
"label": "VSCode Extension (VSIX)"
},
{
"path": "CHANGELOG.md",
"label": "Changelog"
}
]
}
],
[
"@semantic-release/git",
{
"assets": ["package.json", "CHANGELOG.md"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
]
}
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@
"command": "pluto-notebook.toggleView",
"title": "🔄 Pluto: Toggle Code/Notebook View",
"icon": "$(split-vertical)"
},
{
"command": "pluto-notebook.createNewNotebook",
"title": "📝 Pluto: Create New Notebook",
"icon": "$(new-file)"
}
],
"menus": {
Expand Down
5 changes: 5 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export * from "./mcpConfigCommands.ts";
export * from "./terminalCommands.ts";
export * from "./notebooksTreeCommands.ts";
export * from "./viewToggleCommands.ts";
export * from "./notebookCommands.ts";
// Import for registerAllCommands
import {
registerStartServerCommand,
Expand Down Expand Up @@ -44,6 +45,7 @@ import {
} from "./notebooksTreeCommands.ts";
import { registerCreateTerminalCommand } from "./terminalCommands.ts";
import { registerToggleViewCommand } from "./viewToggleCommands.ts";
import { registerCreateNewNotebookCommand } from "./notebookCommands.ts";

/**
* Register all commands at once
Expand Down Expand Up @@ -82,4 +84,7 @@ export function registerAllCommands(

// Register View Toggle commands
registerToggleViewCommand(context);

// Register Notebook commands
registerCreateNewNotebookCommand(context);
}
95 changes: 95 additions & 0 deletions src/commands/notebookCommands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import * as vscode from "vscode";
import * as path from "path";
import * as fs from "fs";
import { serializePlutoNotebook } from "../plutoSerializer.ts";

/**
* Command: Create New Pluto Notebook
* Prompts for a filename and creates a new Pluto notebook with one empty cell
*/
export function registerCreateNewNotebookCommand(
context: vscode.ExtensionContext
): void {
context.subscriptions.push(
vscode.commands.registerCommand(
"pluto-notebook.createNewNotebook",
async () => {
try {
// Get the workspace folder
const workspaceFolders = vscode.workspace.workspaceFolders;
if (!workspaceFolders || workspaceFolders.length === 0) {
vscode.window.showErrorMessage(
"No workspace folder open. Please open a folder first."
);
return;
}

// Prompt for filename
const filename = await vscode.window.showInputBox({
prompt: "Enter notebook filename",
placeHolder: "my-notebook.pluto.jl",
validateInput: (value) => {
if (!value) {
return "Filename cannot be empty";
}
if (!value.endsWith(".pluto.jl") && !value.endsWith(".dyad.jl")) {
return "Filename must end with .pluto.jl or .dyad.jl";
}
return null;
},
});

if (!filename) {
// User cancelled
return;
}

// Get the file path
const workspaceFolder = workspaceFolders[0];
const filePath = path.join(workspaceFolder.uri.fsPath, filename);

// Check if file already exists
if (fs.existsSync(filePath)) {
const overwrite = await vscode.window.showWarningMessage(
`File ${filename} already exists. Overwrite?`,
"Yes",
"No"
);
if (overwrite !== "Yes") {
return;
}
}

// Create a single empty cell
const emptyCell = new vscode.NotebookCellData(
vscode.NotebookCellKind.Code,
"",
"julia"
);

// Serialize the notebook with the empty cell
const notebookContent = serializePlutoNotebook([emptyCell]);

// Write to file
fs.writeFileSync(filePath, notebookContent, "utf-8");

// Open the file in VSCode
const document = await vscode.workspace.openNotebookDocument(
vscode.Uri.file(filePath)
);
await vscode.window.showNotebookDocument(document);

vscode.window.showInformationMessage(
`Created new Pluto notebook: ${filename}`
);
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : String(error);
vscode.window.showErrorMessage(
`Failed to create notebook: ${errorMessage}`
);
}
}
)
);
}
42 changes: 32 additions & 10 deletions src/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
interface Patch {
op: "add" | "remove" | "replace" | "move" | "copy" | "test";
path: Array<string | number>;
value?: any;

Check warning on line 31 in src/controller.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check warning on line 31 in src/controller.ts

View workflow job for this annotation

GitHub Actions / Build

Unexpected any. Specify a different type
from?: Array<string | number>;
}
// --- END: Merged Interfaces ---
Expand Down Expand Up @@ -122,7 +122,7 @@
*/
private async handleRendererMessage(event: {
editor: vscode.NotebookEditor;
message: any;

Check warning on line 125 in src/controller.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check warning on line 125 in src/controller.ts

View workflow job for this annotation

GitHub Actions / Build

Unexpected any. Specify a different type
}): Promise<void> {
const { editor, message } = event;

Expand Down Expand Up @@ -158,7 +158,7 @@
*/
public sendMessageToRenderer(
notebook: vscode.NotebookDocument,
message: any

Check warning on line 161 in src/controller.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check warning on line 161 in src/controller.ts

View workflow job for this annotation

GitHub Actions / Build

Unexpected any. Specify a different type
): void {
// Find ALL editors for this notebook (handles split views)
const editors = vscode.window.visibleNotebookEditors.filter(
Expand Down Expand Up @@ -452,23 +452,40 @@
// );
// }
}
private updateAllCellsFromState = (
private updateAllCellsFromState = async (
notebook: vscode.NotebookDocument,
update: UpdateEvent
) => {
console.warn("Using nuclear reset flow");
// Optimistically send data. May be ignored.
// If not ignored, this makes sure logs, stdout and progress
// are properly propagated to state object
const fullNotebookState = update.notebook;
Object.entries(fullNotebookState?.cell_results ?? {}).forEach(
([cell_id, state]) => {
this.sendMessageToRenderer(notebook, {
type: "setState",
state,
cell_id,
});
for (const [cell_id, state] of Object.entries(
fullNotebookState?.cell_results ?? {}
)) {
const start = Date.now();
const { execution } = this.startExecution(cell_id, notebook);
try {
await execution.replaceOutput([formatCellOutput(state)]);
} catch (e) {
console.error(e);
//
}
);
if (!state.queued || !state.running) {
// This results to many "cannot resolve twice" messages
try {
execution.end(!state.errored, start + (state.runtime ?? 0) / 1000);
} catch (x) {
console.error(x);
}
}
this.sendMessageToRenderer(notebook, {
type: "setState",
state,
cell_id,
});
}
};

/**
Expand All @@ -490,6 +507,11 @@
for (const patch of patches) {
const path = patch.path;
const [action, ...rest] = path;
if (path.length === 0 && patches.length === 0) {
// This is a state reset; handle it accordingly
anyWeird = true;
break;
}
switch (action) {
case "bonds": {
// TODO here we do bound send to the renderers
Expand Down Expand Up @@ -635,7 +657,7 @@
// Update the cell's metadata with the Pluto cell ID
const edit = new vscode.WorkspaceEdit();
const cellMetadata = {
...addedCell.metadata,
...addedCell?.metadata,
pluto_cell_id: cellId,
};

Expand Down
Loading