diff --git a/src/App.css b/src/App.css index 41194b4..73a76d1 100644 --- a/src/App.css +++ b/src/App.css @@ -1,4 +1,5 @@ @import url("https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,100..700;1,100..700&display=swap"); code { font-family: "JetBrains Mono"; diff --git a/src/components/EntriesListSidebar.tsx b/src/components/EntriesListSidebar.tsx index 87b0866..d1a0dbd 100644 --- a/src/components/EntriesListSidebar.tsx +++ b/src/components/EntriesListSidebar.tsx @@ -10,68 +10,68 @@ import { Link } from "react-router" import { useState, useRef, useEffect } from "react" export default function EntriesListSidebar() { - const scrollRef = useRef(null) - const [isOpen, setIsOpen] = useState(() => { - if (typeof window !== "undefined") { - const saved = sessionStorage.getItem("entries_list_sidebar_open") - return saved ? JSON.parse(saved) : false - } - return false - }) + const scrollRef = useRef(null) + const [isOpen, setIsOpen] = useState(() => { + if (typeof window !== "undefined") { + const saved = sessionStorage.getItem("entries_list_sidebar_open") + return saved ? JSON.parse(saved) : false + } + return false + }) - const { data: entries, isLoading } = useQuery({ - queryKey: ["entries_list"], - queryFn: () => api.get("/entries").then((res) => res.data), - }) + const { data: entries, isLoading } = useQuery({ + queryKey: ["entries_list"], + queryFn: () => api.get("/entries").then((res) => res.data), + }) - useEffect(() => { - sessionStorage.setItem("entries_list_sidebar_open", JSON.stringify(isOpen)) - }, [isOpen]) + useEffect(() => { + sessionStorage.setItem("entries_list_sidebar_open", JSON.stringify(isOpen)) + }, [isOpen]) - useEffect(() => { - const savedScroll = sessionStorage.getItem("entries_list_sidebar_scroll_position") - if (savedScroll && scrollRef.current) { - scrollRef.current.scrollTop = parseInt(savedScroll) - } - }, [entries]) + useEffect(() => { + const savedScroll = sessionStorage.getItem("entries_list_sidebar_scroll_position") + if (savedScroll && scrollRef.current) { + scrollRef.current.scrollTop = parseInt(savedScroll) + } + }, [entries]) - const handleScroll = () => { - if (scrollRef.current) { - sessionStorage.setItem("entries_list_sidebar_scroll_position", scrollRef.current.scrollTop) - } - } + const handleScroll = () => { + if (scrollRef.current) { + sessionStorage.setItem("entries_list_sidebar_scroll_position", scrollRef.current.scrollTop) + } + } - return ( - - - - - - - Entries - - {entries?.count} - - - - - - -
- - {entries?.data.map((item, i) => ( - - - - {item.title.replace(/_/g, " ")} - - - - ))} - -
-
-
-
- ) + return ( + + + + + + + Entries + + {entries?.count} + + + + + + +
+ + {entries?.data.map((item, i) => ( + + + + {item.title.replace(/_/g, " ")} + + + + ))} + +
+
+
+
+ ) } diff --git a/src/components/EntryReferences.tsx b/src/components/EntryReferences.tsx index 821a694..f526a4f 100644 --- a/src/components/EntryReferences.tsx +++ b/src/components/EntryReferences.tsx @@ -1,87 +1,70 @@ -import { useQuery } from "@tanstack/react-query" -import api from "../api/eolas-api" import { Link } from "react-router" import { Badge } from "./ui/badge" -export default function EntryReferences({ entryTitle }) { - const { data: tags, isLoading: tagsLoading } = useQuery({ - queryKey: [`tags_for_${entryTitle}`], - queryFn: () => api.get(`/tags/${entryTitle}`).then((res) => res.data), - }) +export default function EntryReferences({ tags, backlinks, outlinks }) { + return ( +
+
+
+

Incoming links

+ + {backlinks?.count} + +
- const { data: backlinks, isLoading: backlinksLoading } = useQuery({ - queryKey: [`backlinks_for_${entryTitle}`], - queryFn: () => api.get(`/entries/backlinks/${entryTitle}`).then((res) => res.data), - }) +
+ {backlinks && + backlinks?.data.map((item, i) => ( + + {item.replace(/_/g, " ")} + + ))} +
- const { data: outlinks, isLoading: outlinksLoading } = useQuery({ - queryKey: [`outlinks_for_${entryTitle}`], - queryFn: () => api.get(`/entries/outlinks/${entryTitle}`).then((res) => res.data), - }) +
+

Outgoing links

+ + {outlinks?.count} + +
- return ( -
-
-
-

Incoming links

- - {backlinks?.count} - -
+
+ {outlinks && + outlinks?.data.map((item, i) => ( + + {item.replace(/_/g, " ")} + + ))} +
+
-
- {backlinks && - backlinks?.data.map((item, i) => ( - - {item.replace(/_/g, " ")} - - ))} -
- -
-

Outgoing links

- - {outlinks?.count} - -
- -
- {outlinks && - outlinks?.data.map((item, i) => ( - - {item.replace(/_/g, " ")} - - ))} -
-
- -
-
-

Tags

- - {tags?.count} - -
-
- {tags?.data.map((item, i) => ( - - {item} - - ))} -
-
-
- ) +
+
+

Tags

+ + {tags?.count} + +
+
+ {tags?.data.map((item, i) => ( + + {item} + + ))} +
+
+
+ ) } diff --git a/src/components/NetworkGraph.tsx b/src/components/NetworkGraph.tsx index 2d5bf7a..f8bc09f 100644 --- a/src/components/NetworkGraph.tsx +++ b/src/components/NetworkGraph.tsx @@ -3,38 +3,38 @@ import LoadGraph from "./LoadGraph" import GraphEvents from "./GraphEvents" export default function NetworkGraph({ data }) { - const nodeCount = data?.nodes.length + const nodeCount = data?.nodes.length - const sigmaStyle = { - height: "400px", - width: "100%", - } + const sigmaStyle = { + height: "400px", + width: "100%", + } - const settings = { - allowInvalidContainer: true, - defaultEdgeColor: "#a4a4a4", - labelColor: { color: "#0a0a0a" }, - labelFont: "Inter", - labelSize: 14, - labelWeight: "400", - labelRenderedSizeThreshold: nodeCount > 15 ? 10 : 8, - renderLabels: true, - } + const settings = { + allowInvalidContainer: true, + defaultEdgeColor: "#a4a4a4", + labelColor: { color: "#0a0a0a" }, + labelFont: "IBM Plex Sans", + labelSize: 14, + labelWeight: "400", + labelRenderedSizeThreshold: nodeCount > 15 ? 10 : 8, + renderLabels: true, + } - return ( -
- - - - -
- ) + return ( +
+ + + + +
+ ) } diff --git a/src/components/TagListSidebar.tsx b/src/components/TagListSidebar.tsx index d4c2b7d..55b8878 100644 --- a/src/components/TagListSidebar.tsx +++ b/src/components/TagListSidebar.tsx @@ -9,69 +9,69 @@ import { Link } from "react-router" import { useState, useRef, useEffect } from "react" export default function TagListSidebar() { - const scrollRef = useRef(null) + const scrollRef = useRef(null) - const [isOpen, setIsOpen] = useState(() => { - if (typeof window !== "undefined") { - const saved = sessionStorage.getItem("tags_list_sidebar_open") - return saved ? JSON.parse(saved) : false - } - return false - }) + const [isOpen, setIsOpen] = useState(() => { + if (typeof window !== "undefined") { + const saved = sessionStorage.getItem("tags_list_sidebar_open") + return saved ? JSON.parse(saved) : false + } + return false + }) - const { data: tags } = useQuery({ - queryKey: ["tag_list"], - queryFn: () => api.get("/tags").then((res) => res.data), - }) + const { data: tags } = useQuery({ + queryKey: ["tag_list"], + queryFn: () => api.get("/tags").then((res) => res.data), + }) - useEffect(() => { - sessionStorage.setItem("tags_list_sidebar_open", JSON.stringify(isOpen)) - }, [isOpen]) + useEffect(() => { + sessionStorage.setItem("tags_list_sidebar_open", JSON.stringify(isOpen)) + }, [isOpen]) - useEffect(() => { - const savedScroll = sessionStorage.getItem("tags_list_sidebar_open") - if (savedScroll && scrollRef.current) { - scrollRef.current.scrollTop = parseInt(savedScroll) - } - }, [tags]) + useEffect(() => { + const savedScroll = sessionStorage.getItem("tags_list_sidebar_open") + if (savedScroll && scrollRef.current) { + scrollRef.current.scrollTop = parseInt(savedScroll) + } + }, [tags]) - const handleScroll = () => { - if (scrollRef.current) { - sessionStorage.setItem("tags_list_sidebar_open", scrollRef.current.scrollTop) - } - } + const handleScroll = () => { + if (scrollRef.current) { + sessionStorage.setItem("tags_list_sidebar_open", scrollRef.current.scrollTop) + } + } - return ( - - - - - - - Tags - - {tags?.count} - - - - - - -
- - {tags?.data.map((item, i) => ( - - - - {item} - - - - ))} - -
-
-
-
- ) + return ( + + + + + + + Tags + + {tags?.count} + + + + + + +
+ + {tags?.data.map((item, i) => ( + + + + {item} + + + + ))} + +
+
+
+
+ ) } diff --git a/src/containers/EntryMetadata.tsx b/src/containers/EntryMetadata.tsx index 0e06bbc..a3f9730 100644 --- a/src/containers/EntryMetadata.tsx +++ b/src/containers/EntryMetadata.tsx @@ -1,53 +1,68 @@ +import { useQuery } from "@tanstack/react-query" +import api from "../api/eolas-api" import EntryReferences from "@/components/EntryReferences" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Bookmark } from "lucide-react" -import { History } from "lucide-react" import { Braces } from "lucide-react" - +import { Badge } from "@/components/ui/badge" +import { convertDateFriendly } from "@/lib/utils" export default function EntryMetadata({ entryTitle, history, metadata }) { - return ( - - - - - References - + const { data: tags } = useQuery({ + queryKey: [`tags_for_${entryTitle}`], + queryFn: () => api.get(`/tags/${entryTitle}`).then((res) => res.data), + }) - - - History - + const { data: backlinks } = useQuery({ + queryKey: [`backlinks_for_${entryTitle}`], + queryFn: () => api.get(`/entries/backlinks/${entryTitle}`).then((res) => res.data), + }) - - - Metadata - - + const { data: outlinks } = useQuery({ + queryKey: [`outlinks_for_${entryTitle}`], + queryFn: () => api.get(`/entries/outlinks/${entryTitle}`).then((res) => res.data), + }) -
- - - - -
-

Last modified: {history.lastModified}

-
-
- -
-

Size on disk: {metadata.fileSize} B

-
-
-
-
- ) + const sizeInKb = parseFloat((metadata?.fileSize / 1000).toFixed(1)) + + return ( + + + + + References + + + + + Metadata + + + +
+ + + + +
+
+ Last modified +
+ {convertDateFriendly(new Date(history.lastModified))} +
+
+
+ Size on disk +
{sizeInKb} Kb
+
+
+
+
+
+ ) } diff --git a/src/index.css b/src/index.css index 4256742..0252064 100644 --- a/src/index.css +++ b/src/index.css @@ -2,130 +2,130 @@ @import "tw-animate-css"; html { - overflow-y: hidden; + overflow-y: hidden; } @custom-variant dark (&:is(.dark *)); @theme inline { - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); - --color-background: var(--background); - --color-foreground: var(--foreground); - --color-card: var(--card); - --color-card-foreground: var(--card-foreground); - --color-popover: var(--popover); - --color-popover-foreground: var(--popover-foreground); - --color-primary: var(--primary); - --color-primary-foreground: var(--primary-foreground); - --color-secondary: var(--secondary); - --color-secondary-foreground: var(--secondary-foreground); - --color-muted: var(--muted); - --color-muted-foreground: var(--muted-foreground); - --color-accent: var(--accent); - --color-accent-foreground: var(--accent-foreground); - --color-destructive: var(--destructive); - --color-border: var(--border); - --color-input: var(--input); - --color-ring: var(--ring); - --color-chart-1: var(--chart-1); - --color-chart-2: var(--chart-2); - --color-chart-3: var(--chart-3); - --color-chart-4: var(--chart-4); - --color-chart-5: var(--chart-5); - --color-sidebar: var(--sidebar); - --color-sidebar-foreground: var(--sidebar-foreground); - --color-sidebar-primary: var(--sidebar-primary); - --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); - --color-sidebar-accent: var(--sidebar-accent); - --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); - --color-sidebar-border: var(--sidebar-border); - --color-sidebar-ring: var(--sidebar-ring); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); } :root { - --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); - --card: oklch(1 0 0); - --card-foreground: oklch(0.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.205 0 0); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.922 0 0); - --input: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.205 0 0); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); - color-scheme: light; + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); + color-scheme: light; } .dark { - --background: oklch(0.145 0 0); - --foreground: oklch(0.985 0 0); - --card: oklch(0.205 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.205 0 0); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.922 0 0); - --primary-foreground: oklch(0.205 0 0); - --secondary: oklch(0.269 0 0); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.269 0 0); - --muted-foreground: oklch(0.708 0 0); - --accent: oklch(0.269 0 0); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.556 0 0); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.556 0 0); - color-scheme: dark; + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); + color-scheme: dark; } @layer base { - * { - @apply border-border outline-ring/50; - } + * { + @apply border-border outline-ring/50; + } - html { - font-family: "Inter", sans-serif; - } + html { + font-family: "IBM Plex Sans", sans-serif; + } - body { - @apply bg-background text-foreground; - } + body { + @apply bg-background text-foreground; + } } diff --git a/src/lib/utils.ts b/src/lib/utils.ts index bd0c391..5709946 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -2,5 +2,28 @@ import { clsx, type ClassValue } from "clsx" import { twMerge } from "tailwind-merge" export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(inputs)) +} + +export const convertDateFriendly = (isoStamp) => { + const months = [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ] + + const unixSeconds = new Date(isoStamp) + const day = unixSeconds.getDate() + const month = months[unixSeconds.getMonth()] + const year = unixSeconds.getFullYear() + return `${day} ${month} ${year}` }