mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 03:01:21 +02:00
Reporter (@zumik3-del, seconded by @unLieb) asked for three File Manager
improvements: recursive search, tags, and a markdown preview side panel.
This commit ships the two scoped ones; tags is held back gated on the
"give the issue a thumbs up" interest check Martin posted on the issue
because it's a much larger surface (M2M schema, CRUD endpoints, tag UI +
filter + autocomplete + i18n for the management surface) and isn't the
right call without a real demand signal.
1) Recursive search inside the selected folder.
Until now, selecting "Toys" and typing "robot" only found files
directly in Toys/ — anything under Toys/Cars/Race/ stayed invisible.
The page's client-side filter ran over a server-narrowed listing
(/library/files?folder_id=X is strict equality on folder_id), so the
client filter couldn't see what the listing never loaded.
list_files (backend/app/api/routes/library.py:1729+) gains a
recursive=true query param. When combined with folder_id, the route
walks library_folders.parent_id via a recursive CTE rooted at the
requested folder and returns every descendant folder's files in one
query. Recursive CTEs work on both SQLite >=3.8.3 (2014, well below
Bambuddy's runtime floor) and Postgres without dialect branching.
Default off so the existing folder-browsing call sites (Project /
Archive detail, the FE's no-search case) keep their narrow scope.
FE opts in only when both a folder is selected AND searchQuery is
non-empty (FileManagerPage.tsx — derived as searchExpandsSubfolders,
threaded through the useQuery key so the cache invalidates on
toggle). Small "Including subfolders" caption renders under the
search input when active so the user understands why a file from two
levels deep showed up.
2) Per-folder markdown description panel.
New endpoint GET /library/folders/{folder_id}/readme returns the
first .md file in the folder as {filename, content, truncated}.
Selection prefers README.md / readme.md / description.md
(case-insensitive via func.lower(filename) LIKE '%.md' + an
in-Python stem-preference sort), falls back to the
alphabetically-first *.md otherwise. 404 when no markdown is present
so the FE can hide the side panel — non-users pay no UI cost.
Bytes are clipped at 512 KiB (_README_BYTES_CAP) with a truncated
flag so the panel can warn the reader. UTF-8 decode uses
errors="replace" so one bad byte never blanks the panel.
New FolderReadmePanel.tsx fetches on folder-select and renders via
react-markdown@9 + remark-gfm@4 (tables, strikethrough, task lists).
Collapsible (default expanded), max-height 24rem with internal
scroll. react-markdown 9 doesn't render raw HTML by default — no
dompurify needed. Links open in a new tab with rel=noopener
noreferrer. Tailwind has no typography plugin in this project so
per-element components map h1/h2/h3/p/ul/ol/code/blockquote/table
to explicit utility classes that match the rest of the app.
Scope and permissions.
Both endpoints reuse the existing LIBRARY_READ_ALL / LIBRARY_READ_OWN
ownership-aware pair, so a viewer-tier user with read_own only sees
their own files in recursive listings and can only fetch the README of
folders containing their own files. No new permission, no DB migration.
The recursive CTE is a single SQL query — no N+1, no per-folder
round-trip, scales to deeply-nested model libraries.
React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- @vitejs/plugin-react uses Babel (or oxc when used in rolldown-vite) for Fast Refresh
- @vitejs/plugin-react-swc uses SWC for Fast Refresh
React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see this documentation.
Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
You can also install eslint-plugin-react-x and eslint-plugin-react-dom for React-specific lint rules:
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])