feat: plex sans font and tidy up
All checks were successful
Deploy eolas-app / deploy (push) Successful in 52s
All checks were successful
Deploy eolas-app / deploy (push) Successful in 52s
This commit is contained in:
parent
4c6a4d619f
commit
7143d0f389
8 changed files with 405 additions and 383 deletions
|
|
@ -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=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 {
|
code {
|
||||||
font-family: "JetBrains Mono";
|
font-family: "JetBrains Mono";
|
||||||
|
|
|
||||||
|
|
@ -10,68 +10,68 @@ import { Link } from "react-router"
|
||||||
import { useState, useRef, useEffect } from "react"
|
import { useState, useRef, useEffect } from "react"
|
||||||
|
|
||||||
export default function EntriesListSidebar() {
|
export default function EntriesListSidebar() {
|
||||||
const scrollRef = useRef(null)
|
const scrollRef = useRef(null)
|
||||||
const [isOpen, setIsOpen] = useState(() => {
|
const [isOpen, setIsOpen] = useState(() => {
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
const saved = sessionStorage.getItem("entries_list_sidebar_open")
|
const saved = sessionStorage.getItem("entries_list_sidebar_open")
|
||||||
return saved ? JSON.parse(saved) : false
|
return saved ? JSON.parse(saved) : false
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: entries, isLoading } = useQuery({
|
const { data: entries, isLoading } = useQuery({
|
||||||
queryKey: ["entries_list"],
|
queryKey: ["entries_list"],
|
||||||
queryFn: () => api.get("/entries").then((res) => res.data),
|
queryFn: () => api.get("/entries").then((res) => res.data),
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
sessionStorage.setItem("entries_list_sidebar_open", JSON.stringify(isOpen))
|
sessionStorage.setItem("entries_list_sidebar_open", JSON.stringify(isOpen))
|
||||||
}, [isOpen])
|
}, [isOpen])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const savedScroll = sessionStorage.getItem("entries_list_sidebar_scroll_position")
|
const savedScroll = sessionStorage.getItem("entries_list_sidebar_scroll_position")
|
||||||
if (savedScroll && scrollRef.current) {
|
if (savedScroll && scrollRef.current) {
|
||||||
scrollRef.current.scrollTop = parseInt(savedScroll)
|
scrollRef.current.scrollTop = parseInt(savedScroll)
|
||||||
}
|
}
|
||||||
}, [entries])
|
}, [entries])
|
||||||
|
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
if (scrollRef.current) {
|
if (scrollRef.current) {
|
||||||
sessionStorage.setItem("entries_list_sidebar_scroll_position", scrollRef.current.scrollTop)
|
sessionStorage.setItem("entries_list_sidebar_scroll_position", scrollRef.current.scrollTop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Collapsible open={isOpen} onOpenChange={setIsOpen} className="group/collapsible">
|
<Collapsible open={isOpen} onOpenChange={setIsOpen} className="group/collapsible">
|
||||||
<SidebarMenuItem key="entries">
|
<SidebarMenuItem key="entries">
|
||||||
<CollapsibleTrigger asChild className="rounded-none">
|
<CollapsibleTrigger asChild className="rounded-none">
|
||||||
<SidebarMenuButton asChild className="rounded-none">
|
<SidebarMenuButton asChild className="rounded-none">
|
||||||
<a href="#">
|
<a href="#">
|
||||||
<FileText />
|
<FileText />
|
||||||
<span>Entries</span>
|
<span className="font-medium">Entries</span>
|
||||||
<Badge className="ml-0" variant="secondary">
|
<Badge className="ml-0" variant="secondary">
|
||||||
{entries?.count}
|
{entries?.count}
|
||||||
</Badge>
|
</Badge>
|
||||||
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
|
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
|
||||||
</a>
|
</a>
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
<CollapsibleContent>
|
<CollapsibleContent>
|
||||||
<div ref={scrollRef} onScroll={handleScroll} className="max-h-100 overflow-y-auto">
|
<div ref={scrollRef} onScroll={handleScroll} className="max-h-100 overflow-y-auto">
|
||||||
<SidebarMenuSub>
|
<SidebarMenuSub>
|
||||||
{entries?.data.map((item, i) => (
|
{entries?.data.map((item, i) => (
|
||||||
<SidebarMenuItem key={i}>
|
<SidebarMenuItem key={i}>
|
||||||
<Link to={`/entries/${item.title}`}>
|
<Link to={`/entries/${item.title}`}>
|
||||||
<span className="text-xs text-foreground dark:hover:text-green-300 dark:active:text-green-400 hover:text-gray-600 active:text-gray-700 focus:text-gray-900 dark:focus:text-green-900">
|
<span className="text-xs text-foreground dark:hover:text-green-300 dark:active:text-green-400 hover:text-gray-600 active:text-gray-700 focus:text-gray-900 dark:focus:text-green-900">
|
||||||
{item.title.replace(/_/g, " ")}
|
{item.title.replace(/_/g, " ")}
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
))}
|
))}
|
||||||
</SidebarMenuSub>
|
</SidebarMenuSub>
|
||||||
</div>
|
</div>
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,87 +1,70 @@
|
||||||
import { useQuery } from "@tanstack/react-query"
|
|
||||||
import api from "../api/eolas-api"
|
|
||||||
import { Link } from "react-router"
|
import { Link } from "react-router"
|
||||||
import { Badge } from "./ui/badge"
|
import { Badge } from "./ui/badge"
|
||||||
|
|
||||||
export default function EntryReferences({ entryTitle }) {
|
export default function EntryReferences({ tags, backlinks, outlinks }) {
|
||||||
const { data: tags, isLoading: tagsLoading } = useQuery({
|
return (
|
||||||
queryKey: [`tags_for_${entryTitle}`],
|
<div className="w-full md:flex flex-row justify-stretch gap-3">
|
||||||
queryFn: () => api.get(`/tags/${entryTitle}`).then((res) => res.data),
|
<div className="w-full">
|
||||||
})
|
<div className="flex flex row justify-between bg-sidebar">
|
||||||
|
<h3 className="font-medium text-sm p-1 ml-1">Incoming links</h3>
|
||||||
|
<Badge variant="secondary" className="rounded-none">
|
||||||
|
{backlinks?.count}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
const { data: backlinks, isLoading: backlinksLoading } = useQuery({
|
<div className="mt-2 mb-3 pl-1">
|
||||||
queryKey: [`backlinks_for_${entryTitle}`],
|
{backlinks &&
|
||||||
queryFn: () => api.get(`/entries/backlinks/${entryTitle}`).then((res) => res.data),
|
backlinks?.data.map((item, i) => (
|
||||||
})
|
<Link
|
||||||
|
key={i}
|
||||||
|
to={`/entries/${item}`}
|
||||||
|
className="text-foreground underline-offset-3 text-sm underline hover:text-gray-700 dark:hover:text-green-300 pr-2 block mb-2"
|
||||||
|
>
|
||||||
|
{item.replace(/_/g, " ")}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
const { data: outlinks, isLoading: outlinksLoading } = useQuery({
|
<div className="flex flex row justify-between bg-sidebar">
|
||||||
queryKey: [`outlinks_for_${entryTitle}`],
|
<h3 className="font-medium text-sm p-1 ml-1">Outgoing links</h3>
|
||||||
queryFn: () => api.get(`/entries/outlinks/${entryTitle}`).then((res) => res.data),
|
<Badge variant="secondary" className="rounded-none">
|
||||||
})
|
{outlinks?.count}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
return (
|
<div className="mt-2 mb-3 pl-1">
|
||||||
<div className="w-full md:flex flex-row justify-stretch gap-3">
|
{outlinks &&
|
||||||
<div className="w-full">
|
outlinks?.data.map((item, i) => (
|
||||||
<div className="flex flex row justify-between bg-sidebar">
|
<Link
|
||||||
<h3 className="font-medium text-sm p-1 ml-1">Incoming links</h3>
|
key={i}
|
||||||
<Badge variant="secondary" className="rounded-none">
|
to={`/entries/${item}`}
|
||||||
{backlinks?.count}
|
className="text-foreground underline-offset-3 text-sm underline hover:text-gray-700 dark:hover:text-green-300 pr-2 block mb-2"
|
||||||
</Badge>
|
>
|
||||||
</div>
|
{item.replace(/_/g, " ")}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="mt-2 mb-3 pl-1">
|
<div className="w-full">
|
||||||
{backlinks &&
|
<div className="flex flex row justify-between bg-sidebar">
|
||||||
backlinks?.data.map((item, i) => (
|
<h3 className="font-medium text-sm p-1 ml-1">Tags</h3>
|
||||||
<Link
|
<Badge variant="secondary" className="rounded-none">
|
||||||
key={i}
|
{tags?.count}
|
||||||
to={`/entries/${item}`}
|
</Badge>
|
||||||
className="text-foreground underline-offset-3 text-sm underline hover:text-gray-700 dark:hover:text-green-300 pr-2 block mb-2"
|
</div>
|
||||||
>
|
<div className="mt-2 mb-3 pl-1">
|
||||||
{item.replace(/_/g, " ")}
|
{tags?.data.map((item, i) => (
|
||||||
</Link>
|
<Link
|
||||||
))}
|
key={i}
|
||||||
</div>
|
to={`/tags/${item}`}
|
||||||
|
className="text-foreground underline-offset-3 text-sm underline hover:text-gray-700 dark:hover:text-green-300 pr-2 block mb-2"
|
||||||
<div className="flex flex row justify-between bg-sidebar">
|
>
|
||||||
<h3 className="font-medium text-sm p-1 ml-1">Outgoing links</h3>
|
{item}
|
||||||
<Badge variant="secondary" className="rounded-none">
|
</Link>
|
||||||
{outlinks?.count}
|
))}
|
||||||
</Badge>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div className="mt-2 mb-3 pl-1">
|
)
|
||||||
{outlinks &&
|
|
||||||
outlinks?.data.map((item, i) => (
|
|
||||||
<Link
|
|
||||||
key={i}
|
|
||||||
to={`/entries/${item}`}
|
|
||||||
className="text-foreground underline-offset-3 text-sm underline hover:text-gray-700 dark:hover:text-green-300 pr-2 block mb-2"
|
|
||||||
>
|
|
||||||
{item.replace(/_/g, " ")}
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-full">
|
|
||||||
<div className="flex flex row justify-between bg-sidebar">
|
|
||||||
<h3 className="font-medium text-sm p-1 ml-1">Tags</h3>
|
|
||||||
<Badge variant="secondary" className="rounded-none">
|
|
||||||
{tags?.count}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
<div className="mt-2 mb-3 pl-1">
|
|
||||||
{tags?.data.map((item, i) => (
|
|
||||||
<Link
|
|
||||||
key={i}
|
|
||||||
to={`/tags/${item}`}
|
|
||||||
className="text-foreground underline-offset-3 text-sm underline hover:text-gray-700 dark:hover:text-green-300 pr-2 block mb-2"
|
|
||||||
>
|
|
||||||
{item}
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,38 +3,38 @@ import LoadGraph from "./LoadGraph"
|
||||||
import GraphEvents from "./GraphEvents"
|
import GraphEvents from "./GraphEvents"
|
||||||
|
|
||||||
export default function NetworkGraph({ data }) {
|
export default function NetworkGraph({ data }) {
|
||||||
const nodeCount = data?.nodes.length
|
const nodeCount = data?.nodes.length
|
||||||
|
|
||||||
const sigmaStyle = {
|
const sigmaStyle = {
|
||||||
height: "400px",
|
height: "400px",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
}
|
}
|
||||||
|
|
||||||
const settings = {
|
const settings = {
|
||||||
allowInvalidContainer: true,
|
allowInvalidContainer: true,
|
||||||
defaultEdgeColor: "#a4a4a4",
|
defaultEdgeColor: "#a4a4a4",
|
||||||
labelColor: { color: "#0a0a0a" },
|
labelColor: { color: "#0a0a0a" },
|
||||||
labelFont: "Inter",
|
labelFont: "IBM Plex Sans",
|
||||||
labelSize: 14,
|
labelSize: 14,
|
||||||
labelWeight: "400",
|
labelWeight: "400",
|
||||||
labelRenderedSizeThreshold: nodeCount > 15 ? 10 : 8,
|
labelRenderedSizeThreshold: nodeCount > 15 ? 10 : 8,
|
||||||
renderLabels: true,
|
renderLabels: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "400px",
|
height: "400px",
|
||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
contain: "strict",
|
contain: "strict",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SigmaContainer style={sigmaStyle} settings={settings}>
|
<SigmaContainer style={sigmaStyle} settings={settings}>
|
||||||
<LoadGraph data={data} />
|
<LoadGraph data={data} />
|
||||||
<GraphEvents />
|
<GraphEvents />
|
||||||
</SigmaContainer>
|
</SigmaContainer>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,69 +9,69 @@ import { Link } from "react-router"
|
||||||
import { useState, useRef, useEffect } from "react"
|
import { useState, useRef, useEffect } from "react"
|
||||||
|
|
||||||
export default function TagListSidebar() {
|
export default function TagListSidebar() {
|
||||||
const scrollRef = useRef(null)
|
const scrollRef = useRef(null)
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(() => {
|
const [isOpen, setIsOpen] = useState(() => {
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
const saved = sessionStorage.getItem("tags_list_sidebar_open")
|
const saved = sessionStorage.getItem("tags_list_sidebar_open")
|
||||||
return saved ? JSON.parse(saved) : false
|
return saved ? JSON.parse(saved) : false
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: tags } = useQuery({
|
const { data: tags } = useQuery({
|
||||||
queryKey: ["tag_list"],
|
queryKey: ["tag_list"],
|
||||||
queryFn: () => api.get("/tags").then((res) => res.data),
|
queryFn: () => api.get("/tags").then((res) => res.data),
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
sessionStorage.setItem("tags_list_sidebar_open", JSON.stringify(isOpen))
|
sessionStorage.setItem("tags_list_sidebar_open", JSON.stringify(isOpen))
|
||||||
}, [isOpen])
|
}, [isOpen])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const savedScroll = sessionStorage.getItem("tags_list_sidebar_open")
|
const savedScroll = sessionStorage.getItem("tags_list_sidebar_open")
|
||||||
if (savedScroll && scrollRef.current) {
|
if (savedScroll && scrollRef.current) {
|
||||||
scrollRef.current.scrollTop = parseInt(savedScroll)
|
scrollRef.current.scrollTop = parseInt(savedScroll)
|
||||||
}
|
}
|
||||||
}, [tags])
|
}, [tags])
|
||||||
|
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
if (scrollRef.current) {
|
if (scrollRef.current) {
|
||||||
sessionStorage.setItem("tags_list_sidebar_open", scrollRef.current.scrollTop)
|
sessionStorage.setItem("tags_list_sidebar_open", scrollRef.current.scrollTop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Collapsible open={isOpen} onOpenChange={setIsOpen} className="group/collapsible">
|
<Collapsible open={isOpen} onOpenChange={setIsOpen} className="group/collapsible">
|
||||||
<SidebarMenuItem key="tags">
|
<SidebarMenuItem key="tags">
|
||||||
<CollapsibleTrigger asChild>
|
<CollapsibleTrigger asChild>
|
||||||
<SidebarMenuButton asChild className="rounded-none">
|
<SidebarMenuButton asChild className="rounded-none">
|
||||||
<a href="#">
|
<a href="#">
|
||||||
<Tags />
|
<Tags />
|
||||||
<span>Tags</span>
|
<span className="font-medium">Tags</span>
|
||||||
<Badge className="ml-0" variant="secondary">
|
<Badge className="ml-0" variant="secondary">
|
||||||
{tags?.count}
|
{tags?.count}
|
||||||
</Badge>
|
</Badge>
|
||||||
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
|
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
|
||||||
</a>
|
</a>
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
<CollapsibleContent>
|
<CollapsibleContent>
|
||||||
<div ref={scrollRef} onScroll={handleScroll} className="max-h-100 overflow-y-auto">
|
<div ref={scrollRef} onScroll={handleScroll} className="max-h-100 overflow-y-auto">
|
||||||
<SidebarMenuSub>
|
<SidebarMenuSub>
|
||||||
{tags?.data.map((item, i) => (
|
{tags?.data.map((item, i) => (
|
||||||
<SidebarMenuItem key={i}>
|
<SidebarMenuItem key={i}>
|
||||||
<Link to={`/tags/${item}`}>
|
<Link to={`/tags/${item}`}>
|
||||||
<span className="text-xs text-foreground dark:hover:text-green-300 dark:active:text-green-400 hover:text-gray-600 active:text-gray-700 focus:text-gray-900 dark:focus:text-green-900">
|
<span className="text-xs text-foreground dark:hover:text-green-300 dark:active:text-green-400 hover:text-gray-600 active:text-gray-700 focus:text-gray-900 dark:focus:text-green-900">
|
||||||
{item}
|
{item}
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
))}
|
))}
|
||||||
</SidebarMenuSub>
|
</SidebarMenuSub>
|
||||||
</div>
|
</div>
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,53 +1,68 @@
|
||||||
|
import { useQuery } from "@tanstack/react-query"
|
||||||
|
import api from "../api/eolas-api"
|
||||||
import EntryReferences from "@/components/EntryReferences"
|
import EntryReferences from "@/components/EntryReferences"
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||||
import { Bookmark } from "lucide-react"
|
import { Bookmark } from "lucide-react"
|
||||||
import { History } from "lucide-react"
|
|
||||||
import { Braces } 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 }) {
|
export default function EntryMetadata({ entryTitle, history, metadata }) {
|
||||||
return (
|
const { data: tags } = useQuery({
|
||||||
<Tabs defaultValue="references" className="w-full h-full flex flex-col">
|
queryKey: [`tags_for_${entryTitle}`],
|
||||||
<TabsList className="rounded-none shadow-none drop-shadow-none w-full bg-sidebar sticky top-0 z-10 flex-shrink-0">
|
queryFn: () => api.get(`/tags/${entryTitle}`).then((res) => res.data),
|
||||||
<TabsTrigger
|
})
|
||||||
value="references"
|
|
||||||
className="pl-2 justify-start text-sm font-semibold rounded-none shadow-none data-[state=active]:border-1 data-[state=active]:border-border py-4"
|
|
||||||
>
|
|
||||||
<Bookmark />
|
|
||||||
References
|
|
||||||
</TabsTrigger>
|
|
||||||
|
|
||||||
<TabsTrigger
|
const { data: backlinks } = useQuery({
|
||||||
className="pl-2 justify-start text-sm font-semibold rounded-none shadow-none data-[state=active]:border-1 data-[state=active]:border-border py-4"
|
queryKey: [`backlinks_for_${entryTitle}`],
|
||||||
value="history"
|
queryFn: () => api.get(`/entries/backlinks/${entryTitle}`).then((res) => res.data),
|
||||||
>
|
})
|
||||||
<History />
|
|
||||||
History
|
|
||||||
</TabsTrigger>
|
|
||||||
|
|
||||||
<TabsTrigger
|
const { data: outlinks } = useQuery({
|
||||||
className="pl-2 justify-start text-sm font-semibold rounded-none shadow-none data-[state=active]:border-1 data-[state=active]:border-border py-4"
|
queryKey: [`outlinks_for_${entryTitle}`],
|
||||||
value="metadata"
|
queryFn: () => api.get(`/entries/outlinks/${entryTitle}`).then((res) => res.data),
|
||||||
>
|
})
|
||||||
<Braces />
|
|
||||||
Metadata
|
|
||||||
</TabsTrigger>
|
|
||||||
</TabsList>
|
|
||||||
|
|
||||||
<div className="flex-1 overflow-auto">
|
const sizeInKb = parseFloat((metadata?.fileSize / 1000).toFixed(1))
|
||||||
<TabsContent value="references" className="px-2 lg:px-2 m-0">
|
|
||||||
<EntryReferences entryTitle={entryTitle} />
|
return (
|
||||||
</TabsContent>
|
<Tabs defaultValue="references" className="w-full h-full flex flex-col">
|
||||||
<TabsContent className="pl-4 lg:pl-6 m-0" value="history">
|
<TabsList className="rounded-none shadow-none drop-shadow-none w-full bg-sidebar sticky top-0 z-10 flex-shrink-0">
|
||||||
<div className="w-full">
|
<TabsTrigger
|
||||||
<p>Last modified: {history.lastModified}</p>
|
value="references"
|
||||||
</div>
|
className="pl-2 justify-start text-sm font-semibold rounded-none shadow-none data-[state=active]:border-1 data-[state=active]:border-border py-4"
|
||||||
</TabsContent>
|
>
|
||||||
<TabsContent className="pl-4 lg:pl-6 m-0" value="metadata">
|
<Bookmark />
|
||||||
<div className="w-full">
|
References
|
||||||
<p>Size on disk: {metadata.fileSize} B</p>
|
</TabsTrigger>
|
||||||
</div>
|
|
||||||
</TabsContent>
|
<TabsTrigger
|
||||||
</div>
|
className="pl-2 justify-start text-sm font-semibold rounded-none shadow-none data-[state=active]:border-1 data-[state=active]:border-border py-4"
|
||||||
</Tabs>
|
value="metadata"
|
||||||
)
|
>
|
||||||
|
<Braces />
|
||||||
|
Metadata
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
<div className="flex-1 overflow-auto">
|
||||||
|
<TabsContent value="references" className="px-2 lg:px-2 m-0">
|
||||||
|
<EntryReferences tags={tags} backlinks={backlinks} outlinks={outlinks} />
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent className="px-2 lg:px-2 m-0" value="metadata">
|
||||||
|
<div className="w-full">
|
||||||
|
<div className="flex mb-4 gap-2">
|
||||||
|
<Badge variant="secondary">Last modified</Badge>
|
||||||
|
<div className="text-sm">
|
||||||
|
{convertDateFriendly(new Date(history.lastModified))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex mb-2 gap-2">
|
||||||
|
<Badge variant="secondary">Size on disk</Badge>
|
||||||
|
<div className="text-sm">{sizeInKb} Kb</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
</div>
|
||||||
|
</Tabs>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
220
src/index.css
220
src/index.css
|
|
@ -2,130 +2,130 @@
|
||||||
@import "tw-animate-css";
|
@import "tw-animate-css";
|
||||||
|
|
||||||
html {
|
html {
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
--radius-sm: calc(var(--radius) - 4px);
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
--radius-md: calc(var(--radius) - 2px);
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
--radius-lg: var(--radius);
|
--radius-lg: var(--radius);
|
||||||
--radius-xl: calc(var(--radius) + 4px);
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
--color-background: var(--background);
|
--color-background: var(--background);
|
||||||
--color-foreground: var(--foreground);
|
--color-foreground: var(--foreground);
|
||||||
--color-card: var(--card);
|
--color-card: var(--card);
|
||||||
--color-card-foreground: var(--card-foreground);
|
--color-card-foreground: var(--card-foreground);
|
||||||
--color-popover: var(--popover);
|
--color-popover: var(--popover);
|
||||||
--color-popover-foreground: var(--popover-foreground);
|
--color-popover-foreground: var(--popover-foreground);
|
||||||
--color-primary: var(--primary);
|
--color-primary: var(--primary);
|
||||||
--color-primary-foreground: var(--primary-foreground);
|
--color-primary-foreground: var(--primary-foreground);
|
||||||
--color-secondary: var(--secondary);
|
--color-secondary: var(--secondary);
|
||||||
--color-secondary-foreground: var(--secondary-foreground);
|
--color-secondary-foreground: var(--secondary-foreground);
|
||||||
--color-muted: var(--muted);
|
--color-muted: var(--muted);
|
||||||
--color-muted-foreground: var(--muted-foreground);
|
--color-muted-foreground: var(--muted-foreground);
|
||||||
--color-accent: var(--accent);
|
--color-accent: var(--accent);
|
||||||
--color-accent-foreground: var(--accent-foreground);
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
--color-destructive: var(--destructive);
|
--color-destructive: var(--destructive);
|
||||||
--color-border: var(--border);
|
--color-border: var(--border);
|
||||||
--color-input: var(--input);
|
--color-input: var(--input);
|
||||||
--color-ring: var(--ring);
|
--color-ring: var(--ring);
|
||||||
--color-chart-1: var(--chart-1);
|
--color-chart-1: var(--chart-1);
|
||||||
--color-chart-2: var(--chart-2);
|
--color-chart-2: var(--chart-2);
|
||||||
--color-chart-3: var(--chart-3);
|
--color-chart-3: var(--chart-3);
|
||||||
--color-chart-4: var(--chart-4);
|
--color-chart-4: var(--chart-4);
|
||||||
--color-chart-5: var(--chart-5);
|
--color-chart-5: var(--chart-5);
|
||||||
--color-sidebar: var(--sidebar);
|
--color-sidebar: var(--sidebar);
|
||||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||||
--color-sidebar-primary: var(--sidebar-primary);
|
--color-sidebar-primary: var(--sidebar-primary);
|
||||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||||
--color-sidebar-accent: var(--sidebar-accent);
|
--color-sidebar-accent: var(--sidebar-accent);
|
||||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
--color-sidebar-border: var(--sidebar-border);
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
--color-sidebar-ring: var(--sidebar-ring);
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--radius: 0.625rem;
|
--radius: 0.625rem;
|
||||||
--background: oklch(1 0 0);
|
--background: oklch(1 0 0);
|
||||||
--foreground: oklch(0.145 0 0);
|
--foreground: oklch(0.145 0 0);
|
||||||
--card: oklch(1 0 0);
|
--card: oklch(1 0 0);
|
||||||
--card-foreground: oklch(0.145 0 0);
|
--card-foreground: oklch(0.145 0 0);
|
||||||
--popover: oklch(1 0 0);
|
--popover: oklch(1 0 0);
|
||||||
--popover-foreground: oklch(0.145 0 0);
|
--popover-foreground: oklch(0.145 0 0);
|
||||||
--primary: oklch(0.205 0 0);
|
--primary: oklch(0.205 0 0);
|
||||||
--primary-foreground: oklch(0.985 0 0);
|
--primary-foreground: oklch(0.985 0 0);
|
||||||
--secondary: oklch(0.97 0 0);
|
--secondary: oklch(0.97 0 0);
|
||||||
--secondary-foreground: oklch(0.205 0 0);
|
--secondary-foreground: oklch(0.205 0 0);
|
||||||
--muted: oklch(0.97 0 0);
|
--muted: oklch(0.97 0 0);
|
||||||
--muted-foreground: oklch(0.556 0 0);
|
--muted-foreground: oklch(0.556 0 0);
|
||||||
--accent: oklch(0.97 0 0);
|
--accent: oklch(0.97 0 0);
|
||||||
--accent-foreground: oklch(0.205 0 0);
|
--accent-foreground: oklch(0.205 0 0);
|
||||||
--destructive: oklch(0.577 0.245 27.325);
|
--destructive: oklch(0.577 0.245 27.325);
|
||||||
--border: oklch(0.922 0 0);
|
--border: oklch(0.922 0 0);
|
||||||
--input: oklch(0.922 0 0);
|
--input: oklch(0.922 0 0);
|
||||||
--ring: oklch(0.708 0 0);
|
--ring: oklch(0.708 0 0);
|
||||||
--chart-1: oklch(0.646 0.222 41.116);
|
--chart-1: oklch(0.646 0.222 41.116);
|
||||||
--chart-2: oklch(0.6 0.118 184.704);
|
--chart-2: oklch(0.6 0.118 184.704);
|
||||||
--chart-3: oklch(0.398 0.07 227.392);
|
--chart-3: oklch(0.398 0.07 227.392);
|
||||||
--chart-4: oklch(0.828 0.189 84.429);
|
--chart-4: oklch(0.828 0.189 84.429);
|
||||||
--chart-5: oklch(0.769 0.188 70.08);
|
--chart-5: oklch(0.769 0.188 70.08);
|
||||||
--sidebar: oklch(0.985 0 0);
|
--sidebar: oklch(0.985 0 0);
|
||||||
--sidebar-foreground: oklch(0.145 0 0);
|
--sidebar-foreground: oklch(0.145 0 0);
|
||||||
--sidebar-primary: oklch(0.205 0 0);
|
--sidebar-primary: oklch(0.205 0 0);
|
||||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-accent: oklch(0.97 0 0);
|
--sidebar-accent: oklch(0.97 0 0);
|
||||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||||
--sidebar-border: oklch(0.922 0 0);
|
--sidebar-border: oklch(0.922 0 0);
|
||||||
--sidebar-ring: oklch(0.708 0 0);
|
--sidebar-ring: oklch(0.708 0 0);
|
||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: oklch(0.145 0 0);
|
--background: oklch(0.145 0 0);
|
||||||
--foreground: oklch(0.985 0 0);
|
--foreground: oklch(0.985 0 0);
|
||||||
--card: oklch(0.205 0 0);
|
--card: oklch(0.205 0 0);
|
||||||
--card-foreground: oklch(0.985 0 0);
|
--card-foreground: oklch(0.985 0 0);
|
||||||
--popover: oklch(0.205 0 0);
|
--popover: oklch(0.205 0 0);
|
||||||
--popover-foreground: oklch(0.985 0 0);
|
--popover-foreground: oklch(0.985 0 0);
|
||||||
--primary: oklch(0.922 0 0);
|
--primary: oklch(0.922 0 0);
|
||||||
--primary-foreground: oklch(0.205 0 0);
|
--primary-foreground: oklch(0.205 0 0);
|
||||||
--secondary: oklch(0.269 0 0);
|
--secondary: oklch(0.269 0 0);
|
||||||
--secondary-foreground: oklch(0.985 0 0);
|
--secondary-foreground: oklch(0.985 0 0);
|
||||||
--muted: oklch(0.269 0 0);
|
--muted: oklch(0.269 0 0);
|
||||||
--muted-foreground: oklch(0.708 0 0);
|
--muted-foreground: oklch(0.708 0 0);
|
||||||
--accent: oklch(0.269 0 0);
|
--accent: oklch(0.269 0 0);
|
||||||
--accent-foreground: oklch(0.985 0 0);
|
--accent-foreground: oklch(0.985 0 0);
|
||||||
--destructive: oklch(0.704 0.191 22.216);
|
--destructive: oklch(0.704 0.191 22.216);
|
||||||
--border: oklch(1 0 0 / 10%);
|
--border: oklch(1 0 0 / 10%);
|
||||||
--input: oklch(1 0 0 / 15%);
|
--input: oklch(1 0 0 / 15%);
|
||||||
--ring: oklch(0.556 0 0);
|
--ring: oklch(0.556 0 0);
|
||||||
--chart-1: oklch(0.488 0.243 264.376);
|
--chart-1: oklch(0.488 0.243 264.376);
|
||||||
--chart-2: oklch(0.696 0.17 162.48);
|
--chart-2: oklch(0.696 0.17 162.48);
|
||||||
--chart-3: oklch(0.769 0.188 70.08);
|
--chart-3: oklch(0.769 0.188 70.08);
|
||||||
--chart-4: oklch(0.627 0.265 303.9);
|
--chart-4: oklch(0.627 0.265 303.9);
|
||||||
--chart-5: oklch(0.645 0.246 16.439);
|
--chart-5: oklch(0.645 0.246 16.439);
|
||||||
--sidebar: oklch(0.205 0 0);
|
--sidebar: oklch(0.205 0 0);
|
||||||
--sidebar-foreground: oklch(0.985 0 0);
|
--sidebar-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-accent: oklch(0.269 0 0);
|
--sidebar-accent: oklch(0.269 0 0);
|
||||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||||
--sidebar-border: oklch(1 0 0 / 10%);
|
--sidebar-border: oklch(1 0 0 / 10%);
|
||||||
--sidebar-ring: oklch(0.556 0 0);
|
--sidebar-ring: oklch(0.556 0 0);
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@apply border-border outline-ring/50;
|
@apply border-border outline-ring/50;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-family: "Inter", sans-serif;
|
font-family: "IBM Plex Sans", sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,28 @@ import { clsx, type ClassValue } from "clsx"
|
||||||
import { twMerge } from "tailwind-merge"
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
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}`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue