super basic notepad

This commit is contained in:
lone-cloud 2025-09-23 01:33:18 -07:00
parent 901eba2363
commit 5d5c8da3c4
13 changed files with 1211 additions and 143 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "gerbil", "name": "gerbil",
"productName": "Gerbil", "productName": "Gerbil",
"version": "1.4.3", "version": "1.5.0-beta.1",
"description": "Run Large Language Models locally", "description": "Run Large Language Models locally",
"main": "out/main/index.js", "main": "out/main/index.js",
"homepage": "./", "homepage": "./",
@ -64,10 +64,14 @@
"vite": "^7.1.7" "vite": "^7.1.7"
}, },
"dependencies": { "dependencies": {
"@codemirror/search": "^6.5.11",
"@codemirror/theme-one-dark": "^6.1.3",
"@codemirror/view": "^6.38.3",
"@fontsource/inter": "^5.2.8", "@fontsource/inter": "^5.2.8",
"@mantine/core": "^8.3.1", "@mantine/core": "^8.3.1",
"@mantine/hooks": "^8.3.1", "@mantine/hooks": "^8.3.1",
"@types/yauzl": "^2.10.3", "@types/yauzl": "^2.10.3",
"@uiw/react-codemirror": "^4.25.2",
"electron-updater": "6.6.2", "electron-updater": "6.6.2",
"execa": "^9.6.0", "execa": "^9.6.0",
"lucide-react": "^0.544.0", "lucide-react": "^0.544.0",

View file

@ -1,6 +1,8 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Group, AppShell } from '@mantine/core'; import { Group, AppShell, ActionIcon, Tooltip } from '@mantine/core';
import { NotepadText } from 'lucide-react';
import { usePreferencesStore } from '@/stores/preferences'; import { usePreferencesStore } from '@/stores/preferences';
import { useNotepadStore } from '@/stores/notepad';
import type { import type {
CpuMetrics, CpuMetrics,
MemoryMetrics, MemoryMetrics,
@ -19,6 +21,7 @@ export const StatusBar = ({ maxDataPoints = 60 }: StatusBarProps) => {
); );
const [gpuMetrics, setGpuMetrics] = useState<GpuMetrics | null>(null); const [gpuMetrics, setGpuMetrics] = useState<GpuMetrics | null>(null);
const { resolvedColorScheme: colorScheme } = usePreferencesStore(); const { resolvedColorScheme: colorScheme } = usePreferencesStore();
const { isVisible, setVisible } = useNotepadStore();
useEffect(() => { useEffect(() => {
let isMounted = true; let isMounted = true;
@ -64,10 +67,23 @@ export const StatusBar = ({ maxDataPoints = 60 }: StatusBarProps) => {
<Group <Group
px="xs" px="xs"
gap="xs" gap="xs"
justify="flex-end" justify="space-between"
h="100%" h="100%"
bg={colorScheme === 'dark' ? 'dark.6' : 'gray.1'} bg={colorScheme === 'dark' ? 'dark.6' : 'gray.1'}
> >
<Group gap="xs">
<Tooltip label="Notepad">
<ActionIcon
variant={isVisible ? 'filled' : 'subtle'}
size="sm"
onClick={() => setVisible(!isVisible)}
>
<NotepadText size="1.25rem" />
</ActionIcon>
</Tooltip>
</Group>
<Group gap="xs">
{cpuMetrics && memoryMetrics && ( {cpuMetrics && memoryMetrics && (
<> <>
<PerformanceBadge <PerformanceBadge
@ -100,6 +116,7 @@ export const StatusBar = ({ maxDataPoints = 60 }: StatusBarProps) => {
</Group> </Group>
))} ))}
</Group> </Group>
</Group>
</AppShell.Footer> </AppShell.Footer>
); );
}; };

View file

@ -13,6 +13,7 @@ import { TitleBar } from '@/components/App/TitleBar';
import { StatusBar } from '@/components/App/StatusBar'; import { StatusBar } from '@/components/App/StatusBar';
import { ErrorBoundary } from '@/components/App/ErrorBoundary'; import { ErrorBoundary } from '@/components/App/ErrorBoundary';
import { AppRouter } from '@/components/App/Router'; import { AppRouter } from '@/components/App/Router';
import { NotepadContainer } from '@/components/Notepad/Container';
import { useUpdateChecker } from '@/hooks/useUpdateChecker'; import { useUpdateChecker } from '@/hooks/useUpdateChecker';
import { useKoboldVersionsStore } from '@/stores/koboldVersions'; import { useKoboldVersionsStore } from '@/stores/koboldVersions';
import { usePreferencesStore } from '@/stores/preferences'; import { usePreferencesStore } from '@/stores/preferences';
@ -204,6 +205,8 @@ export const App = () => {
onClose={() => setEjectConfirmModalOpen(false)} onClose={() => setEjectConfirmModalOpen(false)}
onConfirm={handleEjectConfirm} onConfirm={handleEjectConfirm}
/> />
<NotepadContainer />
</AppShell> </AppShell>
); );
}; };

View file

@ -0,0 +1,190 @@
import { useEffect, useRef, useState, type MouseEvent } from 'react';
import { Box, Paper, ActionIcon } from '@mantine/core';
import { X, Plus } from 'lucide-react';
import { useNotepadStore } from '@/stores/notepad';
import { usePreferencesStore } from '@/stores/preferences';
import { NOTEPAD_MIN_WIDTH, NOTEPAD_MIN_HEIGHT } from '@/constants/notepad';
import { NotepadTabs } from './Tabs.tsx';
import { NotepadEditor } from './Editor.tsx';
export const NotepadContainer = () => {
const {
position,
setPosition,
tabs,
activeTabId,
addTab,
isLoaded,
isVisible,
setVisible,
} = useNotepadStore();
const { resolvedColorScheme } = usePreferencesStore();
const containerRef = useRef<HTMLDivElement>(null);
const [resizeDirection, setResizeDirection] = useState<string | null>(null);
const activeTab = tabs.find((tab) => tab.id === activeTabId);
const handleCreateNewTab = async () => {
const newTab = await window.electronAPI.notepad.createNewTab();
addTab(newTab);
};
const handleResizeStart =
(direction: string) => (e: MouseEvent<HTMLDivElement>) => {
e.stopPropagation();
setResizeDirection(direction);
};
useEffect(() => {
const handleMouseMove = (e: globalThis.MouseEvent) => {
if (resizeDirection) {
const rect = containerRef.current?.getBoundingClientRect();
if (!rect) return;
let newWidth = position.width;
let newHeight = position.height;
if (resizeDirection.includes('right')) {
newWidth = Math.max(NOTEPAD_MIN_WIDTH, e.clientX - rect.left);
}
if (resizeDirection.includes('top')) {
newHeight = Math.max(
NOTEPAD_MIN_HEIGHT,
window.innerHeight - e.clientY - 5
);
}
setPosition({
...position,
width: newWidth,
height: newHeight,
});
}
};
const handleMouseUp = () => {
setResizeDirection(null);
};
if (resizeDirection) {
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
return () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};
}
}, [resizeDirection, position, setPosition]);
if (!isLoaded || !isVisible) return null;
return (
<Paper
ref={containerRef}
shadow="lg"
withBorder
style={{
position: 'fixed',
left: 5,
bottom: 5,
width: position.width,
height: position.height,
zIndex: 1000,
backgroundColor:
resolvedColorScheme === 'dark'
? 'var(--mantine-color-dark-6)'
: 'var(--mantine-color-white)',
cursor: 'default',
userSelect: 'none',
}}
>
<Box
style={{
position: 'absolute',
top: -2,
left: 0,
width: '100%',
height: 4,
cursor: 'ns-resize',
}}
onMouseDown={handleResizeStart('top')}
/>
<Box
style={{
position: 'absolute',
top: 0,
right: -2,
width: 4,
height: '100%',
cursor: 'ew-resize',
}}
onMouseDown={handleResizeStart('right')}
/>
<Box
style={{
position: 'absolute',
top: -2,
right: -2,
width: 12,
height: 12,
cursor: 'ne-resize',
backgroundColor:
resolvedColorScheme === 'dark'
? 'var(--mantine-color-dark-5)'
: 'var(--mantine-color-gray-4)',
borderRadius: '2px',
}}
onMouseDown={handleResizeStart('top-right')}
/>
<Box h="100%" style={{ display: 'flex', flexDirection: 'column' }}>
<Box
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
borderBottom: `1px solid ${
resolvedColorScheme === 'dark'
? 'var(--mantine-color-dark-4)'
: 'var(--mantine-color-gray-3)'
}`,
minHeight: 28,
}}
>
<NotepadTabs />
<Box
style={{
display: 'flex',
gap: 4,
paddingLeft: 8,
paddingRight: 8,
flexShrink: 0,
}}
>
<ActionIcon variant="subtle" size="xs" onClick={handleCreateNewTab}>
<Plus size={12} />
</ActionIcon>
<ActionIcon
variant="subtle"
size="xs"
onClick={() => setVisible(false)}
color="red"
>
<X size={12} />
</ActionIcon>
</Box>
</Box>
<Box style={{ flex: 1, position: 'relative' }}>
{activeTab && <NotepadEditor tab={activeTab} />}
</Box>
</Box>
</Paper>
);
};

