switch to monorepo structure

This commit is contained in:
2025-05-06 11:00:35 +01:00
parent e1f927ad27
commit 07b7f1b51f
63 changed files with 2440 additions and 1011 deletions

View File

@@ -0,0 +1,111 @@
/* eslint-disable */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
// Import Routes
import { Route as rootRoute } from "./__root"
import { Route as BookmarksImport } from "./bookmarks"
import { Route as IndexImport } from "./index"
// Create/Update Routes
const BookmarksRoute = BookmarksImport.update({
id: "/bookmarks",
path: "/bookmarks",
getParentRoute: () => rootRoute,
} as any)
const IndexRoute = IndexImport.update({
id: "/",
path: "/",
getParentRoute: () => rootRoute,
} as any)
// Populate the FileRoutesByPath interface
declare module "@tanstack/react-router" {
interface FileRoutesByPath {
"/": {
id: "/"
path: "/"
fullPath: "/"
preLoaderRoute: typeof IndexImport
parentRoute: typeof rootRoute
}
"/bookmarks": {
id: "/bookmarks"
path: "/bookmarks"
fullPath: "/bookmarks"
preLoaderRoute: typeof BookmarksImport
parentRoute: typeof rootRoute
}
}
}
// Create and export the route tree
export interface FileRoutesByFullPath {
"/": typeof IndexRoute
"/bookmarks": typeof BookmarksRoute
}
export interface FileRoutesByTo {
"/": typeof IndexRoute
"/bookmarks": typeof BookmarksRoute
}
export interface FileRoutesById {
__root__: typeof rootRoute
"/": typeof IndexRoute
"/bookmarks": typeof BookmarksRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: "/" | "/bookmarks"
fileRoutesByTo: FileRoutesByTo
to: "/" | "/bookmarks"
id: "__root__" | "/" | "/bookmarks"
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
BookmarksRoute: typeof BookmarksRoute
}
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
BookmarksRoute: BookmarksRoute,
}
export const routeTree = rootRoute
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
/* ROUTE_MANIFEST_START
{
"routes": {
"__root__": {
"filePath": "__root.tsx",
"children": [
"/",
"/bookmarks"
]
},
"/": {
"filePath": "index.tsx"
},
"/bookmarks": {
"filePath": "bookmarks.tsx"
}
}
}
ROUTE_MANIFEST_END */

View File

@@ -0,0 +1,15 @@
import { Outlet, createRootRoute } from "@tanstack/react-router"
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"
function Root() {
return (
<>
<Outlet />
<TanStackRouterDevtools />
</>
)
}
export const Route = createRootRoute({
component: Root,
})

View File

