@notebook-intelligence/notebook-intelligence 1.3.2 → 1.3.4

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/README.md CHANGED
@@ -11,10 +11,16 @@ See blog posts for features and usage.
11
11
 
12
12
  ### Code generation with inline chat
13
13
 
14
+ Use the sparkle icon on cell toolbar or the keyboard shortcuts to show the inline chat popover.
15
+
16
+ Keyboard shortcuts: `Ctrl + G` / `Cmd + G` is the shortcut to show the inline chat popover and `Ctrl + Enter` / `Cmd + Enter` is the shortcut to accept the suggestion. Clicking `Escape` key closes the popover.
17
+
14
18
  ![Generate code](media/generate-code.gif)
15
19
 
16
20
  ### Auto-complete
17
21
 
22
+ Auto-complete suggestions are shown as you type. Clicking `Tab` key accepts the suggestion. NBI provides auto-complete suggestions in code cells and Python file editors.
23
+
18
24
  <img src="media/inline-completion.gif" alt="Auto-complete" width=700 />
19
25
 
20
26
  ### Chat interface
@@ -45,19 +51,21 @@ for the frontend extension.
45
51
 
46
52
  ### Remembering GitHub Copilot login
47
53
 
48
- Notebook Intelligence uses system keyring to store the GitHub access tokens. If your stored access token fails to login (due to expiration or other reasons), you will be prompted to relogin on the UI. If you run into issues with this feature, check the Jupyter server logs and the [keyring package](https://github.com/jaraco/keyring) documentation.
54
+ Notebook Intelligence can remember your GitHub Copilot login so that you don't need to re-login after a JupyterLab or system restart. Please be aware of the security implications of using this feature.
49
55
 
50
- To let Notebook Intelligence remember your GitHub access token after you logged in:
56
+ > [!CAUTION]
57
+ > If you configure NBI to remember your GitHub Copilot login, it will encrypt the token and store into a data file at `~/.jupyter/nbi-data.json`. You should never share this file with others as they can access your tokens.
58
+ > Even though the token is encrypted, it is done so by using a default password and that's why it can be decrypted by others. In order to prevent that you can specify a custom password using the environment variable `NBI_GH_ACCESS_TOKEN_PASSWORD`.
51
59
 
52
60
  ```bash
53
- jupyter lab --NotebookIntelligence.github_access_token=remember
61
+ NBI_GH_ACCESS_TOKEN_PASSWORD=my_custom_password
54
62
  ```
55
63
 
56
- Once you set it to remember, it will continue to remember even if you skip `--NotebookIntelligence.github_access_token` at following launches. In order to forget the GitHub access token stored:
64
+ To let Notebook Intelligence remember your GitHub access token, go to Notebook Intelligence Settings dialog and check the option `Remember my GitHub Copilot access token` as shown below.
57
65
 
58
- ```bash
59
- jupyter lab --NotebookIntelligence.github_access_token=forget
60
- ```
66
+ <img src="media/remember-gh-access-token.png" alt="Remember access token" width=500 />
67
+
68
+ If your stored access token fails to login (due to expiration or other reasons), you will be prompted to relogin on the UI.
61
69
 
62
70
  ### Configuration files
63
71
 
package/lib/api.d.ts CHANGED
@@ -17,6 +17,7 @@ export declare class NBIConfig {
17
17
  get chatModel(): any;
18
18
  get inlineCompletionModel(): any;
19
19
  get usingGitHubCopilotModel(): boolean;
20
+ get storeGitHubAccessToken(): boolean;
20
21
  capabilities: any;
21
22
  chatParticipants: IChatParticipant[];
22
23
  changed: Signal<this, void>;
package/lib/api.js CHANGED
@@ -39,6 +39,9 @@ export class NBIConfig {
39
39
  return (this.chatModel.provider === GITHUB_COPILOT_PROVIDER_ID ||
40
40
  this.inlineCompletionModel.provider === GITHUB_COPILOT_PROVIDER_ID);
41
41
  }
42
+ get storeGitHubAccessToken() {
43
+ return this.capabilities.store_github_access_token === true;
44
+ }
42
45
  }
43
46
  class NBIAPI {
44
47
  static async initialize() {
@@ -9,7 +9,7 @@ import { MarkdownRenderer } from './markdown-renderer';
9
9
  import copySvgstr from '../style/icons/copy.svg';
10
10
  import copilotSvgstr from '../style/icons/copilot.svg';
11
11
  import copilotWarningSvgstr from '../style/icons/copilot-warning.svg';
12
- import { VscSend, VscStopCircle, VscEye, VscEyeClosed, VscTriangleRight, VscTriangleDown } from 'react-icons/vsc';
12
+ import { VscSend, VscStopCircle, VscEye, VscEyeClosed, VscTriangleRight, VscTriangleDown, VscWarning } from 'react-icons/vsc';
13
13
  import { extractLLMGeneratedCode, isDarkTheme } from './utils';
14
14
  const OPENAI_COMPATIBLE_CHAT_MODEL_ID = 'openai-compatible-chat-model';
15
15
  const LITELLM_COMPATIBLE_CHAT_MODEL_ID = 'litellm-compatible-chat-model';
@@ -1123,7 +1123,7 @@ function ConfigurationDialogBodyComponent(props) {
1123
1123
  const [chatModels, setChatModels] = useState([]);
1124
1124
  const [inlineCompletionModels, setInlineCompletionModels] = useState([]);
1125
1125
  const handleSaveClick = async () => {
1126
- await NBIAPI.setConfig({
1126
+ const config = {
1127
1127
  chat_model: {
1128
1128
  provider: chatModelProvider,
1129
1129
  model: chatModel,
@@ -1134,7 +1134,12 @@ function ConfigurationDialogBodyComponent(props) {
1134
1134
  model: inlineCompletionModel,
1135
1135
  properties: inlineCompletionModelProperties
1136
1136
  }
1137
- });
1137
+ };
1138
+ if (chatModelProvider === 'github-copilot' ||
1139
+ inlineCompletionModelProvider === 'github-copilot') {
1140
+ config.store_github_access_token = storeGitHubAccessToken;
1141
+ }
1142
+ await NBIAPI.setConfig(config);
1138
1143
  props.onSave();
1139
1144
  };
1140
1145
  const handleRefreshOllamaModelListClick = async () => {
@@ -1147,6 +1152,7 @@ function ConfigurationDialogBodyComponent(props) {
1147
1152
  const [chatModelProperties, setChatModelProperties] = useState([]);
1148
1153
  const [inlineCompletionModelProperties, setInlineCompletionModelProperties] = useState([]);
1149
1154
  const [inlineCompletionModel, setInlineCompletionModel] = useState(nbiConfig.inlineCompletionModel.model);
1155
+ const [storeGitHubAccessToken, setStoreGitHubAccessToken] = useState(nbiConfig.storeGitHubAccessToken);
1150
1156
  const updateModelOptionsForProvider = (providerId, modelType) => {
1151
1157
  if (modelType === 'chat') {
1152
1158
  setChatModelProvider(providerId);
@@ -1275,7 +1281,23 @@ function ConfigurationDialogBodyComponent(props) {
1275
1281
  property.name,
1276
1282
  " ",
1277
1283
  property.optional ? '(optional)' : ''),
1278
- React.createElement("input", { name: "inline-completion-model-id-input", placeholder: property.description, className: "jp-mod-styled", spellCheck: false, value: property.value, onChange: event => onModelPropertyChange('inline-completion', property.id, event.target.value) }))))))))),
1284
+ React.createElement("input", { name: "inline-completion-model-id-input", placeholder: property.description, className: "jp-mod-styled", spellCheck: false, value: property.value, onChange: event => onModelPropertyChange('inline-completion', property.id, event.target.value) })))))))),
1285
+ (chatModelProvider === 'github-copilot' ||
1286
+ inlineCompletionModelProvider === 'github-copilot') && (React.createElement("div", { className: "model-config-section" },
1287
+ React.createElement("div", { className: "model-config-section-header access-token-config-header" },
1288
+ "GitHub Copilot login",
1289
+ ' ',
1290
+ React.createElement("a", { href: "https://github.com/notebook-intelligence/notebook-intelligence/blob/main/README.md#remembering-github-copilot-login", target: "_blank" },
1291
+ ' ',
1292
+ React.createElement(VscWarning, { className: "access-token-warning", title: "Click to learn more about security implications" }))),
1293
+ React.createElement("div", { className: "model-config-section-body" },
1294
+ React.createElement("div", { className: "model-config-section-row" },
1295
+ React.createElement("div", { className: "model-config-section-column" },
1296
+ React.createElement("label", null,
1297
+ React.createElement("input", { type: "checkbox", checked: storeGitHubAccessToken, onChange: event => {
1298
+ setStoreGitHubAccessToken(event.target.checked);
1299
+ } }),
1300
+ "Remember my GitHub Copilot access token"))))))),
1279
1301
  React.createElement("div", { className: "config-dialog-footer" },
1280
1302
  React.createElement("button", { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: handleSaveClick },
1281
1303
  React.createElement("div", { className: "jp-Dialog-buttonLabel" }, "Save")))));
