diff --git a/packages/common/env/src/global.ts b/packages/common/env/src/global.ts index 21fbdbf814..dc694d55e1 100644 --- a/packages/common/env/src/global.ts +++ b/packages/common/env/src/global.ts @@ -47,6 +47,7 @@ export type Environment = { isElectron: boolean; isDesktopWeb: boolean; isMobileWeb: boolean; + isStandalone?: boolean; // Device isLinux: boolean; @@ -116,6 +117,7 @@ export function setupGlobal() { isFireFox: uaHelper.isFireFox, isChrome: uaHelper.isChrome, isIOS: uaHelper.isIOS, + isStandalone: uaHelper.isStandalone, }; // Chrome on iOS is still Safari if (environment.isChrome && !environment.isIOS) { diff --git a/packages/common/env/src/ua-helper.ts b/packages/common/env/src/ua-helper.ts index 8a0322fcfb..f1149f00be 100644 --- a/packages/common/env/src/ua-helper.ts +++ b/packages/common/env/src/ua-helper.ts @@ -8,6 +8,7 @@ export class UaHelper { public isMobile = false; public isChrome = false; public isIOS = false; + public isStandalone = false; getChromeVersion = (): number => { let raw = this.navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./); @@ -30,6 +31,13 @@ export class UaHelper { return Boolean(this.uaMap[isUseragent]); } + private isStandaloneMode() { + if ('standalone' in window.navigator) { + return !!window.navigator.standalone; + } + return !!window.matchMedia('(display-mode: standalone)').matches; + } + private initUaFlags() { this.isLinux = this.checkUseragent('linux'); this.isMacOs = this.checkUseragent('mac'); @@ -39,6 +47,7 @@ export class UaHelper { this.isMobile = this.checkUseragent('mobile'); this.isChrome = this.checkUseragent('chrome'); this.isIOS = this.checkUseragent('ios'); + this.isStandalone = this.isStandaloneMode(); } } diff --git a/packages/frontend/component/src/hooks/index.ts b/packages/frontend/component/src/hooks/index.ts index 93ae56c400..1655abe94f 100644 --- a/packages/frontend/component/src/hooks/index.ts +++ b/packages/frontend/component/src/hooks/index.ts @@ -1,2 +1,4 @@ export { useAutoFocus, useAutoSelect } from './focus-and-select'; export { useRefEffect } from './use-ref-effect'; +export * from './use-theme-color-meta'; +export * from './use-theme-value'; diff --git a/packages/frontend/component/src/hooks/use-theme-color-meta.ts b/packages/frontend/component/src/hooks/use-theme-color-meta.ts new file mode 100644 index 0000000000..fa1a6aaf2b --- /dev/null +++ b/packages/frontend/component/src/hooks/use-theme-color-meta.ts @@ -0,0 +1,45 @@ +import { useLayoutEffect } from 'react'; + +import { useThemeValueV1, useThemeValueV2 } from './use-theme-value'; + +let meta: HTMLMetaElement | null = null; + +function getMeta() { + if (meta) return meta; + + const exists = document.querySelector('meta[name="theme-color"]'); + if (exists) { + meta = exists as HTMLMetaElement; + return meta; + } + + // create and append meta + meta = document.createElement('meta'); + meta.name = 'theme-color'; + document.head.append(meta); + return meta; +} + +export const useThemeColorMeta = (color: string) => { + useLayoutEffect(() => { + const meta = getMeta(); + const old = meta.content; + meta.content = color; + + return () => { + meta.content = old; + }; + }, [color]); +}; + +export const useThemeColorV1 = ( + ...args: Parameters +) => { + useThemeColorMeta(useThemeValueV1(...args)); +}; + +export const useThemeColorV2 = ( + ...args: Parameters +) => { + useThemeColorMeta(useThemeValueV2(...args)); +}; diff --git a/packages/frontend/component/src/hooks/use-theme-value.ts b/packages/frontend/component/src/hooks/use-theme-value.ts new file mode 100644 index 0000000000..c549a0c6c8 --- /dev/null +++ b/packages/frontend/component/src/hooks/use-theme-value.ts @@ -0,0 +1,19 @@ +import { type AffineTheme, darkTheme, lightTheme } from '@toeverything/theme'; +import { + type AffineThemeKeyV2, + darkThemeV2, + lightThemeV2, +} from '@toeverything/theme/v2'; +import { useTheme } from 'next-themes'; + +export const useThemeValueV2 = (key: AffineThemeKeyV2) => { + const { resolvedTheme } = useTheme(); + + return resolvedTheme === 'dark' ? darkThemeV2[key] : lightThemeV2[key]; +}; + +export const useThemeValueV1 = (key: keyof Omit) => { + const { resolvedTheme } = useTheme(); + + return resolvedTheme === 'dark' ? darkTheme[key] : lightTheme[key]; +}; diff --git a/packages/frontend/component/src/index.ts b/packages/frontend/component/src/index.ts index 9ba18efae0..2ad4ce1a4d 100644 --- a/packages/frontend/component/src/index.ts +++ b/packages/frontend/component/src/index.ts @@ -21,6 +21,7 @@ export * from './ui/modal'; export * from './ui/notification'; export * from './ui/popover'; export * from './ui/radio'; +export * from './ui/safe-area'; export * from './ui/scrollbar'; export * from './ui/skeleton'; export * from './ui/slider'; diff --git a/packages/frontend/component/src/ui/menu/mobile/root.tsx b/packages/frontend/component/src/ui/menu/mobile/root.tsx index fbe67dfc8e..bf4b33c837 100644 --- a/packages/frontend/component/src/ui/menu/mobile/root.tsx +++ b/packages/frontend/component/src/ui/menu/mobile/root.tsx @@ -111,10 +111,6 @@ export const MobileMenu = ({ className: clsx(className, styles.mobileMenuModal), ...otherContentOptions, }} - contentWrapperStyle={{ - alignItems: 'end', - paddingBottom: 10, - }} >
( }} {...otherOverlayOptions} > -
( {children} -
+ diff --git a/packages/frontend/component/src/ui/modal/styles.css.ts b/packages/frontend/component/src/ui/modal/styles.css.ts index 78171882eb..7af5448555 100644 --- a/packages/frontend/component/src/ui/modal/styles.css.ts +++ b/packages/frontend/component/src/ui/modal/styles.css.ts @@ -83,7 +83,7 @@ export const modalContentWrapper = style({ 'screen and (width <= 640px)': { // todo: adjust animation alignItems: 'flex-end', - paddingBottom: 32, + paddingBottom: 'env(safe-area-inset-bottom, 20px)', }, }, diff --git a/packages/frontend/component/src/ui/safe-area/index.tsx b/packages/frontend/component/src/ui/safe-area/index.tsx new file mode 100644 index 0000000000..dcf97cf960 --- /dev/null +++ b/packages/frontend/component/src/ui/safe-area/index.tsx @@ -0,0 +1,49 @@ +import { assignInlineVars } from '@vanilla-extract/dynamic'; +import clsx from 'clsx'; +import { forwardRef, type HTMLAttributes } from 'react'; + +import { withUnit } from '../../utils/with-unit'; +import { bottomOffsetVar, safeArea, topOffsetVar } from './style.css'; + +interface SafeAreaProps extends HTMLAttributes { + top?: boolean; + bottom?: boolean; + topOffset?: number | string; + bottomOffset?: number | string; +} + +export const SafeArea = forwardRef( + function SafeArea( + { + children, + className, + style, + top, + bottom, + topOffset = 0, + bottomOffset = 0, + ...attrs + }, + ref + ) { + return ( +
+ {children} +
+ ); + } +); diff --git a/packages/frontend/component/src/ui/safe-area/style.css.ts b/packages/frontend/component/src/ui/safe-area/style.css.ts new file mode 100644 index 0000000000..af217c0f4e --- /dev/null +++ b/packages/frontend/component/src/ui/safe-area/style.css.ts @@ -0,0 +1,22 @@ +import { createVar, style } from '@vanilla-extract/css'; + +export const topOffsetVar = createVar(); +export const bottomOffsetVar = createVar(); + +export const safeArea = style({ + selectors: { + '&[data-top]': { + paddingTop: `calc(${topOffsetVar} + 12px)`, + }, + '&[data-bottom]': { + paddingBottom: `calc(${bottomOffsetVar} + 0px)`, + }, + '&[data-standalone][data-top]': { + paddingTop: `calc(env(safe-area-inset-top, 12px) + ${topOffsetVar})`, + }, + '&[data-standalone][data-bottom]': { + // paddingBottom: 'env(safe-area-inset-bottom, 12px)', + paddingBottom: `calc(env(safe-area-inset-bottom, 0px) + ${bottomOffsetVar})`, + }, + }, +}); diff --git a/packages/frontend/core/public/apple-touch-icon.png b/packages/frontend/core/public/apple-touch-icon.png index af8af0dbf8..1251d86c1c 100644 Binary files a/packages/frontend/core/public/apple-touch-icon.png and b/packages/frontend/core/public/apple-touch-icon.png differ diff --git a/packages/frontend/mobile/src/components/app-tabs/index.tsx b/packages/frontend/mobile/src/components/app-tabs/index.tsx index 552d7e5b1a..de1df39f23 100644 --- a/packages/frontend/mobile/src/components/app-tabs/index.tsx +++ b/packages/frontend/mobile/src/components/app-tabs/index.tsx @@ -1,3 +1,4 @@ +import { SafeArea } from '@affine/component'; import { WorkbenchLink, WorkbenchService, @@ -39,29 +40,31 @@ export const AppTabs = () => { const location = useLiveData(workbench.location$); return ( -
    - {routes.map(route => { - const Link = route.LinkComponent || WorkbenchLink; + +
      + {routes.map(route => { + const Link = route.LinkComponent || WorkbenchLink; - const isActive = route.isActive - ? route.isActive(location) - : location.pathname === route.to; - return ( - -
    • - -
    • - - ); - })} -
    + const isActive = route.isActive + ? route.isActive(location) + : location.pathname === route.to; + return ( + +
  • + +
  • + + ); + })} +
+ ); }; diff --git a/packages/frontend/mobile/src/components/app-tabs/styles.css.ts b/packages/frontend/mobile/src/components/app-tabs/styles.css.ts index c464b73e8f..38d081127c 100644 --- a/packages/frontend/mobile/src/components/app-tabs/styles.css.ts +++ b/packages/frontend/mobile/src/components/app-tabs/styles.css.ts @@ -4,23 +4,24 @@ import { style } from '@vanilla-extract/css'; import { globalVars } from '../../styles/mobile.css'; export const appTabs = style({ - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - backgroundColor: cssVarV2('layer/background/secondary'), borderTop: `1px solid ${cssVarV2('layer/insideBorder/border')}`, width: '100dvw', - height: `calc(${globalVars.appTabHeight} + 2px)`, - padding: 16, - gap: 15.5, position: 'fixed', - paddingBottom: 18, bottom: -2, zIndex: 1, }); +export const appTabsInner = style({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + gap: 15.5, + + height: `calc(${globalVars.appTabHeight} + 2px)`, + padding: 16, +}); export const tabItem = style({ display: 'flex', alignItems: 'center', diff --git a/packages/frontend/mobile/src/components/page-header/index.tsx b/packages/frontend/mobile/src/components/page-header/index.tsx index 9cf509887e..9a7383b3dd 100644 --- a/packages/frontend/mobile/src/components/page-header/index.tsx +++ b/packages/frontend/mobile/src/components/page-header/index.tsx @@ -1,4 +1,4 @@ -import { IconButton } from '@affine/component'; +import { IconButton, SafeArea } from '@affine/component'; import { ArrowLeftSmallIcon } from '@blocksuite/icons/rc'; import clsx from 'clsx'; import { @@ -42,7 +42,7 @@ export interface PageHeaderProps suffixClassName?: string; suffixStyle?: React.CSSProperties; } -export const PageHeader = forwardRef( +export const PageHeader = forwardRef( function PageHeader( { back, @@ -65,38 +65,41 @@ export const PageHeader = forwardRef( }, [backAction]); return ( -
-
- {back ? ( - } - /> - ) : null} - {prefix} -
+
+
+ {back ? ( + } + /> + ) : null} + {prefix} +
-
- {children} -
+
+ {children} +
-
- {suffix} -
-
+
+ {suffix} +
+
+ ); } ); diff --git a/packages/frontend/mobile/src/components/page-header/styles.css.ts b/packages/frontend/mobile/src/components/page-header/styles.css.ts index 2ea30885ab..6a2f9af065 100644 --- a/packages/frontend/mobile/src/components/page-header/styles.css.ts +++ b/packages/frontend/mobile/src/components/page-header/styles.css.ts @@ -3,18 +3,18 @@ import { style } from '@vanilla-extract/css'; export const root = style({ width: '100%', - minHeight: 44, - padding: '0 6px', - paddingTop: 16, - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - position: 'sticky', top: 0, zIndex: 1, backgroundColor: cssVarV2('layer/background/secondary'), }); +export const inner = style({ + minHeight: 44, + padding: '0 6px', + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', +}); export const content = style({ selectors: { '&.center': { diff --git a/packages/frontend/mobile/src/pages/workspace/all.tsx b/packages/frontend/mobile/src/pages/workspace/all.tsx index 129266e697..8361d37ff9 100644 --- a/packages/frontend/mobile/src/pages/workspace/all.tsx +++ b/packages/frontend/mobile/src/pages/workspace/all.tsx @@ -1,11 +1,17 @@ +import { SafeArea, useThemeColorV2 } from '@affine/component'; + import { AppTabs } from '../../components'; import { AllDocList, AllDocsHeader, AllDocsMenu } from '../../views'; export const Component = () => { + useThemeColorV2('layer/background/secondary'); + return ( <> } /> - + + + ); diff --git a/packages/frontend/mobile/src/pages/workspace/collection/detail.tsx b/packages/frontend/mobile/src/pages/workspace/collection/detail.tsx index 888ec06289..34eccd2ecd 100644 --- a/packages/frontend/mobile/src/pages/workspace/collection/detail.tsx +++ b/packages/frontend/mobile/src/pages/workspace/collection/detail.tsx @@ -1,4 +1,4 @@ -import { notify } from '@affine/component'; +import { notify, useThemeColorV2 } from '@affine/component'; import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper'; import { CollectionService } from '@affine/core/modules/collection'; import { isEmptyCollection } from '@affine/core/pages/workspace/collection'; @@ -16,6 +16,7 @@ import { AppTabs } from '../../../components'; import { CollectionDetail, EmptyCollection } from '../../../views'; export const Component = () => { + useThemeColorV2('layer/background/secondary'); const { collectionService, globalContextService, workspaceService } = useServices({ WorkspaceService, diff --git a/packages/frontend/mobile/src/pages/workspace/collection/index.tsx b/packages/frontend/mobile/src/pages/workspace/collection/index.tsx index 6d1a83d80c..21e75c971a 100644 --- a/packages/frontend/mobile/src/pages/workspace/collection/index.tsx +++ b/packages/frontend/mobile/src/pages/workspace/collection/index.tsx @@ -1,7 +1,10 @@ +import { useThemeColorV2 } from '@affine/component'; + import { AppTabs } from '../../../components'; import { AllDocsHeader, CollectionList } from '../../../views'; export const Component = () => { + useThemeColorV2('layer/background/secondary'); return ( <> diff --git a/packages/frontend/mobile/src/pages/workspace/detail/mobile-detail-page.tsx b/packages/frontend/mobile/src/pages/workspace/detail/mobile-detail-page.tsx index 01cfb66e05..899df02a8c 100644 --- a/packages/frontend/mobile/src/pages/workspace/detail/mobile-detail-page.tsx +++ b/packages/frontend/mobile/src/pages/workspace/detail/mobile-detail-page.tsx @@ -1,3 +1,4 @@ +import { useThemeColorV2 } from '@affine/component'; import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton'; import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary'; import { PageDetailEditor } from '@affine/core/components/page-detail-editor'; @@ -213,6 +214,7 @@ const notFound = ( ); export const Component = () => { + useThemeColorV2('layer/background/primary'); const params = useParams(); const pageId = params.pageId; diff --git a/packages/frontend/mobile/src/pages/workspace/home.tsx b/packages/frontend/mobile/src/pages/workspace/home.tsx index 970a70ffd1..d055b5e35c 100644 --- a/packages/frontend/mobile/src/pages/workspace/home.tsx +++ b/packages/frontend/mobile/src/pages/workspace/home.tsx @@ -1,3 +1,4 @@ +import { SafeArea, useThemeColorV2 } from '@affine/component'; import { ExplorerCollections, ExplorerFavorites, @@ -11,24 +12,28 @@ import { AppTabs } from '../../components'; import { HomeHeader, RecentDocs } from '../../views'; export const Component = () => { + useThemeColorV2('layer/background/secondary'); + return ( -
- - {runtimeConfig.enableOrganize && } - - - -
+ +
+ + {runtimeConfig.enableOrganize && } + + + +
+
); diff --git a/packages/frontend/mobile/src/pages/workspace/search.tsx b/packages/frontend/mobile/src/pages/workspace/search.tsx index 687aa90a75..f480c7dd82 100644 --- a/packages/frontend/mobile/src/pages/workspace/search.tsx +++ b/packages/frontend/mobile/src/pages/workspace/search.tsx @@ -1,3 +1,4 @@ +import { SafeArea, useThemeColorV2 } from '@affine/component'; import { CollectionService } from '@affine/core/modules/collection'; import { type QuickSearchItem, @@ -112,6 +113,7 @@ const WithQueryList = () => { }; export const Component = () => { + useThemeColorV2('layer/background/secondary'); const searchInput = useLiveData(searchInput$); const searchService = useService(MobileSearchService); @@ -133,15 +135,17 @@ export const Component = () => { return ( <> -
- -
+ +
+ +
+
{searchInput ? : } diff --git a/packages/frontend/mobile/src/pages/workspace/tag/detail.tsx b/packages/frontend/mobile/src/pages/workspace/tag/detail.tsx index e1d0d08f30..a2bfb3c824 100644 --- a/packages/frontend/mobile/src/pages/workspace/tag/detail.tsx +++ b/packages/frontend/mobile/src/pages/workspace/tag/detail.tsx @@ -1,3 +1,4 @@ +import { useThemeColorV2 } from '@affine/component'; import { TagService } from '@affine/core/modules/tag'; import { PageNotFound } from '@affine/core/pages/404'; import { @@ -11,6 +12,7 @@ import { useParams } from 'react-router-dom'; import { TagDetail } from '../../../views'; export const Component = () => { + useThemeColorV2('layer/background/secondary'); const params = useParams(); const tagId = params.tagId; diff --git a/packages/frontend/mobile/src/pages/workspace/tag/index.tsx b/packages/frontend/mobile/src/pages/workspace/tag/index.tsx index 0cfd2487b2..98d7826407 100644 --- a/packages/frontend/mobile/src/pages/workspace/tag/index.tsx +++ b/packages/frontend/mobile/src/pages/workspace/tag/index.tsx @@ -1,7 +1,10 @@ +import { useThemeColorV2 } from '@affine/component'; + import { AppTabs } from '../../../components'; import { AllDocsHeader, TagList } from '../../../views'; export const Component = () => { + useThemeColorV2('layer/background/secondary'); return ( <> diff --git a/packages/frontend/mobile/src/styles/mobile.css.ts b/packages/frontend/mobile/src/styles/mobile.css.ts index d7f315ed91..0c00a2ea5e 100644 --- a/packages/frontend/mobile/src/styles/mobile.css.ts +++ b/packages/frontend/mobile/src/styles/mobile.css.ts @@ -13,6 +13,7 @@ globalStyle(':root', { globalStyle('body', { height: 'auto', + minHeight: '100dvh', }); globalStyle('body:has(#app-tabs)', { paddingBottom: globalVars.appTabHeight, @@ -21,6 +22,3 @@ globalStyle('html', { overflowY: 'auto', background: cssVarV2('layer/background/secondary'), }); -globalStyle('body[data-scroll-locked][style]', { - overflow: 'clip !important', -}); diff --git a/packages/frontend/mobile/src/views/all-docs/header.tsx b/packages/frontend/mobile/src/views/all-docs/header.tsx index 32547d95c5..3e129e15ef 100644 --- a/packages/frontend/mobile/src/views/all-docs/header.tsx +++ b/packages/frontend/mobile/src/views/all-docs/header.tsx @@ -1,7 +1,7 @@ -import { IconButton, MobileMenu } from '@affine/component'; +import { IconButton, MobileMenu, SafeArea } from '@affine/component'; import { MoreHorizontalIcon } from '@blocksuite/icons/rc'; -import { header, headerSpace } from './style.css'; +import { header, headerContent, headerSpace } from './style.css'; import { AllDocsTabs } from './tabs'; export interface AllDocsHeaderProps { @@ -11,17 +11,21 @@ export interface AllDocsHeaderProps { export const AllDocsHeader = ({ operations }: AllDocsHeaderProps) => { return ( <> -
- -
- {operations ? ( - - } /> - - ) : null} -
-
-
+ +
+ +
+ {operations ? ( + + } /> + + ) : null} +
+
+
+ +
+ ); }; diff --git a/packages/frontend/mobile/src/views/all-docs/style.css.ts b/packages/frontend/mobile/src/views/all-docs/style.css.ts index b9b33cc71a..90ecc5775c 100644 --- a/packages/frontend/mobile/src/views/all-docs/style.css.ts +++ b/packages/frontend/mobile/src/views/all-docs/style.css.ts @@ -1,32 +1,31 @@ import { cssVarV2 } from '@toeverything/theme/v2'; import { style } from '@vanilla-extract/css'; -const headerContentHeight = 56; -const headerPaddingTop = 16; - const basicHeader = style({ width: '100%', - height: headerContentHeight + headerPaddingTop, + height: 56, }); -export const header = style([ +export const header = style({ + width: '100%', + position: 'fixed', + top: 0, + backgroundColor: cssVarV2('layer/background/secondary'), + zIndex: 1, +}); +export const headerSpace = style([basicHeader]); +export const headerContent = style([ basicHeader, { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16, - padding: `${headerPaddingTop}px 16px 0px 16px`, - - position: 'fixed', - top: 0, - backgroundColor: cssVarV2('layer/background/secondary'), - zIndex: 1, + padding: `0px 16px`, }, ]); -export const headerSpace = style([basicHeader]); export const tabs = style({ - height: headerContentHeight, + height: 56, gap: 16, display: 'flex', alignItems: 'center', diff --git a/packages/frontend/mobile/src/views/home-header/index.tsx b/packages/frontend/mobile/src/views/home-header/index.tsx index b9e6861c51..37b92660ef 100644 --- a/packages/frontend/mobile/src/views/home-header/index.tsx +++ b/packages/frontend/mobile/src/views/home-header/index.tsx @@ -1,4 +1,8 @@ -import { IconButton, startScopedViewTransition } from '@affine/component'; +import { + IconButton, + SafeArea, + startScopedViewTransition, +} from '@affine/component'; import { openSettingModalAtom } from '@affine/core/atoms'; import { WorkbenchService } from '@affine/core/modules/workbench'; import { useI18n } from '@affine/i18n'; @@ -41,7 +45,7 @@ export const HomeHeader = () => { return (
-
+
@@ -60,8 +64,10 @@ export const HomeHeader = () => {
-
-
+ + +
+
); }; diff --git a/packages/frontend/mobile/src/views/home-header/styles.css.ts b/packages/frontend/mobile/src/views/home-header/styles.css.ts index 19f1c3fb16..c23743ca4c 100644 --- a/packages/frontend/mobile/src/views/home-header/styles.css.ts +++ b/packages/frontend/mobile/src/views/home-header/styles.css.ts @@ -22,7 +22,6 @@ export const float = style({ top: 0, width: '100%', background: cssVarV2('layer/background/secondary'), - paddingTop: 12, zIndex: 1, }); export const space = style({ diff --git a/tools/cli/src/webpack/template.html b/tools/cli/src/webpack/template.html index b7e098bcc8..3a80c2874e 100644 --- a/tools/cli/src/webpack/template.html +++ b/tools/cli/src/webpack/template.html @@ -4,8 +4,16 @@ + + + + + AFFiNE diff --git a/tools/cli/src/webpack/webpack.config.ts b/tools/cli/src/webpack/webpack.config.ts index bd7f574564..4ef523fee1 100644 --- a/tools/cli/src/webpack/webpack.config.ts +++ b/tools/cli/src/webpack/webpack.config.ts @@ -74,6 +74,7 @@ export function createWebpackConfig(cwd: string, flags: BuildFlags) { GIT_SHORT_SHA: gitShortHash(), DESCRIPTION, PUBLIC_PATH: config.output?.publicPath, + VIEWPORT_FIT: flags.distribution === 'mobile' ? 'cover' : 'auto', }; }, });