feat: dark-tech UI redesign + markdown patch cards

Redesign the frontend with a dark-tech theme: add Tailwind + PostCSS,
design tokens, and shadcn-style primitives (Button/Card/Input/Dialog/
DropdownMenu/Tabs/ScrollArea/etc.); restyle the app shell, navigation,
sidebar tree, content view, markdown rendering, editors, modals and
settings panels. Behavior/props unchanged; Font Awesome replaced with
lucide-react.

Add the patch cards feature UI: patch-queries hooks and a PatchCards
component rendered below the markdown body, with an Add Patch button
and create/edit dialog.

Fix tree expandability: folders with an index page now expand on name
click (and navigate), and the chevron+folder icon is one larger toggle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
h z
2026-05-16 17:28:13 +01:00
parent 045c7c51d6
commit 952387d50f
54 changed files with 4503 additions and 1765 deletions

View File

@@ -0,0 +1,61 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { fetch_ } from "../request-utils";
import { useConfig } from "../../ConfigProvider";
export const usePatches = (markdownId) => {
const config = useConfig();
return useQuery({
queryKey: ["patches", markdownId],
queryFn: () =>
fetch_(`${config.BACKEND_HOST}/api/patch/by_markdown/${markdownId}`),
enabled: !!markdownId,
});
};
export const useCreatePatch = () => {
const queryClient = useQueryClient();
const config = useConfig();
return useMutation({
mutationFn: (data) =>
fetch_(`${config.BACKEND_HOST}/api/patch/`, {
method: "POST",
body: JSON.stringify(data),
}),
onSuccess: (res) => {
queryClient.invalidateQueries({
queryKey: ["patches", res.markdown_id],
});
},
});
};
export const useUpdatePatch = () => {
const queryClient = useQueryClient();
const config = useConfig();
return useMutation({
mutationFn: ({ id, data }) =>
fetch_(`${config.BACKEND_HOST}/api/patch/${id}`, {
method: "PATCH",
body: JSON.stringify(data),
}),
onSuccess: (res) => {
queryClient.invalidateQueries({
queryKey: ["patches", res.markdown_id],
});
},
});
};
export const useDeletePatch = () => {
const queryClient = useQueryClient();
const config = useConfig();
return useMutation({
mutationFn: ({ id }) =>
fetch_(`${config.BACKEND_HOST}/api/patch/${id}`, {
method: "DELETE",
}),
onSuccess: (_res, { markdownId }) => {
queryClient.invalidateQueries({ queryKey: ["patches", markdownId] });
},
});
};