View file

@ -0,0 +1,108 @@
import { useCallback, useEffect, useState, useRef } from 'react';
import { Box } from '@mantine/core';
import CodeMirror, { type ReactCodeMirrorRef } from '@uiw/react-codemirror';
import { search } from '@codemirror/search';
import { EditorView } from '@codemirror/view';
import { oneDark } from '@codemirror/theme-one-dark';
import { useNotepadStore } from '@/stores/notepad';
import { usePreferencesStore } from '@/stores/preferences';
import type { NotepadTab } from '@/types/electron';
interface NotepadEditorProps {
tab: NotepadTab;
}
export const NotepadEditor = ({ tab }: NotepadEditorProps) => {
const { saveTabContent } = useNotepadStore();
const { resolvedColorScheme } = usePreferencesStore();
const [content, setContent] = useState(tab.content);
const [saveTimeout, setSaveTimeout] = useState<ReturnType<
typeof setTimeout
> | null>(null);
const editorRef = useRef<ReactCodeMirrorRef>(null);
const handleContentChange = useCallback(
(newContent: string) => {
setContent(newContent);
if (saveTimeout) {
clearTimeout(saveTimeout);
}
const timeout = setTimeout(() => {
saveTabContent(tab.id, newContent);
}, 500);
setSaveTimeout(timeout);
},
[tab.id, saveTabContent, saveTimeout]
);
const handleBoxClick = useCallback(() => {
if (editorRef.current?.view) {
editorRef.current.view.focus();
}
}, []);
useEffect(() => {
setContent(tab.content);
}, [tab.content, tab.id]);
useEffect(
() => () => {
if (saveTimeout) {
clearTimeout(saveTimeout);
}
},
[saveTimeout]
);
const extensions = [
search(),
EditorView.lineWrapping,
EditorView.theme({
'&': {
fontSize: '0.8em',
height: '100%',
},
}),
];
const theme = resolvedColorScheme === 'dark' ? oneDark : undefined;
return (
<Box
h="100%"
style={{
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',
}}
onClick={handleBoxClick}
>
<CodeMirror
ref={editorRef}
value={content}
onChange={handleContentChange}
theme={theme}
extensions={extensions}
basicSetup={{
lineNumbers: true,
foldGutter: false,
dropCursor: false,
allowMultipleSelections: false,
indentOnInput: true,
bracketMatching: true,
closeBrackets: true,
autocompletion: true,
highlightSelectionMatches: false,
searchKeymap: true,
}}
style={{
height: '100%',
flex: '1 1 0',
minHeight: '0',
}}
/>
</Box>
);
};

View file

@ -0,0 +1,166 @@
import { type MouseEvent, type DragEvent, useState } from 'react';
import { Box, ActionIcon, Text } from '@mantine/core';
import { X } from 'lucide-react';
import { useNotepadStore } from '@/stores/notepad';
import { usePreferencesStore } from '@/stores/preferences';
interface TabProps {
id: string;
index: number;
isActive: boolean;
title: string;
onSelect: () => void;
onClose: (e: MouseEvent) => void;
onDragStart: (e: DragEvent, index: number) => void;
onDragOver: (e: DragEvent) => void;
onDrop: (e: DragEvent, index: number) => void;
isDragOver: boolean;
}
const Tab = ({
index,
isActive,
title,
onSelect,
onClose,
onDragStart,
onDragOver,
onDrop,
isDragOver,
}: TabProps) => {
const { resolvedColorScheme } = usePreferencesStore();
return (
<Box
draggable
onClick={onSelect}
onDragStart={(e) => onDragStart(e, index)}
onDragOver={onDragOver}
onDrop={(e) => onDrop(e, index)}
style={{
padding: '6px 8px',
backgroundColor: isActive
? resolvedColorScheme === 'dark'
? 'var(--mantine-color-dark-4)'
: 'var(--mantine-color-gray-1)'
: isDragOver
? resolvedColorScheme === 'dark'
? 'var(--mantine-color-dark-5)'
: 'var(--mantine-color-gray-2)'
: 'transparent',
borderRight: `1px solid ${
resolvedColorScheme === 'dark'
? 'var(--mantine-color-dark-4)'
: 'var(--mantine-color-gray-3)'
}`,
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: '4px',
minWidth: 0,
maxWidth: 120,
opacity: isDragOver ? 0.5 : 1,
}}
>
<Text
size="xs"
style={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
flex: 1,
}}
>
{title}
</Text>
<ActionIcon variant="subtle" size="xs" onClick={onClose}>
<X size={10} />
</ActionIcon>
</Box>
);
};
export const NotepadTabs = () => {
const { tabs, activeTabId, setActiveTab, removeTab, reorderTabs } =
useNotepadStore();
const { resolvedColorScheme } = usePreferencesStore();
const [draggedTabIndex, setDraggedTabIndex] = useState<number | null>(null);
const [dragOverIndex, setDragOverIndex] = useState<number | null>(null);
const handleTabSelect = (tabId: string) => {
setActiveTab(tabId);
};
const handleTabClose = (e: MouseEvent, tabId: string) => {
e.stopPropagation();
removeTab(tabId);
};
const handleDragStart = (e: DragEvent, index: number) => {
setDraggedTabIndex(index);
e.dataTransfer.effectAllowed = 'move';
};
const handleDragOver = (e: DragEvent) => {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
};
const handleDragEnter = (index: number) => {
setDragOverIndex(index);
};
const handleDragLeave = () => {
setDragOverIndex(null);
};
const handleDrop = (e: DragEvent, dropIndex: number) => {
e.preventDefault();
if (draggedTabIndex !== null && draggedTabIndex !== dropIndex) {
reorderTabs(draggedTabIndex, dropIndex);
}
setDraggedTabIndex(null);
setDragOverIndex(null);
};
if (tabs.length === 0) return null;
return (
<Box
style={{
borderBottom: `1px solid ${
resolvedColorScheme === 'dark'
? 'var(--mantine-color-dark-4)'
: 'var(--mantine-color-gray-3)'
}`,
display: 'flex',
overflow: 'hidden',
minHeight: 32,
}}
>
{tabs.map((tab, index) => (
<Box
key={tab.id}
onDragEnter={() => handleDragEnter(index)}
onDragLeave={handleDragLeave}
>
<Tab
id={tab.id}
index={index}
isActive={tab.id === activeTabId}
title={tab.title}
onSelect={() => handleTabSelect(tab.id)}
onClose={(e) => handleTabClose(e, tab.id)}
onDragStart={handleDragStart}
onDragOver={handleDragOver}
onDrop={handleDrop}
isDragOver={dragOverIndex === index}
/>
</Box>
))}
</Box>
);
};