package/lib/index.js CHANGED
@@ -759,7 +759,19 @@ const plugin = {
759
759
  return editorRect;
760
760
  }
761
761
  const yOffset = 30;
762
- const rect = new DOMRect(editorRect.left, coords.top - yOffset, editorRect.right - editorRect.left, coords.bottom - coords.top);
762
+ const diffViewHeight = 300;
763
+ let top = coords.top - yOffset;
764
+ const height = coords.bottom - coords.top;
765
+ // adjust top to fit in file editor rect
766
+ if (!isCodeCell) {
767
+ if (top + height + diffViewHeight > editorRect.bottom) {
768
+ top = editorRect.bottom - height - diffViewHeight;
769
+ if (top < editorRect.top) {
770
+ top = editorRect.top;
771
+ }
772
+ }
773
+ }
774
+ const rect = new DOMRect(editorRect.left, top, editorRect.right - editorRect.left, height);
763
775
  return rect;
764
776
  };
765
777
  const rect = getRectAtCursor();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notebook-intelligence/notebook-intelligence",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "description": "AI coding assistant for JupyterLab",
5
5
  "keywords": [
6
6
  "AI",
package/src/api.ts CHANGED
@@ -56,6 +56,10 @@ export class NBIConfig {
56
56
  );
57
57
  }
