chore: Release v0.0.1-alpha.21 (#913)

This commit is contained in:
DIYgod 2024-10-13 12:17:02 +08:00 committed by GitHub
commit f690e6e260
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 292 additions and 172 deletions

View File

@ -21,6 +21,7 @@ env:
VITE_IMGPROXY_URL: ${{ vars.VITE_IMGPROXY_URL }}
VITE_SENTRY_DSN: ${{ vars.VITE_SENTRY_DSN }}
VITE_POSTHOG_KEY: ${{ vars.VITE_POSTHOG_KEY }}
VITE_FIREBASE_CONFIG: ${{ vars.VITE_FIREBASE_CONFIG }}
NODE_OPTIONS: --max-old-space-size=8192
jobs:
@ -150,7 +151,7 @@ jobs:
out/make/**/*.AppImage
out/make/**/*.yml
- run: npx @innei/changelogithub
- run: npx changelogithub
if: github.ref_type == 'tag' || github.event.inputs.tag_version != ''
continue-on-error: true
env:

View File

@ -10,6 +10,7 @@ env:
VITE_IMGPROXY_URL: ${{ vars.VITE_IMGPROXY_URL }}
VITE_SENTRY_DSN: ${{ vars.VITE_SENTRY_DSN }}
VITE_POSTHOG_KEY: ${{ vars.VITE_POSTHOG_KEY }}
VITE_FIREBASE_CONFIG: ${{ vars.VITE_FIREBASE_CONFIG }}
NODE_OPTIONS: --max-old-space-size=8192
jobs:

View File

@ -1,6 +1,6 @@
# CHANGELOG
## [0.0.1-alpha.20](https://github.com/RSSNext/follow/compare/v0.0.1-alpha.19...v0.0.1-alpha.20) (2024-10-12)
## [0.0.1-alpha.21](https://github.com/RSSNext/follow/compare/v0.0.1-alpha.19...v0.0.1-alpha.21) (2024-10-12)
### Bug Fixes
@ -17,6 +17,7 @@
* accept import opml ([39ecc82](https://github.com/RSSNext/follow/commit/39ecc82a8ceecd77139ffccb31a33904082a3d1b))
* **achievement:** loading button style ([96bb514](https://github.com/RSSNext/follow/commit/96bb51425427864bc4e77b75808271371839c57a))
* add app version on posthog ([9933463](https://github.com/RSSNext/follow/commit/99334639e28b2a2a17b9c3fdd64f0d6d37bdd181))
* add aria-describedby to modal content ([bb463a3](https://github.com/RSSNext/follow/commit/bb463a311fe020d079ed021489239a9141246a48))
* add bg when context menu trigger, fix [#389](https://github.com/RSSNext/follow/issues/389) ([53f3185](https://github.com/RSSNext/follow/commit/53f3185e7955d1a72912905d8565c5efb0a0bda0))
* add copy image in electron, fix [#317](https://github.com/RSSNext/follow/issues/317) ([466e0b7](https://github.com/RSSNext/follow/commit/466e0b78ccc5df66ab11a1e71a800e70c9b34312))
* add db index ([a888a78](https://github.com/RSSNext/follow/commit/a888a789e10ecd8b3fab713e859bfe9f56bfe0ad))
@ -246,6 +247,7 @@
* improve proxy URI handling ([#810](https://github.com/RSSNext/follow/issues/810)) ([e7e930d](https://github.com/RSSNext/follow/commit/e7e930d1771e19d909433021d6757eabb44de73f))
* inbox data refreshment ([a3a7c82](https://github.com/RSSNext/follow/commit/a3a7c82ea6308756a800514d9df528815ba77781))
* incorrect tooltip in read history ([#385](https://github.com/RSSNext/follow/issues/385)) ([cabe210](https://github.com/RSSNext/follow/commit/cabe210aaf8f4a1133fa653532874fa86d02c252))
* init store after app init ([728ea84](https://github.com/RSSNext/follow/commit/728ea84fa6e9faff1990184285390a1d86eb2c56))
* inline table style ([5837bd4](https://github.com/RSSNext/follow/commit/5837bd4089b59855d0b583aed5d0e97fce04e094))
* input box table style ([e0890d5](https://github.com/RSSNext/follow/commit/e0890d5b388facd1f9c16ac706a32f0aa2efdef5))
* Input issues fixed [#535](https://github.com/RSSNext/follow/issues/535),[#536](https://github.com/RSSNext/follow/issues/536) ([3d48637](https://github.com/RSSNext/follow/commit/3d486379b91dff13bfbcc90532efd3e186405a13))
@ -254,6 +256,7 @@
* invitation page error display area ([5915357](https://github.com/RSSNext/follow/commit/59153573d8a5d368adfe41b0f5cebc4d6d44a9f3))
* **invitation:** reset app data then logout ([5933cea](https://github.com/RSSNext/follow/commit/5933ceab63de0a7a46e85d0b841acbf893d8fcec))
* **item:** center content if no desc ([18e26a4](https://github.com/RSSNext/follow/commit/18e26a41c3cf80316bc97c44a1239e6ec45c5db9))
* json parse for env VITE_FIREBASE_CONFIG ([b2916bd](https://github.com/RSSNext/follow/commit/b2916bd8b81d02e1f5b673ed34c7f0f181d641c2))
* kbd cls and set home scope in shortcuts guideline ([d9999a3](https://github.com/RSSNext/follow/commit/d9999a3fc6cfeae0f3b45b6390cf79d9135566b7))
* lang/*.json ([#526](https://github.com/RSSNext/follow/issues/526)) ([be3b2e7](https://github.com/RSSNext/follow/commit/be3b2e7503c1061a1e312394bb0c3556e39ec062))
* language setting syncing ([fa1dc5d](https://github.com/RSSNext/follow/commit/fa1dc5df35a2225bb22320a8fc049d3c0b75fa5b))
@ -507,6 +510,7 @@
* user modal list padding ([992ea15](https://github.com/RSSNext/follow/commit/992ea1594af02e3815adae2fe99de4f205543239))
* user profile can not scroll by scrollbar ([eac1965](https://github.com/RSSNext/follow/commit/eac1965cfc3d2bc86e11f256237322f15892b897))
* userActions in feed store ([8da4fb7](https://github.com/RSSNext/follow/commit/8da4fb71d9cc14832bc985cf8c1bd6f04dbe4e4c))
* **vercel:** filter subpath ([fc38072](https://github.com/RSSNext/follow/commit/fc38072320f8225e6ae60f4e25719eaeb9ff5e6a))
* video preview ([e38820c](https://github.com/RSSNext/follow/commit/e38820c1aa2294f45e05d331bf4bce0e4d8efb44))
* view icon color in dark mode ([991bff7](https://github.com/RSSNext/follow/commit/991bff7660e93b872f1398c89c0fe4316f4327d9))
* view source content in picture view ([16938f6](https://github.com/RSSNext/follow/commit/16938f60786f8496152361405c0fcff4635f7d59))
@ -522,6 +526,7 @@
* windows maximize will lost frame and background material ([2bd0e78](https://github.com/RSSNext/follow/commit/2bd0e78e4f859d9c98f53f748c9871a41348db1c))
* windows multi-display ([7490cd1](https://github.com/RSSNext/follow/commit/7490cd12d0e42a7f41931acd9d77bf64be39a26d))
* windows sep, fixes [#741](https://github.com/RSSNext/follow/issues/741) ([9b1de77](https://github.com/RSSNext/follow/commit/9b1de775d2157606c30c4ece3287a42e1e9463a3))
* windows web mark all read ui ([#908](https://github.com/RSSNext/follow/issues/908)) ([45b4bd8](https://github.com/RSSNext/follow/commit/45b4bd8085377e8178b6804c0718726ecc15145b))
* **windows:** dont remove locale ([7305eba](https://github.com/RSSNext/follow/commit/7305eba3fee218330818d96ca155f833aa99d631))
* **windows:** skip remove locle ([5df82d0](https://github.com/RSSNext/follow/commit/5df82d0c0f98bebb6e78a694a6ceda3644eb36ef))
* withdraw availableBalance ([167d82a](https://github.com/RSSNext/follow/commit/167d82adc061078102fa9984d29f94735d052ce9))
@ -541,6 +546,7 @@
* add biz code i18n for fr and ru ([#521](https://github.com/RSSNext/follow/issues/521)) ([0dc4b86](https://github.com/RSSNext/follow/commit/0dc4b866d7eff2d32df2158902d6d1fbdea41f27))
* add biz code i18n for zh-cn ([#503](https://github.com/RSSNext/follow/issues/503)) ([d35e011](https://github.com/RSSNext/follow/commit/d35e0119953a154d18273c1d155f14d03c9a6ce8))
* add biz user info on sentry tracker ([e3e52ab](https://github.com/RSSNext/follow/commit/e3e52abedeb5507ca71d663faa7ef050b661cf0a))
* add bundleId to PushReceiver ([f196493](https://github.com/RSSNext/follow/commit/f196493b68a84489de93ac0c3944b039c79f25c0))
* add copy title to context menu of feed entry ([#801](https://github.com/RSSNext/follow/issues/801)) ([1f57ab2](https://github.com/RSSNext/follow/commit/1f57ab2ac76ef04aff37814b4c7e961ad33b6e11))
* add discover back to top fab ([a97e60c](https://github.com/RSSNext/follow/commit/a97e60cd005ca7ebb5aed56655b94975ebf1ccb7))
* add divider when sticky for date item ([755e292](https://github.com/RSSNext/follow/commit/755e292af05c3adeae8943cc5c4c05af11d30d16))
@ -705,6 +711,7 @@
* new power page ([c3630f8](https://github.com/RSSNext/follow/commit/c3630f85bd221ecf875b20337e5c6a452c613362))
* no media available tip in picture item ([1d4fca4](https://github.com/RSSNext/follow/commit/1d4fca4e7d0e7c6f02f1ac775f36079ef5ce1129))
* no media available tip in picture item ([58d9b3b](https://github.com/RSSNext/follow/commit/58d9b3b1b09f52def911521c08d9835f31221401))
* notifications logger ([97041ab](https://github.com/RSSNext/follow/commit/97041abab5f0846249343de07f592ba6fc421870))
* only closing window can trigger query invalidation ([a8e48f4](https://github.com/RSSNext/follow/commit/a8e48f493127104035e9537e0461c4a40e2dc339))
* only show has media entry item in picture view ([f7dedf2](https://github.com/RSSNext/follow/commit/f7dedf2b27f2597a49e9381af5581b6d0550421b))
* optimize 404 page ([f51b1e2](https://github.com/RSSNext/follow/commit/f51b1e2de9e8d8c3bdea9db79476c17cd7247a4b))
@ -763,6 +770,7 @@
* support pageup/pagedown to scroll up/down ([6e0c3bc](https://github.com/RSSNext/follow/commit/6e0c3bc5e3e2437a51fa991940b4e4496ed2bb75))
* support setting proxy for app ([#452](https://github.com/RSSNext/follow/issues/452)) ([cfd5275](https://github.com/RSSNext/follow/commit/cfd527545ade50a529698bdd57ec16d0f3ccee98))
* support shortcut to toggle wide mode ([b7a054f](https://github.com/RSSNext/follow/commit/b7a054f3132079693f0d93a65eb236c95920bcb8))
* support specific import for web and electron ([9a84a29](https://github.com/RSSNext/follow/commit/9a84a293967d5d415e86041c7a586846519def5f))
* support toc parser ([fb4ca52](https://github.com/RSSNext/follow/commit/fb4ca522440aced4d2aa18fda2a9d5c43e32d0d1))
* sync indicator when disable ([945ddda](https://github.com/RSSNext/follow/commit/945dddae2534f07e5289e307c36de1f8334636bc))
* tiny styles ([78bcc55](https://github.com/RSSNext/follow/commit/78bcc5562baa12cece51da6564b77c6f37a1596c))

View File

@ -12,16 +12,13 @@ import { getIconPath } from "./helper"
import { t } from "./lib/i18n"
import { store } from "./lib/store"
import { updateNotificationsToken } from "./lib/user"
import { logger } from "./logger"
import { registerAppMenu } from "./menu"
import type { RendererHandlers } from "./renderer-handlers"
import { initializeSentry } from "./sentry"
import { router } from "./tipc"
import { createMainWindow, getMainWindow } from "./window"
const appFolder = {
prod: "Follow",
dev: "Follow (dev)",
}
if (process.argv.length === 3 && process.argv[2].startsWith("follow-dev:")) {
process.env.NODE_ENV = "development"
}
@ -31,7 +28,8 @@ const isDev = process.env.NODE_ENV === "development"
* Mandatory and fast initializers for the app
*/
export function initializeAppStage0() {
app.setPath("appData", path.join(app.getPath("appData"), isDev ? appFolder.dev : appFolder.prod))
if (isDev) app.setPath("appData", path.join(app.getPath("appData"), "Follow (dev)"))
initializeSentry()
}
export const initializeAppStage1 = () => {
if (process.defaultApp) {
@ -44,8 +42,6 @@ export const initializeAppStage1 = () => {
app.setAsDefaultProtocolClient(APP_PROTOCOL)
}
initializeSentry()
registerIpcMain(router)
if (app.dock) {
@ -149,17 +145,28 @@ const registerPushNotifications = async () => {
updateNotificationsToken()
const instance = new PushReceiver({
debug: isDev,
firebase: env.VITE_FIREBASE_CONFIG,
debug: true,
firebase: JSON.parse(env.VITE_FIREBASE_CONFIG),
persistentIds: persistentIds || [],
credentials,
credentials: credentials || null,
bundleId: "is.follow",
chromeId: "is.follow",
})
logger.info(
`PushReceiver initialized with credentials ${JSON.stringify(credentials)} and firebase config ${env.VITE_FIREBASE_CONFIG}`,
)
instance.onReady(() => {
logger.info("PushReceiver ready")
})
instance.onCredentialsChanged(({ newCredentials }) => {
logger.info(`PushReceiver credentials changed to ${newCredentials?.fcm?.token}`)
updateNotificationsToken(newCredentials)
})
instance.onNotification((notification) => {
logger.info(`PushReceiver received notification: ${JSON.stringify(notification.message.data)}`)
const data = notification.message.data as MessagingData
switch (data.type) {
case "new-entry": {
@ -191,5 +198,11 @@ const registerPushNotifications = async () => {
store.set(persistentIdsKey, instance.persistentIds)
})
await instance.connect()
try {
await instance.connect()
} catch (error) {
logger.error(`PushReceiver error: ${error instanceof Error ? error.stack : error}`)
}
logger.info("PushReceiver connected")
}

View File

@ -3,15 +3,26 @@ import { resolve } from "node:path"
import { app } from "electron"
import { JSONFileSyncPreset } from "lowdb/node"
const db = JSONFileSyncPreset(resolve(app.getPath("userData"), "db.json"), {}) as {
let db: {
data: Record<string, unknown>
write: () => void
read: () => void
}
const createOrGetDb = () => {
if (!db) {
db = JSONFileSyncPreset(resolve(app.getPath("userData"), "db.json"), {}) as typeof db
}
return db
}
export const store = {
get: (key: string) => db.data[key] as any,
get: (key: string) => {
const db = createOrGetDb()
return db.data[key] as any
},
set: (key: string, value: any) => {
const db = createOrGetDb()
db.data[key] = value
db.write()
},

View File

@ -46,7 +46,7 @@ const ContextMenuSubContent = React.forwardRef<
<ContextMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-[1001] min-w-32 overflow-hidden rounded-md border bg-theme-modal-background-opaque p-1 text-theme-foreground/90 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:shadow-zinc-800/60",
"min-w-32 overflow-hidden rounded-md border bg-theme-modal-background-opaque p-1 text-theme-foreground/90 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:shadow-zinc-800/60",
className,
)}
{...props}

View File

@ -6,9 +6,7 @@ import { jotaiStore } from "~/lib/jotai"
import { cn } from "~/lib/utils"
import { modalStackAtom } from "./atom"
import { MODAL_STACK_Z_INDEX } from "./constants"
import { ModalInternal } from "./modal"
import { ModalOverlay } from "./overlay"
import type { ModalProps } from "./types"
export interface DeclarativeModalProps extends Omit<ModalProps, "content"> {
@ -40,12 +38,9 @@ const DeclarativeModalImpl: FC<DeclarativeModalProps> = ({
return (
<AnimatePresence>
{open && (
<>
<ModalInternal isTop onClose={onOpenChange} index={index} item={item}>
{children}
</ModalInternal>
<ModalOverlay zIndex={MODAL_STACK_Z_INDEX - 1 + index} />
</>
<ModalInternal isTop onClose={onOpenChange} index={index} item={item}>
{children}
</ModalInternal>
)}
</AnimatePresence>
)

View File

@ -34,11 +34,12 @@ import { Divider } from "../../divider"
import { RootPortalProvider } from "../../portal/provider"
import { EllipsisHorizontalTextWithTooltip } from "../../typography"
import { modalStackAtom } from "./atom"
import { MODAL_STACK_Z_INDEX, modalMontionConfig } from "./constants"
import { modalMontionConfig } from "./constants"
import type { CurrentModalContentProps, ModalActionsInternal } from "./context"
import { CurrentModalContext } from "./context"
import { useResizeableModal } from "./hooks"
import type { ModalProps } from "./types"
import { ModalOverlay } from "./overlay"
import type { ModalOverlayOptions, ModalProps } from "./types"
const DragBar = isElectronBuild ? (
<span className="drag-region fixed left-0 right-36 top-0 h-8" />
@ -51,9 +52,10 @@ export const ModalInternal = memo(
index: number
isTop: boolean
overlayOptions?: ModalOverlayOptions
onClose?: (open: boolean) => void
} & PropsWithChildren
>(function Modal({ item, index, onClose: onPropsClose, children, isTop }, ref) {
>(function Modal({ item, overlayOptions, onClose: onPropsClose, children, isTop }, ref) {
const {
CustomModalComponent,
modalClassName,
@ -96,8 +98,8 @@ export const ModalInternal = memo(
)
const opaque = useUISettingKey("modalOpaque")
const modalSettingOverlay = useUISettingKey("modalOverlay")
const zIndexStyle = useMemo(() => ({ zIndex: MODAL_STACK_Z_INDEX + index + 1 }), [index])
const dismiss = useCallback(
(e: SyntheticEvent) => {
e.stopPropagation()
@ -224,10 +226,7 @@ export const ModalInternal = memo(
}
}, [switchHotkeyScope])
const modalStyle = useMemo(
() => ({ ...zIndexStyle, ...resizeableStyle }),
[resizeableStyle, zIndexStyle],
)
const modalStyle = resizeableStyle
const isSelectingRef = useRef(false)
const handleSelectStart = useCallback(() => {
isSelectingRef.current = true
@ -269,17 +268,27 @@ export const ModalInternal = memo(
}, [])
useImperativeHandle(ref, () => modalElementRef.current!)
const Overlay = (
<ModalOverlay
blur={overlayOptions?.blur}
className={cn(overlayOptions?.className, {
hidden: item.overlay ? false : !modalSettingOverlay,
})}
/>
)
if (CustomModalComponent) {
return (
<Wrapper>
<Dialog.Root open onOpenChange={onClose} modal={modal}>
<Dialog.Portal>
{Overlay}
<Dialog.DialogTitle className="sr-only">{title}</Dialog.DialogTitle>
<Dialog.Content asChild onOpenAutoFocus={openAutoFocus}>
<Dialog.Content asChild aria-describedby={undefined} onOpenAutoFocus={openAutoFocus}>
<div
ref={edgeElementRef}
className={cn(
"no-drag-region fixed z-20",
"no-drag-region fixed",
modal ? "inset-0 overflow-auto" : "left-0 top-0",
currentIsClosing ? "!pointer-events-none" : "!pointer-events-auto",
modalContainerClassName,
@ -287,7 +296,6 @@ export const ModalInternal = memo(
onPointerUp={handleDetectSelectEnd}
onClick={handleClickOutsideToDismiss}
onFocus={stopPropagation}
style={zIndexStyle}
>
{DragBar}
<div
@ -315,12 +323,12 @@ export const ModalInternal = memo(
<Wrapper>
<Dialog.Root modal={modal} open onOpenChange={onClose}>
<Dialog.Portal>
<Dialog.Content asChild onOpenAutoFocus={openAutoFocus}>
{Overlay}
<Dialog.Content asChild aria-describedby={undefined} onOpenAutoFocus={openAutoFocus}>
<div
ref={edgeElementRef}
style={zIndexStyle}
className={cn(
"fixed z-20 flex",
"fixed flex",
modal ? "inset-0 overflow-auto" : "left-0 top-0",
currentIsClosing && "!pointer-events-none",
modalContainerClassName,

View File

@ -1,10 +1,11 @@
import * as Dialog from "@radix-ui/react-dialog"
import type { ForwardedRef } from "react"
import { forwardRef } from "react"
import { m } from "~/components/common/Motion"
import { cn } from "~/lib/utils"
import { RootPortal } from "../../portal"
import { softSpringPreset } from "../../constants/spring"
export const ModalOverlay = forwardRef(
(
@ -19,20 +20,22 @@ export const ModalOverlay = forwardRef(
},
ref: ForwardedRef<HTMLDivElement>,
) => (
<RootPortal>
<Dialog.Overlay asChild>
<m.div
ref={ref}
id="modal-overlay"
className={cn(
"!pointer-events-none fixed inset-0 z-[11] rounded-[var(--fo-window-radius)] bg-zinc-50/80 dark:bg-neutral-900/80",
// NOTE: pointer-events-none is required, if remove this, when modal is closing, you can not click element behind the modal
"!pointer-events-none fixed inset-0 rounded-[var(--fo-window-radius)] bg-zinc-50/80 dark:bg-neutral-900/80",
blur && "backdrop-blur-sm",
className,
)}
transition={softSpringPreset}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
style={{ zIndex }}
/>
</RootPortal>
</Dialog.Overlay>
),
)

View File

@ -2,12 +2,8 @@ import { AnimatePresence } from "framer-motion"
import { useAtomValue } from "jotai"
import type { FC, PropsWithChildren } from "react"
import { useUISettingKey } from "~/atoms/settings/ui"
import { modalStackAtom } from "./atom"
import { MODAL_STACK_Z_INDEX } from "./constants"
import { ModalInternal } from "./modal"
import { ModalOverlay } from "./overlay"
export const ModalStackProvider: FC<PropsWithChildren> = ({ children }) => (
<>
@ -19,11 +15,6 @@ export const ModalStackProvider: FC<PropsWithChildren> = ({ children }) => (
const ModalStack = () => {
const stack = useAtomValue(modalStackAtom)
const modalSettingOverlay = useUISettingKey("modalOverlay")
const forceOverlay = stack.some((item) => item.overlay)
const allForceHideOverlay = stack.every((item) => item.overlay === false)
const topModalIndex = stack.findLastIndex((item) => item.modal)
const overlayIndex = stack.findLastIndex((item) => item.overlay || item.modal)
const overlayOptions = stack[overlayIndex]?.overlayOptions
@ -35,15 +26,9 @@ const ModalStack = () => {
item={item}
index={index * 2}
isTop={index === topModalIndex * 2}
overlayOptions={overlayOptions}
/>
))}
{stack.length > 0 && (modalSettingOverlay || forceOverlay) && !allForceHideOverlay && (
<ModalOverlay
zIndex={MODAL_STACK_Z_INDEX + overlayIndex * 2 - 2}
blur={overlayOptions?.blur}
className={overlayOptions?.className}
/>
)}
</AnimatePresence>
)
}

View File

@ -71,7 +71,7 @@ const SelectContent = React.forwardRef<
<SelectPrimitive.Content
ref={ref}
className={cn(
"shadow-perfect relative z-[1000] max-h-96 min-w-32 overflow-hidden rounded-md border bg-popover text-popover-foreground",
"shadow-perfect relative max-h-96 min-w-32 overflow-hidden rounded-md border bg-popover text-popover-foreground",
position === "popper" && [
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",

View File

@ -9,7 +9,7 @@ import { useOnClickOutside } from "usehooks-ts"
import { ActionButton, Button, IconButton } from "~/components/ui/button"
import { Kbd, KbdCombined } from "~/components/ui/kbd/Kbd"
import { RootPortal } from "~/components/ui/portal"
import { ElECTRON_CUSTOM_TITLEBAR_HEIGHT, HotKeyScopeMap } from "~/constants"
import { ElECTRON_CUSTOM_TITLEBAR_HEIGHT, HotKeyScopeMap, isElectronBuild } from "~/constants"
import { shortcuts } from "~/constants/shortcuts"
import { useI18n } from "~/hooks/common"
import { cn, getOS } from "~/lib/utils"
@ -51,7 +51,9 @@ export const MarkAllReadWithOverlay = forwardRef<
<m.div
ref={setPopoverRef}
initial={{ y: -70 }}
animate={{ y: getOS() === "Windows" ? -ElECTRON_CUSTOM_TITLEBAR_HEIGHT : 0 }}
animate={{
y: isElectronBuild && getOS() === "Windows" ? -ElECTRON_CUSTOM_TITLEBAR_HEIGHT : 0,
}}
exit={{ y: -70 }}
transition={{ type: "spring", damping: 20, stiffness: 300 }}
className="shadow-modal absolute z-50 bg-theme-modal-background-opaque shadow"

View File

@ -14,12 +14,12 @@ export const EntryPlaceholderLogo = () => {
<div
onContextMenu={stopPropagation}
className={cn(
"flex w-full min-w-0 flex-col items-center justify-center gap-1 text-balance px-12 pb-6 text-center text-lg font-medium text-zinc-400 duration-500",
"flex w-full min-w-0 flex-col items-center justify-center gap-1 px-12 pb-6 text-center text-lg font-medium text-zinc-400 duration-500",
!logoShow && "translate-y-[-50px] opacity-0",
)}
>
<Logo className="size-16 opacity-40 grayscale" />
<span className="max-w-[60ch]">{title}</span>
<div className="line-clamp-3 w-[60ch] max-w-full">{title}</div>
</div>
)
}

View File

@ -9,6 +9,7 @@ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react
import { useDebounceCallback, useEventCallback } from "usehooks-ts"
import { softSpringPreset } from "~/components/ui/constants/spring"
import { RootPortal } from "~/components/ui/portal"
import { useInputComposition, useRefValue } from "~/hooks/common"
import { tipcClient } from "~/lib/client"
import { nextFrame } from "~/lib/dom"
@ -117,7 +118,7 @@ const CmdFImpl: FC<{
e.preventDefault()
nativeSearch(value)
}}
className="center shadow-perfect fixed right-8 top-12 z-[1000] size-9 w-64 gap-2 rounded-2xl border bg-zinc-50/90 pl-3 pr-2 backdrop-blur duration-200 focus-within:border-accent dark:bg-neutral-800/80"
className="center shadow-perfect fixed right-8 top-12 size-9 w-64 gap-2 rounded-2xl border bg-zinc-50/90 pl-3 pr-2 backdrop-blur duration-200 focus-within:border-accent dark:bg-neutral-800/80"
>
<div className="relative h-full grow">
<input
@ -238,22 +239,24 @@ export const CmdF = () => {
setShow(true)
})
return (
<AnimatePresence>
{show && (
<m.div
className="relative z-[1000]"
initial={{ opacity: 0.8, y: -150 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -150 }}
transition={softSpringPreset}
>
<CmdFImpl
onClose={() => {
setShow(false)
}}
/>
</m.div>
)}
</AnimatePresence>
<RootPortal>
<AnimatePresence>
{show && (
<m.div
className="fixed top-0 w-full"
initial={{ opacity: 0.8, y: -150 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -150 }}
transition={softSpringPreset}
>
<CmdFImpl
onClose={() => {
setShow(false)
}}
/>
</m.div>
)}
</AnimatePresence>
</RootPortal>
)
}

View File

@ -56,7 +56,7 @@ export const TransactionsSection: Component = ({ className }) => {
</Tabs>
<div className={cn("w-fit min-w-0 grow overflow-x-auto", className)}>
<Table className="w-full table-fixed">
<TableHeader className="sticky top-0 z-10 bg-theme-background">
<TableHeader className="sticky top-0 bg-theme-background">
<TableRow className="[&_*]:!pl-0 [&_*]:!font-semibold">
<TableHead>{t("wallet.transactions.type")}</TableHead>
<TableHead>{t("wallet.transactions.amount")}</TableHead>

View File

@ -398,7 +398,7 @@ const SubscriptionItem: FC<{
!isLoose && "flex items-center",
)}
>
<div className="truncate font-medium leading-none">{subscription.feeds?.title}</div>
<div className="truncate font-medium leading-tight">{subscription.feeds?.title}</div>
{isLoose && (
<div className="mt-1 line-clamp-1 text-xs text-zinc-500">
{subscription.feeds?.description}

View File

@ -123,10 +123,6 @@ export function Component() {
</AppErrorBoundary>
</main>
<SearchCmdK />
<CmdNTrigger />
{ELECTRON && <CmdF />}
{isAuthFail && !user && (
<RootPortal>
<DeclarativeModal
@ -141,6 +137,10 @@ export function Component() {
</DeclarativeModal>
</RootPortal>
)}
<SearchCmdK />
<CmdNTrigger />
{ELECTRON && <CmdF />}
</RootContainer>
)
}
@ -155,7 +155,7 @@ const RootContainer = forwardRef<HTMLDivElement, PropsWithChildren>(({ children
"--fo-feed-col-w": `${feedColWidth}px`,
} as any
}
className="flex h-screen overflow-hidden"
className="relative z-0 flex h-screen overflow-hidden"
onContextMenu={preventDefault}
>
{children}

View File

@ -28,7 +28,7 @@ export const AppLayoutGridContainerProvider: FC<PropsWithChildren> = ({ children
return (
<AppLayoutGridContainerWidthContext.Provider value={width}>
<div ref={ref} className="contents">
<div ref={ref} className="relative z-0 contents">
{children}
</div>
</AppLayoutGridContainerWidthContext.Provider>

View File

@ -0,0 +1,5 @@
export { ContextMenuProvider as LazyContextMenuProvider } from "../context-menu-provider"
export { ExtensionExposeProvider as LazyExtensionExposeProvider } from "../extension-expose-provider"
export { LottieRenderContainer as LazyLottieRenderContainer } from "~/components/ui/lottie-container"
export { ModalStackProvider as LazyModalStackProvider } from "~/components/ui/modal"
export { FeatureFlagDebugger as LazyFeatureFlagDebugger } from "~/modules/ab/providers"

View File

@ -0,0 +1,35 @@
import { lazy } from "react"
const LazyLottieRenderContainer = lazy(() =>
import("../../components/ui/lottie-container").then((res) => ({
default: res.LottieRenderContainer,
})),
)
const LazyContextMenuProvider = lazy(() =>
import("./../context-menu-provider").then((res) => ({
default: res.ContextMenuProvider,
})),
)
const LazyModalStackProvider = lazy(() =>
import("../../components/ui/modal/stacked/provider").then((res) => ({
default: res.ModalStackProvider,
})),
)
const LazyExtensionExposeProvider = lazy(() =>
import("./../extension-expose-provider").then((res) => ({
default: res.ExtensionExposeProvider,
})),
)
const LazyFeatureFlagDebugger = lazy(() =>
import("../../modules/ab/providers").then((res) => ({
default: res.FeatureFlagDebugger,
})),
)
export {
LazyContextMenuProvider,
LazyExtensionExposeProvider,
LazyFeatureFlagDebugger,
LazyLottieRenderContainer,
LazyModalStackProvider,
}

View File

@ -4,7 +4,7 @@ import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client
import { LazyMotion, MotionConfig } from "framer-motion"
import { Provider } from "jotai"
import type { FC, PropsWithChildren } from "react"
import { lazy, Suspense } from "react"
import { Suspense } from "react"
import { HotkeysProvider } from "react-hotkeys-hook"
import { Toaster } from "~/components/ui/sonner"
@ -15,36 +15,18 @@ import { persistConfig, queryClient } from "~/lib/query-client"
import { EventProvider } from "./event-provider"
import { I18nProvider } from "./i18n-provider"
import { InvalidateQueryProvider } from "./invalidate-query-provider"
import {
LazyContextMenuProvider,
LazyExtensionExposeProvider,
LazyFeatureFlagDebugger,
LazyLottieRenderContainer,
LazyModalStackProvider,
// specific import should add `index` postfix
} from "./lazy/index"
import { SettingSync } from "./setting-sync"
import { StableRouterProvider } from "./stable-router-provider"
import { UserProvider } from "./user-provider"
const LazyLottieRenderContainer = lazy(() =>
import("../components/ui/lottie-container").then((res) => ({
default: res.LottieRenderContainer,
})),
)
const LazyContextMenuProvider = lazy(() =>
import("./context-menu-provider").then((res) => ({
default: res.ContextMenuProvider,
})),
)
const LazyModalStackProvider = lazy(() =>
import("../components/ui/modal/stacked/provider").then((res) => ({
default: res.ModalStackProvider,
})),
)
const LazyExtensionExposeProvider = lazy(() =>
import("./extension-expose-provider").then((res) => ({
default: res.ExtensionExposeProvider,
})),
)
const LazyFeatureFlagDebugger = lazy(() =>
import("../modules/ab/providers").then((res) => ({
default: res.FeatureFlagDebugger,
})),
)
const loadFeatures = () => import("../framer-lazy-feature").then((res) => res.default)
export const RootProviders: FC<PropsWithChildren> = ({ children }) => (
<LazyMotion features={loadFeatures} strict key="framer">
@ -62,6 +44,13 @@ export const RootProviders: FC<PropsWithChildren> = ({ children }) => (
<EventProvider />
<UserProvider />
<StableRouterProvider />
<SettingSync />
{import.meta.env.DEV && <Devtools />}
{children}
<Suspense>
<LazyExtensionExposeProvider />
<LazyModalStackProvider />
@ -69,12 +58,6 @@ export const RootProviders: FC<PropsWithChildren> = ({ children }) => (
<LazyLottieRenderContainer />
<LazyFeatureFlagDebugger />
</Suspense>
<StableRouterProvider />
<SettingSync />
{import.meta.env.DEV && <Devtools />}
{children}
<Toaster />
</I18nProvider>
</Provider>

View File

@ -0,0 +1,29 @@
import { IN_ELECTRON } from "@follow/shared/constants"
import { wrapCreateBrowserRouter } from "@sentry/react"
import { createBrowserRouter, createHashRouter } from "react-router-dom"
import { ErrorElement } from "./components/common/ErrorElement"
import { NotFound } from "./components/common/NotFound"
import { buildGlobRoutes } from "./lib/route-builder"
const globTree = import.meta.glob("./pages/**/*.tsx")
const tree = buildGlobRoutes(globTree)
let routerCreator =
IN_ELECTRON || globalThis["__DEBUG_PROXY__"] ? createHashRouter : createBrowserRouter
if (window.SENTRY_RELEASE) {
routerCreator = wrapCreateBrowserRouter(routerCreator)
}
export const router = routerCreator([
{
path: "/",
lazy: () => import("./App"),
children: tree,
errorElement: <ErrorElement />,
},
{
path: "*",
element: <NotFound />,
},
])

View File

@ -1,4 +1,4 @@
export default {
tagFilter: (tag: string) => tag.startsWith("v") && !tag.includes("nightly"),
dry: true,
dry: !process.env.CI,
}

View File

@ -3,6 +3,7 @@ import { resolve } from "node:path"
import { defineConfig } from "electron-vite"
import { viteRenderBaseConfig } from "./configs/vite.render.config"
import { createPlatformSpecificImportPlugin } from "./plugins/vite/specific-import"
export default defineConfig({
main: {
@ -41,6 +42,8 @@ export default defineConfig({
renderer: {
...viteRenderBaseConfig,
plugins: [...viteRenderBaseConfig.plugins, createPlatformSpecificImportPlugin(true)],
root: "apps/renderer",
build: {
outDir: "dist/renderer",

View File

@ -154,6 +154,7 @@ const config: ForgeConfig = {
new MakerSquirrel({
name: "Follow",
setupIcon: "resources/icon.ico",
iconUrl: "https://app.follow.is/favicon.ico",
}),
new MakerAppImage({
options: {

View File

@ -54,9 +54,9 @@
"entry_actions.copied_notify": "{{which}}已複製到剪貼板",
"entry_actions.copy_link": "複製連結",
"entry_actions.copy_title": "複製標題",
"entry_actions.failed_to_save_to_eagle": "無法保存至 Eagle",
"entry_actions.failed_to_save_to_instapaper": "無法保存至 Instapaper",
"entry_actions.failed_to_save_to_readwise": "無法保存至 Readwise",
"entry_actions.failed_to_save_to_eagle": "無法保存至 Eagle",
"entry_actions.failed_to_save_to_instapaper": "無法保存至 Instapaper",
"entry_actions.failed_to_save_to_readwise": "無法保存至 Readwise",
"entry_actions.mark_as_read": "標記為已讀",
"entry_actions.mark_as_unread": "標記為未讀",
"entry_actions.open_in_browser": "在{{which}}中開啟",
@ -64,17 +64,17 @@
"entry_actions.save_media_to_eagle": "保存媒體至 Eagle",
"entry_actions.save_to_instapaper": "保存至 Instapaper",
"entry_actions.save_to_readwise": "保存至 Readwise",
"entry_actions.saved_to_eagle": "已保存至 Eagle",
"entry_actions.saved_to_instapaper": "已保存至 Instapaper",
"entry_actions.saved_to_readwise": "已保存至 Readwise",
"entry_actions.saved_to_eagle": "已保存至 Eagle",
"entry_actions.saved_to_instapaper": "已保存至 Instapaper",
"entry_actions.saved_to_readwise": "已保存至 Readwise",
"entry_actions.share": "分享",
"entry_actions.star": "收藏",
"entry_actions.starred": "已收藏",
"entry_actions.starred": "已收藏",
"entry_actions.tip": "贊助",
"entry_actions.unstar": "取消收藏",
"entry_actions.unstarred": "已取消收藏",
"entry_actions.unstarred": "已取消收藏",
"entry_actions.view_source_content": "查看原始內容",
"entry_column.filtered_content_tip": "你已隱藏篩選內容",
"entry_column.filtered_content_tip": "你已隱藏篩選內容",
"entry_column.filtered_content_tip_2": "除了以上顯示的條目,還有一些已篩選的內容。",
"entry_column.refreshing": "刷新新條目中...",
"entry_content.ai_summary": "AI 摘要",
@ -85,12 +85,12 @@
"entry_content.readability_notice": "此內容由 Readability 提供。如果你發現排版異常,請到來源網站查看原始內容。",
"entry_content.render_error": "渲染錯誤:",
"entry_content.report_issue": "報告問題",
"entry_content.support_amount": "{{amount}} 人支持了此訂閱源的創作者",
"entry_content.support_amount": "{{amount}} 人支持了此訂閱源的創作者",
"entry_content.support_creator": "支持創作者",
"entry_content.web_app_notice": "網頁應用可能不支持此內容類型。你可以下載桌面應用程式。",
"entry_list.zero_unread": "全部已讀",
"entry_list_header.daily_report": "每日報告",
"entry_list_header.hide_no_image_items": "隱藏沒有圖片的條目",
"entry_list_header.hide_no_image_items": "隱藏沒有圖片的條目",
"entry_list_header.items": "項目",
"entry_list_header.new_entries_available": "有新條目可用",
"entry_list_header.refetch": "重新抓取",
@ -105,12 +105,12 @@
"entry_list_header.unread": "未讀",
"feed_claim_modal.choose_verification_method": "有三種驗證方法可選,你可以選擇其中一種進行驗證。",
"feed_claim_modal.claim_button": "認領",
"feed_claim_modal.content_instructions": "複製以下內容並貼到你最新的 RSS 訂閱源中",
"feed_claim_modal.content_instructions": "複製以下內容並貼到你最新的 RSS 訂閱源中",
"feed_claim_modal.description_current": "當前描述:",
"feed_claim_modal.description_instructions": "複製以下內容並貼到你的 RSS 訂閱源中的 <code /> 欄位",
"feed_claim_modal.description_instructions": "複製以下內容並貼到你的 RSS 訂閱源中的 <code /> 欄位",
"feed_claim_modal.failed_to_load": "無法加載認領信息",
"feed_claim_modal.rss_format_choice": "RSS 生成器通常有兩種格式可選。請根據需要複製以下的 XML 和 JSON 格式。",
"feed_claim_modal.rss_instructions": "複製以下代碼並貼到你的 RSS 生成器中",
"feed_claim_modal.rss_instructions": "複製以下代碼並貼到你的 RSS 生成器中",
"feed_claim_modal.rss_json_format": "JSON 格式",
"feed_claim_modal.rss_xml_format": "XML 格式",
"feed_claim_modal.rsshub_notice": "此訂閱源由 RSSHub 提供,緩存時間為 1 小時。發布內容後,請等待最多 1 小時更改才能生效。",
@ -122,23 +122,23 @@
"feed_form.add_follow": "新增關注",
"feed_form.category": "分類",
"feed_form.category_description": "默認情況下,你的關注會按網站分組。",
"feed_form.error_fetching_feed": "獲取訂閱源時出錯",
"feed_form.error_fetching_feed": "獲取訂閱源時出錯",
"feed_form.fee": "跟隨費用",
"feed_form.fee_description": "要跟隨此清單,您必須向清單創建者支付費用。",
"feed_form.feed_not_found": "未找到相關資訊流",
"feed_form.feed_not_found": "未找到相關資訊流",
"feed_form.feedback": "反饋",
"feed_form.follow": "關注",
"feed_form.follow_with_fee": "跟隨需要 {{fee}} Power",
"feed_form.followed": "🎉 已關注",
"feed_form.followed": "🎉 已關注",
"feed_form.private_follow": "私人關注",
"feed_form.private_follow_description": "此關注是否在你的個人資料頁面上公開可見",
"feed_form.private_follow_description": "此關注是否在你的個人資料頁面上公開可見",
"feed_form.retry": "重試",
"feed_form.title": "標題",
"feed_form.title_description": "此資訊流的自定義標題。留空則使用默認標題。",
"feed_form.unfollow": "取消關注",
"feed_form.update": "更新",
"feed_form.update_follow": "更新關注",
"feed_form.updated": "🎉 已更新",
"feed_form.updated": "🎉 已更新",
"feed_form.view": "視圖",
"feed_item.claimed_by_owner": "訂閲源所有者",
"feed_item.claimed_by_unknown": "未知所有者",
@ -152,7 +152,7 @@
"feed_view_type.pictures": "圖片",
"feed_view_type.social_media": "社交媒體",
"feed_view_type.videos": "視頻",
"mark_all_read_button.auto_confirm_info": "將在 3 秒後自動確認",
"mark_all_read_button.auto_confirm_info": "將在 3 秒後自動確認",
"mark_all_read_button.confirm": "確認",
"mark_all_read_button.confirm_mark_all": "確定將 <which /> 標記為已讀?",
"mark_all_read_button.confirm_mark_all_info": "確定將所有標記為已讀?",
@ -163,7 +163,7 @@
"notify.unSupportWidth": "{{app_name}} 目前尚未支援流動裝置",
"notify.unSupportWidth_1": "您的裝置寬度為 <b>{{width}}</b>,低於支援的最小寬度 {{minWidth}}",
"notify.unSupportWidth_2": "請切換至桌面應用程式繼續使用 {{app_name}} <br /> 下載連結:<url />",
"notify.unfollow_feed": "已取消關注 <FeedItem />",
"notify.unfollow_feed": "已取消關注 <FeedItem />",
"notify.update_info": "{{app_name}} 已準備好更新!",
"notify.update_info_1": "點擊以重新啟動",
"player.back_10s": "倒退 10 秒",
@ -181,7 +181,7 @@
"player.volume": "音量",
"resize.tooltip.double_click_to_collapse": "<b>雙擊</b>以摺疊",
"resize.tooltip.drag_to_resize": "<b>拖曳</b>以調整大小",
"search.empty.no_results": "未找到結果",
"search.empty.no_results": "未找到結果",
"search.group.entries": "條目",
"search.group.feeds": "訂閱源",
"search.options.all": "所有",
@ -239,7 +239,7 @@
"tip_modal.feed_owner": "訂閱源擁有者",
"tip_modal.low_balance": "你的餘額不足以覆蓋此贊助。請調整金額。",
"tip_modal.no_wallet": "你尚未擁有錢包。請創建一個錢包以進行贊助。",
"tip_modal.tip_amount_sent": "已經發送給作者",
"tip_modal.tip_amount_sent": "已經發送給作者",
"tip_modal.tip_now": "立刻贊助",
"tip_modal.tip_sent": "贊助成功!感謝你的支持。",
"tip_modal.tip_support": "⭐ 贊助以顯示你的支持!",
@ -284,6 +284,7 @@
"words.search": "搜尋",
"words.starred": "收藏",
"words.title": "標題",
"words.transform": "轉換",
"words.trending": "熱門",
"words.undo": "撤銷",
"words.unread": "未讀",

View File

@ -16,6 +16,7 @@
"words.back": "返回",
"words.copy": "複製",
"words.create": "創建",
"words.delete": "刪除",
"words.edit": "編輯",
"words.entry": "條目",
"words.id": "ID",

View File

@ -89,6 +89,9 @@
"general.app": "應用程式",
"general.data_persist.description": "本地保存數據以啟用離線存取和本地搜尋。",
"general.data_persist.label": "離線使用時保留數據",
"general.export.button": "匯出",
"general.export.description": "匯出你的訂閱到 OPML 文件",
"general.export.label": "匯出訂閱",
"general.group_by_date.description": "按日期分組條目。",
"general.group_by_date.label": "按日期分組",
"general.language": "語言",
@ -159,6 +162,8 @@
"lists.create": "建立新清單",
"lists.created.error": "建立清單失敗。",
"lists.created.success": "成功建立清單!",
"lists.delete.error": "刪除清單失敗。",
"lists.delete.success": "成功刪除清單!",
"lists.description": "描述",
"lists.earnings": "收益",
"lists.edit.error": "編輯清單失敗。",
@ -184,7 +189,7 @@
"lists.submit": "提交",
"lists.subscriptions": "訂閱",
"lists.title": "標題",
"lists.view": "查看",
"lists.view": "視圖",
"profile.avatar.label": "頭像",
"profile.handle.description": "你的唯一識別符。",
"profile.handle.label": "識別符",
@ -226,6 +231,7 @@
"wallet.transactions.to": "發送至",
"wallet.transactions.tx": "交易",
"wallet.transactions.type": "類型",
"wallet.transactions.types.all": "全部",
"wallet.transactions.types.burn": "銷燬",
"wallet.transactions.types.mint": "鑄造",
"wallet.transactions.types.purchase": "購買",

View File

@ -1,7 +1,7 @@
{
"name": "Follow",
"type": "module",
"version": "0.0.1-alpha.20",
"version": "0.0.1-alpha.21",
"private": true,
"packageManager": "pnpm@9.11.0",
"description": "Next generation information browser",

View File

@ -11,27 +11,7 @@ export const env = createEnv({
VITE_SENTRY_DSN: z.string().optional(),
VITE_POSTHOG_KEY: z.string().optional(),
VITE_INBOXES_EMAIL: z.string().default("@follow.re"),
VITE_FIREBASE_CONFIG: z
.string()
.transform((content) => {
try {
return JSON.parse(content)
} catch {
return z.NEVER
}
})
.pipe(
z.object({
apiKey: z.string(),
authDomain: z.string(),
projectId: z.string(),
storageBucket: z.string(),
messagingSenderId: z.string(),
appId: z.string(),
measurementId: z.string(),
}),
)
.optional(),
VITE_FIREBASE_CONFIG: z.string().optional(),
},
emptyStringAsUndefined: true,

View File

@ -1,6 +1,6 @@
import type { Plugin, UserConfig } from "vite"
export function createDependencyChunksPlugin(dependencies: string[] | string[][]): Plugin {
export function createDependencyChunksPlugin(dependencies: string[][]): Plugin {
return {
name: "dependency-chunks",
config(config: UserConfig) {

View File

@ -0,0 +1,42 @@
import type { Plugin } from "vite"
export function createPlatformSpecificImportPlugin(isElectron = false): Plugin {
return {
name: "platform-specific-import",
enforce: "pre",
async resolveId(source, importer) {
if (!importer) {
return null
}
const allowExts = [".js", ".jsx", ".ts", ".tsx"]
if (!allowExts.some((ext) => importer.endsWith(ext))) return null
if (importer.includes("node_modules")) return null
const [path, query] = source.split("?")
if (path.startsWith(".") || path.startsWith("/")) {
const priorities = isElectron
? [".electron.ts", ".electron.tsx", ".electron.js", ".electron.jsx"]
: [".web.ts", ".web.tsx", ".web.js", ".web.jsx"]
for (const ext of priorities) {
const resolvedPath = await this.resolve(
`${path}${ext}${query ? `?${query}` : ""}`,
importer,
{
skipSelf: true,
},
)
if (resolvedPath) {
return resolvedPath.id
}
}
}
return null
},
}
}

1
types/vite.d.ts vendored
View File

@ -6,6 +6,7 @@ interface ImportMetaEnv {
VITE_IMGPROXY_URL: string
VITE_SENTRY_DSN: string
VITE_POSTHOG_KEY: string
VITE_FIREBASE_CONFIG: string
}
interface ImportMeta {

View File

@ -11,6 +11,7 @@ import { viteRenderBaseConfig } from "./configs/vite.render.config"
import type { env as EnvType } from "./packages/shared/src/env"
import { createDependencyChunksPlugin } from "./plugins/vite/deps"
import { htmlInjectPlugin } from "./plugins/vite/html-inject"
import { createPlatformSpecificImportPlugin } from "./plugins/vite/specific-import"
const __dirname = fileURLToPath(new URL(".", import.meta.url))
const isCI = process.env.CI === "true" || process.env.CI === "1"
@ -75,7 +76,8 @@ export default ({ mode }) => {
devPrint(),
createDependencyChunksPlugin([
// React framework
["react", "react-dom", "react-router-dom", "react-error-boundary", "react-dom/server"],
["react", "react-dom"],
["react-error-boundary", "react-dom/server", "react-router-dom"],
// Data Statement
["zustand", "jotai", "use-context-selector", "immer", "dexie"],
// Remark
@ -131,7 +133,6 @@ export default ({ mode }) => {
"@tanstack/react-query-persist-client",
"@tanstack/query-sync-storage-persister",
],
["blurhash", "react-blurhash"],
["tldts"],
["shiki", "@shikijs/transformers"],
["@sentry/react", "posthog-js"],
@ -139,6 +140,8 @@ export default ({ mode }) => {
["swiper"],
]),
createPlatformSpecificImportPlugin(false),
],
define: {