Skip to content

Commit 44483f7

Browse files
authored
feat: Add commands to add Text blocks to notebooks (#286)
* feat: Add commands to add Text blocks to notebooks * Minor changes * Fix typo * Minor changes * Change picker items labels
1 parent 5cd119e commit 44483f7

File tree

5 files changed

+169
-1
lines changed

5 files changed

+169
-1
lines changed

package.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,36 @@
230230
"category": "Deepnote",
231231
"icon": "$(add)"
232232
},
233+
{
234+
"command": "deepnote.addTextBlock",
235+
"title": "%deepnote.commands.addTextBlock.title%",
236+
"category": "Deepnote",
237+
"icon": "$(text-size)"
238+
},
239+
{
240+
"command": "deepnote.addTextBlockParagraph",
241+
"title": "%deepnote.commands.addTextBlockParagraph.title%",
242+
"category": "Deepnote",
243+
"icon": "$(text-size)"
244+
},
245+
{
246+
"command": "deepnote.addTextBlockHeading1",
247+
"title": "%deepnote.commands.addTextBlockHeading1.title%",
248+
"category": "Deepnote",
249+
"icon": "$(text-size)"
250+
},
251+
{
252+
"command": "deepnote.addTextBlockHeading2",
253+
"title": "%deepnote.commands.addTextBlockHeading2.title%",
254+
"category": "Deepnote",
255+
"icon": "$(text-size)"
256+
},
257+
{
258+
"command": "deepnote.addTextBlockHeading3",
259+
"title": "%deepnote.commands.addTextBlockHeading3.title%",
260+
"category": "Deepnote",
261+
"icon": "$(text-size)"
262+
},
233263
{
234264
"command": "deepnote.newNotebook",
235265
"title": "%deepnote.commands.newNotebook.title%",
@@ -1026,6 +1056,11 @@
10261056
"group": "navigation@12",
10271057
"when": "notebookType == 'deepnote'"
10281058
},
1059+
{
1060+
"command": "deepnote.addTextBlock",
1061+
"group": "navigation@13",
1062+
"when": "notebookType == 'deepnote'"
1063+
},
10291064
{
10301065
"command": "deepnote.restartkernel",
10311066
"group": "navigation/execute@5",

package.nls.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,11 @@
266266
"deepnote.commands.addInputDateRangeBlock.title": "Add Input Date Range Block",
267267
"deepnote.commands.addInputFileBlock.title": "Add Input File Block",
268268
"deepnote.commands.addButtonBlock.title": "Add Button Block",
269+
"deepnote.commands.addTextBlock.title": "Add Text Block",
270+
"deepnote.commands.addTextBlockParagraph.title": "Add Paragraph Block",
271+
"deepnote.commands.addTextBlockHeading1.title": "Add Heading 1 Block",
272+
"deepnote.commands.addTextBlockHeading2.title": "Add Heading 2 Block",
273+
"deepnote.commands.addTextBlockHeading3.title": "Add Heading 3 Block",
269274
"deepnote.commands.newNotebook.title": "New Notebook",
270275
"deepnote.commands.renameProject.title": "Rename Project",
271276
"deepnote.commands.deleteProject.title": "Delete Project",

src/commands.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,11 @@ export interface ICommandNameArgumentTypeMapping {
197197
[DSCommands.AddInputDateRangeBlock]: [];
198198
[DSCommands.AddInputFileBlock]: [];
199199
[DSCommands.AddButtonBlock]: [];
200+
[DSCommands.AddTextBlock]: [];
201+
[DSCommands.AddTextBlockHeading1]: [];
202+
[DSCommands.AddTextBlockHeading2]: [];
203+
[DSCommands.AddTextBlockHeading3]: [];
204+
[DSCommands.AddTextBlockParagraph]: [];
200205
[DSCommands.NewProject]: [];
201206
[DSCommands.ImportNotebook]: [];
202207
[DSCommands.ImportJupyterNotebook]: [];

src/notebooks/deepnote/deepnoteNotebookCommandListener.ts

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import {
88
NotebookRange,
99
NotebookCell,
1010
NotebookEditorRevealType,
11-
l10n
11+
l10n,
12+
QuickPickItem,
13+
NotebookEditor
1214
} from 'vscode';
1315
import z from 'zod';
1416

@@ -33,6 +35,7 @@ import {
3335
DeepnoteSqlMetadata
3436
} from './deepnoteSchemas';
3537
import { DATAFRAME_SQL_INTEGRATION_ID } from '../../platform/notebooks/deepnote/integrationTypes';
38+
import { Pocket } from '../../platform/deepnote/pocket';
3639

3740
export type InputBlockType =
3841
| 'input-text'
@@ -45,6 +48,10 @@ export type InputBlockType =
4548
| 'input-file'
4649
| 'button';
4750

51+
export const TEXT_BLOCK_TYPES = ['text-cell-p', 'text-cell-h1', 'text-cell-h2', 'text-cell-h3'] as const;
52+
53+
export type TextBlockType = (typeof TEXT_BLOCK_TYPES)[number];
54+
4855
export function getInputBlockMetadata(blockType: InputBlockType, variableName: string) {
4956
const defaultInput = {
5057
deepnote_variable_name: variableName
@@ -181,6 +188,29 @@ export class DeepnoteNotebookCommandListener implements IExtensionSyncActivation
181188
this.disposableRegistry.push(
182189
commands.registerCommand(Commands.AddButtonBlock, () => this.addInputBlock('button'))
183190
);
191+
this.disposableRegistry.push(
192+
commands.registerCommand(Commands.AddTextBlock, () => this.addTextBlockThroughPicker())
193+
);
194+
this.disposableRegistry.push(
195+
commands.registerCommand(Commands.AddTextBlockHeading1, () =>
196+
this.addTextBlockCommandHandler({ textBlockType: 'text-cell-h1' })
197+
)
198+
);
199+
this.disposableRegistry.push(
200+
commands.registerCommand(Commands.AddTextBlockHeading2, () =>
201+
this.addTextBlockCommandHandler({ textBlockType: 'text-cell-h2' })
202+
)
203+
);
204+
this.disposableRegistry.push(
205+
commands.registerCommand(Commands.AddTextBlockHeading3, () =>
206+
this.addTextBlockCommandHandler({ textBlockType: 'text-cell-h3' })
207+
)
208+
);
209+
this.disposableRegistry.push(
210+
commands.registerCommand(Commands.AddTextBlockParagraph, () =>
211+
this.addTextBlockCommandHandler({ textBlockType: 'text-cell-p' })
212+
)
213+
);
184214
}
185215

186216
public async addSqlBlock(): Promise<void> {
@@ -368,4 +398,92 @@ export class DeepnoteNotebookCommandListener implements IExtensionSyncActivation
368398
// Enter edit mode on the new cell
369399
await commands.executeCommand('notebook.cell.edit');
370400
}
401+
402+
public async addTextBlockThroughPicker(): Promise<void> {
403+
const TEXT_BLOCK_TYPE_LABELS = {
404+
'text-cell-p': l10n.t('Paragraph'),
405+
'text-cell-h1': l10n.t('Heading 1'),
406+
'text-cell-h2': l10n.t('Heading 2'),
407+
'text-cell-h3': l10n.t('Heading 3')
408+
} as const satisfies Record<TextBlockType, string>;
409+
410+
const editor = window.activeNotebookEditor;
411+
if (!editor) {
412+
throw new Error(l10n.t('No active notebook editor found'));
413+
}
414+
415+
const items: (QuickPickItem & { textBlockType: TextBlockType })[] = TEXT_BLOCK_TYPES.map((textBlockType) => {
416+
const label = TEXT_BLOCK_TYPE_LABELS[textBlockType];
417+
const description = l10n.t('Add a {0} text block', label);
418+
return {
419+
label,
420+
description,
421+
textBlockType
422+
};
423+
});
424+
425+
const selected = await window.showQuickPick(items, {
426+
placeHolder: l10n.t('Select a text block type'),
427+
matchOnDescription: true,
428+
matchOnDetail: true
429+
});
430+
431+
if (selected == null) {
432+
return;
433+
}
434+
435+
logger.info(`Selected text block type: ${selected.textBlockType}`);
436+
437+
await this.addTextBlock({ editor, textBlockType: selected.textBlockType });
438+
}
439+
440+
public async addTextBlockCommandHandler({ textBlockType }: { textBlockType: TextBlockType }): Promise<void> {
441+
const editor = window.activeNotebookEditor;
442+
if (!editor) {
443+
throw new Error(l10n.t('No active notebook editor found'));
444+
}
445+
446+
await this.addTextBlock({ editor, textBlockType });
447+
}
448+
449+
public async addTextBlock({
450+
editor,
451+
textBlockType
452+
}: {
453+
editor: NotebookEditor;
454+
textBlockType: TextBlockType;
455+
}): Promise<void> {
456+
const TEXT_BLOCK_TYPE_EMPTY_VALUES = {
457+
'text-cell-p': '',
458+
'text-cell-h1': '# ',
459+
'text-cell-h2': '## ',
460+
'text-cell-h3': '### '
461+
} as const satisfies Record<TextBlockType, string>;
462+
463+
const cellContent = TEXT_BLOCK_TYPE_EMPTY_VALUES[textBlockType];
464+
465+
const document = editor.notebook;
466+
const selection = editor.selection;
467+
const insertIndex = selection ? selection.end : document.cellCount;
468+
469+
const result = await notebookUpdaterUtils.chainWithPendingUpdates(document, (edit) => {
470+
const newCell = new NotebookCellData(NotebookCellKind.Markup, cellContent, 'markdown');
471+
newCell.metadata = {
472+
__deepnotePocket: {
473+
type: textBlockType
474+
} satisfies Pocket
475+
};
476+
const nbEdit = NotebookEdit.insertCells(insertIndex, [newCell]);
477+
edit.set(document.uri, [nbEdit]);
478+
});
479+
if (result !== true) {
480+
throw new Error(l10n.t('Failed to insert text block'));
481+
}
482+
483+
const notebookRange = new NotebookRange(insertIndex, insertIndex + 1);
484+
editor.revealRange(notebookRange, NotebookEditorRevealType.Default);
485+
editor.selection = notebookRange;
486+
// Enter edit mode on the new cell
487+
await commands.executeCommand('notebook.cell.edit');
488+
}
371489
}

src/platform/common/constants.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,11 @@ export namespace Commands {
236236
export const AddInputDateRangeBlock = 'deepnote.addInputDateRangeBlock';
237237
export const AddInputFileBlock = 'deepnote.addInputFileBlock';
238238
export const AddButtonBlock = 'deepnote.addButtonBlock';
239+
export const AddTextBlock = 'deepnote.addTextBlock';
240+
export const AddTextBlockParagraph = 'deepnote.addTextBlockParagraph';
241+
export const AddTextBlockHeading1 = 'deepnote.addTextBlockHeading1';
242+
export const AddTextBlockHeading2 = 'deepnote.addTextBlockHeading2';
243+
export const AddTextBlockHeading3 = 'deepnote.addTextBlockHeading3';
239244
export const NewNotebook = 'deepnote.newNotebook';
240245
export const NewProject = 'deepnote.newProject';
241246
export const ImportNotebook = 'deepnote.importNotebook';

0 commit comments

Comments
 (0)