diff --git a/README.md b/README.md index 6513857..0440512 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,15 @@ The compose service uses tmpfs storage, giving each test run a clean database. - `ocrmypdf` (optional but recommended): Used by the OCR worker to extract text from PDFs when no embedded text layer is available. Ensure it is installed and available on the worker hosts if OCR is desired. - Quickwit (optional): The Quickwit indexer is used to ingest extracted text for search. Set `QUICKWIT_ENDPOINT` and `QUICKWIT_INDEX` in the environment when running workers if you want indexing jobs to run. The local compose file starts a Quickwit instance on `http://localhost:7280` and seeds the `documents` index automatically. +## Configuration + +The backend reads its settings from environment variables (see `backend/.env` for local defaults). In particular: + +- `DATABASE_URL` – connection string for the primary Postgres database (required). +- `DATABASE_MAX_POOL_SIZE` – optional override for the r2d2 connection pool size. Defaults to `2`; increase it in staging/production to match expected concurrency. + +On startup each binary logs the effective configuration with secrets redacted (for example, the database password is masked). This makes it easier to confirm the runtime settings in staging without exposing credentials. + ## Running Migrations in Kubernetes The backend container image ships the `diesel` CLI, so schema migrations can be executed as a short-lived Job (or Helm hook) before rolling out new pods. Example manifest: diff --git a/frontend/src/documents/DocumentsTable.jsx b/frontend/src/documents/DocumentsTable.jsx index 0e9285f..0480956 100644 --- a/frontend/src/documents/DocumentsTable.jsx +++ b/frontend/src/documents/DocumentsTable.jsx @@ -76,6 +76,7 @@ const DocumentsTable = ({ onDocumentTagDrop, viewMode = 'list', onViewModeChange, + onClearSelection, }) => { const showingSearchResults = searchResults !== null; const rows = showingSearchResults ? searchResults : documents; @@ -213,6 +214,16 @@ const DocumentsTable = ({ [isTagDragEvent, onDocumentTagDrop], ); + const handleGridBackgroundClick = useCallback( + (event) => { + if (event.target !== event.currentTarget) { + return; + } + onClearSelection?.(); + }, + [onClearSelection], + ); + return (
) : isGridView ? ( -
+
{!showingSearchResults && subfolders.map((folder) => { const canDragFolder = folder.id !== 'root'; @@ -353,7 +368,7 @@ const DocumentsTable = ({ }} >
- +
@@ -756,7 +771,7 @@ const DocumentsTable = ({ )}
); -}; +}; export default DocumentsTable; export { DocumentThumbnailImage }; diff --git a/frontend/src/index.jsx b/frontend/src/index.jsx index 7c26d12..cecd90e 100644 --- a/frontend/src/index.jsx +++ b/frontend/src/index.jsx @@ -1246,6 +1246,11 @@ const AppLayout = () => { [handleRowSelection], ); + const clearDocumentSelection = useCallback(() => { + setFocusedRowKey(null); + applySelection([], { anchor: null, interactedKeys: [] }); + }, [applySelection]); + const handleFolderRowClick = useCallback( (folderId, event) => { const rowKey = resolveFolderRowKey(folderId); @@ -4522,6 +4527,7 @@ const AppLayout = () => { onDocumentTagDrop: handleDocumentTagDrop, viewMode: documentsViewMode, onViewModeChange: handleDocumentsViewModeChange, + onClearSelection: clearDocumentSelection, }; const detailPanelProps = { diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 948e902..09e2cd3 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -1309,7 +1309,7 @@ button.icon-button.ghost:hover:not([disabled]) { .documents-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); - gap: 0.2rem 1.3rem; + gap: 0.3rem 1.3rem; padding: 0; } @@ -1317,7 +1317,7 @@ button.icon-button.ghost:hover:not([disabled]) { padding: 0rem; display: flex; flex-direction: column; - gap: 0.1rem; + gap: 0.3rem; cursor: pointer; align-items: center; text-align: center; @@ -1364,6 +1364,7 @@ button.icon-button.ghost:hover:not([disabled]) { display: flex; flex-wrap: wrap; gap: 0.25rem; + justify-content: center; } .folder-card { @@ -1375,11 +1376,16 @@ button.icon-button.ghost:hover:not([disabled]) { display: flex; align-items: center; justify-content: center; - width: 100%; - aspect-ratio: 1; + width: 128px; + height: 128px; color: var(--accent); } +.folder-card__icon-svg { + width: 100%; + height: 100%; +} + .folder-card__meta { display: flex; flex-direction: column; @@ -1396,16 +1402,6 @@ button.icon-button.ghost:hover:not([disabled]) { white-space: nowrap; } -.folder-card__badge { - display: inline-flex; - align-self: center; - padding: 0.12rem 0.45rem; - font-size: 0.72rem; - border-radius: 999px; - background: var(--surface-soft); - color: var(--muted); - border: 1px solid var(--border); -} .doc-name__title { font-weight: 600;