diff --git a/auto-imports.app.d.ts b/auto-imports.app.d.ts new file mode 100644 index 0000000..6910f92 --- /dev/null +++ b/auto-imports.app.d.ts @@ -0,0 +1,94 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// noinspection JSUnusedGlobalSymbols +// Generated by unplugin-auto-import +// biome-ignore lint: disable +export {} +declare global { + const $: typeof import('clsx')['default'] + const Author: typeof import('./shared/consts')['Author'] + const Homepage: typeof import('./shared/consts')['Homepage'] + const Interval: typeof import('./shared/consts')['Interval'] + const TTL: typeof import('./shared/consts')['TTL'] + const Timer: typeof import('./src/utils/index')['Timer'] + const Version: typeof import('./shared/consts')['Version'] + const atom: typeof import('jotai')['atom'] + const atomFamily: typeof import('jotai/utils')['atomFamily'] + const atomWithDefault: typeof import('jotai/utils')['atomWithDefault'] + const atomWithHash: typeof import('jotai/utils')['atomWithHash'] + const atomWithObservable: typeof import('jotai/utils')['atomWithObservable'] + const atomWithReducer: typeof import('jotai/utils')['atomWithReducer'] + const atomWithReset: typeof import('jotai/utils')['atomWithReset'] + const atomWithStorage: typeof import('jotai/utils')['atomWithStorage'] + const columns: typeof import('./shared/metadata')['columns'] + const createJSONStorage: typeof import('jotai/utils')['createJSONStorage'] + const createRef: typeof import('react')['createRef'] + const currentColumnIDAtom: typeof import('./src/atoms/index')['currentColumnIDAtom'] + const currentSourcesAtom: typeof import('./src/atoms/index')['currentSourcesAtom'] + const delay: typeof import('./shared/utils')['delay'] + const downloadMetadata: typeof import('./src/hooks/useSync')['downloadMetadata'] + const fixedColumnIds: typeof import('./shared/metadata')['fixedColumnIds'] + const focusSourcesAtom: typeof import('./src/atoms/index')['focusSourcesAtom'] + const forwardRef: typeof import('react')['forwardRef'] + const freezeAtom: typeof import('jotai/utils')['freezeAtom'] + const freezeAtomCreator: typeof import('jotai/utils')['freezeAtomCreator'] + const goToTopAtom: typeof import('./src/atoms/index')['goToTopAtom'] + const hiddenColumns: typeof import('./shared/metadata')['hiddenColumns'] + const lazy: typeof import('react')['lazy'] + const loadable: typeof import('jotai/utils')['loadable'] + const memo: typeof import('react')['memo'] + const metadata: typeof import('./shared/metadata')['metadata'] + const originSources: typeof import('./shared/sources')['originSources'] + const preprocessMetadata: typeof import('./src/atoms/primitiveMetadataAtom')['preprocessMetadata'] + const primitiveMetadataAtom: typeof import('./src/atoms/primitiveMetadataAtom')['primitiveMetadataAtom'] + const projectDir: typeof import('./shared/dir')['projectDir'] + const randomItem: typeof import('./shared/utils')['randomItem'] + const randomUUID: typeof import('./shared/utils')['randomUUID'] + const refetchSourcesAtom: typeof import('./src/atoms/index')['refetchSourcesAtom'] + const relativeTime: typeof import('./shared/utils')['relativeTime'] + const safeParseString: typeof import('./src/utils/index')['safeParseString'] + const selectAtom: typeof import('jotai/utils')['selectAtom'] + const sources: typeof import('./shared/sources')['sources'] + const splitAtom: typeof import('jotai/utils')['splitAtom'] + const startTransition: typeof import('react')['startTransition'] + const toastAtom: typeof import('./src/hooks/useToast')['toastAtom'] + const typeSafeObjectEntries: typeof import('./shared/type.util')['typeSafeObjectEntries'] + const typeSafeObjectFromEntries: typeof import('./shared/type.util')['typeSafeObjectFromEntries'] + const typeSafeObjectValues: typeof import('./shared/type.util')['typeSafeObjectValues'] + const uploadMetadata: typeof import('./src/hooks/useSync')['uploadMetadata'] + const useAtom: typeof import('jotai')['useAtom'] + const useAtomCallback: typeof import('jotai/utils')['useAtomCallback'] + const useAtomValue: typeof import('jotai')['useAtomValue'] + const useCallback: typeof import('react')['useCallback'] + const useContext: typeof import('react')['useContext'] + const useDark: typeof import('./src/hooks/useDark')['useDark'] + const useDebugValue: typeof import('react')['useDebugValue'] + const useDeferredValue: typeof import('react')['useDeferredValue'] + const useEffect: typeof import('react')['useEffect'] + const useFocus: typeof import('./src/hooks/useFocus')['useFocus'] + const useFocusWith: typeof import('./src/hooks/useFocus')['useFocusWith'] + const useHydrateAtoms: typeof import('jotai/utils')['useHydrateAtoms'] + const useId: typeof import('react')['useId'] + const useImperativeHandle: typeof import('react')['useImperativeHandle'] + const useInsertionEffect: typeof import('react')['useInsertionEffect'] + const useLayoutEffect: typeof import('react')['useLayoutEffect'] + const useLogin: typeof import('./src/hooks/useLogin')['useLogin'] + const useMemo: typeof import('react')['useMemo'] + const useOnReload: typeof import('./src/hooks/useOnReload')['useOnReload'] + const usePWA: typeof import('./src/hooks/usePWA')['usePWA'] + const useReducer: typeof import('react')['useReducer'] + const useReducerAtom: typeof import('jotai/utils')['useReducerAtom'] + const useRef: typeof import('react')['useRef'] + const useRelativeTime: typeof import('./src/hooks/useRelativeTime')['useRelativeTime'] + const useResetAtom: typeof import('jotai/utils')['useResetAtom'] + const useSearchBar: typeof import('./src/hooks/useSearch')['useSearchBar'] + const useSetAtom: typeof import('jotai')['useSetAtom'] + const useState: typeof import('react')['useState'] + const useSync: typeof import('./src/hooks/useSync')['useSync'] + const useSyncExternalStore: typeof import('react')['useSyncExternalStore'] + const useToast: typeof import('./src/hooks/useToast')['useToast'] + const useTransition: typeof import('react')['useTransition'] + const verifyPrimitiveMetadata: typeof import('./shared/verify')['verifyPrimitiveMetadata'] + const waitForAll: typeof import('jotai/utils')['waitForAll'] +} diff --git a/nitro.config.ts b/nitro.config.ts new file mode 100644 index 0000000..ed103e3 --- /dev/null +++ b/nitro.config.ts @@ -0,0 +1,55 @@ +import process from "node:process" +import { join } from "node:path" +import viteNitro from "vite-plugin-with-nitro" +import { RollopGlob } from "./tools/rollup-glob" +import { projectDir } from "./shared/dir" + +const nitroOption: Parameters[0] = { + experimental: { + database: true, + }, + rollupConfig: { + plugins: [RollopGlob()], + }, + sourceMap: false, + database: { + default: { + connector: "sqlite", + }, + }, + imports: { + dirs: ["server/utils", "shared"], + }, + preset: "node-server", + alias: { + "@shared": join(projectDir, "shared"), + "#": join(projectDir, "server"), + }, +} + +if (process.env.VERCEL) { + nitroOption.preset = "vercel-edge" + // You can use other online database, do it yourself. For more info: https://db0.unjs.io/connectors + nitroOption.database = undefined +} else if (process.env.CF_PAGES) { + nitroOption.preset = "cloudflare-pages" + nitroOption.database = { + default: { + connector: "cloudflare-d1", + options: { + bindingName: "NEWSNOW_DEV", + }, + }, + } +} else if (process.env.BUN) { + nitroOption.preset = "bun" + nitroOption.database = { + default: { + connector: "bun-sqlite", + }, + } +} + +export default function () { + return viteNitro(nitroOption) +} diff --git a/package.json b/package.json index 169ae66..922325b 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,6 @@ "type": "module", "version": "0.0.8", "private": true, - "commit-id": "", "packageManager": "pnpm@9.12.1", "author": { "url": "https://github.com/ourongxing/", @@ -94,7 +93,6 @@ "vite": "^5.4.8", "vite-plugin-pwa": "^0.20.5", "vite-plugin-with-nitro": "0.0.3", - "vite-tsconfig-paths": "^5.0.1", "vitest": "^2.1.2", "workbox-window": "^7.1.0", "wrangler": "^3.80.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fdd4cef..0c69781 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -216,9 +216,6 @@ importers: vite-plugin-with-nitro: specifier: 0.0.3 version: 0.0.3(better-sqlite3@11.5.0)(idb-keyval@6.2.1)(typescript@5.6.3)(vite@5.4.10(@types/node@22.7.9)(terser@5.36.0)) - vite-tsconfig-paths: - specifier: ^5.0.1 - version: 5.0.1(typescript@5.6.3)(vite@5.4.10(@types/node@22.7.9)(terser@5.36.0)) vitest: specifier: ^2.1.2 version: 2.1.3(@types/node@22.7.9)(terser@5.36.0) @@ -3914,9 +3911,6 @@ packages: resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==} engines: {node: '>=18'} - globrex@0.1.2: - resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - goober@2.1.16: resolution: {integrity: sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==} peerDependencies: @@ -5666,16 +5660,6 @@ packages: ts-pattern@5.5.0: resolution: {integrity: sha512-jqbIpTsa/KKTJYWgPNsFNbLVpwCgzXfFJ1ukNn4I8hMwyQzHMJnk/BqWzggB0xpkILuKzaO/aMYhS0SkaJyKXg==} - tsconfck@3.1.4: - resolution: {integrity: sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==} - engines: {node: ^18 || >=20} - hasBin: true - peerDependencies: - typescript: ^5.0.0 - peerDependenciesMeta: - typescript: - optional: true - tslib@2.8.0: resolution: {integrity: sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==} @@ -5979,14 +5963,6 @@ packages: peerDependencies: vite: ^5 - vite-tsconfig-paths@5.0.1: - resolution: {integrity: sha512-yqwv+LstU7NwPeNqajZzLEBVpUFU6Dugtb2P84FXuvaoYA+/70l9MHE+GYfYAycVyPSDYZ7mjOFuYBRqlEpTig==} - peerDependencies: - vite: '*' - peerDependenciesMeta: - vite: - optional: true - vite@5.4.10: resolution: {integrity: sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -10305,8 +10281,6 @@ snapshots: slash: 5.1.0 unicorn-magic: 0.1.0 - globrex@0.1.2: {} - goober@2.1.16(csstype@3.1.3): dependencies: csstype: 3.1.3 @@ -12184,10 +12158,6 @@ snapshots: ts-pattern@5.5.0: {} - tsconfck@3.1.4(typescript@5.6.3): - optionalDependencies: - typescript: 5.6.3 - tslib@2.8.0: {} tsx@4.19.1: @@ -12551,17 +12521,6 @@ snapshots: - webpack-sources - xml2js - vite-tsconfig-paths@5.0.1(typescript@5.6.3)(vite@5.4.10(@types/node@22.7.9)(terser@5.36.0)): - dependencies: - debug: 4.3.7(supports-color@9.4.0) - globrex: 0.1.2 - tsconfck: 3.1.4(typescript@5.6.3) - optionalDependencies: - vite: 5.4.10(@types/node@22.7.9)(terser@5.36.0) - transitivePeerDependencies: - - supports-color - - typescript - vite@5.4.10(@types/node@22.7.9)(terser@5.36.0): dependencies: esbuild: 0.21.5 diff --git a/pwa.config.ts b/pwa.config.ts new file mode 100644 index 0000000..21089aa --- /dev/null +++ b/pwa.config.ts @@ -0,0 +1,50 @@ +import process from "node:process" +import type { VitePWAOptions } from "vite-plugin-pwa" +import { VitePWA } from "vite-plugin-pwa" + +const pwaOption: Partial = { + includeAssets: ["icon.svg", "apple-touch-icon.png"], + manifest: { + name: "NewsNow", + short_name: "NewsNow", + description: "Elegant reading of real-time and hottest news", + theme_color: "#F14D42", + icons: [ + { + src: "pwa-192x192.png", + sizes: "192x192", + type: "image/png", + }, + { + src: "pwa-512x512.png", + sizes: "512x512", + type: "image/png", + }, + { + src: "pwa-512x512.png", + sizes: "512x512", + type: "image/png", + purpose: "any", + }, + { + src: "pwa-512x512.png", + sizes: "512x512", + type: "image/png", + purpose: "maskable", + }, + ], + }, + workbox: { + navigateFallbackDenylist: [/^\/api/], + }, + devOptions: { + enabled: process.env.SW_DEV === "true", + type: "module", + navigateFallback: "index.html", + }, + +} + +export default function pwa() { + return VitePWA(pwaOption) +} diff --git a/server/api/me/sync.ts b/server/api/me/sync.ts index fa8d9cd..8335989 100644 --- a/server/api/me/sync.ts +++ b/server/api/me/sync.ts @@ -1,5 +1,4 @@ import process from "node:process" -import { verifyPrimitiveMetadata } from "@shared/verify" import { UserTable } from "#/database/user" export default defineEventHandler(async (event) => { diff --git a/server/api/s/[id].ts b/server/api/s/[id].ts index 633dd72..5bb8e6e 100644 --- a/server/api/s/[id].ts +++ b/server/api/s/[id].ts @@ -1,6 +1,4 @@ -import { TTL } from "@shared/consts" import type { SourceID, SourceResponse } from "@shared/types" -import { sources } from "@shared/sources" import { getters } from "#/getters" import { getCacheTable } from "#/database/cache" diff --git a/server/getters.ts b/server/getters.ts index 1255505..644bab4 100644 --- a/server/getters.ts +++ b/server/getters.ts @@ -1,4 +1,3 @@ -import { typeSafeObjectEntries } from "@shared/type.util" import type { SourceID } from "@shared/types" import * as x from "glob:./sources/{*.ts,**/index.ts}" import type { SourceGetter } from "./types" diff --git a/src/atoms/index.ts b/src/atoms/index.ts index 7e76847..594c47d 100644 --- a/src/atoms/index.ts +++ b/src/atoms/index.ts @@ -1,11 +1,6 @@ -import { atom } from "jotai" import type { FixedColumnID, SourceID } from "@shared/types" -import { sources } from "@shared/sources" -import { primitiveMetadataAtom } from "./primitiveMetadataAtom" import type { Update } from "./types" -export { primitiveMetadataAtom, preprocessMetadata } from "./primitiveMetadataAtom" - export const focusSourcesAtom = atom((get) => { return get(primitiveMetadataAtom).data.focus }, (get, set, update: Update) => { diff --git a/src/atoms/primitiveMetadataAtom.ts b/src/atoms/primitiveMetadataAtom.ts index 4bee0a6..f1babf0 100644 --- a/src/atoms/primitiveMetadataAtom.ts +++ b/src/atoms/primitiveMetadataAtom.ts @@ -1,10 +1,5 @@ -import { fixedColumnIds, metadata } from "@shared/metadata" -import { typeSafeObjectEntries, typeSafeObjectFromEntries } from "@shared/type.util" import type { PrimitiveAtom } from "jotai" -import { atom } from "jotai" import type { FixedColumnID, PrimitiveMetadata, SourceID } from "@shared/types" -import { verifyPrimitiveMetadata } from "@shared/verify" -import { sources } from "@shared/sources" import type { Update } from "./types" function createPrimitiveMetadataAtom( diff --git a/src/components/column/card.tsx b/src/components/column/card.tsx index df20637..9dc5f7f 100644 --- a/src/components/column/card.tsx +++ b/src/components/column/card.tsx @@ -1,18 +1,12 @@ import type { NewsItem, SourceID, SourceResponse } from "@shared/types" import { useQuery } from "@tanstack/react-query" -import clsx from "clsx" import { AnimatePresence, motion, useInView } from "framer-motion" -import { useAtom } from "jotai" -import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react" -import { sources } from "@shared/sources" import type { SyntheticListenerMap } from "@dnd-kit/core/dist/hooks/utilities" import { ofetch } from "ofetch" import { useWindowSize } from "react-use" import { OverlayScrollbar } from "../common/overlay-scrollbar" import { refetchSourcesAtom } from "~/atoms" -import { useRelativeTime } from "~/hooks/useRelativeTime" import { safeParseString } from "~/utils" -import { useFocusWith } from "~/hooks/useFocus" export interface ItemsProps extends React.HTMLAttributes { id: SourceID @@ -38,7 +32,7 @@ export const CardWrapper = forwardRef(({ id, isDragg return (
-
+
-
+
-
+
{!!data?.items?.length && (sources[id].type === "hottest" ? : )}
@@ -202,7 +196,7 @@ function DiffNumber({ diff }: { diff: number }) { initial={{ opacity: 0, y: -15 }} animate={{ opacity: 0.5, y: -7 }} exit={{ opacity: 0, y: -15 }} - className={clsx("absolute left-0 text-xs", diff < 0 ? "text-green" : "text-red")} + className={$("absolute left-0 text-xs", diff < 0 ? "text-green" : "text-red")} > {diff > 0 ? `+${diff}` : diff} @@ -243,12 +237,12 @@ function NewsListHot({ items }: { items: NewsItem[] }) { target="_blank" key={item.id} title={item.extra?.hover} - className={clsx( + className={$( "flex gap-2 items-center mb-2 items-stretch relative", "hover:bg-neutral-400/10 rounded-md pr-1 visited:(text-neutral-400)", )} > - + {i + 1} {!!item.extra?.diff && } @@ -282,7 +276,7 @@ function NewsListTimeLine({ items }: { items: NewsItem[] }) {
) function CardOverlay({ id }: { id: SourceID }) { return ( -
-
+
{sources[id].name} - {sources[id]?.title && {sources[id].title}} + {sources[id]?.title && {sources[id].title}} 拖拽中
-
+
diff --git a/src/components/column/index.tsx b/src/components/column/index.tsx index ba46dc2..329c942 100644 --- a/src/components/column/index.tsx +++ b/src/components/column/index.tsx @@ -1,8 +1,5 @@ import type { FixedColumnID } from "@shared/types" -import { useAtom } from "jotai" -import { useEffect } from "react" import { useTitle } from "react-use" -import { metadata } from "@shared/metadata" import { NavBar } from "../navbar" import { Dnd } from "./dnd" import { currentColumnIDAtom } from "~/atoms" diff --git a/src/components/common/overlay-scrollbar.tsx b/src/components/common/overlay-scrollbar.tsx index eeb9512..9515a35 100644 --- a/src/components/common/overlay-scrollbar.tsx +++ b/src/components/common/overlay-scrollbar.tsx @@ -1,9 +1,7 @@ import type { UseOverlayScrollbarsParams } from "overlayscrollbars-react" import { useOverlayScrollbars } from "overlayscrollbars-react" import type { HTMLProps, PropsWithChildren } from "react" -import { useCallback, useMemo, useRef } from "react" import { defu } from "defu" -import { useSetAtom } from "jotai" import { useMount } from "react-use" import { goToTopAtom } from "~/atoms" diff --git a/src/components/common/search-bar/index.tsx b/src/components/common/search-bar/index.tsx index 4940583..58a7a1d 100644 --- a/src/components/common/search-bar/index.tsx +++ b/src/components/common/search-bar/index.tsx @@ -2,15 +2,9 @@ import { Command } from "cmdk" import { useMount } from "react-use" import type { SourceID } from "@shared/types" import { useMemo, useRef, useState } from "react" -import { sources } from "@shared/sources" -import clsx from "clsx" -import { typeSafeObjectEntries } from "@shared/type.util" import pinyin from "@shared/pinyin.json" -import { columns } from "@shared/metadata" import { OverlayScrollbar } from "../overlay-scrollbar" -import { useSearchBar } from "~/hooks/useSearch" import { CardWrapper } from "~/components/column/card" -import { useFocusWith } from "~/hooks/useFocus" import "./cmdk.css" @@ -128,7 +122,7 @@ function SourceItem({ item }: { > {item.name} {item.title} - + ) } diff --git a/src/components/common/toast.tsx b/src/components/common/toast.tsx index ac97ca1..d2e3c15 100644 --- a/src/components/common/toast.tsx +++ b/src/components/common/toast.tsx @@ -1,10 +1,7 @@ -import clsx from "clsx" import { AnimatePresence, motion } from "framer-motion" -import { useAtomValue, useSetAtom } from "jotai" import { useCallback, useMemo, useRef } from "react" import { useHoverDirty, useMount, useUpdateEffect, useWindowSize } from "react-use" import type { ToastItem } from "~/atoms/types" -import { toastAtom } from "~/hooks/useToast" import { Timer } from "~/utils" const WIDTH = 320 @@ -91,11 +88,11 @@ function Item({ info }: { info: ToastItem }) { opacity: 1, }, }} - className={clsx( + className={$( "bg-base rounded-lg shadow-xl relative", )} > -
diff --git a/src/components/header/index.tsx b/src/components/header/index.tsx index 88bed6f..b79ff98 100644 --- a/src/components/header/index.tsx +++ b/src/components/header/index.tsx @@ -1,14 +1,9 @@ import { Link } from "@tanstack/react-router" -import { useCallback } from "react" -import { useAtomValue, useSetAtom } from "jotai" import { useIsFetching } from "@tanstack/react-query" -import clsx from "clsx" import type { SourceID } from "@shared/types" -import { Homepage, Version } from "@shared/consts" import { NavBar } from "../navbar" import { Menu } from "./menu" import { currentSourcesAtom, goToTopAtom, refetchSourcesAtom } from "~/atoms" -import { useSearchBar } from "~/hooks/useSearch" export function Search() { const { toggle } = useSearchBar() @@ -22,7 +17,7 @@ function GoTop() {