Skip to content

Commit

Permalink
[MLC-8] app: Try to emulate spotlight with view
Browse files Browse the repository at this point in the history
  • Loading branch information
ParkerSm1th committed Feb 28, 2024
1 parent f9017ee commit d60f686
Show file tree
Hide file tree
Showing 11 changed files with 204 additions and 107 deletions.
80 changes: 61 additions & 19 deletions app/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
globalShortcut,
ipcMain,
Menu,
nativeImage,
Tray,
} from 'electron';

import * as net from 'net';
Expand Down Expand Up @@ -106,6 +108,8 @@ const createSplashScreen = () => {
focusable: false,
/// remove the window frame, so it will become a frameless window
frame: false,
skipTaskbar: true,
autoHideMenuBar: true,
},
);
splash.setResizable(false);
Expand All @@ -126,28 +130,32 @@ if (isProd) {
app.setPath('userData', `${app.getPath('userData')} (development)`);
}

let directoryOpen = false;

const createWindow = () => {
if (process.platform === 'darwin') {
app.dock.hide();
}
const icon = nativeImage.createFromPath('path/to/asset.png');
let tray = new Tray(icon);
tray.setTitle('M');

const win = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
devTools: !isProd,
},
show: false,
width: 1000,
height: 800,
minHeight: 800,
minWidth: 1000,
titleBarStyle: 'hidden',
titleBarOverlay: {
height: 20,
},
width: 600,
height: 95,
resizable: false,
type: 'panel',
frame: false,
skipTaskbar: true,
autoHideMenuBar: true,
vibrancy: 'under-window', // on MacOS
backgroundMaterial: 'acrylic',
});
win.setAlwaysOnTop(true, 'screen-saver');
app.dock.hide();
win.setWindowButtonVisibility(false);
win.setAlwaysOnTop(true, 'floating');
win.setVisibleOnAllWorkspaces(true);

// win.webContents.openDevTools();
Expand All @@ -160,6 +168,13 @@ const createWindow = () => {
win.loadURL('http://localhost:3000/');
}

tray.addListener('click', () => {
if (win.isFocused()) {
return;
}
win.show();
});

win.webContents.on('did-finish-load', () => {
/// then close the loading screen window and show the main window
if (splash) {
Expand All @@ -173,30 +188,48 @@ const createWindow = () => {
}
win.show();
});
globalShortcut.register('Escape', () => {
win.blur();
win.hide();
Menu.sendActionToFirstResponder('hide:');
});
});

// @ts-expect-error -- We don't have types for electron
win.on('blur', (event) => {
// event.preventDefault();
if (directoryOpen) {
win.setAlwaysOnTop(false);
return;
}

globalShortcut.unregister('Escape');
win.hide();
Menu.sendActionToFirstResponder('hide:');
});

win.on('focus', () => {
globalShortcut.register('Escape', () => {
if (!win.isFocused()) {
return;
}
win.blur();
});
});

win.on('close', (event) => {
event.preventDefault();
win.hide();
});
};

app.whenReady().then(() => {
if (process.platform == 'darwin') {
app.dock.hide();
}
ipcMain.on('set-title', handleSetTitle);
ipcMain.on('select-directory', (event: any) => {
directoryOpen = true;
dialog.showOpenDialog({ properties: ['openDirectory'] }).then((result: any) => {
const win = BrowserWindow.fromWebContents(event.sender);
// Weird hack to bring the window to the front after allowing windows in front of it
win?.setAlwaysOnTop(true, 'floating');

directoryOpen = false;
event.sender.send('selected-directory', result.filePaths);
});
});
Expand All @@ -209,9 +242,18 @@ app.whenReady().then(() => {
.catch(error => console.error('Error starting server:', error));
});

ipcMain.on('resize-window', (event, arg) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (!win) {
return;
}
win.setBounds({
height: arg.height,
});
});

createSplashScreen();

// createWindow();
setTimeout(() => {
createWindow();
}, 2000);
Expand Down
1 change: 1 addition & 0 deletions app/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const electronAPI = {
});
},
startServer: (model: string) => ipcRenderer.send('start-server', model),
resizeWindow: (height: number) => ipcRenderer.send('resize-window', { height }),
};

contextBridge.exposeInMainWorld('electronAPI', electronAPI);
6 changes: 5 additions & 1 deletion app/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
@apply border-border;
}
body {
@apply bg-background text-foreground;
@apply text-foreground;
/* font-feature-settings: "rlig" 1, "calt" 1; */
font-synthesis-weight: none;
text-rendering: optimizeLegibility;
Expand Down Expand Up @@ -98,3 +98,7 @@
background-color: transparent;
}
}

html {
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont;
}
21 changes: 6 additions & 15 deletions app/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
'use client';

import './globals.css';
import {
Inter,
} from 'next/font/google';

// eslint-disable-next-line new-cap
const inter = Inter({ subsets: ['latin'] });

