From 0bcfc9d74a5ba6e208df983e052e6eab200a89c7 Mon Sep 17 00:00:00 2001 From: monoid Date: Tue, 8 Oct 2024 02:43:00 +0900 Subject: [PATCH 1/3] name normalization --- packages/server/src/SettingConfig.ts | 2 +- packages/server/src/database.ts | 4 +--- packages/server/src/diff/content_list.ts | 2 +- packages/server/src/diff/diff.ts | 6 +++--- packages/server/src/diff/router.ts | 8 ++++---- packages/server/src/diff/watcher.ts | 2 +- packages/server/src/permission/permission.ts | 4 ++-- packages/server/src/util/oshash.ts | 10 ++++++---- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/server/src/SettingConfig.ts b/packages/server/src/SettingConfig.ts index a59944e..fe08b33 100644 --- a/packages/server/src/SettingConfig.ts +++ b/packages/server/src/SettingConfig.ts @@ -1,6 +1,6 @@ import { randomBytes } from "node:crypto"; import { existsSync, readFileSync, writeFileSync } from "node:fs"; -import type { Permission } from "./permission/permission"; +import type { Permission } from "./permission/permission.ts"; export interface SettingConfig { /** diff --git a/packages/server/src/database.ts b/packages/server/src/database.ts index 997fe9f..725d947 100644 --- a/packages/server/src/database.ts +++ b/packages/server/src/database.ts @@ -1,6 +1,4 @@ -import { existsSync } from "node:fs"; -import { get_setting } from "./SettingConfig"; -import { getKysely } from "./db/kysely"; +import { getKysely } from "./db/kysely.ts"; export async function connectDB() { const kysely = getKysely(); diff --git a/packages/server/src/diff/content_list.ts b/packages/server/src/diff/content_list.ts index 9e3f7f8..600a581 100644 --- a/packages/server/src/diff/content_list.ts +++ b/packages/server/src/diff/content_list.ts @@ -1,4 +1,4 @@ -import type { ContentFile } from "../content/mod"; +import type { ContentFile } from "../content/mod.ts"; export class ContentList { /** path map */ diff --git a/packages/server/src/diff/diff.ts b/packages/server/src/diff/diff.ts index 3f69f50..eeea3cb 100644 --- a/packages/server/src/diff/diff.ts +++ b/packages/server/src/diff/diff.ts @@ -1,7 +1,7 @@ import asyncPool from "tiny-async-pool"; -import type { DocumentAccessor } from "../model/doc"; -import { ContentDiffHandler } from "./content_handler"; -import type { IDiffWatcher } from "./watcher"; +import type { DocumentAccessor } from "../model/doc.ts"; +import { ContentDiffHandler } from "./content_handler.ts"; +import type { IDiffWatcher } from "./watcher.ts"; export class DiffManager { watching: { [content_type: string]: ContentDiffHandler }; diff --git a/packages/server/src/diff/router.ts b/packages/server/src/diff/router.ts index 41ab912..c3be4f2 100644 --- a/packages/server/src/diff/router.ts +++ b/packages/server/src/diff/router.ts @@ -1,9 +1,9 @@ import type Koa from "koa"; import Router from "koa-router"; -import type { ContentFile } from "../content/mod"; -import { AdminOnlyMiddleware } from "../permission/permission"; -import { sendError } from "../route/error_handler"; -import type { DiffManager } from "./diff"; +import type { ContentFile } from "../content/mod.ts"; +import { AdminOnlyMiddleware } from "../permission/permission.ts"; +import { sendError } from "../route/error_handler.ts"; +import type { DiffManager } from "./diff.ts"; function content_file_to_return(x: ContentFile) { return { path: x.path, type: x.type }; diff --git a/packages/server/src/diff/watcher.ts b/packages/server/src/diff/watcher.ts index 70b01c8..62e306a 100644 --- a/packages/server/src/diff/watcher.ts +++ b/packages/server/src/diff/watcher.ts @@ -2,7 +2,7 @@ import type event from "node:events"; import { FSWatcher, watch } from "node:fs"; import { promises } from "node:fs"; import { join } from "node:path"; -import type { DocumentAccessor } from "../model/doc"; +import type { DocumentAccessor } from "../model/doc.ts"; const readdir = promises.readdir; diff --git a/packages/server/src/permission/permission.ts b/packages/server/src/permission/permission.ts index 8229316..4632ef6 100644 --- a/packages/server/src/permission/permission.ts +++ b/packages/server/src/permission/permission.ts @@ -1,6 +1,6 @@ import type Koa from "koa"; -import type { UserState } from "../login"; -import { sendError } from "../route/error_handler"; +import type { UserState } from "../login.ts"; +import { sendError } from "../route/error_handler.ts"; export enum Permission { // ======== diff --git a/packages/server/src/util/oshash.ts b/packages/server/src/util/oshash.ts index 3584e0a..92a6a45 100644 --- a/packages/server/src/util/oshash.ts +++ b/packages/server/src/util/oshash.ts @@ -24,19 +24,21 @@ export async function oshash( } // read first and last chunk - const firstChunk = Buffer.alloc(chunkSize); + const firstChunk = new Uint8Array(chunkSize); await fd.read(firstChunk, 0, chunkSize, 0); - const lastChunk = Buffer.alloc(chunkSize); + const lastChunk = new Uint8Array(chunkSize); await fd.read(lastChunk, 0, chunkSize, st.size - chunkSize); // iterate over first and last chunk. // for each uint64_t, add it to the hash. + const firstChunkView = new DataView(firstChunk.buffer); for (let i = 0; i < chunkSize; i += 8){ - hash += firstChunk.readBigUInt64LE(i); + hash += firstChunkView.getBigUint64(i, true); // prevent overflow hash = (hash & 0xFFFFFFFFFFFFFFFFn); } + const lastChunkView = new DataView(lastChunk.buffer); for (let i = 0; i < chunkSize; i += 8){ - hash += lastChunk.readBigUInt64LE(i); + hash += lastChunkView.getBigUint64(i, true); // prevent overflow hash = (hash & 0xFFFFFFFFFFFFFFFFn); } -- 2.30.2 From b4bae924ace093257152031b06d9f97c7725597b Mon Sep 17 00:00:00 2001 From: monoid Date: Tue, 8 Oct 2024 21:48:28 +0900 Subject: [PATCH 2/3] feat: tags page --- packages/client/package.json | 9 +- packages/client/src/App.tsx | 2 + packages/client/src/components/layout/nav.tsx | 10 +- packages/client/src/components/ui/chart.tsx | 368 +++++++++ .../client/src/components/ui/progress.tsx | 26 + .../client/src/components/ui/scroll-area.tsx | 46 ++ packages/client/src/components/ui/select.tsx | 162 ++++ packages/client/src/components/ui/tabs.tsx | 53 ++ packages/client/src/hook/useSearchGallery.ts | 2 +- packages/client/src/hook/useTags.ts | 3 +- packages/client/src/page/reader/comicPage.tsx | 2 +- packages/client/src/page/tagsPage.tsx | 93 +++ packages/server/migrations/initial.ts | 122 +-- packages/server/src/db/tag.ts | 8 +- packages/server/src/model/tag.ts | 3 +- pnpm-lock.yaml | 700 ++++++++++++++++++ 16 files changed, 1547 insertions(+), 62 deletions(-) create mode 100644 packages/client/src/components/ui/chart.tsx create mode 100644 packages/client/src/components/ui/progress.tsx create mode 100644 packages/client/src/components/ui/scroll-area.tsx create mode 100644 packages/client/src/components/ui/select.tsx create mode 100644 packages/client/src/components/ui/tabs.tsx create mode 100644 packages/client/src/page/tagsPage.tsx diff --git a/packages/client/package.json b/packages/client/package.json index e10751f..0f2b728 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -14,18 +14,24 @@ "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.2", + "@radix-ui/react-progress": "^1.1.0", "@radix-ui/react-radio-group": "^1.2.1", + "@radix-ui/react-scroll-area": "^1.2.0", + "@radix-ui/react-select": "^2.1.2", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-tabs": "^1.1.1", "@radix-ui/react-tooltip": "^1.1.3", "@tanstack/react-virtual": "^3.10.8", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "dbtype": "workspace:dbtype", "jotai": "^2.10.0", + "lucide-react": "^0.451.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-resizable-panels": "^2.1.4", + "recharts": "^2.12.7", "swr": "^2.2.5", "tailwind-merge": "^2.5.3", "tailwindcss-animate": "^1.0.7", @@ -47,6 +53,7 @@ "shadcn-ui": "^0.8.0", "tailwindcss": "^3.4.13", "typescript": "^5.6.2", - "vite": "^5.4.8" + "vite": "^5.4.8", + "vitest": "^2.1.2" } } diff --git a/packages/client/src/App.tsx b/packages/client/src/App.tsx index 45a6fe5..78e992f 100644 --- a/packages/client/src/App.tsx +++ b/packages/client/src/App.tsx @@ -15,6 +15,7 @@ import ContentInfoPage from "@/page/contentInfoPage.tsx"; import SettingPage from "@/page/settingPage.tsx"; import ComicPage from "@/page/reader/comicPage.tsx"; import DifferencePage from "./page/differencePage.tsx"; +import TagsPage from "./page/tagsPage.tsx"; const App = () => { const { isDarkMode } = useTernaryDarkMode(); @@ -40,6 +41,7 @@ const App = () => { + diff --git a/packages/client/src/components/layout/nav.tsx b/packages/client/src/components/layout/nav.tsx index 88f1c37..89da992 100644 --- a/packages/client/src/components/layout/nav.tsx +++ b/packages/client/src/components/layout/nav.tsx @@ -1,5 +1,5 @@ import { Link } from "wouter" -import { MagnifyingGlassIcon, GearIcon, ActivityLogIcon, ArchiveIcon, PersonIcon } from "@radix-ui/react-icons" +import { Search, Settings, Tags, ArchiveIcon, UserIcon } from "lucide-react" import { Button, buttonVariants } from "@/components/ui/button.tsx" import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip.tsx" import { useLogin } from "@/state/user.ts"; @@ -66,13 +66,13 @@ export function NavList() { return } \ No newline at end of file diff --git a/packages/client/src/components/ui/chart.tsx b/packages/client/src/components/ui/chart.tsx new file mode 100644 index 0000000..b58b494 --- /dev/null +++ b/packages/client/src/components/ui/chart.tsx @@ -0,0 +1,368 @@ +import * as React from "react" +import * as RechartsPrimitive from "recharts" +import { + NameType, + Payload, + ValueType, +} from "recharts/types/component/DefaultTooltipContent" + +import { cn } from "@/lib/utils" + +// Format: { THEME_NAME: CSS_SELECTOR } +const THEMES = { light: "", dark: ".dark" } as const + +export type ChartConfig = { + [k in string]: { + label?: React.ReactNode + icon?: React.ComponentType + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ) +} + +type ChartContextProps = { + config: ChartConfig +} + +const ChartContext = React.createContext(null) + +function useChart() { + const context = React.useContext(ChartContext) + + if (!context) { + throw new Error("useChart must be used within a ") + } + + return context +} + +const ChartContainer = React.forwardRef< + HTMLDivElement, + React.ComponentProps<"div"> & { + config: ChartConfig + children: React.ComponentProps< + typeof RechartsPrimitive.ResponsiveContainer + >["children"] + } +>(({ id, className, children, config, ...props }, ref) => { + const uniqueId = React.useId() + const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` + + return ( + +
+ + + {children} + +
+
+ ) +}) +ChartContainer.displayName = "Chart" + +const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { + const colorConfig = Object.entries(config).filter( + ([_, config]) => config.theme || config.color + ) + + if (!colorConfig.length) { + return null + } + + return ( +