@notebook-intelligence/notebook-intelligence 1.3.5 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts CHANGED
@@ -104,6 +104,23 @@ namespace CommandIDs {
104
104
  'notebook-intelligence:open-github-copilot-login-dialog';
105
105
  export const openConfigurationDialog =
106
106
  'notebook-intelligence:open-configuration-dialog';
107
+ export const addMarkdownCellToActiveNotebook =
108
+ 'notebook-intelligence:add-markdown-cell-to-active-notebook';
109
+ export const addCodeCellToActiveNotebook =
110
+ 'notebook-intelligence:add-code-cell-to-active-notebook';
111
+ export const deleteCellAtIndex = 'notebook-intelligence:delete-cell-at-index';
112
+ export const insertCellAtIndex = 'notebook-intelligence:insert-cell-at-index';
113
+ export const setCellTypeAndSource =
114
+ 'notebook-intelligence:set-cell-type-and-source';
115
+ export const getNumberOfCells = 'notebook-intelligence:get-number-of-cells';
116
+ export const getCellType = 'notebook-intelligence:get-cell-type';
117
+ export const getCellSource = 'notebook-intelligence:get-cell-source';
118
+ export const getCellOutput = 'notebook-intelligence:get-cell-output';
119
+ export const runCellAtIndex = 'notebook-intelligence:run-cell-at-index';
120
+ export const getCurrentFileContent =
121
+ 'notebook-intelligence:get-current-file-content';
122
+ export const setCurrentFileContent =
123
+ 'notebook-intelligence:set-current-file-content';
107
124
  }
108
125
 
109
126
  const DOCUMENT_WATCH_INTERVAL = 1000;
@@ -856,6 +873,240 @@ const plugin: JupyterFrontEndPlugin<INotebookIntelligence> = {
856
873
  }
857
874
  });
858
875
 
