Update README and code to remove folder registration option
This commit is contained in:
@@ -17,7 +17,7 @@ Heimgeist is a local desktop chat client for Ollama. It combines an Electron + R
|
||||
The `DBs` tab is no longer a placeholder. You can:
|
||||
|
||||
- create and rename libraries
|
||||
- register files and folders
|
||||
- register files
|
||||
- let Heimgeist rebuild retrieval automatically when files change
|
||||
- open or remove registered files from the UI
|
||||
|
||||
|
||||
@@ -475,20 +475,9 @@ ipcMain.handle('update-settings', (event, settings) => {
|
||||
return true
|
||||
})
|
||||
|
||||
function pickDialogProperties(kind) {
|
||||
if (kind === 'directories') {
|
||||
return ['openDirectory', 'multiSelections']
|
||||
}
|
||||
if (kind === 'mixed') {
|
||||
return ['openFile', 'openDirectory', 'multiSelections']
|
||||
}
|
||||
return ['openFile', 'multiSelections']
|
||||
}
|
||||
|
||||
ipcMain.handle('pick-paths', async (event, options = {}) => {
|
||||
const kind = typeof options?.kind === 'string' ? options.kind : 'files'
|
||||
ipcMain.handle('pick-paths', async () => {
|
||||
const result = await dialog.showOpenDialog(mainWindow, {
|
||||
properties: pickDialogProperties(kind),
|
||||
properties: ['openFile', 'multiSelections'],
|
||||
})
|
||||
return result.canceled ? [] : result.filePaths
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
checkForUpdates: () => ipcRenderer.invoke('check-for-updates'),
|
||||
setSetting: (key, value) => ipcRenderer.invoke('set-setting', key, value),
|
||||
updateSettings: (settings) => ipcRenderer.invoke('update-settings', settings),
|
||||
pickPaths: (options = {}) => ipcRenderer.invoke('pick-paths', options),
|
||||
pickPaths: () => ipcRenderer.invoke('pick-paths'),
|
||||
openPath: (filePath) => ipcRenderer.invoke('open-path', filePath),
|
||||
openExternalLink: (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
32
src/App.jsx
32
src/App.jsx
@@ -1532,6 +1532,28 @@ async function createNewChat() {
|
||||
});
|
||||
}
|
||||
|
||||
function handleLibraryDelete(slug) {
|
||||
fetch(`${backendApiUrl}/libraries/${slug}`, { method: 'DELETE' })
|
||||
.then(async (response) => {
|
||||
if (!response.ok) {
|
||||
const detail = await response.text()
|
||||
throw new Error(detail || `HTTP ${response.status}`)
|
||||
}
|
||||
|
||||
const nextLibraries = libraries.filter(library => library.slug !== slug)
|
||||
setLibraries(nextLibraries)
|
||||
setLibraryJobs(prevJobs => prevJobs.filter(job => job.slug !== slug))
|
||||
setEditingLibrarySlug(current => current === slug ? null : current)
|
||||
if (activeLibrarySlug === slug) {
|
||||
setActiveLibrarySlug(nextLibraries[0]?.slug || null)
|
||||
}
|
||||
removeLibraryFromChatSelections(slug)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to delete library', error)
|
||||
})
|
||||
}
|
||||
|
||||
// Auto-delete empty "New Chat" sessions
|
||||
useEffect(() => {
|
||||
const emptyNewChats = chatSessions.filter(
|
||||
@@ -1661,6 +1683,9 @@ async function createNewChat() {
|
||||
<button className="icon-button" onClick={(e) => { e.stopPropagation(); setEditingLibrarySlug(library.slug) }}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-edit-2"><path d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"></path></svg>
|
||||
</button>
|
||||
<button className="icon-button" onClick={(e) => { e.stopPropagation(); handleLibraryDelete(library.slug) }}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -1991,13 +2016,6 @@ async function createNewChat() {
|
||||
await refreshLibraries();
|
||||
await refreshLibraryJobs();
|
||||
}}
|
||||
onDeleted={(slug) => {
|
||||
if (activeLibrarySlug === slug) {
|
||||
const next = libraries.find(lib => lib.slug !== slug);
|
||||
setActiveLibrarySlug(next?.slug || null);
|
||||
}
|
||||
removeLibraryFromChatSelections(slug)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -53,11 +53,9 @@ export default function LibraryManager({
|
||||
apiBase,
|
||||
library,
|
||||
jobs,
|
||||
onRefresh,
|
||||
onDeleted
|
||||
onRefresh
|
||||
}) {
|
||||
const [busy, setBusy] = useState(false)
|
||||
const [confirmDelete, setConfirmDelete] = useState(false)
|
||||
const [errorMessage, setErrorMessage] = useState('')
|
||||
const [toasts, setToasts] = useState([])
|
||||
const toastTimeoutsRef = useRef(new Map())
|
||||
@@ -65,7 +63,6 @@ export default function LibraryManager({
|
||||
const previousLibraryStateRef = useRef(null)
|
||||
|
||||
useEffect(() => {
|
||||
setConfirmDelete(false)
|
||||
setErrorMessage('')
|
||||
}, [library?.slug, library?.name])
|
||||
|
||||
@@ -114,7 +111,6 @@ export default function LibraryManager({
|
||||
try {
|
||||
setErrorMessage('')
|
||||
await fn()
|
||||
setConfirmDelete(false)
|
||||
} finally {
|
||||
setBusy(false)
|
||||
await onRefresh()
|
||||
@@ -132,9 +128,9 @@ export default function LibraryManager({
|
||||
})
|
||||
}
|
||||
|
||||
async function addPaths(kind = 'files') {
|
||||
async function addPaths() {
|
||||
if (!library) return
|
||||
const paths = await window.electronAPI?.pickPaths?.({ kind })
|
||||
const paths = await window.electronAPI?.pickPaths?.()
|
||||
if (!Array.isArray(paths) || paths.length === 0) return
|
||||
try {
|
||||
await registerPaths(paths)
|
||||
@@ -175,15 +171,6 @@ export default function LibraryManager({
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteLibrary() {
|
||||
if (!library) return
|
||||
await runAction(async () => {
|
||||
const response = await fetch(`${apiBase}/libraries/${library.slug}`, { method: 'DELETE' })
|
||||
await expectOk(response)
|
||||
})
|
||||
onDeleted?.(library.slug)
|
||||
}
|
||||
|
||||
async function retrySync() {
|
||||
if (!library) return
|
||||
try {
|
||||
@@ -276,22 +263,6 @@ export default function LibraryManager({
|
||||
return (
|
||||
<div className="library-panel">
|
||||
<div className="library-panel-scroll">
|
||||
{confirmDelete && (
|
||||
<div className="library-inline-form danger-zone">
|
||||
<div className="muted-copy">Delete "{library.name}"? This removes the registered files and local retrieval data for this database.</div>
|
||||
<div className="new-db-actions">
|
||||
<button
|
||||
className="button danger"
|
||||
disabled={busy}
|
||||
onClick={() => deleteLibrary().catch((error) => setErrorMessage(String(error?.message || error)))}
|
||||
>
|
||||
Confirm Delete
|
||||
</button>
|
||||
<button className="button ghost" onClick={() => setConfirmDelete(false)}>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{errorMessage && <div className="form-error">{errorMessage}</div>}
|
||||
|
||||
<div className="library-files">
|
||||
@@ -350,20 +321,10 @@ export default function LibraryManager({
|
||||
</div>
|
||||
|
||||
<div className="library-footer-actions">
|
||||
<button className="button" disabled={busy} onClick={() => addPaths('files')}>Add Files</button>
|
||||
<button className="button ghost" disabled={busy} onClick={() => addPaths('directories')}>Add Folder</button>
|
||||
<button className="button" disabled={busy} onClick={addPaths}>Add Files</button>
|
||||
{library.files?.length > 0 && !isSyncing && !isReadyForChat && (
|
||||
<button className="button ghost" disabled={busy} onClick={retrySync}>Retry Sync</button>
|
||||
)}
|
||||
<button
|
||||
className="button danger"
|
||||
onClick={() => {
|
||||
setConfirmDelete(true)
|
||||
setErrorMessage('')
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{toasts.length > 0 && (
|
||||
|
||||
Reference in New Issue
Block a user