Skip to content

Commit 4972d3c

Browse files
committed
Update adapter to handle undo/redo actions
1 parent c665de2 commit 4972d3c

File tree

12 files changed

+154
-70
lines changed

12 files changed

+154
-70
lines changed

.lintstagedrc.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,21 @@ module.exports = {
88
// TypeScript and TSX files
99
'*.{ts,tsx}': ['eslint --fix', 'prettier --write'],
1010

11-
// JavaScript and JSX files
12-
'*.{js,jsx}': ['eslint --fix', 'prettier --write'],
11+
// JavaScript and JSX files (exclude config files)
12+
'*.{js,jsx}': filenames => {
13+
const filtered = filenames.filter(
14+
file =>
15+
!file.endsWith('.config.js') &&
16+
!file.includes('gulpfile') &&
17+
!file.includes('/scripts/'),
18+
);
19+
return filtered.length > 0
20+
? [
21+
`eslint --fix ${filtered.join(' ')}`,
22+
`prettier --write ${filtered.join(' ')}`,
23+
]
24+
: [];
25+
},
1326

1427
// JSON files
1528
'*.json': ['prettier --write'],

eslint.config.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,16 @@ module.exports = tseslint.config(
1717
languageOptions: {
1818
parserOptions: {
1919
tsconfigRootDir: path.resolve(__dirname),
20-
projectService: true,
20+
projectService: {
21+
allowDefaultProject: [
22+
'*.config.js',
23+
'*.config.ts',
24+
'.commitlintrc.js',
25+
'.eslintrc.js',
26+
'.lintstagedrc.js',
27+
'gulpfile.js',
28+
],
29+
},
2130
},
2231
},
2332
},

packages/react/eslint.config.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
*/
1111

1212
// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
13+
import { fileURLToPath } from 'url';
14+
import { dirname } from 'path';
1315
import storybook from 'eslint-plugin-storybook';
1416
import js from '@eslint/js';
1517
import globals from 'globals';
@@ -19,6 +21,9 @@ import reactRefresh from 'eslint-plugin-react-refresh';
1921
import prettier from 'eslint-plugin-prettier';
2022
import tseslint from 'typescript-eslint';
2123

24+
const __filename = fileURLToPath(import.meta.url);
25+
const __dirname = dirname(__filename);
26+
2227
export default tseslint.config(
2328
{
2429
ignores: [
@@ -33,6 +38,9 @@ export default tseslint.config(
3338
'**/storybook-static/**',
3439
'**/.next/**',
3540
'tsconfig.tsbuildinfo',
41+
'*.config.js',
42+
'scripts/**/*.js',
43+
'gulpfile.js',
3644
],
3745
},
3846
{
@@ -56,8 +64,16 @@ export default tseslint.config(
5664
parserOptions: {
5765
ecmaFeatures: {
5866
jsx: true,
59-
tsconfigRootDir: __dirname,
6067
},
68+
projectService: {
69+
allowDefaultProject: [
70+
'*.js',
71+
'*.mjs',
72+
'*.cjs',
73+
'playwright.config.ts',
74+
],
75+
},
76+
tsconfigRootDir: __dirname,
6177
},
6278
},
6379
settings: {
@@ -170,10 +186,15 @@ export default tseslint.config(
170186
},
171187
// Config files (Node.js environment)
172188
{
173-
files: ['*.config.{js,mjs,cjs}', 'gulpfile.{js,mjs,cjs}'],
189+
files: ['*.config.{js,mjs,cjs}', 'gulpfile.{js,mjs,cjs}', 'scripts/**/*.js'],
174190
languageOptions: {
191+
ecmaVersion: 2020,
192+
sourceType: 'module',
175193
globals: globals.node,
176194
},
195+
rules: {
196+
'no-console': 'off',
197+
},
177198
},
178199
storybook.configs['flat/recommended']
179200
);

packages/react/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"clean:lib": "rimraf lib tsconfig.tsbuildinfo",
4242
"clean:lintcache": "rimraf .eslintcache .stylelintcache",
4343
"dev": "run-p -c 'start:*'",
44-
"eslint": "npm eslint:check --fix",
44+
"eslint": "eslint . --cache --ext .ts,.tsx --fix",
4545
"eslint:check": "eslint . --cache --ext .ts,.tsx",
4646
"example": "run-p -c 'start:*'",
4747
"install:extension": "npm run build",
@@ -131,9 +131,9 @@
131131
"@jupyterlab/toc-extension": "^6.0.0",
132132
"@jupyterlab/translation-extension": "^4.0.0",
133133
"@jupyterlab/ui-components-extension": "^4.0.0",
134+
"@jupyterlite/kernel": "^0.5.1",
134135
"@jupyterlite/licenses": "^0.5.1",
135136
"@jupyterlite/localforage": "^0.5.1",
136-
"@jupyterlite/kernel": "^0.5.1",
137137
"@jupyterlite/pyodide-kernel": "^0.5.1",
138138
"@jupyterlite/pyodide-kernel-extension": "^0.5.1",
139139
"@jupyterlite/server": "^0.5.1",
@@ -203,6 +203,8 @@
203203
"eslint": "^9.0.0",
204204
"eslint-config-prettier": "^10.1.2",
205205
"eslint-plugin-prettier": "^5.2.6",
206+
"eslint-plugin-react-refresh": "^0.4.24",
207+
"eslint-plugin-storybook": "^9.1.12",
206208
"gulp": "^4.0.2",
207209
"gulp-append-prepend": "^1.0.8",
208210
"gulp-filter": "^6.0.0",

packages/react/scripts/patch-vscode-highlighting.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
*/
88

99
/* eslint-env node */
10-
/* global require */
11-
/* eslint-disable @typescript-eslint/no-require-imports, no-console */
10+
/* eslint-disable no-console */
1211

1312
/**
1413
* Post-build script to patch NotebookAdapter with VS Code syntax highlighting support

packages/react/src/components/kernel/Kernelndicator.tsx

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -57,21 +57,26 @@ export type ExecutionState =
5757
*
5858
* Status = 'unknown' | 'starting' | 'idle' | 'busy' | 'terminating' | 'restarting' | 'autorestarting' | 'dead';
5959
*/
60-
/* eslint-disable react/jsx-key */
60+
6161
export const KERNEL_STATES: Map<ExecutionState, ReactElement> = new Map([
62-
['connecting', <PlusCircleIcon />],
63-
['connected-unknown', <CircleCurrentColorIcon color="lightgray" />],
64-
['connected-starting', <CircleYellowIcon />],
65-
['connected-idle', <CircleGreenIcon />],
66-
['connected-busy', <CircleOrangeIcon />],
67-
['connected-terminating', <CircleWhiteIcon />],
68-
['connected-restarting', <CirclePurpleIcon />],
69-
['connected-autorestarting', <CircleHollowRedIcon />],
70-
['connected-dead', <CircleRedIcon />],
71-
['disconnecting', <CircleBrownIcon />],
72-
['undefined', <CircleBlackIcon />],
62+
['connecting', <PlusCircleIcon key="connecting" />],
63+
[
64+
'connected-unknown',
65+
<CircleCurrentColorIcon key="connected-unknown" color="lightgray" />,
66+
],
67+
['connected-starting', <CircleYellowIcon key="connected-starting" />],
68+
['connected-idle', <CircleGreenIcon key="connected-idle" />],
69+
['connected-busy', <CircleOrangeIcon key="connected-busy" />],
70+
['connected-terminating', <CircleWhiteIcon key="connected-terminating" />],
71+
['connected-restarting', <CirclePurpleIcon key="connected-restarting" />],
72+
[
73+
'connected-autorestarting',
74+
<CircleHollowRedIcon key="connected-autorestarting" />,
75+
],
76+
['connected-dead', <CircleRedIcon key="connected-dead" />],
77+
['disconnecting', <CircleBrownIcon key="disconnecting" />],
78+
['undefined', <CircleBlackIcon key="undefined" />],
7379
]);
74-
/* eslint-enable react/jsx-key */
7580

7681
export const toKernelState = (
7782
connectionStatus: ConnectionStatus,

packages/react/src/components/lumino/Lumino.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ type LuminoProps = {
1313
children: Widget;
1414
};
1515

16-
export const Lumino = (props: LuminoProps) => {
16+
export const Lumino = ({
17+
id = 'lumino-id',
18+
height = '100%',
19+
children,
20+
}: LuminoProps) => {
1721
const ref = useRef<HTMLDivElement>(null);
18-
const { children, id, height } = props;
1922
useEffect(() => {
2023
console.log(
2124
'Lumino useEffect - ref.current:',
@@ -76,9 +79,4 @@ export const Lumino = (props: LuminoProps) => {
7679
);
7780
};
7881

79-
Lumino.defaultProps = {
80-
id: 'lumino-id',
81-
height: '100%',
82-
};
83-
8482
export default Lumino;

packages/react/src/components/notebook/Notebook2Adapter.ts

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
*/
66

77
import { CommandRegistry } from '@lumino/commands';
8-
import { NotebookPanel, NotebookActions, type Notebook } from '@jupyterlab/notebook';
8+
import {
9+
NotebookPanel,
10+
NotebookActions,
11+
type Notebook,
12+
} from '@jupyterlab/notebook';
913
import { Context } from '@jupyterlab/docregistry';
1014
import { NotebookModel } from '@jupyterlab/notebook';
1115
import * as nbformat from '@jupyterlab/nbformat';
@@ -75,20 +79,20 @@ export class Notebook2Adapter {
7579
*/
7680
insertAbove(source?: string): void {
7781
const notebook = this._notebook;
78-
82+
7983
// Insert above using NotebookActions
8084
NotebookActions.insertAbove(notebook);
81-
85+
8286
// Set the cell type if different from default
8387
if (this._defaultCellType !== 'code') {
8488
NotebookActions.changeCellType(notebook, this._defaultCellType);
8589
}
86-
90+
8791
// Set the source if provided
8892
if (source && notebook.activeCell) {
8993
notebook.activeCell.model.sharedModel.setSource(source);
9094
}
91-
95+
9296
// Enter edit mode
9397
notebook.mode = 'edit';
9498
}
@@ -98,20 +102,20 @@ export class Notebook2Adapter {
98102
*/
99103
insertBelow(source?: string): void {
100104
const notebook = this._notebook;
101-
105+
102106
// Insert below using NotebookActions
103107
NotebookActions.insertBelow(notebook);
104-
108+
105109
// Set the cell type if different from default
106110
if (this._defaultCellType !== 'code') {
107111
NotebookActions.changeCellType(notebook, this._defaultCellType);
108112
}
109-
113+
110114
// Set the source if provided
111115
if (source && notebook.activeCell) {
112116
notebook.activeCell.model.sharedModel.setSource(source);
113117
}
114-
118+
115119
// Enter edit mode
116120
notebook.mode = 'edit';
117121
}
@@ -121,7 +125,7 @@ export class Notebook2Adapter {
121125
*/
122126
changeCellType(cellType: nbformat.CellType): void {
123127
const notebook = this._notebook;
124-
128+
125129
if (notebook.activeCell && notebook.activeCell.model.type !== cellType) {
126130
NotebookActions.changeCellType(notebook, cellType);
127131
}
@@ -142,6 +146,20 @@ export class Notebook2Adapter {
142146
return this._context.model;
143147
}
144148

149+
/**
150+
* Undo the last change in the notebook.
151+
*/
152+
undo(): void {
153+
NotebookActions.undo(this._notebook);
154+
}
155+
156+
/**
157+
* Redo the last undone change in the notebook.
158+
*/
159+
redo(): void {
160+
NotebookActions.redo(this._notebook);
161+
}
162+
145163
/**
146164
* Dispose of the adapter.
147165
*/

packages/react/src/components/notebook/Notebook2State.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { createStore } from 'zustand/vanilla';
88
import { useStore } from 'zustand';
99
import * as nbformat from '@jupyterlab/nbformat';
1010
import { NotebookCommandIds } from './NotebookCommands';
11-
import { Notebook2Adapter} from './Notebook2Adapter';
11+
import { Notebook2Adapter } from './Notebook2Adapter';
1212

1313
export type INotebook2State = {
1414
adapter?: Notebook2Adapter;
@@ -35,6 +35,8 @@ export type Notebook2State = INotebooks2State & {
3535
insertBelow: (mutation: CellMutation) => void;
3636
delete: (id: string) => void;
3737
changeCellType: (mutation: CellMutation) => void;
38+
undo: (id: string) => void;
39+
redo: (id: string) => void;
3840
reset: () => void;
3941
};
4042

@@ -87,6 +89,16 @@ export const notebookStore2 = createStore<Notebook2State>((set, get) => ({
8789
.notebooks.get(mutation.id)
8890
?.adapter?.changeCellType(mutation.cellType);
8991
},
92+
undo: (id: string): void => {
93+
// Directly call adapter's undo method which uses NotebookActions
94+
// This works for both local notebooks and collaborative notebooks
95+
get().notebooks.get(id)?.adapter?.undo();
96+
},
97+
redo: (id: string): void => {
98+
// Directly call adapter's redo method which uses NotebookActions
99+
// This works for both local notebooks and collaborative notebooks
100+
get().notebooks.get(id)?.adapter?.redo();
101+
},
90102
reset: () =>
91103
set((state: Notebook2State) => ({
92104
notebooks: new Map<string, INotebook2State>(),

packages/react/src/components/output/ipywidgets/IPyWidgetsViewManager.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,8 @@ export class IPyWidgetsViewManager extends ManagerBase {
5757
});
5858
}
5959
async display_view(view: any) {
60-
// eslint-disable-next-line @typescript-eslint/no-this-alias
61-
const that = this;
6260
return Promise.resolve(view).then(view => {
63-
Widget.attach(view.luminoWidget, that.el);
61+
Widget.attach(view.luminoWidget, this.el);
6462
return view;
6563
});
6664
}

0 commit comments

Comments
 (0)