mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-02 18:10:18 +05:30
style: run prettier formatter over src and tests
This commit is contained in:
+205
-203
@@ -73,226 +73,228 @@ function updateNode(tree: FileNode, dirPath: string, updater: (node: FileNode) =
|
||||
export const useFileStore = create<FileState>()(
|
||||
persist(
|
||||
immer((set) => ({
|
||||
tree: null,
|
||||
rootPath: null,
|
||||
expanded: new Set<string>(),
|
||||
openTabs: [],
|
||||
activeTabId: null,
|
||||
tree: null,
|
||||
rootPath: null,
|
||||
expanded: new Set<string>(),
|
||||
openTabs: [],
|
||||
activeTabId: null,
|
||||
|
||||
openFolder: async (path) => {
|
||||
const result = await ipc.file.list(path);
|
||||
if (!result.ok) {
|
||||
toast.error(`Failed to open folder: ${result.error.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const raw: any = result.data!;
|
||||
const entries: any[] = Array.isArray(raw) ? raw : raw.entries;
|
||||
const children: FileNode[] = entries.map(entryToNode);
|
||||
|
||||
set((s) => {
|
||||
s.tree = {
|
||||
name: path.split('/').filter(Boolean).pop() ?? path,
|
||||
path,
|
||||
isDirectory: true,
|
||||
children,
|
||||
loaded: true,
|
||||
};
|
||||
s.rootPath = path;
|
||||
});
|
||||
},
|
||||
|
||||
loadChildren: async (dirPath) => {
|
||||
// Find the node first
|
||||
const tree = useFileStore.getState().tree;
|
||||
if (!tree) return;
|
||||
|
||||
let found = false;
|
||||
updateNode(tree, dirPath, (node) => {
|
||||
if (node.loaded === true) return;
|
||||
found = true;
|
||||
});
|
||||
if (!found) return;
|
||||
|
||||
const result = await ipc.file.list(dirPath);
|
||||
if (!result.ok) return;
|
||||
|
||||
set((s) => {
|
||||
updateNode(s.tree!, dirPath, (node) => {
|
||||
const raw: any = result.data!;
|
||||
const items: any[] = Array.isArray(raw) ? raw : raw.entries;
|
||||
node.children = items.map(entryToNode);
|
||||
node.loaded = true;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
toggleExpanded: (dirPath) => {
|
||||
set((s) => {
|
||||
if (s.expanded.has(dirPath)) {
|
||||
s.expanded.delete(dirPath);
|
||||
} else {
|
||||
s.expanded.add(dirPath);
|
||||
openFolder: async (path) => {
|
||||
const result = await ipc.file.list(path);
|
||||
if (!result.ok) {
|
||||
toast.error(`Failed to open folder: ${result.error.message}`);
|
||||
return;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
openFile: async (filePath) => {
|
||||
// If already open, sync dirty flag from editor buffer and activate
|
||||
const existing = useFileStore.getState().openTabs.find((t) => t.id === filePath);
|
||||
if (existing) {
|
||||
const buffer = useEditorStore.getState().buffers.get(filePath);
|
||||
const raw: any = result.data!;
|
||||
const entries: any[] = Array.isArray(raw) ? raw : raw.entries;
|
||||
const children: FileNode[] = entries.map(entryToNode);
|
||||
|
||||
set((s) => {
|
||||
s.activeTabId = filePath;
|
||||
if (buffer) {
|
||||
const tab = s.openTabs.find((t) => t.id === filePath);
|
||||
if (tab) tab.dirty = buffer.dirty;
|
||||
s.tree = {
|
||||
name: path.split('/').filter(Boolean).pop() ?? path,
|
||||
path,
|
||||
isDirectory: true,
|
||||
children,
|
||||
loaded: true,
|
||||
};
|
||||
s.rootPath = path;
|
||||
});
|
||||
},
|
||||
|
||||
loadChildren: async (dirPath) => {
|
||||
// Find the node first
|
||||
const tree = useFileStore.getState().tree;
|
||||
if (!tree) return;
|
||||
|
||||
let found = false;
|
||||
updateNode(tree, dirPath, (node) => {
|
||||
if (node.loaded === true) return;
|
||||
found = true;
|
||||
});
|
||||
if (!found) return;
|
||||
|
||||
const result = await ipc.file.list(dirPath);
|
||||
if (!result.ok) return;
|
||||
|
||||
set((s) => {
|
||||
updateNode(s.tree!, dirPath, (node) => {
|
||||
const raw: any = result.data!;
|
||||
const items: any[] = Array.isArray(raw) ? raw : raw.entries;
|
||||
node.children = items.map(entryToNode);
|
||||
node.loaded = true;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
toggleExpanded: (dirPath) => {
|
||||
set((s) => {
|
||||
if (s.expanded.has(dirPath)) {
|
||||
s.expanded.delete(dirPath);
|
||||
} else {
|
||||
s.expanded.add(dirPath);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
const result = await ipc.file.read(filePath);
|
||||
if (!result.ok) {
|
||||
toast.error(`Failed to open file: ${result.error.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const content = result.data!;
|
||||
const title = filePath.split('/').pop() ?? filePath;
|
||||
|
||||
// Open in editor store
|
||||
useEditorStore.getState().openBuffer(filePath, filePath, content);
|
||||
|
||||
set((s) => {
|
||||
s.openTabs.push({ id: filePath, path: filePath, title, dirty: false });
|
||||
s.activeTabId = filePath;
|
||||
});
|
||||
},
|
||||
|
||||
// Used when the main process has already read the file (e.g. file-opened
|
||||
// IPC from File → Open menu). Skips the renderer-side IPC read since main
|
||||
// already has the content. Preserves the editor's current buffer content
|
||||
// if the file is already open.
|
||||
openFileFromMain: async (filePath, content) => {
|
||||
const existing = useFileStore.getState().openTabs.find((t) => t.id === filePath);
|
||||
if (existing) {
|
||||
set((s) => { s.activeTabId = filePath; });
|
||||
return;
|
||||
}
|
||||
|
||||
const title = filePath.split('/').pop() ?? filePath;
|
||||
useEditorStore.getState().openBuffer(filePath, filePath, content);
|
||||
set((s) => {
|
||||
s.openTabs.push({ id: filePath, path: filePath, title, dirty: false });
|
||||
s.activeTabId = filePath;
|
||||
});
|
||||
},
|
||||
|
||||
closeTab: (id) => {
|
||||
set((s) => {
|
||||
const idx = s.openTabs.findIndex((t) => t.id === id);
|
||||
if (idx === -1) return;
|
||||
s.openTabs.splice(idx, 1);
|
||||
if (s.activeTabId === id) {
|
||||
s.activeTabId = idx > 0 ? s.openTabs[idx - 1]?.id ?? null : null;
|
||||
openFile: async (filePath) => {
|
||||
// If already open, sync dirty flag from editor buffer and activate
|
||||
const existing = useFileStore.getState().openTabs.find((t) => t.id === filePath);
|
||||
if (existing) {
|
||||
const buffer = useEditorStore.getState().buffers.get(filePath);
|
||||
set((s) => {
|
||||
s.activeTabId = filePath;
|
||||
if (buffer) {
|
||||
const tab = s.openTabs.find((t) => t.id === filePath);
|
||||
if (tab) tab.dirty = buffer.dirty;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setActiveTab: (id) => {
|
||||
set((s) => {
|
||||
s.activeTabId = id;
|
||||
});
|
||||
},
|
||||
const result = await ipc.file.read(filePath);
|
||||
if (!result.ok) {
|
||||
toast.error(`Failed to open file: ${result.error.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
markTabClean: (id) => {
|
||||
set((s) => {
|
||||
const tab = s.openTabs.find((t) => t.id === id);
|
||||
if (tab) tab.dirty = false;
|
||||
});
|
||||
},
|
||||
const content = result.data!;
|
||||
const title = filePath.split('/').pop() ?? filePath;
|
||||
|
||||
markTabDirty: (id) => {
|
||||
set((s) => {
|
||||
const tab = s.openTabs.find((t) => t.id === id);
|
||||
if (tab) tab.dirty = true;
|
||||
});
|
||||
},
|
||||
// Open in editor store
|
||||
useEditorStore.getState().openBuffer(filePath, filePath, content);
|
||||
|
||||
reorderTabs: (fromIndex, toIndex) => {
|
||||
set((s) => {
|
||||
const tabs = s.openTabs;
|
||||
const [moved] = tabs.splice(fromIndex, 1);
|
||||
tabs.splice(toIndex, 0, moved);
|
||||
});
|
||||
},
|
||||
|
||||
openFolderDialog: async () => {
|
||||
const result = await ipc.file.pickFolder();
|
||||
if (!result.ok || result.data === null) return;
|
||||
await useFileStore.getState().openFolder(result.data);
|
||||
},
|
||||
|
||||
openFileDialog: async () => {
|
||||
const result = await ipc.file.pickFile();
|
||||
if (!result.ok || result.data === null) return;
|
||||
await useFileStore.getState().openFile(result.data);
|
||||
},
|
||||
|
||||
saveActiveBuffer: async () => {
|
||||
const { activeTabId } = useFileStore.getState();
|
||||
if (!activeTabId) return false;
|
||||
|
||||
const buffer = useEditorStore.getState().buffers.get(activeTabId);
|
||||
if (!buffer) return false;
|
||||
|
||||
let savePath = activeTabId;
|
||||
const isUnsaved = activeTabId.startsWith('untitled-');
|
||||
|
||||
if (isUnsaved) {
|
||||
const dialogResult = await ipc.app.showSaveDialog({
|
||||
title: 'Save Markdown File',
|
||||
defaultPath: 'document.md',
|
||||
filters: [
|
||||
{ name: 'Markdown', extensions: ['md', 'markdown'] },
|
||||
{ name: 'All Files', extensions: ['*'] }
|
||||
]
|
||||
set((s) => {
|
||||
s.openTabs.push({ id: filePath, path: filePath, title, dirty: false });
|
||||
s.activeTabId = filePath;
|
||||
});
|
||||
if (!dialogResult.ok || !dialogResult.data) {
|
||||
},
|
||||
|
||||
// Used when the main process has already read the file (e.g. file-opened
|
||||
// IPC from File → Open menu). Skips the renderer-side IPC read since main
|
||||
// already has the content. Preserves the editor's current buffer content
|
||||
// if the file is already open.
|
||||
openFileFromMain: async (filePath, content) => {
|
||||
const existing = useFileStore.getState().openTabs.find((t) => t.id === filePath);
|
||||
if (existing) {
|
||||
set((s) => {
|
||||
s.activeTabId = filePath;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const title = filePath.split('/').pop() ?? filePath;
|
||||
useEditorStore.getState().openBuffer(filePath, filePath, content);
|
||||
set((s) => {
|
||||
s.openTabs.push({ id: filePath, path: filePath, title, dirty: false });
|
||||
s.activeTabId = filePath;
|
||||
});
|
||||
},
|
||||
|
||||
closeTab: (id) => {
|
||||
set((s) => {
|
||||
const idx = s.openTabs.findIndex((t) => t.id === id);
|
||||
if (idx === -1) return;
|
||||
s.openTabs.splice(idx, 1);
|
||||
if (s.activeTabId === id) {
|
||||
s.activeTabId = idx > 0 ? (s.openTabs[idx - 1]?.id ?? null) : null;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setActiveTab: (id) => {
|
||||
set((s) => {
|
||||
s.activeTabId = id;
|
||||
});
|
||||
},
|
||||
|
||||
markTabClean: (id) => {
|
||||
set((s) => {
|
||||
const tab = s.openTabs.find((t) => t.id === id);
|
||||
if (tab) tab.dirty = false;
|
||||
});
|
||||
},
|
||||
|
||||
markTabDirty: (id) => {
|
||||
set((s) => {
|
||||
const tab = s.openTabs.find((t) => t.id === id);
|
||||
if (tab) tab.dirty = true;
|
||||
});
|
||||
},
|
||||
|
||||
reorderTabs: (fromIndex, toIndex) => {
|
||||
set((s) => {
|
||||
const tabs = s.openTabs;
|
||||
const [moved] = tabs.splice(fromIndex, 1);
|
||||
tabs.splice(toIndex, 0, moved);
|
||||
});
|
||||
},
|
||||
|
||||
openFolderDialog: async () => {
|
||||
const result = await ipc.file.pickFolder();
|
||||
if (!result.ok || result.data === null) return;
|
||||
await useFileStore.getState().openFolder(result.data);
|
||||
},
|
||||
|
||||
openFileDialog: async () => {
|
||||
const result = await ipc.file.pickFile();
|
||||
if (!result.ok || result.data === null) return;
|
||||
await useFileStore.getState().openFile(result.data);
|
||||
},
|
||||
|
||||
saveActiveBuffer: async () => {
|
||||
const { activeTabId } = useFileStore.getState();
|
||||
if (!activeTabId) return false;
|
||||
|
||||
const buffer = useEditorStore.getState().buffers.get(activeTabId);
|
||||
if (!buffer) return false;
|
||||
|
||||
let savePath = activeTabId;
|
||||
const isUnsaved = activeTabId.startsWith('untitled-');
|
||||
|
||||
if (isUnsaved) {
|
||||
const dialogResult = await ipc.app.showSaveDialog({
|
||||
title: 'Save Markdown File',
|
||||
defaultPath: 'document.md',
|
||||
filters: [
|
||||
{ name: 'Markdown', extensions: ['md', 'markdown'] },
|
||||
{ name: 'All Files', extensions: ['*'] },
|
||||
],
|
||||
});
|
||||
if (!dialogResult.ok || !dialogResult.data) {
|
||||
return false;
|
||||
}
|
||||
savePath = dialogResult.data;
|
||||
}
|
||||
|
||||
const writeResult = await ipc.file.write(savePath, buffer.content);
|
||||
if (!writeResult.ok) {
|
||||
toast.error(`Failed to save: ${writeResult.error.message}`);
|
||||
return false;
|
||||
}
|
||||
savePath = dialogResult.data;
|
||||
}
|
||||
|
||||
const writeResult = await ipc.file.write(savePath, buffer.content);
|
||||
if (!writeResult.ok) {
|
||||
toast.error(`Failed to save: ${writeResult.error.message}`);
|
||||
return false;
|
||||
}
|
||||
if (isUnsaved) {
|
||||
useEditorStore.getState().renameBuffer(activeTabId, savePath, savePath);
|
||||
set((s) => {
|
||||
const tab = s.openTabs.find((t) => t.id === activeTabId);
|
||||
if (tab) {
|
||||
tab.id = savePath;
|
||||
tab.path = savePath;
|
||||
tab.title = savePath.split('/').pop() ?? savePath;
|
||||
tab.dirty = false;
|
||||
}
|
||||
s.activeTabId = savePath;
|
||||
});
|
||||
} else {
|
||||
useEditorStore.getState().markSaved(activeTabId);
|
||||
useFileStore.getState().markTabClean(activeTabId);
|
||||
}
|
||||
|
||||
if (isUnsaved) {
|
||||
useEditorStore.getState().renameBuffer(activeTabId, savePath, savePath);
|
||||
set((s) => {
|
||||
const tab = s.openTabs.find((t) => t.id === activeTabId);
|
||||
if (tab) {
|
||||
tab.id = savePath;
|
||||
tab.path = savePath;
|
||||
tab.title = savePath.split('/').pop() ?? savePath;
|
||||
tab.dirty = false;
|
||||
}
|
||||
s.activeTabId = savePath;
|
||||
});
|
||||
} else {
|
||||
useEditorStore.getState().markSaved(activeTabId);
|
||||
useFileStore.getState().markTabClean(activeTabId);
|
||||
}
|
||||
|
||||
const title = savePath.split('/').pop() ?? savePath;
|
||||
toast.success(`Saved ${title}`);
|
||||
return true;
|
||||
},
|
||||
})),
|
||||
const title = savePath.split('/').pop() ?? savePath;
|
||||
toast.success(`Saved ${title}`);
|
||||
return true;
|
||||
},
|
||||
})),
|
||||
{
|
||||
name: 'mc-file-store',
|
||||
storage: createJSONStorage(() => localStorage),
|
||||
|
||||
Reference in New Issue
Block a user