@@ -0,0 +1,306 @@
import type { LinkBookmark } from "@markone/core/bookmark"
import { createFileRoute } from "@tanstack/react-router"
import clsx from "clsx"
import { useEffect } from "react"
import { create } from "zustand"
import { Button } from "~/components/button"
const testBookmarks: LinkBookmark[] = [
{
kind: "link",
id: "1",
title: "Running a Docker container as a non-root user",
url: "https://test.website.com/article/123",
},
{
kind: "link",
id: "2",
title: "Running a Docker container as a non-root user",
url: "https://test.website.com/article/123",
},
]
const LAYOUT_MODE = {
popup: "popup",
sideBySide: "side-by-side",
} as const
type LayoutMode = (typeof LAYOUT_MODE)[keyof typeof LAYOUT_MODE]
interface BookmarkPageState {
bookmarks: LinkBookmark[]
selectedBookmarkIndex: number
isBookmarkItemExpanded: boolean
isBookmarkPreviewOpened: boolean
layoutMode: LayoutMode
setBookmarkItemExpanded: (isExpanded: boolean) => void
setBookmarkPreviewOpened: (isOpened: boolean) => void
setLayoutMode: (mode: LayoutMode) => void
selectBookmarkAt: (index: number) => void
}
const useBookmarkPageStore = create<BookmarkPageState>()((set, get) => ({
bookmarks: testBookmarks,
selectedBookmarkIndex: 0,
isBookmarkItemExpanded: false,
isBookmarkPreviewOpened: false,
layoutMode: LAYOUT_MODE.popup,
setBookmarkItemExpanded(isExpanded: boolean) {
set({ isBookmarkItemExpanded: isExpanded })
},
setBookmarkPreviewOpened(isOpened: boolean) {
set({ isBookmarkPreviewOpened: isOpened })
},
setLayoutMode(mode: LayoutMode) {
set({ layoutMode: mode })
},
selectBookmarkAt(index: number) {
const bookmarks = get().bookmarks
if (index >= 0 && index < bookmarks.length) {
set({ selectedBookmarkIndex: index })
}
},
}))
function Page() {
const setLayoutMode = useBookmarkPageStore((state) => state.setLayoutMode)
useEffect(() => {
function onKeyDown(event: KeyboardEvent) {
const state = useBookmarkPageStore.getState()
switch (event.key) {
case "ArrowDown":
state.selectBookmarkAt(state.selectedBookmarkIndex + 1)
break
case "ArrowUp":
state.selectBookmarkAt(state.selectedBookmarkIndex - 1)
break
case "ArrowLeft":
state.setBookmarkItemExpanded(false)
break
case "ArrowRight":
state.setBookmarkItemExpanded(true)
break
default:
break
}
}
window.addEventListener("keydown", onKeyDown)
return () => {
window.removeEventListener("keydown", onKeyDown)
}
}, [])
useEffect(() => {
function mediaQueryListener(this: MediaQueryList) {
if (this.matches) {
setLayoutMode(LAYOUT_MODE.sideBySide)
} else {
setLayoutMode(LAYOUT_MODE.popup)
}
}
const q = window.matchMedia("(width >= 64rem)")
q.addEventListener("change", mediaQueryListener)
mediaQueryListener.call(q)
return () => {
q.removeEventListener("change", mediaQueryListener)
}
}, [setLayoutMode])
return (
<div className="flex justify-center h-full">
<Main>
<div className="flex flex-col md:flex-row justify-center py-16 lg:py-32 ">
<header className="mb-4 md:mb-0 md:mr-16 text-start">
<h1 className="font-bold text-start">
<span className="invisible md:hidden">&gt;&nbsp;</span>
YOUR BOOKMARKS
</h1>
</header>
<div className="flex flex-col container max-w-2xl -mt-2">
{testBookmarks.map((bookmark, i) => (
<BookmarkListItem key={bookmark.id} index={i} bookmark={bookmark} />
))}
</div>
</div>
<BookmarkPreview />
</Main>
</div>
)
}
function Main({ children }: React.PropsWithChildren) {
const isPreviewOpened = useBookmarkPageStore((state) => state.isBookmarkPreviewOpened)
const layoutMode = useBookmarkPageStore((state) => state.layoutMode)
return (
<main
className={clsx(
"px-4 lg:px-8 2xl:px-0 grid flex justify-center relative w-full",
isPreviewOpened && layoutMode === LAYOUT_MODE.sideBySide ? "grid-cols-2" : "grid-cols-1",
)}
>
{children}
</main>
)
}
function BookmarkPreview() {
const isVisible = useBookmarkPageStore((state) => state.isBookmarkPreviewOpened)
const layoutMode = useBookmarkPageStore((state) => state.layoutMode)
if (!isVisible) {
return null
}
return (
<div
className={clsx(
"h-screen flex justify-center items-center border-l border-stone-700 dark:border-stone-300 flex dark:bg-stone-900",
{
"absolute inset-0 border-l-0": layoutMode === LAYOUT_MODE.popup,
},
)}
>
<p>Content here</p>
</div>
)
}
function BookmarkListItem({ bookmark, index }: { bookmark: LinkBookmark; index: number }) {
const url = new URL(bookmark.url)
const selectedBookmark = useBookmarkPageStore((state) => state.bookmarks[state.selectedBookmarkIndex])
const isSelected = selectedBookmark.id === bookmark.id
const isBookmarkItemExpanded = useBookmarkPageStore((state) => state.isBookmarkItemExpanded)
const setBookmarkItemExpanded = useBookmarkPageStore((state) => state.setBookmarkItemExpanded)
const selectBookmarkAt = useBookmarkPageStore((state) => state.selectBookmarkAt)
const setBookmarkPreviewOpened = useBookmarkPageStore((state) => state.setBookmarkPreviewOpened)
function expandOrOpenPreview() {
setBookmarkItemExpanded(true)
if (useBookmarkPageStore.getState().layoutMode === LAYOUT_MODE.sideBySide) {
console.log(useBookmarkPageStore.getState().layoutMode)
setBookmarkPreviewOpened(true)
}
}
return (
<div
className={clsx("group flex flex-row justify-start py-2", {
"bg-teal-600 text-stone-100": isBookmarkItemExpanded && isSelected,
"text-teal-600": isSelected && !isBookmarkItemExpanded,
})}
onMouseEnter={() => {
if (!isBookmarkItemExpanded) {
selectBookmarkAt(index)
}
}}
>
<button
type="button"
disabled={!isSelected}
className={clsx("select-none flex items-start font-bold hover:bg-teal-600 hover:text-stone-100", {
invisible: !isSelected,
})}
onClick={() => {
setBookmarkItemExpanded(!isBookmarkItemExpanded)
setBookmarkPreviewOpened(false)
}}
>
<span className="sr-only">Options for this bookmark</span>
<span>&nbsp;</span>
<span className={isBookmarkItemExpanded ? "rotate-90" : ""}>&gt;</span>
<span>&nbsp;</span>
</button>
<div className="flex flex-col w-full">
<button type="button" className="text-start font-bold" onClick={expandOrOpenPreview}>
{bookmark.title}
</button>
<p className="opacity-80 text-sm">{url.host}</p>
{isBookmarkItemExpanded && isSelected ? (
<div className="flex flex-col space-y-1 md:flex-row md:space-y-0 md:space-x-2 items-end justify-between pt-2">
<p className="text-sm">#dev #devops #devops #devops #devops #devops #devops</p>
<div className="flex space-x-2">
<OpenBookmarkPreviewButton />
<Button className="text-sm">
<span className="underline">E</span>dit
</Button>
<Button className="text-sm">
<span className="underline">D</span>elete
</Button>
<span className="-ml-2">&nbsp;</span>
</div>
</div>
) : null}
</div>
</div>
)
}
function OpenBookmarkPreviewButton() {
const isBookmarkPreviewOpened = useBookmarkPageStore((state) => state.isBookmarkPreviewOpened)
const setBookmarkPreviewOpened = useBookmarkPageStore((state) => state.setBookmarkPreviewOpened)
const setBookmarkItemExpanded = useBookmarkPageStore((state) => state.setBookmarkItemExpanded)
useEffect(() => {
function onKeyDown(event: KeyboardEvent) {
if (isBookmarkPreviewOpened && event.key === "c") {
closePreview()
} else if (!isBookmarkPreviewOpened && event.key === "o") {
openPreview()
}
}
window.addEventListener("keydown", onKeyDown)
return () => {
window.removeEventListener("keydown", onKeyDown)
}
}, [isBookmarkPreviewOpened])
function closePreview() {
setBookmarkPreviewOpened(false)
setBookmarkItemExpanded(false)
}
function openPreview() {
setBookmarkPreviewOpened(true)
}
return (
<Button
className="text-sm"
onClick={() => {
if (isBookmarkPreviewOpened) {
closePreview()
} else {
openPreview()
}
}}
>
{isBookmarkPreviewOpened ? (
<>
<span className="underline">C</span>lose
</>
) : (
<>
<span className="underline">O</span>pen
</>
)}
</Button>
)
}
export const Route = createFileRoute("/bookmarks")({
component: Page,
})