export default function RootLayout({
children,
Expand All @@ -15,15 +9,12 @@ export default function RootLayout({
}) {
return (
<html lang='en'>
<body className={`${inter.className} bg-background min-h-screen overflow-y-hidden`}>
<div
className='h-10 w-full'
style={{
// @ts-expect-error -- WebkitAppRegion is a valid property
// eslint-disable-next-line @typescript-eslint/naming-convention
WebkitAppRegion: 'drag',
}}
/>
<body
className={'min-h-screen overflow-y-hidden'}
style={{
userSelect: 'none',
}}
>
{children}
</body>
</html>
Expand Down
60 changes: 8 additions & 52 deletions app/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,8 @@ import React, {
useState,
} from 'react';
import Chat from '../components/chat/Chat';
import {
Button,
} from '../components/ui/button';
import {
Input,
} from '../components/ui/input';
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from '../components/ui/select';
import SelectDirectory from '../components/options/SelectDirectory';
import SelectModel from '../components/options/SelectModel';

export default function Home() {
const [selectedDirectory, setSelectedDirectory] = useState<string | null>(null);
Expand All @@ -45,45 +32,14 @@ export default function Home() {
};

return (
<main className='flex flex-col min-h-[calc(100vh-40px)] pb-4 px-8 gap-4 mb-4'>
<div className=''>
<div className='grid grid-cols-2 gap-4'>
<div className='bg-slate-100 rounded-sm dark:bg-zinc-900 border px-8 py-4'>
<h1 className='text-md font-bold'>AI Model</h1>
<p className='text-sm text-opacity-50 pt-1'>
Choose between a set of different models
</p>
<div className='pt-4'>
<Select
value={selectedModel ?? ''}
onValueChange={(value) => handleModelChange(value)}
>
<SelectTrigger className='w-[180px]'>
<SelectValue placeholder='Select a model' />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>AI Model</SelectLabel>
<SelectItem value='llama'>LLama</SelectItem>
<SelectItem value='mlx-community/quantized-gemma-7b-it'>Gemma</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</div>
</div>
<div className='bg-slate-100 rounded-sm dark:bg-zinc-900 border px-8 py-4'>
<h1 className='text-md font-bold'>Data Set</h1>
<p className='text-sm text-opacity-50 pt-1'>
This folder can contain .md, .pdf, and .doc files
</p>
<div className='flex gap-2 mt-4'>
<Input onClick={handleOpen} value={selectedDirectory ?? ''} readOnly />
<Button onClick={handleOpen}>Open</Button>
</div>
</div>
<main className='flex flex-col'>
<Chat selectedDirectory={selectedDirectory} />
<div className='border-t border-t-neutral-400 dark:border-t-neutral-700 pt-1 px-2'>
<div className='flex justify-between'>
<SelectModel selectedModel={selectedModel} handleModelChange={handleModelChange} />
<SelectDirectory handleOpen={handleOpen} selectedDirectory={selectedDirectory} />
</div>
</div>
<Chat selectedDirectory={selectedDirectory} />
</main>
);
}
21 changes: 19 additions & 2 deletions app/src/components/chat/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import React, {
useState,
} from 'react';
import {
cn,
} from '../../lib/utils';
import ChatInput from './ChatInput';
import ChatMessages from './ChatMessages';

Expand All @@ -12,6 +15,9 @@ const Chat = ({
const [chatHistory, setChatHistory] = useState<{ role: string; content: string | null; }[]>([]);
const sendMessage = async (message: string) => {
try {
if (chatHistory.length === 0) {
window.electronAPI.resizeWindow(500);
}
const newHistory = [
...chatHistory,
{ role: 'user', content: message },
Expand Down Expand Up @@ -46,10 +52,21 @@ const Chat = ({

return (
<>
<div className='flex justify-center'>
<div
className={cn('flex justify-center border-b-neutral-400 dark:border-b-neutral-700', {
'border-b': chatHistory.length > 0,
})}
>
<ChatInput sendMessage={sendMessage} />
</div>
<div className='flex-grow min-w-full bg-slate-100 rounded-sm dark:bg-zinc-900 border flex h-[1px]'>
<div
className={cn(
'flex-grow min-w-full rounded-sm border-0 flex h-0',
{
'border h-[400px]': chatHistory.length > 0,
},
)}
>
<ChatMessages chatHistory={chatHistory} />
</div>
</>
Expand Down
35 changes: 29 additions & 6 deletions app/src/components/chat/ChatInput.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, {
useRef,
useState,
} from 'react';
import {
Expand All @@ -11,6 +12,7 @@ const ChatInput = ({
sendMessage: (text: string) => void;
}) => {
const [message, setMessage] = useState<string>('');
const inputRef = useRef<HTMLInputElement>(null);

const handleSend = (e: React.KeyboardEvent) => {
if (e.key !== 'Enter' || message.length === 0) {
Expand All @@ -22,12 +24,33 @@ const ChatInput = ({
};

return (
<Input
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder='Enter prompt here'
onKeyDown={handleSend}
/>
<div
className='w-full py-2'
onClick={() => {
if (document.activeElement !== inputRef.current) {
inputRef.current?.focus();
}
}}
style={{
// @ts-expect-error -- WebkitAppRegion is a valid property
// eslint-disable-next-line @typescript-eslint/naming-convention
WebkitAppRegion: 'drag',
}}
>
<Input
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder='Enter prompt here'
onKeyDown={handleSend}
ref={inputRef}
className={'text-xl border-0 focus-visible:outline-transparent focus-visible:ring-0 focus-visible:shadow-0 w-full'}
style={{
// @ts-expect-error -- WebkitAppRegion is a valid property
// eslint-disable-next-line @typescript-eslint/naming-convention
WebkitAppRegion: 'no-drag',
}}
/>
</div>
);
};

Expand Down
Loading

0 comments on commit d60f686

Please sign in to comment.