876
+ const ensureANotebookIsActive = (): boolean => {
877
+ const currentWidget = app.shell.currentWidget;
878
+ const notebookOpen =
879
+ currentWidget instanceof NotebookPanel && currentWidget.model;
880
+ if (!notebookOpen) {
881
+ app.commands.execute('apputils:notify', {
882
+ message: 'Failed to find active notebook',
883
+ type: 'error',
884
+ options: { autoClose: true }
885
+ });
886
+ return false;
887
+ }
888
+
889
+ return true;
890
+ };
891
+
892
+ const ensureAFileEditorIsActive = (): boolean => {
893
+ const currentWidget = app.shell.currentWidget;
894
+ const textFileOpen = currentWidget instanceof FileEditorWidget;
895
+ if (!textFileOpen) {
896
+ app.commands.execute('apputils:notify', {
897
+ message: 'Failed to find active file',
898
+ type: 'error',
899
+ options: { autoClose: true }
900
+ });
901
+ return false;
902
+ }
903
+
904
+ return true;
905
+ };
906
+
907
+ app.commands.addCommand(CommandIDs.addMarkdownCellToActiveNotebook, {
908
+ execute: args => {
909
+ if (!ensureANotebookIsActive()) {
910
+ return false;
911
+ }
912
+
913
+ const np = app.shell.currentWidget as NotebookPanel;
914
+ const model = np.model.sharedModel;
915
+
916
+ const newCellIndex = isNewEmptyNotebook(model)
917
+ ? 0
918
+ : model.cells.length - 1;
919
+ model.insertCell(newCellIndex, {
920
+ cell_type: 'markdown',
921
+ metadata: { trusted: true },
922
+ source: args.source as string
923
+ });
924
+
925
+ return true;
926
+ }
927
+ });
928
+
929
+ app.commands.addCommand(CommandIDs.addCodeCellToActiveNotebook, {
930
+ execute: args => {
931
+ if (!ensureANotebookIsActive()) {
932
+ return false;
933
+ }
934
+
935
+ const np = app.shell.currentWidget as NotebookPanel;
936
+ const model = np.model.sharedModel;
937
+
938
+ const newCellIndex = isNewEmptyNotebook(model)
939
+ ? 0
940
+ : model.cells.length - 1;
941
+ model.insertCell(newCellIndex, {
942
+ cell_type: 'code',
943
+ metadata: { trusted: true },
944
+ source: args.source as string
945
+ });
946
+
947
+ return true;
948
+ }
949
+ });
950
+
951
+ app.commands.addCommand(CommandIDs.setCellTypeAndSource, {
952
+ execute: args => {
953
+ if (!ensureANotebookIsActive()) {
954
+ return false;
955
+ }
956
+
957
+ const np = app.shell.currentWidget as NotebookPanel;
958
+ const model = np.model.sharedModel;
959
+
960
+ const cellIndex = args.cellIndex as number;
961
+ const cellType = args.cellType as 'code' | 'markdown';
962
+ const cell = model.getCell(cellIndex);
963
+
964
+ model.deleteCell(cellIndex);
965
+ model.insertCell(cellIndex, {
966
+ cell_type: cellType,
967
+ metadata: cell.metadata,
968
+ source: args.source as string
969
+ });
970
+
971
+ return true;
972
+ }
973
+ });
974
+
975
+ app.commands.addCommand(CommandIDs.getNumberOfCells, {
976
+ execute: args => {
977
+ if (!ensureANotebookIsActive()) {
978
+ return false;
979
+ }
980
+
981
+ const np = app.shell.currentWidget as NotebookPanel;
982
+ const model = np.model.sharedModel;
983
+
984
+ return model.cells.length;
985
+ }
986
+ });
987
+
988
+ app.commands.addCommand(CommandIDs.getCellType, {
989
+ execute: args => {
990
+ if (!ensureANotebookIsActive()) {
991
+ return false;
992
+ }
993
+
994
+ const np = app.shell.currentWidget as NotebookPanel;
995
+ const model = np.model.sharedModel;
996
+
997
+ return model.cells[args.cellIndex as number].cell_type;
998
+ }
999
+ });
1000
+
1001
+ app.commands.addCommand(CommandIDs.getCellSource, {
1002
+ execute: args => {
1003
+ if (!ensureANotebookIsActive()) {
1004
+ return false;
1005
+ }
1006
+
1007
+ const np = app.shell.currentWidget as NotebookPanel;
1008
+ const model = np.model.sharedModel;
1009
+
1010
+ return model.cells[args.cellIndex as number].source;
1011
+ }
1012
+ });
1013
+
1014
+ app.commands.addCommand(CommandIDs.getCellOutput, {
1015
+ execute: args => {
1016
+ if (!ensureANotebookIsActive()) {
1017
+ return false;
1018
+ }
1019
+
1020
+ const np = app.shell.currentWidget as NotebookPanel;
1021
+ const cellIndex = args.cellIndex as number;
1022
+
1023
+ const cell = np.content.widgets[cellIndex];
1024
+
1025
+ if (!(cell instanceof CodeCell)) {
1026
+ return '';
1027
+ }
1028
+
1029
+ const content = cellOutputAsText(cell as CodeCell);
1030
+
1031
+ return content;
1032
+ }
1033
+ });
1034
+
1035
+ app.commands.addCommand(CommandIDs.insertCellAtIndex, {
1036
+ execute: args => {
1037
+ if (!ensureANotebookIsActive()) {
1038
+ return false;
1039
+ }
1040
+
1041
+ const np = app.shell.currentWidget as NotebookPanel;
1042
+ const model = np.model.sharedModel;
1043
+ const cellIndex = args.cellIndex as number;
1044
+ const cellType = args.cellType as 'code' | 'markdown';
1045
+
1046
+ model.insertCell(cellIndex, {
1047
+ cell_type: cellType,
1048
+ metadata: { trusted: true },
1049
+ source: args.source as string
1050
+ });
1051
+
1052
+ return true;
1053
+ }
1054
+ });
1055
+
1056
+ app.commands.addCommand(CommandIDs.deleteCellAtIndex, {
1057
+ execute: args => {
1058
+ if (!ensureANotebookIsActive()) {
1059
+ return false;
1060
+ }
1061
+
1062
+ const np = app.shell.currentWidget as NotebookPanel;
1063
+ const model = np.model.sharedModel;
1064
+ const cellIndex = args.cellIndex as number;
1065
+
1066
+ model.deleteCell(cellIndex);
1067
+
1068
+ return true;
1069
+ }
1070
+ });
1071
+
1072
+ app.commands.addCommand(CommandIDs.runCellAtIndex, {
1073
+ execute: async args => {
1074
+ if (!ensureANotebookIsActive()) {
1075
+ return false;
1076
+ }
1077
+
1078
+ const currentWidget = app.shell.currentWidget as NotebookPanel;
1079
+ currentWidget.content.activeCellIndex = args.cellIndex as number;
1080
+
1081
+ await app.commands.execute('notebook:run-cell');
1082
+ }
1083
+ });
1084
+
1085
+ app.commands.addCommand(CommandIDs.getCurrentFileContent, {
1086
+ execute: async args => {
1087
+ if (!ensureAFileEditorIsActive()) {
1088
+ return false;
1089
+ }
1090
+
1091
+ const currentWidget = app.shell.currentWidget as FileEditorWidget;
1092
+ const editor = currentWidget.content.editor;
1093
+ return editor.model.sharedModel.getSource();
1094
+ }
1095
+ });
1096
+
1097
+ app.commands.addCommand(CommandIDs.setCurrentFileContent, {
1098
+ execute: async args => {
1099
+ if (!ensureAFileEditorIsActive()) {
1100
+ return false;
1101
+ }
1102
+
1103
+ const currentWidget = app.shell.currentWidget as FileEditorWidget;
1104
+ const editor = currentWidget.content.editor;
1105
+ editor.model.sharedModel.setSource(args.content as string);
1106
+ return editor.model.sharedModel.getSource();
1107
+ }
1108
+ });
1109
+
859
1110
  app.commands.addCommand(CommandIDs.openGitHubCopilotLoginDialog, {
860
1111
  execute: args => {
861
1112
  let dialog: Dialog<unknown> | null = null;
@@ -1358,7 +1609,7 @@ const plugin: JupyterFrontEndPlugin<INotebookIntelligence> = {
1358
1609
 
1359
1610
  const copilotContextMenu = new Menu({ commands: copilotMenuCommands });
1360
1611
  copilotContextMenu.id = 'notebook-intelligence:editor-context-menu';
1361
- copilotContextMenu.title.label = 'Copilot';
1612
+ copilotContextMenu.title.label = 'Notebook Intelligence';
1362
1613
  copilotContextMenu.title.icon = sidebarIcon;
1363
1614
  copilotContextMenu.addItem({ command: CommandIDs.editorGenerateCode });
1364
1615
  copilotContextMenu.addItem({ command: CommandIDs.editorExplainThisCode });
package/src/tokens.ts CHANGED
@@ -74,6 +74,21 @@ export interface IChatParticipant {
74
74
  commands: string[];
75
75
  }
76
76
 
77
+ export interface IToolSelections {
78
+ builtinToolsets?: string[];
79
+ mcpServers?: {
80
+ [key: string]: string[];
81
+ };
82
+ extensions?: {
83
+ [key: string]: string[];
84
+ };
85
+ }
86
+
87
+ export enum BuiltinToolsetType {
88
+ NotebookEdit = 'nbi-notebook-edit',
89
+ NotebookExecute = 'nbi-notebook-execute'
90
+ }
91
+
77
92
  export const GITHUB_COPILOT_PROVIDER_ID = 'github-copilot';
78
93
 
79
94
  export enum TelemetryEventType {
package/style/base.css CHANGED
@@ -108,6 +108,57 @@
108
108
  display: flex;
109
109
  flex-direction: row;
110
110
  align-items: center;
111
+ gap: 10px;
112
+ }
113
+
114
+ .chat-mode-widgets-container {
115
+ display: flex;
116
+ flex-direction: row;
117
+ align-items: center;
118
+ gap: 5px;
119
+ margin-right: 5px;
120
+ }
121
+
122
+ .chat-mode-select {
123
+ height: 24px;
124
+ background-color: initial;
125
+ color: var(--jp-ui-font-color1);
126
+ padding: 0 5px;
127
+ border-color: var(--jp-border-color0);
128
+ outline: none;
129
+ }
130
+
131
+ .user-input-footer-button {
132
+ width: 24px;
133
+ height: 24px;
134
+ box-sizing: border-box;
135
+ padding: 2px;
136
+ cursor: pointer;
137
+ color: var(--jp-ui-font-color2);
138
+ }
139
+
140
+ .user-input-footer-button.tools-button {
141
+ width: auto;
142
+ border: 1px solid var(--jp-border-color0);
143
+ display: flex;
144
+ align-items: center;
145
+ gap: 2px;
146
+ padding-right: 3px;
147
+ }
148
+
149
+ .user-input-footer-button.tools-button-warning {
150
+ color: var(--jp-warn-color0);
151
+ border-color: var(--jp-warn-color0);
152
+ }
153
+
154
+ .user-input-footer-button.tools-button-active {
155
+ color: var(--jp-brand-color0);
156
+ border-color: var(--jp-brand-color0);
157
+ }
158
+
159
+ .user-input-footer-button svg {
160
+ width: 18px;
161
+ height: 18px;
111
162
  }
112
163
 
113
164
  .chat-message {
@@ -292,17 +343,99 @@ pre:has(.code-block-header) {
292
343
  margin-top: 2px;
293
344
  }
294
345
 
295
- .user-input-autocomplete {
346
+ .user-input-autocomplete,
347
+ .mode-tools-popover {
296
348
  display: flex;
297
349
  background-color: var(--jp-layout-color2);
298
350
  border: 1px solid var(--jp-border-color1);
299
351
  flex-direction: column;
300
352
  position: absolute;
301
- bottom: 92px;
353
+ bottom: 98px;
302
354
  left: 4px;
303
355
  gap: 2px;
304
- max-height: 300px;
356
+ /* stylelint-disable */
357
+ max-height: min(calc(100% - 120px), 400px);
358
+ /* stylelint-enable */
305
359
  overflow-y: auto;
360
+ user-select: none;
361
+ }
362
+
363
+ .mode-tools-popover {
364
+ width: calc(100% - 10px);
365
+ /* stylelint-disable */
366
+ max-height: min(calc(100% - 200px), 400px);
367
+ /* stylelint-enable */
368
+ box-shadow: var(--jp-brand-color0) 0 0 1px 1px;
369
+ display: flex;
370
+ flex-direction: column;
371
+ }
372
+
373
+ .mode-tools-popover-title {
374
+ font-weight: bold;
375
+ }
376
+
377
+ .mode-tools-popover-header {
378
+ display: flex;
379
+ align-items: center;
380
+ gap: 5px;
381
+ height: 24px;
382
+ background-color: var(--jp-layout-color1);
383
+ padding: 2px;
384
+ }
385
+
386
+ .mode-tools-popover-header-icon svg {
387
+ width: 18px;
388
+ height: 18px;
389
+ }
390
+
391
+ .mode-tools-popover-clear-tools-button {
392
+ flex-grow: 1;
393
+ color: var(--jp-ui-font-color2);
394
+ display: flex;
395
+ padding-top: 3px;
396
+ }
397
+
398
+ .mode-tools-popover-clear-tools-button svg {
399
+ width: 16px;
400
+ height: 16px;
401
+ }
402
+
403
+ .mode-tools-popover-close-button {
404
+ display: flex;
405
+ align-items: center;
406
+ cursor: pointer;
407
+ width: auto;
408
+ height: 24px;
409
+ color: white;
410
+ background-color: var(--jp-brand-color0);
411
+ padding-right: 6px;
412
+ border-radius: 2px;
413
+ }
414
+
415
+ .mode-tools-popover-close-button svg {
416
+ width: 16px;
417
+ height: 16px;
418
+ padding: 4px;
419
+ margin-top: 2px;
420
+ }
421
+
422
+ .mode-tools-popover-tool-list {
423
+ flex-grow: 1;
424
+ overflow-y: auto;
425
+ }
426
+
427
+ .mode-tools-group {
428
+ display: flex;
429
+ flex-direction: column;
430
+ padding-left: 5px;
431
+ margin-bottom: 5px;
432
+ }
433
+
434
+ .mode-tools-group-header {
435
+ padding: 5px 0;
436
+ border-bottom: 1px solid var(--jp-border-color1);
437
+ margin: 0 5px;
438
+ font-weight: bold;
306
439
  }
307
440
 
308
441
  .user-input-autocomplete-item {
@@ -626,3 +759,63 @@ svg.access-token-warning {
626
759
  width: 18px;
627
760
  height: 18px;
628
761
  }
762
+
763
+ .checkbox-item {
764
+ display: flex;
765
+ flex-direction: column;
766
+ cursor: pointer;
767
+ }
768
+
769
+ .checkbox-item:hover {
770
+ background-color: var(--jp-layout-color1);
771
+ }
772
+
773
+ .checkbox-item-indent-0 {
774
+ padding-left: 0;
775
+ }
776
+
777
+ .checkbox-item-indent-1 {
778
+ padding-left: 12px;
779
+ }
780
+
781
+ .checkbox-item-indent-2 {
782
+ padding-left: 24px;
783
+ }
784
+
785
+ .checkbox-item-indent-3 {
786
+ padding-left: 36px;
787
+ }
788
+
789
+ .checkbox-icon {
790
+ width: 16px;
791
+ height: 16px;
792
+ color: var(--jp-brand-color1);
793
+ padding-top: 1px;
794
+ }
795
+
796
+ .checkbox-item-toggle {
797
+ display: flex;
798
+ align-items: center;
799
+ gap: 5px;
800
+ margin: 0 2px;
801
+ padding: 3px 2px;
802
+ color: var(--jp-ui-font-color1);
803
+ }
804
+
805
+ .checkbox-item-header .checkbox-item-toggle {
806
+ color: var(--jp-ui-font-color0);
807
+ font-weight: bold;
808
+ }
809
+
810
+ .mode-tools-group-built-in .checkbox-item-toggle {
811
+ font-weight: normal;
812
+ }
813
+
814
+ .checkbox-item-description {
815
+ padding-left: 25px;
816
+ color: var(--jp-ui-font-color2);
817
+ padding-bottom: 4px;
818
+ overflow: hidden;
819
+ text-overflow: ellipsis;
820
+ white-space: nowrap;
821
+ }