View File

@@ -0,0 +1,75 @@
import { DEMO_USER } from "@markone/core/user"
import { createFileRoute, useNavigate } from "@tanstack/react-router"
import { useLogin } from "~/auth"
import { Link } from "~/components/link"
function Index() {
return (
<main className="p-48 flex flex-row items-start justify-center space-x-24">
<div className="flex flex-col items-start">
<h1 className="text-2xl">
<span className="font-bold">MARKONE</span>
<br />
</h1>
<p className="pb-4 text-2xl uppercase">BOOKMARK MANAGER</p>
<div className="flex flex-col text-lg">
<Link>LOGIN</Link>
<Link>SIGN UP</Link>
<DemoButton />
</div>
</div>
<div>
<p>
<strong>WHAT IS MARKONE?</strong>
<br />
MARKONE is a local-first, self-hostable bookmark manager.
</p>
<ul className="px-2 pt-2">
<li>* Curate interesting websites you find online, and let MARKONE archive them.</li>
<li>* Reference your saved bookmarks anytime, even when offline.</li>
<li>* Share your collections to others with a permalink.</li>
</ul>
<br />
<p>
<strong>WHERE IS MARKONE?</strong>
<br />
MARKONE is available as a web app and a browser extension for now.
</p>
<br />
<p>
<strong>TECHNICAL STUFF</strong>
<br />
Source code, as well as hosting guide for MARKONE is <Link href="https://github.com/">available here</Link>.
<br />
</p>
</div>
</main>
)
}
function DemoButton() {
const loginMutation = useLogin()
const navigate = useNavigate()
async function startDemo() {
await loginMutation.mutateAsync({
username: DEMO_USER.username,
password: DEMO_USER.unhashedPassword,
})
navigate({ to: "/bookmarks" })
}
return (
<button
type="button"
onClick={startDemo}
className="underline active:bg-stone-700 dark:active:bg-stone-200 dark:active:text-stone-800 active:text-stone-200 text-left"
>
DEMO
</button>
)
}
export const Route = createFileRoute("/")({
component: Index,
})