58
58
 
59
+ get storeGitHubAccessToken(): boolean {
60
+ return this.capabilities.store_github_access_token === true;
61
+ }
62
+
59
63
  capabilities: any = {};
60
64
  chatParticipants: IChatParticipant[] = [];
61
65
 
@@ -41,7 +41,8 @@ import {
41
41
  VscEye,
42
42
  VscEyeClosed,
43
43
  VscTriangleRight,
44
- VscTriangleDown
44
+ VscTriangleDown,
45
+ VscWarning
45
46
  } from 'react-icons/vsc';
46
47
  import { extractLLMGeneratedCode, isDarkTheme } from './utils';
47
48
 
@@ -1823,7 +1824,7 @@ function ConfigurationDialogBodyComponent(props: any) {
1823
1824
  const [inlineCompletionModels, setInlineCompletionModels] = useState([]);
1824
1825
 
1825
1826
  const handleSaveClick = async () => {
1826
- await NBIAPI.setConfig({
1827
+ const config: any = {
1827
1828
  chat_model: {
1828
1829
  provider: chatModelProvider,
1829
1830
  model: chatModel,
@@ -1834,7 +1835,16 @@ function ConfigurationDialogBodyComponent(props: any) {
1834
1835
  model: inlineCompletionModel,
1835
1836
  properties: inlineCompletionModelProperties
1836
1837
  }
1837
- });
1838
+ };
1839
+
1840
+ if (
1841
+ chatModelProvider === 'github-copilot' ||
1842
+ inlineCompletionModelProvider === 'github-copilot'
1843
+ ) {
1844
+ config.store_github_access_token = storeGitHubAccessToken;
1845
+ }
1846
+
1847
+ await NBIAPI.setConfig(config);
1838
1848
 
1839
1849
  props.onSave();
1840
1850
  };
@@ -1856,6 +1866,9 @@ function ConfigurationDialogBodyComponent(props: any) {
1856
1866
  const [inlineCompletionModel, setInlineCompletionModel] = useState(
1857
1867
  nbiConfig.inlineCompletionModel.model
1858
1868
  );
1869
+ const [storeGitHubAccessToken, setStoreGitHubAccessToken] = useState(
1870
+ nbiConfig.storeGitHubAccessToken
1871
+ );
1859
1872
 
1860
1873
  const updateModelOptionsForProvider = (
1861
1874
  providerId: string,
@@ -2161,6 +2174,41 @@ function ConfigurationDialogBodyComponent(props: any) {
2161
2174
  </div>
2162
2175
  </div>
2163
2176
  </div>
2177
+
2178
+ {(chatModelProvider === 'github-copilot' ||
2179
+ inlineCompletionModelProvider === 'github-copilot') && (
2180
+ <div className="model-config-section">
2181
+ <div className="model-config-section-header access-token-config-header">
2182
+ GitHub Copilot login{' '}
2183
+ <a
2184
+ href="https://github.com/notebook-intelligence/notebook-intelligence/blob/main/README.md#remembering-github-copilot-login"
2185
+ target="_blank"
2186
+ >
2187
+ {' '}
2188
+ <VscWarning
2189
+ className="access-token-warning"
2190
+ title="Click to learn more about security implications"
2191
+ />
2192
+ </a>
2193
+ </div>
2194
+ <div className="model-config-section-body">
2195
+ <div className="model-config-section-row">
2196
+ <div className="model-config-section-column">
2197
+ <label>
2198
+ <input
2199
+ type="checkbox"
2200
+ checked={storeGitHubAccessToken}
2201
+ onChange={event => {
2202
+ setStoreGitHubAccessToken(event.target.checked);
2203
+ }}
2204
+ />
2205
+ Remember my GitHub Copilot access token
2206
+ </label>
2207
+ </div>
2208
+ </div>
2209
+ </div>
2210
+ </div>
2211
+ )}
2164
2212
  </div>
2165
2213
 
2166
2214
  <div className="config-dialog-footer">
package/src/index.ts CHANGED
@@ -1018,12 +1018,28 @@ const plugin: JupyterFrontEndPlugin<INotebookIntelligence> = {
1018
1018
  return editorRect;
1019
1019
  }
1020
1020
  const yOffset = 30;
1021
+ const diffViewHeight = 300;
1022
+
1023
+ let top = coords.top - yOffset;
1024
+ const height = coords.bottom - coords.top;
1025
+
1026
+ // adjust top to fit in file editor rect
1027
+ if (!isCodeCell) {
1028
+ if (top + height + diffViewHeight > editorRect.bottom) {
1029
+ top = editorRect.bottom - height - diffViewHeight;
1030
+ if (top < editorRect.top) {
1031
+ top = editorRect.top;
1032
+ }
1033
+ }
1034
+ }
1035
+
1021
1036
  const rect: DOMRect = new DOMRect(
1022
1037
  editorRect.left,
1023
- coords.top - yOffset,
1038
+ top,
1024
1039
  editorRect.right - editorRect.left,
1025
- coords.bottom - coords.top
1040
+ height
1026
1041
  );
1042
+
1027
1043
  return rect;
1028
1044
  };
1029
1045
 
package/style/base.css CHANGED
@@ -614,3 +614,15 @@ body[data-jp-theme-light='false'] .inline-popover {
614
614
  .chat-reasoning-content.expanded .collapsed-icon {
615
615
  display: none;
616
616
  }
617
+
618
+ .access-token-config-header {
619
+ display: flex;
620
+ align-items: center;
621
+ gap: 10px;
622
+ }
623
+
624
+ svg.access-token-warning {
625
+ color: var(--jp-warn-color0);
626
+ width: 18px;
627
+ height: 18px;
628
+ }