9
src/constants/notepad.ts Normal file
View file

@ -0,0 +1,9 @@
export const DEFAULT_NOTEPAD_POSITION = {
x: 0,
y: 0,
width: 400,
height: 400,
};
export const NOTEPAD_MIN_WIDTH = 300;
export const NOTEPAD_MIN_HEIGHT = 200;

View file

@ -1,6 +1,6 @@
import { ipcMain, shell, app } from 'electron'; import { ipcMain, shell, app } from 'electron';
import { join } from 'path'; import { join } from 'path';
import * as os from 'os'; import { release } from 'os';
import { platform, versions, arch } from 'process'; import { platform, versions, arch } from 'process';
import type { MantineColorScheme } from '@mantine/core'; import type { MantineColorScheme } from '@mantine/core';
import { import {
@ -43,6 +43,14 @@ import {
isAURInstallation, isAURInstallation,
} from '@/main/modules/dependencies'; } from '@/main/modules/dependencies';
import { getMainWindow } from '@/main/modules/window'; import { getMainWindow } from '@/main/modules/window';
import {
saveTabContent,
loadTabContent,
saveNotepadState,
loadNotepadState,
deleteTabFile,
createNewTab,
} from '@/main/modules/notepad';
import { import {
detectGPU, detectGPU,
detectCPU, detectCPU,
@ -67,6 +75,7 @@ import {
canAutoUpdate, canAutoUpdate,
} from '@/main/modules/autoUpdater'; } from '@/main/modules/autoUpdater';
import { getAppVersion } from '@/utils/node/fs'; import { getAppVersion } from '@/utils/node/fs';
import type { NotepadState } from '@/types/electron';
export function setupIPCHandlers() { export function setupIPCHandlers() {
const mainWindow = getMainWindow(); const mainWindow = getMainWindow();
@ -164,7 +173,7 @@ export function setupIPCHandlers() {
nodeVersion: versions.node, nodeVersion: versions.node,
chromeVersion: versions.chrome, chromeVersion: versions.chrome,
v8Version: versions.v8, v8Version: versions.v8,
osVersion: os.release(), osVersion: release(),
platform, platform,
arch, arch,
nodeJsSystemVersion, nodeJsSystemVersion,
@ -267,4 +276,27 @@ export function setupIPCHandlers() {
ipcMain.handle('app:canAutoUpdate', () => canAutoUpdate()); ipcMain.handle('app:canAutoUpdate', () => canAutoUpdate());
ipcMain.handle('app:isAURInstallation', () => isAURInstallation()); ipcMain.handle('app:isAURInstallation', () => isAURInstallation());
ipcMain.handle(
'notepad:saveTabContent',
(_, tabId: string, content: string) => saveTabContent(tabId, content)
);
ipcMain.handle('notepad:loadTabContent', (_, tabId: string) =>
loadTabContent(tabId)
);
ipcMain.handle('notepad:saveState', (_, state: NotepadState) =>
saveNotepadState(state)
);
ipcMain.handle('notepad:loadState', () => loadNotepadState());
ipcMain.handle('notepad:deleteTab', (_, tabId: string) =>
deleteTabFile(tabId)
);
ipcMain.handle('notepad:createNewTab', (_, title?: string) =>
createNewTab(title)
);
} }

113
src/main/modules/notepad.ts Normal file
View file

@ -0,0 +1,113 @@
import { promises as fs } from 'fs';
import { join } from 'path';
import { safeExecute, tryExecute } from '@/utils/node/logging';
import { getInstallDir } from './config';
import type { SavedNotepadState, SavedNotepadTab } from '@/types/electron';
import { DEFAULT_NOTEPAD_POSITION } from '@/constants/notepad';
const NOTEPAD_DIR = join(getInstallDir(), 'notepad');
const NOTEPAD_STATE_FILE = join(NOTEPAD_DIR, 'state.json');
const DEFAULT_NOTEPAD_STATE: SavedNotepadState = {
tabs: [],
activeTabId: null,
position: DEFAULT_NOTEPAD_POSITION,
isVisible: false,
};
async function ensureNotepadDir() {
return tryExecute(async () => {
await fs.mkdir(NOTEPAD_DIR, { recursive: true });
}, 'Failed to create notepad directory');
}
export async function saveTabContent(tabId: string, content: string) {
return tryExecute(async () => {
await ensureNotepadDir();
const filePath = join(NOTEPAD_DIR, `${tabId}.txt`);
await fs.writeFile(filePath, content, 'utf8');
}, 'Failed to save tab content');
}
export async function loadTabContent(tabId: string) {
const filePath = join(NOTEPAD_DIR, `${tabId}.txt`);
try {
return fs.readFile(filePath, 'utf8');
} catch (error) {
if ((error as { code?: string }).code === 'ENOENT') {
return '';
}
throw error;
}
}
export async function saveNotepadState(state: SavedNotepadState) {
return tryExecute(async () => {
await ensureNotepadDir();
await fs.writeFile(
NOTEPAD_STATE_FILE,
JSON.stringify(state, null, 2),
'utf8'
);
}, 'Failed to save notepad state');
}
export async function loadNotepadState() {
const result = await safeExecute(async () => {
try {
const data = await fs.readFile(NOTEPAD_STATE_FILE, 'utf8');
return JSON.parse(data) as SavedNotepadState;
} catch {
return DEFAULT_NOTEPAD_STATE;
}
}, 'Failed to load notepad state');
return result || DEFAULT_NOTEPAD_STATE;
}
export async function deleteTabFile(tabId: string) {
return tryExecute(async () => {
const filePath = join(NOTEPAD_DIR, `${tabId}.txt`);
try {
await fs.unlink(filePath);
} catch (error) {
if ((error as { code?: string }).code !== 'ENOENT') {
throw error;
}
}
}, 'Failed to delete tab file');
}
let tabCounter = 1;
export async function createNewTab(title?: string) {
if (!title) {
const state = await loadNotepadState();
const noteNumbers = state.tabs
.map((tab: SavedNotepadTab) => {
const match = tab.title.match(/^Note (\d+)$/);
return match ? parseInt(match[1], 10) : 0;
})
.filter((num: number) => num > 0)
.sort((a: number, b: number) => a - b);
tabCounter = 1;
for (const num of noteNumbers) {
if (num === tabCounter) {
tabCounter++;
} else {
break;
}
}
}
const newTab = {
id: `tab-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
title: title || `Note ${tabCounter++}`,
content: '',
};
await saveTabContent(newTab.id, '');
return newTab;
}

View file

@ -7,6 +7,7 @@ import type {
DependenciesAPI, DependenciesAPI,
MonitoringAPI, MonitoringAPI,
UpdaterAPI, UpdaterAPI,
NotepadAPI,
} from '@/types/electron'; } from '@/types/electron';
import type { import type {
CpuMetrics, CpuMetrics,
@ -179,6 +180,17 @@ const updaterAPI: UpdaterAPI = {
isAURInstallation: () => ipcRenderer.invoke('app:isAURInstallation'), isAURInstallation: () => ipcRenderer.invoke('app:isAURInstallation'),
}; };
const notepadAPI: NotepadAPI = {
saveTabContent: (tabId, content) =>
ipcRenderer.invoke('notepad:saveTabContent', tabId, content),
loadTabContent: (tabId) =>
ipcRenderer.invoke('notepad:loadTabContent', tabId),
saveState: (state) => ipcRenderer.invoke('notepad:saveState', state),
loadState: () => ipcRenderer.invoke('notepad:loadState'),
deleteTab: (tabId) => ipcRenderer.invoke('notepad:deleteTab', tabId),
createNewTab: (title) => ipcRenderer.invoke('notepad:createNewTab', title),
};
contextBridge.exposeInMainWorld('electronAPI', { contextBridge.exposeInMainWorld('electronAPI', {
kobold: koboldAPI, kobold: koboldAPI,
app: appAPI, app: appAPI,
@ -187,4 +199,5 @@ contextBridge.exposeInMainWorld('electronAPI', {
dependencies: dependenciesAPI, dependencies: dependenciesAPI,
monitoring: monitoringAPI, monitoring: monitoringAPI,
updater: updaterAPI, updater: updaterAPI,
notepad: notepadAPI,
}); });

168
src/stores/notepad.ts Normal file
View file

@ -0,0 +1,168 @@
import { create } from 'zustand';
import { subscribeWithSelector } from 'zustand/middleware';
import type { NotepadTab, NotepadState } from '@/types/electron';
import { DEFAULT_NOTEPAD_POSITION } from '@/constants/notepad';
interface NotepadStore extends NotepadState {
isLoaded: boolean;
setTabs: (tabs: NotepadTab[]) => void;
setActiveTab: (tabId: string | null) => void;
addTab: (tab: NotepadTab) => void;
updateTab: (tabId: string, updates: Partial<NotepadTab>) => void;
removeTab: (tabId: string) => void;
reorderTabs: (fromIndex: number, toIndex: number) => void;
setPosition: (position: NotepadState['position']) => void;
setVisible: (visible: boolean) => void;
loadState: () => Promise<void>;
saveState: () => Promise<void>;
saveTabContent: (tabId: string, content: string) => Promise<void>;
}
export const useNotepadStore = create<NotepadStore>()(
subscribeWithSelector((set, get) => ({
tabs: [],
activeTabId: null,
position: DEFAULT_NOTEPAD_POSITION,
isVisible: false,
isLoaded: false,
setTabs: (tabs) => set({ tabs }),
setActiveTab: (tabId) => set({ activeTabId: tabId }),
addTab: (tab) => {
set((state) => ({
tabs: [...state.tabs, tab],
activeTabId: tab.id,
}));
},
updateTab: (tabId, updates) => {
set((state) => ({
tabs: state.tabs.map((tab) =>
tab.id === tabId ? { ...tab, ...updates } : tab
),
}));
},
removeTab: (tabId) => {
const state = get();
if (state.tabs.length <= 1) {
return;
}
const newTabs = state.tabs.filter((tab) => tab.id !== tabId);
const newActiveTabId =
state.activeTabId === tabId
? newTabs.length > 0
? newTabs[Math.max(0, newTabs.length - 1)].id
: null
: state.activeTabId;
set({
tabs: newTabs,
activeTabId: newActiveTabId,
});
window.electronAPI.notepad.deleteTab(tabId);
},
reorderTabs: (fromIndex, toIndex) => {
const state = get();
const newTabs = [...state.tabs];
const [movedTab] = newTabs.splice(fromIndex, 1);
newTabs.splice(toIndex, 0, movedTab);
set({ tabs: newTabs });
},
setPosition: (position) => {
set({ position });
},
setVisible: (visible) => {
set({ isVisible: visible });
},
loadState: async () => {
try {
const savedState = await window.electronAPI.notepad.loadState();
const tabsWithContent = await Promise.all(
savedState.tabs.map(async (tab) => ({
...tab,
content: await window.electronAPI.notepad.loadTabContent(tab.id),
}))
);
if (tabsWithContent.length === 0) {
const defaultTab = await window.electronAPI.notepad.createNewTab();
tabsWithContent.push(defaultTab);
}
set({
tabs: tabsWithContent,
activeTabId: savedState.activeTabId || tabsWithContent[0]?.id || null,
position: savedState.position,
isVisible: savedState.isVisible,
isLoaded: true,
});
} catch {
const defaultTab = await window.electronAPI.notepad.createNewTab();
set({
tabs: [defaultTab],
activeTabId: defaultTab.id,
position: DEFAULT_NOTEPAD_POSITION,
isVisible: false,
isLoaded: true,
});
}
},
saveState: async () => {
const state = get();
if (!state.isLoaded) return;
await window.electronAPI.notepad.saveState({
tabs: state.tabs.map((tab) => ({
id: tab.id,
title: tab.title,
})),
activeTabId: state.activeTabId,
position: state.position,
isVisible: state.isVisible,
});
},
saveTabContent: async (tabId, content) => {
await window.electronAPI.notepad.saveTabContent(tabId, content);
get().updateTab(tabId, { content });
},
}))
);
useNotepadStore.subscribe(
(state) => ({
tabs: state.tabs,
activeTabId: state.activeTabId,
position: state.position,
isVisible: state.isVisible,
}),
() => {
if (useNotepadStore.getState().isLoaded) {
useNotepadStore.getState().saveState();
}
},
{
equalityFn: (a, b) =>
a.tabs === b.tabs &&
a.activeTabId === b.activeTabId &&
a.position === b.position &&
a.isVisible === b.isVisible,
}
);
setTimeout(() => {
useNotepadStore.getState().loadState();
}, 0);

View file

@ -206,6 +206,50 @@ export interface UpdaterAPI {
isAURInstallation: () => Promise<boolean>; isAURInstallation: () => Promise<boolean>;
} }
export interface NotepadTab {
id: string;
title: string;
content: string;
}
export interface SavedNotepadTab {
id: string;
title: string;
}
export interface NotepadState {
tabs: NotepadTab[];
activeTabId: string | null;
position: {
x: number;
y: number;
width: number;
height: number;
};
isVisible: boolean;
}
export interface SavedNotepadState {
tabs: SavedNotepadTab[];
activeTabId: string | null;
position: {
x: number;
y: number;
width: number;
height: number;
};
isVisible: boolean;
}
export interface NotepadAPI {
saveTabContent: (tabId: string, content: string) => Promise<boolean>;
loadTabContent: (tabId: string) => Promise<string>;
saveState: (state: SavedNotepadState) => Promise<boolean>;
loadState: () => Promise<SavedNotepadState>;
deleteTab: (tabId: string) => Promise<boolean>;
createNewTab: (title?: string) => Promise<NotepadTab>;
}
declare global { declare global {
interface Window { interface Window {
electronAPI: { electronAPI: {
@ -216,6 +260,7 @@ declare global {
dependencies: DependenciesAPI; dependencies: DependenciesAPI;
monitoring: MonitoringAPI; monitoring: MonitoringAPI;
updater: UpdaterAPI; updater: UpdaterAPI;
notepad: NotepadAPI;
}; };
} }
} }

420
yarn.lock
View file

@ -191,7 +191,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.20.13": "@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.20.13":
version: 7.28.4 version: 7.28.4
resolution: "@babel/runtime@npm:7.28.4" resolution: "@babel/runtime@npm:7.28.4"
checksum: 10c0/792ce7af9750fb9b93879cc9d1db175701c4689da890e6ced242ea0207c9da411ccf16dc04e689cc01158b28d7898c40d75598f4559109f761c12ce01e959bf7 checksum: 10c0/792ce7af9750fb9b93879cc9d1db175701c4689da890e6ced242ea0207c9da411ccf16dc04e689cc01158b28d7898c40d75598f4559109f761c12ce01e959bf7
@ -234,6 +234,99 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@codemirror/autocomplete@npm:^6.0.0":
version: 6.18.7
resolution: "@codemirror/autocomplete@npm:6.18.7"
dependencies:
"@codemirror/language": "npm:^6.0.0"
"@codemirror/state": "npm:^6.0.0"
"@codemirror/view": "npm:^6.17.0"
"@lezer/common": "npm:^1.0.0"
checksum: 10c0/562c13711fc1b184d3684300d255ebb0c7d45a4ea2c7f14e9307b5372f5af213720d06431864239d1507ba4613fb0ee7c5091b327a2125b2ee8109d4f5344fb1
languageName: node
linkType: hard
"@codemirror/commands@npm:^6.0.0, @codemirror/commands@npm:^6.1.0":
version: 6.8.1
resolution: "@codemirror/commands@npm:6.8.1"
dependencies:
"@codemirror/language": "npm:^6.0.0"
"@codemirror/state": "npm:^6.4.0"
"@codemirror/view": "npm:^6.27.0"
"@lezer/common": "npm:^1.1.0"
checksum: 10c0/da61311f4c39036f93fbe518c673f2464902cf1b64b071319b14b7d690315b72828de4bc12f28be78eeac6e8b8bd8800d4e7921dc37977f78baac4dd49c5b4bf
languageName: node
linkType: hard
"@codemirror/language@npm:^6.0.0":
version: 6.11.3
resolution: "@codemirror/language@npm:6.11.3"
dependencies:
"@codemirror/state": "npm:^6.0.0"
"@codemirror/view": "npm:^6.23.0"
"@lezer/common": "npm:^1.1.0"
"@lezer/highlight": "npm:^1.0.0"
"@lezer/lr": "npm:^1.0.0"
style-mod: "npm:^4.0.0"
checksum: 10c0/0cbc2a98bd9e94e8e186af30613741a553fc45479e3875c79bddc32340d4d75ecc36229c66e2ee5fcffbc8fdd49b442e43847799d1d68651e3b4f8ec20d3d092
languageName: node
linkType: hard
"@codemirror/lint@npm:^6.0.0":
version: 6.8.5
resolution: "@codemirror/lint@npm:6.8.5"
dependencies:
"@codemirror/state": "npm:^6.0.0"
"@codemirror/view": "npm:^6.35.0"
crelt: "npm:^1.0.5"
checksum: 10c0/3ae3ca239575e81255d6968f73d444335fcbc576eccf96a070cf5d838b3121814e6e0a92a4c5450672ce8178e3f5595bdba00a704915ce29fb0218968e383941
languageName: node
linkType: hard
"@codemirror/search@npm:^6.0.0, @codemirror/search@npm:^6.5.11":
version: 6.5.11
resolution: "@codemirror/search@npm:6.5.11"
dependencies:
"@codemirror/state": "npm:^6.0.0"
"@codemirror/view": "npm:^6.0.0"
crelt: "npm:^1.0.5"
checksum: 10c0/8f25647ceb9a255a6e5797c20ec787587537e8496f651d8815d3f8f6c9fc5bf586b6552dadfcc7ad24364c659fcd12315c5fa235a098ba15840bb76bed35cc09
languageName: node
linkType: hard
"@codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.1.1, @codemirror/state@npm:^6.4.0, @codemirror/state@npm:^6.5.0":
version: 6.5.2
resolution: "@codemirror/state@npm:6.5.2"
dependencies:
"@marijn/find-cluster-break": "npm:^1.0.0"
checksum: 10c0/1ef773394e32c077a8cfc1ec6d881aefb1918876f82161748e505c38d143aa1c6893c314cfec91097d28f704ec07b2a6c6b75abd435086208974256dee997282
languageName: node
linkType: hard
"@codemirror/theme-one-dark@npm:^6.0.0, @codemirror/theme-one-dark@npm:^6.1.3":
version: 6.1.3
resolution: "@codemirror/theme-one-dark@npm:6.1.3"
dependencies:
"@codemirror/language": "npm:^6.0.0"
"@codemirror/state": "npm:^6.0.0"
"@codemirror/view": "npm:^6.0.0"
"@lezer/highlight": "npm:^1.0.0"
checksum: 10c0/de8483c69911bcd61a19679384de663ced9c8bed3c776f08581a8b724e9f456a17053b1cf6e9d1f2a475fa6bc42e905ec8ba1ee0a8b55213d18087d9d9150317
languageName: node
linkType: hard
"@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0, @codemirror/view@npm:^6.35.0, @codemirror/view@npm:^6.38.3":
version: 6.38.3
resolution: "@codemirror/view@npm:6.38.3"
dependencies:
"@codemirror/state": "npm:^6.5.0"
crelt: "npm:^1.0.6"
style-mod: "npm:^4.1.0"
w3c-keyname: "npm:^2.2.4"
checksum: 10c0/70c9ec6d6a6528d64613c922165cc156f564435be50767167785fa97cb1be29d1d4c95a550405ce8144bc4147138ca463c6f1b94afa6b42b45e4f9cbe9045ec5
languageName: node
linkType: hard
"@colors/colors@npm:1.6.0, @colors/colors@npm:^1.6.0": "@colors/colors@npm:1.6.0, @colors/colors@npm:^1.6.0":
version: 1.6.0 version: 1.6.0
resolution: "@colors/colors@npm:1.6.0" resolution: "@colors/colors@npm:1.6.0"
@ -850,6 +943,31 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@lezer/common@npm:^1.0.0, @lezer/common@npm:^1.1.0":
version: 1.2.3
resolution: "@lezer/common@npm:1.2.3"
checksum: 10c0/fe9f8e111080ef94037a34ca2af1221c8d01c1763ba5ecf708a286185c76119509a5d19d924c8842172716716ddce22d7834394670c4a9432f0ba9f3b7c0f50d
languageName: node
linkType: hard
"@lezer/highlight@npm:^1.0.0":
version: 1.2.1
resolution: "@lezer/highlight@npm:1.2.1"
dependencies:
"@lezer/common": "npm:^1.0.0"
checksum: 10c0/51b4c08596a0dfeec6a7b7ed90a7f2743ab42e7e8ff8b89707fd042860e4e133dbd8243639fcaf077305ae6c303aa74e69794015eb16cb34741f5ac6721f283c
languageName: node
linkType: hard
"@lezer/lr@npm:^1.0.0":
version: 1.4.2
resolution: "@lezer/lr@npm:1.4.2"
dependencies:
"@lezer/common": "npm:^1.0.0"
checksum: 10c0/22bb5d0d4b33d0de5eb0706b7e5b5f2d20f570e112d9110009bd35b62ff10f2eb4eff8da4cf373dd4ddf5e06a304120b8f039add7ed9997c981c13945d5329cd
languageName: node
linkType: hard
"@malept/cross-spawn-promise@npm:^2.0.0": "@malept/cross-spawn-promise@npm:^2.0.0":
version: 2.0.0 version: 2.0.0
resolution: "@malept/cross-spawn-promise@npm:2.0.0" resolution: "@malept/cross-spawn-promise@npm:2.0.0"
@ -898,6 +1016,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@marijn/find-cluster-break@npm:^1.0.0":
version: 1.0.2
resolution: "@marijn/find-cluster-break@npm:1.0.2"
checksum: 10c0/1a17a60b16083cc5f7ce89d7b7d8aa87ce4099723e3e9e34e229ef2cd8a980e69d481ca8ee90ffedfec5119af1aed581642fb60ed0365e7e90634c81ea6b630f
languageName: node
linkType: hard
"@nodelib/fs.scandir@npm:2.1.5": "@nodelib/fs.scandir@npm:2.1.5":
version: 2.1.5 version: 2.1.5
resolution: "@nodelib/fs.scandir@npm:2.1.5" resolution: "@nodelib/fs.scandir@npm:2.1.5"
@ -981,156 +1106,156 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-android-arm-eabi@npm:4.52.0": "@rollup/rollup-android-arm-eabi@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-android-arm-eabi@npm:4.52.0" resolution: "@rollup/rollup-android-arm-eabi@npm:4.52.1"
conditions: os=android & cpu=arm conditions: os=android & cpu=arm
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-android-arm64@npm:4.52.0": "@rollup/rollup-android-arm64@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-android-arm64@npm:4.52.0" resolution: "@rollup/rollup-android-arm64@npm:4.52.1"
conditions: os=android & cpu=arm64 conditions: os=android & cpu=arm64
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-darwin-arm64@npm:4.52.0": "@rollup/rollup-darwin-arm64@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-darwin-arm64@npm:4.52.0" resolution: "@rollup/rollup-darwin-arm64@npm:4.52.1"
conditions: os=darwin & cpu=arm64 conditions: os=darwin & cpu=arm64
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-darwin-x64@npm:4.52.0": "@rollup/rollup-darwin-x64@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-darwin-x64@npm:4.52.0" resolution: "@rollup/rollup-darwin-x64@npm:4.52.1"
conditions: os=darwin & cpu=x64 conditions: os=darwin & cpu=x64
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-freebsd-arm64@npm:4.52.0": "@rollup/rollup-freebsd-arm64@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-freebsd-arm64@npm:4.52.0" resolution: "@rollup/rollup-freebsd-arm64@npm:4.52.1"
conditions: os=freebsd & cpu=arm64 conditions: os=freebsd & cpu=arm64
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-freebsd-x64@npm:4.52.0": "@rollup/rollup-freebsd-x64@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-freebsd-x64@npm:4.52.0" resolution: "@rollup/rollup-freebsd-x64@npm:4.52.1"
conditions: os=freebsd & cpu=x64 conditions: os=freebsd & cpu=x64
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-arm-gnueabihf@npm:4.52.0": "@rollup/rollup-linux-arm-gnueabihf@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.52.0" resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.52.1"
conditions: os=linux & cpu=arm & libc=glibc conditions: os=linux & cpu=arm & libc=glibc
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-arm-musleabihf@npm:4.52.0": "@rollup/rollup-linux-arm-musleabihf@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.52.0" resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.52.1"
conditions: os=linux & cpu=arm & libc=musl conditions: os=linux & cpu=arm & libc=musl
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-arm64-gnu@npm:4.52.0": "@rollup/rollup-linux-arm64-gnu@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.52.0" resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.52.1"
conditions: os=linux & cpu=arm64 & libc=glibc conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-arm64-musl@npm:4.52.0": "@rollup/rollup-linux-arm64-musl@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-linux-arm64-musl@npm:4.52.0" resolution: "@rollup/rollup-linux-arm64-musl@npm:4.52.1"
conditions: os=linux & cpu=arm64 & libc=musl conditions: os=linux & cpu=arm64 & libc=musl
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-loong64-gnu@npm:4.52.0": "@rollup/rollup-linux-loong64-gnu@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.52.0" resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.52.1"
conditions: os=linux & cpu=loong64 & libc=glibc conditions: os=linux & cpu=loong64 & libc=glibc
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-ppc64-gnu@npm:4.52.0": "@rollup/rollup-linux-ppc64-gnu@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.52.0" resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.52.1"
conditions: os=linux & cpu=ppc64 & libc=glibc conditions: os=linux & cpu=ppc64 & libc=glibc
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-riscv64-gnu@npm:4.52.0": "@rollup/rollup-linux-riscv64-gnu@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.52.0" resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.52.1"
conditions: os=linux & cpu=riscv64 & libc=glibc conditions: os=linux & cpu=riscv64 & libc=glibc
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-riscv64-musl@npm:4.52.0": "@rollup/rollup-linux-riscv64-musl@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.52.0" resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.52.1"
conditions: os=linux & cpu=riscv64 & libc=musl conditions: os=linux & cpu=riscv64 & libc=musl
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-s390x-gnu@npm:4.52.0": "@rollup/rollup-linux-s390x-gnu@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.52.0" resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.52.1"
conditions: os=linux & cpu=s390x & libc=glibc conditions: os=linux & cpu=s390x & libc=glibc
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-x64-gnu@npm:4.52.0": "@rollup/rollup-linux-x64-gnu@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-linux-x64-gnu@npm:4.52.0" resolution: "@rollup/rollup-linux-x64-gnu@npm:4.52.1"
conditions: os=linux & cpu=x64 & libc=glibc conditions: os=linux & cpu=x64 & libc=glibc
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-x64-musl@npm:4.52.0": "@rollup/rollup-linux-x64-musl@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-linux-x64-musl@npm:4.52.0" resolution: "@rollup/rollup-linux-x64-musl@npm:4.52.1"
conditions: os=linux & cpu=x64 & libc=musl conditions: os=linux & cpu=x64 & libc=musl
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-openharmony-arm64@npm:4.52.0": "@rollup/rollup-openharmony-arm64@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-openharmony-arm64@npm:4.52.0" resolution: "@rollup/rollup-openharmony-arm64@npm:4.52.1"
conditions: os=openharmony & cpu=arm64 conditions: os=openharmony & cpu=arm64
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-win32-arm64-msvc@npm:4.52.0": "@rollup/rollup-win32-arm64-msvc@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.52.0" resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.52.1"
conditions: os=win32 & cpu=arm64 conditions: os=win32 & cpu=arm64
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-win32-ia32-msvc@npm:4.52.0": "@rollup/rollup-win32-ia32-msvc@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.52.0" resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.52.1"
conditions: os=win32 & cpu=ia32 conditions: os=win32 & cpu=ia32
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-win32-x64-gnu@npm:4.52.0": "@rollup/rollup-win32-x64-gnu@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-win32-x64-gnu@npm:4.52.0" resolution: "@rollup/rollup-win32-x64-gnu@npm:4.52.1"
conditions: os=win32 & cpu=x64 conditions: os=win32 & cpu=x64
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-win32-x64-msvc@npm:4.52.0": "@rollup/rollup-win32-x64-msvc@npm:4.52.1":
version: 4.52.0 version: 4.52.1
resolution: "@rollup/rollup-win32-x64-msvc@npm:4.52.0" resolution: "@rollup/rollup-win32-x64-msvc@npm:4.52.1"
conditions: os=win32 & cpu=x64 conditions: os=win32 & cpu=x64
languageName: node languageName: node
linkType: hard linkType: hard
@ -1509,6 +1634,51 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@uiw/codemirror-extensions-basic-setup@npm:4.25.2":
version: 4.25.2
resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.25.2"
dependencies:
"@codemirror/autocomplete": "npm:^6.0.0"
"@codemirror/commands": "npm:^6.0.0"
"@codemirror/language": "npm:^6.0.0"
"@codemirror/lint": "npm:^6.0.0"
"@codemirror/search": "npm:^6.0.0"
"@codemirror/state": "npm:^6.0.0"
"@codemirror/view": "npm:^6.0.0"
peerDependencies:
"@codemirror/autocomplete": ">=6.0.0"
"@codemirror/commands": ">=6.0.0"
"@codemirror/language": ">=6.0.0"
"@codemirror/lint": ">=6.0.0"
"@codemirror/search": ">=6.0.0"
"@codemirror/state": ">=6.0.0"
"@codemirror/view": ">=6.0.0"
checksum: 10c0/8bcd5d81009467c31e74126251fd5e41cfb91b9aa98dc61422e1752244962de4477d9aaccc5c92dfd0df60ed3bc34db33ca85b0f7e1202a1f46915fd86430daf
languageName: node
linkType: hard
"@uiw/react-codemirror@npm:^4.25.2":
version: 4.25.2
resolution: "@uiw/react-codemirror@npm:4.25.2"
dependencies:
"@babel/runtime": "npm:^7.18.6"
"@codemirror/commands": "npm:^6.1.0"
"@codemirror/state": "npm:^6.1.1"
"@codemirror/theme-one-dark": "npm:^6.0.0"
"@uiw/codemirror-extensions-basic-setup": "npm:4.25.2"
codemirror: "npm:^6.0.0"
peerDependencies:
"@babel/runtime": ">=7.11.0"
"@codemirror/state": ">=6.0.0"
"@codemirror/theme-one-dark": ">=6.0.0"
"@codemirror/view": ">=6.0.0"
codemirror: ">=6.0.0"
react: ">=17.0.0"
react-dom: ">=17.0.0"
checksum: 10c0/26e5e1a6f401136ba0b0fd5227dc6dec15a4d6ab0af7056c8cbaf1424fe4ab8a4826d16c079d926a9b0b4407d9354bdfc49f05896d164cce4300b0ac92807a39
languageName: node
linkType: hard
"@vitejs/plugin-react@npm:^5.0.3": "@vitejs/plugin-react@npm:^5.0.3":
version: 5.0.3 version: 5.0.3
resolution: "@vitejs/plugin-react@npm:5.0.3" resolution: "@vitejs/plugin-react@npm:5.0.3"
@ -2267,6 +2437,21 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"codemirror@npm:^6.0.0":
version: 6.0.2
resolution: "codemirror@npm:6.0.2"
dependencies:
"@codemirror/autocomplete": "npm:^6.0.0"
"@codemirror/commands": "npm:^6.0.0"
"@codemirror/language": "npm:^6.0.0"
"@codemirror/lint": "npm:^6.0.0"
"@codemirror/search": "npm:^6.0.0"
"@codemirror/state": "npm:^6.0.0"
"@codemirror/view": "npm:^6.0.0"
checksum: 10c0/8d198d8aebc32e56c966ac57b0fe8f832b7d601a2f62819ba3a294570233982bf4d5b499a764194b6b26dbc5313a156c2611cbc542234ea6eae6accf07a651ab
languageName: node
linkType: hard
"color-convert@npm:^1.9.3": "color-convert@npm:^1.9.3":
version: 1.9.3 version: 1.9.3
resolution: "color-convert@npm:1.9.3" resolution: "color-convert@npm:1.9.3"
@ -2382,6 +2567,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"crelt@npm:^1.0.5, crelt@npm:^1.0.6":
version: 1.0.6
resolution: "crelt@npm:1.0.6"
checksum: 10c0/e0fb76dff50c5eb47f2ea9b786c17f9425c66276025adee80876bdbf4a84ab72e899e56d3928431ab0cb057a105ef704df80fe5726ef0f7b1658f815521bdf09
languageName: node
linkType: hard
"cross-env@npm:^10.0.0": "cross-env@npm:^10.0.0":
version: 10.0.0 version: 10.0.0
resolution: "cross-env@npm:10.0.0" resolution: "cross-env@npm:10.0.0"
@ -3666,6 +3858,9 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "gerbil@workspace:." resolution: "gerbil@workspace:."
dependencies: dependencies:
"@codemirror/search": "npm:^6.5.11"
"@codemirror/theme-one-dark": "npm:^6.1.3"
"@codemirror/view": "npm:^6.38.3"
"@eslint/js": "npm:^9.36.0" "@eslint/js": "npm:^9.36.0"
"@fontsource/inter": "npm:^5.2.8" "@fontsource/inter": "npm:^5.2.8"
"@mantine/core": "npm:^8.3.1" "@mantine/core": "npm:^8.3.1"
@ -3676,6 +3871,7 @@ __metadata:
"@types/yauzl": "npm:^2.10.3" "@types/yauzl": "npm:^2.10.3"
"@typescript-eslint/eslint-plugin": "npm:^8.44.1" "@typescript-eslint/eslint-plugin": "npm:^8.44.1"
"@typescript-eslint/parser": "npm:^8.44.1" "@typescript-eslint/parser": "npm:^8.44.1"
"@uiw/react-codemirror": "npm:^4.25.2"
"@vitejs/plugin-react": "npm:^5.0.3" "@vitejs/plugin-react": "npm:^5.0.3"
cross-env: "npm:^10.0.0" cross-env: "npm:^10.0.0"
electron: "npm:^38.1.2" electron: "npm:^38.1.2"
@ -5116,12 +5312,12 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"minizlib@npm:^3.0.1": "minizlib@npm:^3.0.1, minizlib@npm:^3.1.0":
version: 3.0.2 version: 3.1.0
resolution: "minizlib@npm:3.0.2" resolution: "minizlib@npm:3.1.0"
dependencies: dependencies:
minipass: "npm:^7.1.2" minipass: "npm:^7.1.2"
checksum: 10c0/9f3bd35e41d40d02469cb30470c55ccc21cae0db40e08d1d0b1dff01cc8cc89a6f78e9c5d2b7c844e485ec0a8abc2238111213fdc5b2038e6d1012eacf316f78 checksum: 10c0/5aad75ab0090b8266069c9aabe582c021ae53eb33c6c691054a13a45db3b4f91a7fb1bd79151e6b4e9e9a86727b522527c0a06ec7d45206b745d54cd3097bcec
languageName: node languageName: node
linkType: hard linkType: hard
@ -5134,15 +5330,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"mkdirp@npm:^3.0.1":
version: 3.0.1
resolution: "mkdirp@npm:3.0.1"
bin:
mkdirp: dist/cjs/src/bin.js
checksum: 10c0/9f2b975e9246351f5e3a40dcfac99fcd0baa31fbfab615fe059fb11e51f10e4803c63de1f384c54d656e4db31d000e4767e9ef076a22e12a641357602e31d57d
languageName: node
linkType: hard
"moment@npm:^2.29.1": "moment@npm:^2.29.1":
version: 2.30.1 version: 2.30.1
resolution: "moment@npm:2.30.1" resolution: "moment@npm:2.30.1"
@ -6078,31 +6265,31 @@ __metadata:
linkType: hard linkType: hard
"rollup@npm:^4.43.0": "rollup@npm:^4.43.0":
version: 4.52.0 version: 4.52.1
resolution: "rollup@npm:4.52.0" resolution: "rollup@npm:4.52.1"
dependencies: dependencies:
"@rollup/rollup-android-arm-eabi": "npm:4.52.0" "@rollup/rollup-android-arm-eabi": "npm:4.52.1"
"@rollup/rollup-android-arm64": "npm:4.52.0" "@rollup/rollup-android-arm64": "npm:4.52.1"
"@rollup/rollup-darwin-arm64": "npm:4.52.0" "@rollup/rollup-darwin-arm64": "npm:4.52.1"
"@rollup/rollup-darwin-x64": "npm:4.52.0" "@rollup/rollup-darwin-x64": "npm:4.52.1"
"@rollup/rollup-freebsd-arm64": "npm:4.52.0" "@rollup/rollup-freebsd-arm64": "npm:4.52.1"
"@rollup/rollup-freebsd-x64": "npm:4.52.0" "@rollup/rollup-freebsd-x64": "npm:4.52.1"
"@rollup/rollup-linux-arm-gnueabihf": "npm:4.52.0" "@rollup/rollup-linux-arm-gnueabihf": "npm:4.52.1"
"@rollup/rollup-linux-arm-musleabihf": "npm:4.52.0" "@rollup/rollup-linux-arm-musleabihf": "npm:4.52.1"
"@rollup/rollup-linux-arm64-gnu": "npm:4.52.0" "@rollup/rollup-linux-arm64-gnu": "npm:4.52.1"
"@rollup/rollup-linux-arm64-musl": "npm:4.52.0" "@rollup/rollup-linux-arm64-musl": "npm:4.52.1"
"@rollup/rollup-linux-loong64-gnu": "npm:4.52.0" "@rollup/rollup-linux-loong64-gnu": "npm:4.52.1"
"@rollup/rollup-linux-ppc64-gnu": "npm:4.52.0" "@rollup/rollup-linux-ppc64-gnu": "npm:4.52.1"
"@rollup/rollup-linux-riscv64-gnu": "npm:4.52.0" "@rollup/rollup-linux-riscv64-gnu": "npm:4.52.1"
"@rollup/rollup-linux-riscv64-musl": "npm:4.52.0" "@rollup/rollup-linux-riscv64-musl": "npm:4.52.1"
"@rollup/rollup-linux-s390x-gnu": "npm:4.52.0" "@rollup/rollup-linux-s390x-gnu": "npm:4.52.1"
"@rollup/rollup-linux-x64-gnu": "npm:4.52.0" "@rollup/rollup-linux-x64-gnu": "npm:4.52.1"
"@rollup/rollup-linux-x64-musl": "npm:4.52.0" "@rollup/rollup-linux-x64-musl": "npm:4.52.1"
"@rollup/rollup-openharmony-arm64": "npm:4.52.0" "@rollup/rollup-openharmony-arm64": "npm:4.52.1"
"@rollup/rollup-win32-arm64-msvc": "npm:4.52.0" "@rollup/rollup-win32-arm64-msvc": "npm:4.52.1"
"@rollup/rollup-win32-ia32-msvc": "npm:4.52.0" "@rollup/rollup-win32-ia32-msvc": "npm:4.52.1"
"@rollup/rollup-win32-x64-gnu": "npm:4.52.0" "@rollup/rollup-win32-x64-gnu": "npm:4.52.1"
"@rollup/rollup-win32-x64-msvc": "npm:4.52.0" "@rollup/rollup-win32-x64-msvc": "npm:4.52.1"
"@types/estree": "npm:1.0.8" "@types/estree": "npm:1.0.8"
fsevents: "npm:~2.3.2" fsevents: "npm:~2.3.2"
dependenciesMeta: dependenciesMeta:
@ -6154,7 +6341,7 @@ __metadata:
optional: true optional: true
bin: bin:
rollup: dist/bin/rollup rollup: dist/bin/rollup
checksum: 10c0/05b33f5143cfeb2c64df6bfa13a971c3d94081828f763e22b4154ed1452091abe648418d9a45abc8d5656a9a979f5b12e9cd5b390f247c3af4640ad8ed333523 checksum: 10c0/f704256e5de02ef1d7614dde1c94c7c7d42d24fd3fa217bd26bea900c9e5a7a23ed576c0f3698e061caa742137d20338b64fbb67a07bce721ec6658e2793a7ee
languageName: node languageName: node
linkType: hard linkType: hard
@ -6701,6 +6888,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"style-mod@npm:^4.0.0, style-mod@npm:^4.1.0":
version: 4.1.2
resolution: "style-mod@npm:4.1.2"
checksum: 10c0/ad4d870b3642b0e42ecc7be0e106dd14b7af11985e34fee8de34e5e38c3214bfc96fa7055acea86d75a3a59ddea3f6a8c6641001a66494d7df72d09685e3fadb
languageName: node
linkType: hard
"sumchecker@npm:^3.0.1": "sumchecker@npm:^3.0.1":
version: 3.0.1 version: 3.0.1
resolution: "sumchecker@npm:3.0.1" resolution: "sumchecker@npm:3.0.1"
@ -6758,16 +6952,15 @@ __metadata:
linkType: hard linkType: hard
"tar@npm:^7.4.3": "tar@npm:^7.4.3":
version: 7.4.3 version: 7.4.4
resolution: "tar@npm:7.4.3" resolution: "tar@npm:7.4.4"
dependencies: dependencies:
"@isaacs/fs-minipass": "npm:^4.0.0" "@isaacs/fs-minipass": "npm:^4.0.0"
chownr: "npm:^3.0.0" chownr: "npm:^3.0.0"
minipass: "npm:^7.1.2" minipass: "npm:^7.1.2"
minizlib: "npm:^3.0.1" minizlib: "npm:^3.1.0"
mkdirp: "npm:^3.0.1"
yallist: "npm:^5.0.0" yallist: "npm:^5.0.0"
checksum: 10c0/d4679609bb2a9b48eeaf84632b6d844128d2412b95b6de07d53d8ee8baf4ca0857c9331dfa510390a0727b550fd543d4d1a10995ad86cdf078423fbb8d99831d checksum: 10c0/2db46a140095488ed3244ac748f8e4f9362223b212bcae7859840dd9fd9891bc713f243d122906ce2f28eb64b49fa8cefc13cbdda24e66e8f2a5936a7c392b06
languageName: node languageName: node
linkType: hard linkType: hard
@ -7234,6 +7427,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"w3c-keyname@npm:^2.2.4":
version: 2.2.8
resolution: "w3c-keyname@npm:2.2.8"
checksum: 10c0/37cf335c90efff31672ebb345577d681e2177f7ff9006a9ad47c68c5a9d265ba4a7b39d6c2599ceea639ca9315584ce4bd9c9fbf7a7217bfb7a599e71943c4c4
languageName: node
linkType: hard
"wcwidth@npm:^1.0.1": "wcwidth@npm:^1.0.1":
version: 1.0.1 version: 1.0.1
resolution: "wcwidth@npm:1.0.1" resolution: "wcwidth@npm:1.0.1"