Skip to content

Commit

Permalink
add JS lint workflow (#230)
Browse files Browse the repository at this point in the history
* Fixes error and omission in config files

* Changes after running jlpm lint once

* Modifies eslint config

* Ignores dotfiles

* Fix lint errors

* Fixes or ignores eslint warnings

* Add new workflow

* Fix error in config

* Runs linting only on Linux

* Uses standard GitHub action for setup-node

* Force yarn used for caching

* Simplify test config

* Adds reopen trigger

* fix lint workflow

---------

Co-authored-by: David L. Qiu <david@qiu.dev>
  • Loading branch information
JasonWeill and dlqqq authored Jun 21, 2023
1 parent f32c699 commit 4470155
Show file tree
Hide file tree
Showing 25 changed files with 175 additions and 134 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: JS Lint
on:
- pull_request

jobs:
js_lint:
name: JS Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
with:
python_version: "3.10.x"
- name: Install JupyterLab
run: pip install jupyterlab~=3.6
- name: Install JS dependencies
run: jlpm
- name: Run JS Lint
run: jlpm lerna run lint:check
2 changes: 2 additions & 0 deletions packages/jupyter-ai/.eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ tests

**/__tests__
ui-tests

.*.js
9 changes: 6 additions & 3 deletions packages/jupyter-ai/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = {
],
parser: '@typescript-eslint/parser',
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
},
plugins: ['@typescript-eslint'],
Expand Down Expand Up @@ -35,7 +36,9 @@ module.exports = {
eqeqeq: 'error',
'prefer-arrow-callback': 'error'
},
overrides: {
files: ['src/**/*']
}
overrides: [
{
files: ['src/**/*']
}
]
};
1 change: 1 addition & 0 deletions packages/jupyter-ai/.stylelintrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
],
"rules": {
"property-no-vendor-prefix": null,
"selector-class-pattern": null,
"selector-no-vendor-prefix": null,
"value-no-vendor-prefix": null
}
Expand Down
2 changes: 1 addition & 1 deletion packages/jupyter-ai/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ First, install [conda](https://conda.io/projects/conda/en/latest/user-guide/inst

If you are using an Apple Silicon-based Mac (M1, M1 Pro, M2, etc.), you need to uninstall the `pip` provided version of `grpcio` and install the version provided by `conda` instead.

$ pip uninstall grpcio; conda install grpcio
$ pip uninstall grpcio; conda install grpcio

## Uninstall

Expand Down
49 changes: 24 additions & 25 deletions packages/jupyter-ai/src/chat_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class ChatHandler implements IDisposable {
/**
* ID of the connection. Requires `await initialize()`.
*/
id: string = '';
id = '';

/**
* Create a new chat handler.
Expand All @@ -29,11 +29,10 @@ export class ChatHandler implements IDisposable {
* resolved when server acknowledges connection and sends the client ID. This
* must be awaited before calling any other method.
*/
public async initialize() {
public async initialize(): Promise<void> {
await this._initialize();
}


/**
* Sends a message across the WebSocket. Promise resolves to the message ID
* when the server sends the same message back, acknowledging receipt.
Expand Down Expand Up @@ -141,13 +140,13 @@ export class ChatHandler implements IDisposable {
> = {};

private _onClose(e: CloseEvent, reject: any) {
reject(new Error("Chat UI websocket disconnected"))
console.error("Chat UI websocket disconnected")
reject(new Error('Chat UI websocket disconnected'));
console.error('Chat UI websocket disconnected');
// only attempt re-connect if there was an abnormal closure
// WebSocket status codes defined in RFC 6455: https://www.rfc-editor.org/rfc/rfc6455.html#section-7.4.1
if (e.code === 1006) {
const delaySeconds = 1
console.info(`Will try to reconnect in ${delaySeconds} s.`)
const delaySeconds = 1;
console.info(`Will try to reconnect in ${delaySeconds} s.`);
setTimeout(async () => await this._initialize(), delaySeconds * 1000);
}
}
Expand All @@ -157,28 +156,28 @@ export class ChatHandler implements IDisposable {
if (this.isDisposed) {
return;
}
console.log("Creating a new websocket connection for chat...");
console.log('Creating a new websocket connection for chat...');
const { token, WebSocket, wsUrl } = this.serverSettings;
const url =
URLExt.join(wsUrl, CHAT_SERVICE_URL) +
(token ? `?token=${encodeURIComponent(token)}` : '');
const socket = (this._socket = new WebSocket(url));
socket.onclose = (e) => this._onClose(e, reject);
socket.onerror = (e) => reject(e);
socket.onmessage = msg =>
msg.data && this._onMessage(JSON.parse(msg.data));
const listenForConnection = (message: AiService.Message) => {
if (message.type !== 'connection') {
return;
}
this.id = message.client_id;
resolve();
this.removeListener(listenForConnection);
};
this.addListener(listenForConnection);

const socket = (this._socket = new WebSocket(url));
socket.onclose = e => this._onClose(e, reject);
socket.onerror = e => reject(e);
socket.onmessage = msg =>
msg.data && this._onMessage(JSON.parse(msg.data));

const listenForConnection = (message: AiService.Message) => {
if (message.type !== 'connection') {
return;
}
this.id = message.client_id;
resolve();
this.removeListener(listenForConnection);
};

this.addListener(listenForConnection);
});
}

Expand Down
2 changes: 1 addition & 1 deletion packages/jupyter-ai/src/components/chat-code-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export function ChatCodeView({
inline,
className,
...props
}: ChatCodeViewProps) {
}: ChatCodeViewProps): JSX.Element {
const match = /language-(\w+)/.exec(className || '');
return inline ? (
<ChatCodeInline {...props} />
Expand Down
58 changes: 32 additions & 26 deletions packages/jupyter-ai/src/components/chat-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,53 +27,59 @@ type ChatInputProps = {
};

export function ChatInput(props: ChatInputProps): JSX.Element {

function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>) {
if (event.key === 'Enter' && (
(props.sendWithShiftEnter && event.shiftKey)
|| (!props.sendWithShiftEnter && !event.shiftKey)
)) {
if (
event.key === 'Enter' &&
((props.sendWithShiftEnter && event.shiftKey) ||
(!props.sendWithShiftEnter && !event.shiftKey))
) {
props.onSend();
event.stopPropagation();
event.preventDefault();
}
}

// Set the helper text based on whether Shift+Enter is used for sending.
const helperText = props.sendWithShiftEnter
? <span>Press <b>Shift</b>+<b>Enter</b> to send message</span>
: <span>Press <b>Shift</b>+<b>Enter</b> to add a new line</span>;
const helperText = props.sendWithShiftEnter ? (
<span>
Press <b>Shift</b>+<b>Enter</b> to send message
</span>
) : (
<span>
Press <b>Shift</b>+<b>Enter</b> to add a new line
</span>
);

return (
<Box sx={props.sx}>
<Box sx={{ display: 'flex'}}>
<Box sx={{ display: 'flex' }}>
<TextField
value={props.value}
onChange={e => props.onChange(e.target.value)}
fullWidth
variant="outlined"
multiline
onKeyDown={handleKeyDown}
placeholder='Ask Jupyternaut anything'
placeholder="Ask Jupyternaut anything"
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
size="small"
color="primary"
onClick={props.onSend}
disabled={!props.value.trim().length}
title='Send message (SHIFT+ENTER)'
>
<SendIcon />
</IconButton>
</InputAdornment>
<InputAdornment position="end">
<IconButton
size="small"
color="primary"
onClick={props.onSend}
disabled={!props.value.trim().length}
title="Send message (SHIFT+ENTER)"
>
<SendIcon />
</IconButton>
</InputAdornment>
)
}}
FormHelperTextProps={{
sx: {marginLeft: 'auto', marginRight: 0}
}}
helperText={props.value.length > 2 ? helperText : ' '}
}}
FormHelperTextProps={{
sx: { marginLeft: 'auto', marginRight: 0 }
}}
helperText={props.value.length > 2 ? helperText : ' '}
/>
</Box>
{props.hasSelection && (
Expand Down
17 changes: 8 additions & 9 deletions packages/jupyter-ai/src/components/chat-messages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type ChatMessageHeaderProps = {
sx?: SxProps<Theme>;
};

export function ChatMessageHeader(props: ChatMessageHeaderProps) {
export function ChatMessageHeader(props: ChatMessageHeaderProps): JSX.Element {
const collaborators = useCollaboratorsContext();

const sharedStyles: SxProps<Theme> = {
Expand Down Expand Up @@ -101,25 +101,24 @@ export function ChatMessageHeader(props: ChatMessageHeaderProps) {
);
}

export function ChatMessages(props: ChatMessagesProps) {
export function ChatMessages(props: ChatMessagesProps): JSX.Element {
const [timestamps, setTimestamps] = useState<Record<string, string>>({});

/**
* Effect: update cached timestamp strings upon receiving a new message.
*/
useEffect(() => {
const newTimestamps: Record<string, string> = { ...timestamps };
let timestampAdded: boolean = false;
let timestampAdded = false;

for (const message of props.messages) {
if (!(message.id in newTimestamps)) {
// Use the browser's default locale
newTimestamps[message.id] =
new Date(message.time * 1000) // Convert message time to milliseconds
.toLocaleTimeString([], {
hour: 'numeric', // Avoid leading zero for hours; we don't want "03:15 PM"
minute: '2-digit'
});
newTimestamps[message.id] = new Date(message.time * 1000) // Convert message time to milliseconds
.toLocaleTimeString([], {
hour: 'numeric', // Avoid leading zero for hours; we don't want "03:15 PM"
minute: '2-digit'
});

timestampAdded = true;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/jupyter-ai/src/components/chat-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function fromRegistryProvider(
* provider is not a registry provider. Otherwise, it is set to
* `<provider-id>:*`.
*/
export function ChatSettings() {
export function ChatSettings(): JSX.Element {
const [state, setState] = useState<ChatSettingsState>(
ChatSettingsState.Loading
);
Expand Down
29 changes: 16 additions & 13 deletions packages/jupyter-ai/src/components/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@ import { ScrollContainer } from './scroll-container';

type ChatBodyProps = {
chatHandler: ChatHandler;
setChatView: (view: ChatView) => void
setChatView: (view: ChatView) => void;
};

function ChatBody({ chatHandler, setChatView: chatViewHandler }: ChatBodyProps): JSX.Element {
function ChatBody({
chatHandler,
setChatView: chatViewHandler
}: ChatBodyProps): JSX.Element {
const [messages, setMessages] = useState<AiService.ChatMessage[]>([]);
const [showWelcomeMessage, setShowWelcomeMessage] = useState<boolean>(false);
const [includeSelection, setIncludeSelection] = useState(true);
Expand Down Expand Up @@ -106,9 +109,9 @@ function ChatBody({ chatHandler, setChatView: chatViewHandler }: ChatBodyProps):
};

const openSettingsView = () => {
setShowWelcomeMessage(false)
chatViewHandler(ChatView.Settings)
}
setShowWelcomeMessage(false);
chatViewHandler(ChatView.Settings);
};

if (showWelcomeMessage) {
return (
Expand All @@ -127,13 +130,13 @@ function ChatBody({ chatHandler, setChatView: chatViewHandler }: ChatBodyProps):
model to chat with from the settings panel. You will also likely
need to provide API credentials, so be sure to have those handy.
</p>
<Button
variant="contained"
startIcon={<SettingsIcon />}
<Button
variant="contained"
startIcon={<SettingsIcon />}
size={'large'}
onClick={() => openSettingsView()}
>
Start Here
>
Start Here
</Button>
</Stack>
</Box>
Expand Down Expand Up @@ -175,15 +178,15 @@ export type ChatProps = {
selectionWatcher: SelectionWatcher;
chatHandler: ChatHandler;
globalAwareness: Awareness | null;
chatView?: ChatView
chatView?: ChatView;
};

enum ChatView {
Chat,
Settings
}

export function Chat(props: ChatProps) {
export function Chat(props: ChatProps): JSX.Element {
const [view, setView] = useState<ChatView>(props.chatView || ChatView.Chat);

return (
Expand Down Expand Up @@ -221,7 +224,7 @@ export function Chat(props: ChatProps) {
</Box>
{/* body */}
{view === ChatView.Chat && (
<ChatBody chatHandler={props.chatHandler} setChatView={setView}/>
<ChatBody chatHandler={props.chatHandler} setChatView={setView} />
)}
{view === ChatView.Settings && <ChatSettings />}
</Box>
Expand Down
4 changes: 3 additions & 1 deletion packages/jupyter-ai/src/components/jl-theme-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import React, { useState, useEffect } from 'react';
import { Theme, ThemeProvider, createTheme } from '@mui/material/styles';
import { getJupyterLabTheme } from '../theme-provider';

export function JlThemeProvider(props: { children: React.ReactNode }) {
export function JlThemeProvider(props: {
children: React.ReactNode;
}): JSX.Element {
const [theme, setTheme] = useState<Theme>(createTheme());

useEffect(() => {
Expand Down
Loading

0 comments on commit 4470155

Please sign in to comment.