unify notepad config with the app config

This commit is contained in:
Egor 2025-09-23 21:13:41 -07:00
parent 7ca81f05ec
commit 18c94fd7dd
10 changed files with 197 additions and 179 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "gerbil", "name": "gerbil",
"productName": "Gerbil", "productName": "Gerbil",
"version": "1.5.0", "version": "1.5.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": "./",

View file

@ -26,45 +26,40 @@ export const NotepadContainer = () => {
const [resizeDirection, setResizeDirection] = useState<string | null>(null); const [resizeDirection, setResizeDirection] = useState<string | null>(null);
const [confirmCloseModal, setConfirmCloseModal] = useState<{ const [confirmCloseModal, setConfirmCloseModal] = useState<{
isOpen: boolean; isOpen: boolean;
tabId: string | null; title: string;
tabTitle: string;
}>({ }>({
isOpen: false, isOpen: false,
tabId: null, title: '',
tabTitle: '',
}); });
const activeTab = tabs.find((tab) => tab.id === activeTabId); const activeTab = tabs.find((tab) => tab.title === activeTabId);
const handleCreateNewTab = async () => { const handleCreateNewTab = async () => {
const newTab = await window.electronAPI.notepad.createNewTab(); const newTab = await window.electronAPI.notepad.createNewTab();
addTab(newTab); addTab(newTab);
}; };
const handleTabCloseRequest = (tabId: string) => { const handleTabCloseRequest = (title: string) => {
const tab = tabs.find((t) => t.id === tabId); const tab = tabs.find((t) => t.title === title);
if (!tab) return; if (!tab) return;
if (tab.content.trim().length > 0) { if (tab.content.trim().length > 0) {
setConfirmCloseModal({ setConfirmCloseModal({
isOpen: true, isOpen: true,
tabId, title,
tabTitle: tab.title,
}); });
} else { } else {
removeTab(tabId); removeTab(title);
} }
}; };
const handleConfirmClose = () => { const handleConfirmClose = () => {
if (confirmCloseModal.tabId) { removeTab(confirmCloseModal.title);
removeTab(confirmCloseModal.tabId); setConfirmCloseModal({ isOpen: false, title: '' });
}
setConfirmCloseModal({ isOpen: false, tabId: null, tabTitle: '' });
}; };
const handleCancelClose = () => { const handleCancelClose = () => {
setConfirmCloseModal({ isOpen: false, tabId: null, tabTitle: '' }); setConfirmCloseModal({ isOpen: false, title: '' });
}; };
const handleResizeStart = const handleResizeStart =
@ -207,7 +202,7 @@ export const NotepadContainer = () => {
onClick={() => setVisible(false)} onClick={() => setVisible(false)}
color="red" color="red"
> >
<X size={12} /> <X size={16} />
</ActionIcon> </ActionIcon>
</Box> </Box>
</Box> </Box>
@ -219,7 +214,7 @@ export const NotepadContainer = () => {
<CloseConfirmModal <CloseConfirmModal
isOpen={confirmCloseModal.isOpen} isOpen={confirmCloseModal.isOpen}
tabTitle={confirmCloseModal.tabTitle} tabTitle={confirmCloseModal.title}
onConfirm={handleConfirmClose} onConfirm={handleConfirmClose}
onCancel={handleCancelClose} onCancel={handleCancelClose}
/> />

View file

@ -38,12 +38,12 @@ export const NotepadEditor = ({ tab }: NotepadEditorProps) => {
} }
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
saveTabContent(tab.id, newContent); saveTabContent(tab.title, newContent);
}, 500); }, 500);
setSaveTimeout(timeout); setSaveTimeout(timeout);
}, },
[tab.id, saveTabContent, saveTimeout] [tab.title, saveTabContent, saveTimeout]
); );
const handleEditorContextMenu = (e: MouseEvent) => { const handleEditorContextMenu = (e: MouseEvent) => {
@ -62,7 +62,7 @@ export const NotepadEditor = ({ tab }: NotepadEditorProps) => {
useEffect(() => { useEffect(() => {
setContent(tab.content); setContent(tab.content);
}, [tab.content, tab.id]); }, [tab.content, tab.title]);
useEffect( useEffect(
() => () => { () => () => {

View file

@ -13,14 +13,13 @@ import { usePreferencesStore } from '@/stores/preferences';
interface NotepadTabsProps { interface NotepadTabsProps {
onCreateNewTab: () => Promise<void>; onCreateNewTab: () => Promise<void>;
onCloseTab: (tabId: string) => void; onCloseTab: (title: string) => void;
} }
interface TabProps { interface TabProps {
id: string; title: string;
index: number; index: number;
isActive: boolean; isActive: boolean;
title: string;
onSelect: () => void; onSelect: () => void;
onClose: (e: MouseEvent) => void; onClose: (e: MouseEvent) => void;
onDragStart: (e: DragEvent, index: number) => void; onDragStart: (e: DragEvent, index: number) => void;
@ -166,6 +165,7 @@ const Tab = ({
) : ( ) : (
<Text <Text
size="xs" size="xs"
title={title}
onClick={handleTitleClick} onClick={handleTitleClick}
onDoubleClick={handleTitleDoubleClick} onDoubleClick={handleTitleDoubleClick}
onContextMenu={(e) => { onContextMenu={(e) => {
@ -208,17 +208,18 @@ export const NotepadTabs = ({
const [draggedTabIndex, setDraggedTabIndex] = useState<number | null>(null); const [draggedTabIndex, setDraggedTabIndex] = useState<number | null>(null);
const [dragOverIndex, setDragOverIndex] = useState<number | null>(null); const [dragOverIndex, setDragOverIndex] = useState<number | null>(null);
const handleTabSelect = (tabId: string) => { const handleTabSelect = (title: string) => {
setActiveTab(tabId); setActiveTab(title);
}; };
const handleTabClose = (e: MouseEvent, tabId: string) => { const handleTabClose = (e: MouseEvent, title: string) => {
e.stopPropagation(); e.stopPropagation();
onCloseTab(tabId); onCloseTab(title);
}; };
const handleTabRename = (tabId: string, newTitle: string) => { const handleTabRename = (title: string, newTitle: string) => {
updateTab(tabId, { title: newTitle }); updateTab(title, { title: newTitle });
window.electronAPI.notepad.renameTab(title, newTitle);
}; };
const handleTabBarContextMenu = (e: MouseEvent) => { const handleTabBarContextMenu = (e: MouseEvent) => {
@ -258,8 +259,6 @@ export const NotepadTabs = ({
setDragOverIndex(null); setDragOverIndex(null);
}; };
if (tabs.length === 0) return null;
return ( return (
<Box <Box
onContextMenu={handleTabBarContextMenu} onContextMenu={handleTabBarContextMenu}
@ -276,18 +275,17 @@ export const NotepadTabs = ({
> >
{tabs.map((tab, index) => ( {tabs.map((tab, index) => (
<Box <Box
key={tab.id} key={tab.title}
onDragEnter={() => handleDragEnter(index)} onDragEnter={() => handleDragEnter(index)}
onDragLeave={handleDragLeave} onDragLeave={handleDragLeave}
> >
<Tab <Tab
id={tab.id}
index={index}
isActive={tab.id === activeTabId}
title={tab.title} title={tab.title}
onSelect={() => handleTabSelect(tab.id)} index={index}
onClose={(e) => handleTabClose(e, tab.id)} isActive={tab.title === activeTabId}
onRename={(newTitle) => handleTabRename(tab.id, newTitle)} onSelect={() => handleTabSelect(tab.title)}
onClose={(e) => handleTabClose(e, tab.title)}
onRename={(newTitle) => handleTabRename(tab.title, newTitle)}
onDragStart={handleDragStart} onDragStart={handleDragStart}
onDragOver={handleDragOver} onDragOver={handleDragOver}
onDrop={handleDrop} onDrop={handleDrop}
@ -303,7 +301,7 @@ export const NotepadTabs = ({
size="xs" size="xs"
onClick={onCreateNewTab} onClick={onCreateNewTab}
style={{ style={{
margin: '4px', margin: '0.25rem',
alignSelf: 'center', alignSelf: 'center',
}} }}
> >

View file

@ -50,6 +50,7 @@ import {
loadNotepadState, loadNotepadState,
deleteTabFile, deleteTabFile,
createNewTab, createNewTab,
renameTab,
} from '@/main/modules/notepad'; } from '@/main/modules/notepad';
import { import {
detectGPU, detectGPU,
@ -279,11 +280,15 @@ export function setupIPCHandlers() {
ipcMain.handle( ipcMain.handle(
'notepad:saveTabContent', 'notepad:saveTabContent',
(_, tabId: string, content: string) => saveTabContent(tabId, content) (_, title: string, content: string) => saveTabContent(title, content)
); );
ipcMain.handle('notepad:loadTabContent', (_, tabId: string) => ipcMain.handle('notepad:loadTabContent', (_, title: string) =>
loadTabContent(tabId) loadTabContent(title)
);
ipcMain.handle('notepad:renameTab', (_, oldTitle: string, newTitle: string) =>
renameTab(oldTitle, newTitle)
); );
ipcMain.handle('notepad:saveState', (_, state: NotepadState) => ipcMain.handle('notepad:saveState', (_, state: NotepadState) =>
@ -292,8 +297,8 @@ export function setupIPCHandlers() {
ipcMain.handle('notepad:loadState', () => loadNotepadState()); ipcMain.handle('notepad:loadState', () => loadNotepadState());
ipcMain.handle('notepad:deleteTab', (_, tabId: string) => ipcMain.handle('notepad:deleteTab', (_, title: string) =>
deleteTabFile(tabId) deleteTabFile(title)
); );
ipcMain.handle('notepad:createNewTab', (_, title?: string) => ipcMain.handle('notepad:createNewTab', (_, title?: string) =>

View file

@ -8,6 +8,7 @@ import { nativeTheme } from 'electron';
import { PRODUCT_NAME } from '@/constants'; import { PRODUCT_NAME } from '@/constants';
import type { FrontendPreference } from '@/types'; import type { FrontendPreference } from '@/types';
import type { MantineColorScheme } from '@mantine/core'; import type { MantineColorScheme } from '@mantine/core';
import type { SavedNotepadState } from '@/types/electron';
interface WindowBounds { interface WindowBounds {
x: number; x: number;
@ -28,6 +29,7 @@ interface AppConfig {
skipEjectConfirmation?: boolean; skipEjectConfirmation?: boolean;
dismissedUpdates?: string[]; dismissedUpdates?: string[];
zoomLevel?: number; zoomLevel?: number;
notepad?: SavedNotepadState;
} }
let config: AppConfig = {}; let config: AppConfig = {};

View file

@ -1,116 +1,119 @@
import { promises as fs } from 'fs'; import { get, set, getInstallDir } from './config';
import { join } from 'path'; import type { SavedNotepadState } from '@/types/electron';
import { safeExecute, tryExecute } from '@/utils/node/logging';
import { getInstallDir } from './config';
import type { SavedNotepadState, SavedNotepadTab } from '@/types/electron';
import { import {
DEFAULT_NOTEPAD_POSITION, DEFAULT_NOTEPAD_POSITION,
DEFAULT_TAB_CONTENT, DEFAULT_TAB_CONTENT,
} from '@/constants/notepad'; } from '@/constants/notepad';
import { pathExists } from '@/utils/node/fs';
const NOTEPAD_DIR = join(getInstallDir(), 'notepad'); import { join } from 'path';
const NOTEPAD_STATE_FILE = join(NOTEPAD_DIR, 'state.json'); import { readFile, readdir, writeFile, unlink, rename } from 'fs/promises';
const DEFAULT_NOTEPAD_STATE: SavedNotepadState = { const DEFAULT_NOTEPAD_STATE: SavedNotepadState = {
tabs: [],
activeTabId: null, activeTabId: null,
position: DEFAULT_NOTEPAD_POSITION, position: DEFAULT_NOTEPAD_POSITION,
isVisible: false, isVisible: false,
}; };
async function ensureNotepadDir() { const getNotepadDir = () => join(getInstallDir(), 'notepad');
return tryExecute(async () => {
await fs.mkdir(NOTEPAD_DIR, { recursive: true });
}, 'Failed to create notepad directory');
}
export async function saveTabContent(tabId: string, content: string) { const getTabsFromStorage = async () => {
return tryExecute(async () => { const tabs = [];
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 { try {
return fs.readFile(filePath, 'utf8'); const notepadDir = getNotepadDir();
} catch (error) { if (await pathExists(notepadDir)) {
if ((error as { code?: string }).code === 'ENOENT') { const files = await readdir(notepadDir);
const tabFiles = files.filter((f) => f.endsWith('.txt'));
for (const file of tabFiles) {
const title = file.replace('.txt', '');
tabs.push({ title });
}
}
} catch {
return [];
}
return tabs;
};
export const renameTab = async (oldTitle: string, newTitle: string) => {
try {
const notepadDir = getNotepadDir();
await rename(
join(notepadDir, `${oldTitle}.txt`),
join(notepadDir, `${newTitle}.txt`)
);
return true;
} catch {
return false;
}
};
export const saveTabContent = async (title: string, content: string) => {
try {
const notepadDir = getNotepadDir();
const filePath = join(notepadDir, `${title}.txt`);
await writeFile(filePath, content, 'utf-8');
return true;
} catch {
return false;
}
};
export const loadTabContent = async (title: string) => {
try {
const notepadDir = getNotepadDir();
return readFile(join(notepadDir, `${title}.txt`), 'utf-8');
} catch {
return ''; return '';
} }
throw error; };
}
}
export async function saveNotepadState(state: SavedNotepadState) { export const saveNotepadState = async (state: SavedNotepadState) => {
return tryExecute(async () => { await set('notepad', state);
await ensureNotepadDir(); return true;
await fs.writeFile( };
NOTEPAD_STATE_FILE,
JSON.stringify(state, null, 2),
'utf8'
);
}, 'Failed to save notepad state');
}
export async function loadNotepadState() { export const loadNotepadState = async () => {
const result = await safeExecute(async () => { const stored = get('notepad') || DEFAULT_NOTEPAD_STATE;
const tabs = await getTabsFromStorage();
const activeTabId =
stored.activeTabId && tabs.some((tab) => tab.title === stored.activeTabId)
? stored.activeTabId
: tabs[0]?.title || null;
return { ...stored, tabs, activeTabId };
};
export const deleteTabFile = async (title: string) => {
try { try {
const data = await fs.readFile(NOTEPAD_STATE_FILE, 'utf8'); await unlink(join(getNotepadDir(), `${title}.txt`));
return JSON.parse(data) as SavedNotepadState; return true;
} catch { } catch {
return DEFAULT_NOTEPAD_STATE; return false;
} }
}, 'Failed to load notepad state'); };
return result || DEFAULT_NOTEPAD_STATE; export const createNewTab = async (title?: string) => {
}
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) { if (!title) {
const state = await loadNotepadState(); const state = await loadNotepadState();
const noteNumbers = state.tabs const noteNumbers = state.tabs
.map((tab: SavedNotepadTab) => { .map((tab) => tab.title.match(/^Note (\d+)$/)?.[1])
const match = tab.title.match(/^Note (\d+)$/); .filter(Boolean)
return match ? parseInt(match[1], 10) : 0; .map(Number)
}) .sort((a, b) => a - b);
.filter((num: number) => num > 0)
.sort((a: number, b: number) => a - b);
tabCounter = 1; let counter = 1;
for (const num of noteNumbers) { for (const num of noteNumbers) {
if (num === tabCounter) { if (num === counter) counter++;
tabCounter++; else break;
} else {
break;
}
} }
title = `Note ${counter}`;
} }
const newTab = { await saveTabContent(title, DEFAULT_TAB_CONTENT);
id: `tab-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`, return { title, content: DEFAULT_TAB_CONTENT };
title: title || `Note ${tabCounter++}`,
content: DEFAULT_TAB_CONTENT,
}; };
await saveTabContent(newTab.id, newTab.content);
return newTab;
}

View file

@ -191,13 +191,15 @@ const updaterAPI: UpdaterAPI = {
}; };
const notepadAPI: NotepadAPI = { const notepadAPI: NotepadAPI = {
saveTabContent: (tabId, content) => saveTabContent: (title, content) =>
ipcRenderer.invoke('notepad:saveTabContent', tabId, content), ipcRenderer.invoke('notepad:saveTabContent', title, content),
loadTabContent: (tabId) => loadTabContent: (title) =>
ipcRenderer.invoke('notepad:loadTabContent', tabId), ipcRenderer.invoke('notepad:loadTabContent', title),
renameTab: (oldTitle, newTitle) =>
ipcRenderer.invoke('notepad:renameTab', oldTitle, newTitle),
saveState: (state) => ipcRenderer.invoke('notepad:saveState', state), saveState: (state) => ipcRenderer.invoke('notepad:saveState', state),
loadState: () => ipcRenderer.invoke('notepad:loadState'), loadState: () => ipcRenderer.invoke('notepad:loadState'),
deleteTab: (tabId) => ipcRenderer.invoke('notepad:deleteTab', tabId), deleteTab: (title) => ipcRenderer.invoke('notepad:deleteTab', title),
createNewTab: (title) => ipcRenderer.invoke('notepad:createNewTab', title), createNewTab: (title) => ipcRenderer.invoke('notepad:createNewTab', title),
}; };

View file

@ -9,17 +9,17 @@ import {
interface NotepadStore extends NotepadState { interface NotepadStore extends NotepadState {
isLoaded: boolean; isLoaded: boolean;
setTabs: (tabs: NotepadTab[]) => void; setTabs: (tabs: NotepadTab[]) => void;
setActiveTab: (tabId: string | null) => void; setActiveTab: (title: string | null) => void;
addTab: (tab: NotepadTab) => void; addTab: (tab: NotepadTab) => void;
updateTab: (tabId: string, updates: Partial<NotepadTab>) => void; updateTab: (title: string, updates: Partial<NotepadTab>) => void;
removeTab: (tabId: string) => void; removeTab: (title: string) => void;
reorderTabs: (fromIndex: number, toIndex: number) => void; reorderTabs: (fromIndex: number, toIndex: number) => void;
setPosition: (position: NotepadState['position']) => void; setPosition: (position: NotepadState['position']) => void;
setVisible: (visible: boolean) => void; setVisible: (visible: boolean) => void;
setShowLineNumbers: (showLineNumbers: boolean) => void; setShowLineNumbers: (showLineNumbers: boolean) => void;
loadState: () => Promise<void>; loadState: () => Promise<void>;
saveState: () => Promise<void>; saveState: () => Promise<void>;
saveTabContent: (tabId: string, content: string) => Promise<void>; saveTabContent: (title: string, content: string) => Promise<void>;
} }
export const useNotepadStore = create<NotepadStore>()( export const useNotepadStore = create<NotepadStore>()(
@ -33,45 +33,55 @@ export const useNotepadStore = create<NotepadStore>()(
setTabs: (tabs) => set({ tabs }), setTabs: (tabs) => set({ tabs }),
setActiveTab: (tabId) => set({ activeTabId: tabId }), setActiveTab: (title) => set({ activeTabId: title }),
addTab: (tab) => { addTab: (tab) => {
set((state) => ({ set((state) => ({
tabs: [...state.tabs, tab], tabs: [...state.tabs, tab],
activeTabId: tab.id, activeTabId: tab.title,
})); }));
}, },
updateTab: (tabId, updates) => { updateTab: (title, updates) => {
set((state) => ({ set((state) => {
tabs: state.tabs.map((tab) => const updatedTabs = state.tabs.map((tab) =>
tab.id === tabId ? { ...tab, ...updates } : tab tab.title === title ? { ...tab, ...updates } : tab
), );
}));
let newActiveTabId = state.activeTabId;
if (updates.title && state.activeTabId === title) {
newActiveTabId = updates.title;
}
return {
tabs: updatedTabs,
activeTabId: newActiveTabId,
};
});
}, },
removeTab: (tabId) => { removeTab: (title) => {
const state = get(); const state = get();
if (state.tabs.length <= 1) { if (state.tabs.length <= 1) {
const tab = state.tabs.find((t) => t.id === tabId); const tab = state.tabs.find((t) => t.title === title);
if (tab) { if (tab) {
set((state) => ({ set((state) => ({
tabs: state.tabs.map((t) => tabs: state.tabs.map((t) =>
t.id === tabId ? { ...t, content: DEFAULT_TAB_CONTENT } : t t.title === title ? { ...t, content: DEFAULT_TAB_CONTENT } : t
), ),
})); }));
window.electronAPI.notepad.saveTabContent(tabId, DEFAULT_TAB_CONTENT); window.electronAPI.notepad.saveTabContent(title, DEFAULT_TAB_CONTENT);
} }
return; return;
} }
const newTabs = state.tabs.filter((tab) => tab.id !== tabId); const newTabs = state.tabs.filter((tab) => tab.title !== title);
const newActiveTabId = const newActiveTabId =
state.activeTabId === tabId state.activeTabId === title
? newTabs.length > 0 ? newTabs.length > 0
? newTabs[Math.max(0, newTabs.length - 1)].id ? newTabs[Math.max(0, newTabs.length - 1)].title
: null : null
: state.activeTabId; : state.activeTabId;
@ -80,7 +90,7 @@ export const useNotepadStore = create<NotepadStore>()(
activeTabId: newActiveTabId, activeTabId: newActiveTabId,
}); });
window.electronAPI.notepad.deleteTab(tabId); window.electronAPI.notepad.deleteTab(title);
}, },
reorderTabs: (fromIndex, toIndex) => { reorderTabs: (fromIndex, toIndex) => {
@ -109,10 +119,14 @@ export const useNotepadStore = create<NotepadStore>()(
const savedState = await window.electronAPI.notepad.loadState(); const savedState = await window.electronAPI.notepad.loadState();
const tabsWithContent = await Promise.all( const tabsWithContent = await Promise.all(
savedState.tabs.map(async (tab) => ({ savedState.tabs.map((tab) =>
window.electronAPI.notepad
.loadTabContent(tab.title)
.then((content) => ({
...tab, ...tab,
content: await window.electronAPI.notepad.loadTabContent(tab.id), content,
})) }))
)
); );
if (tabsWithContent.length === 0) { if (tabsWithContent.length === 0) {
@ -122,7 +136,8 @@ export const useNotepadStore = create<NotepadStore>()(
set({ set({
tabs: tabsWithContent, tabs: tabsWithContent,
activeTabId: savedState.activeTabId || tabsWithContent[0]?.id || null, activeTabId:
savedState.activeTabId || tabsWithContent[0]?.title || null,
position: savedState.position, position: savedState.position,
isVisible: savedState.isVisible, isVisible: savedState.isVisible,
showLineNumbers: savedState.showLineNumbers ?? true, showLineNumbers: savedState.showLineNumbers ?? true,
@ -132,7 +147,7 @@ export const useNotepadStore = create<NotepadStore>()(
const defaultTab = await window.electronAPI.notepad.createNewTab(); const defaultTab = await window.electronAPI.notepad.createNewTab();
set({ set({
tabs: [defaultTab], tabs: [defaultTab],
activeTabId: defaultTab.id, activeTabId: defaultTab.title,
position: DEFAULT_NOTEPAD_POSITION, position: DEFAULT_NOTEPAD_POSITION,
isVisible: false, isVisible: false,
isLoaded: true, isLoaded: true,
@ -145,10 +160,6 @@ export const useNotepadStore = create<NotepadStore>()(
if (!state.isLoaded) return; if (!state.isLoaded) return;
await window.electronAPI.notepad.saveState({ await window.electronAPI.notepad.saveState({
tabs: state.tabs.map((tab) => ({
id: tab.id,
title: tab.title,
})),
activeTabId: state.activeTabId, activeTabId: state.activeTabId,
position: state.position, position: state.position,
isVisible: state.isVisible, isVisible: state.isVisible,
@ -156,9 +167,9 @@ export const useNotepadStore = create<NotepadStore>()(
}); });
}, },
saveTabContent: async (tabId, content) => { saveTabContent: async (title, content) => {
await window.electronAPI.notepad.saveTabContent(tabId, content); await window.electronAPI.notepad.saveTabContent(title, content);
get().updateTab(tabId, { content }); get().updateTab(title, { content });
}, },
})) }))
); );

View file

@ -210,13 +210,11 @@ export interface UpdaterAPI {
} }
export interface NotepadTab { export interface NotepadTab {
id: string;
title: string; title: string;
content: string; content: string;
} }
export interface SavedNotepadTab { export interface SavedNotepadTab {
id: string;
title: string; title: string;
} }
@ -232,7 +230,6 @@ export interface NotepadState {
} }
export interface SavedNotepadState { export interface SavedNotepadState {
tabs: SavedNotepadTab[];
activeTabId: string | null; activeTabId: string | null;
position: { position: {
width: number; width: number;
@ -242,12 +239,17 @@ export interface SavedNotepadState {
showLineNumbers?: boolean; showLineNumbers?: boolean;
} }
export interface NotepadStateWithTabs extends SavedNotepadState {
tabs: SavedNotepadTab[];
}
export interface NotepadAPI { export interface NotepadAPI {
saveTabContent: (tabId: string, content: string) => Promise<boolean>; saveTabContent: (title: string, content: string) => Promise<boolean>;
loadTabContent: (tabId: string) => Promise<string>; loadTabContent: (title: string) => Promise<string>;
renameTab: (oldTitle: string, newTitle: string) => Promise<boolean>;
saveState: (state: SavedNotepadState) => Promise<boolean>; saveState: (state: SavedNotepadState) => Promise<boolean>;
loadState: () => Promise<SavedNotepadState>; loadState: () => Promise<NotepadStateWithTabs>;
deleteTab: (tabId: string) => Promise<boolean>; deleteTab: (title: string) => Promise<boolean>;
createNewTab: (title?: string) => Promise<NotepadTab>; createNewTab: (title?: string) => Promise<NotepadTab>;
} }