diff --git a/packages/frontend/component/src/components/auth-components/index.tsx b/packages/frontend/component/src/components/auth-components/index.tsx index 640e1867fc..2993906ecd 100644 --- a/packages/frontend/component/src/components/auth-components/index.tsx +++ b/packages/frontend/component/src/components/auth-components/index.tsx @@ -15,3 +15,4 @@ export * from './sign-in-page-container'; export * from './sign-in-success-page'; export * from './sign-up-page'; export type { User } from './type'; +export * from './utils'; diff --git a/packages/frontend/core/src/bootstrap/preload.ts b/packages/frontend/core/src/bootstrap/preload.ts index 49a0bbac66..0a2a5f19c4 100644 --- a/packages/frontend/core/src/bootstrap/preload.ts +++ b/packages/frontend/core/src/bootstrap/preload.ts @@ -7,3 +7,5 @@ import './edgeless-template'; import { setupGlobal } from '@affine/env/global'; setupGlobal(); + +import '../types/types.d.ts'; diff --git a/packages/frontend/core/src/components/affine/auth/oauth.tsx b/packages/frontend/core/src/components/affine/auth/oauth.tsx index b815bb7130..9eead3b41d 100644 --- a/packages/frontend/core/src/components/affine/auth/oauth.tsx +++ b/packages/frontend/core/src/components/affine/auth/oauth.tsx @@ -40,12 +40,7 @@ export function OAuth() { ); if (!oauth) { - return ( - <> - - - > - ); + return ; } return oauthProviders?.map(provider => ( diff --git a/packages/frontend/core/src/pages/workspace/detail-page/detail-page-wrapper.tsx b/packages/frontend/core/src/pages/workspace/detail-page/detail-page-wrapper.tsx index 3840bc4d61..cdb503ade0 100644 --- a/packages/frontend/core/src/pages/workspace/detail-page/detail-page-wrapper.tsx +++ b/packages/frontend/core/src/pages/workspace/detail-page/detail-page-wrapper.tsx @@ -1,4 +1,3 @@ -import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton'; import type { Editor } from '@affine/core/modules/editor'; import { EditorsService } from '@affine/core/modules/editor'; import { ViewService } from '@affine/core/modules/workbench/services/view'; @@ -13,13 +12,12 @@ import { } from '@toeverything/infra'; import { type PropsWithChildren, + type ReactNode, useEffect, useLayoutEffect, useState, } from 'react'; -import { PageNotFound } from '../../404'; - const useLoadDoc = (pageId: string) => { const currentWorkspace = useService(WorkspaceService).workspace; const docsService = useService(DocsService); @@ -136,15 +134,21 @@ const useLoadDoc = (pageId: string) => { export const DetailPageWrapper = ({ pageId, children, -}: PropsWithChildren<{ pageId: string }>) => { + skeleton, + notFound, +}: PropsWithChildren<{ + pageId: string; + skeleton: ReactNode; + notFound: ReactNode; +}>) => { const { doc, editor, docListReady } = useLoadDoc(pageId); // if sync engine has been synced and the page is null, show 404 page. if (docListReady && !doc) { - return ; + return notFound; } if (!doc || !editor) { - return ; + return skeleton; } return ( diff --git a/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx b/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx index 181f7794e2..d502ba83e2 100644 --- a/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx +++ b/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx @@ -1,4 +1,5 @@ import { Scrollable, useHasScrollTop } from '@affine/component'; +import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton'; import type { ChatPanel } from '@affine/core/blocksuite/presets/ai'; import { AIProvider } from '@affine/core/blocksuite/presets/ai'; import { PageAIOnboarding } from '@affine/core/components/affine/ai-onboarding'; @@ -53,6 +54,7 @@ import { WorkbenchService, } from '../../../modules/workbench'; import { performanceRenderLogger } from '../../../shared'; +import { PageNotFound } from '../../404'; import * as styles from './detail-page.css'; import { DetailPageHeader } from './detail-page-header'; import { DetailPageWrapper } from './detail-page-wrapper'; @@ -347,7 +349,11 @@ export const Component = () => { const pageId = params.pageId; return pageId ? ( - + } + notFound={} + > ) : null; diff --git a/packages/frontend/core/src/types/types.d.ts b/packages/frontend/core/src/types/types.d.ts index e5298c2cdb..cfc08c0ac2 100644 --- a/packages/frontend/core/src/types/types.d.ts +++ b/packages/frontend/core/src/types/types.d.ts @@ -1,9 +1,5 @@ /// -// not using import because it will break the declare module line below -// eslint-disable-next-line @typescript-eslint/triple-slash-reference -/// - declare module '*.md' { const text: string; export default text; @@ -28,3 +24,8 @@ declare module '*.jpg' { const url: string; export default url; } + +declare module '*.inline.svg' { + const src: string; + export default src; +} diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index face279d6b..d19dc32cf4 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -1580,5 +1580,7 @@ "com.affine.mobile.setting.others.website": "Official website", "com.affine.mobile.setting.others.privacy": "Privacy", "com.affine.mobile.setting.others.terms": "Terms of use", - "com.affine.mobile.search.empty": "No results found" + "com.affine.mobile.search.empty": "No results found", + "com.affine.mobile.sign-in.skip.link": "Start AFFiNE without an account", + "com.affine.mobile.sign-in.skip.hint": "Want to keep data local?" } diff --git a/packages/frontend/mobile/src/components/workspace-selector/menu.css.ts b/packages/frontend/mobile/src/components/workspace-selector/menu.css.ts index 62ea571527..b965e4a5f0 100644 --- a/packages/frontend/mobile/src/components/workspace-selector/menu.css.ts +++ b/packages/frontend/mobile/src/components/workspace-selector/menu.css.ts @@ -3,7 +3,7 @@ import { cssVarV2 } from '@toeverything/theme/v2'; import { style } from '@vanilla-extract/css'; export const root = style({ - maxHeight: 'calc(100vh - 100px)', + maxHeight: 'calc(100dvh - 100px)', display: 'flex', flexDirection: 'column', }); diff --git a/packages/frontend/mobile/src/index.tsx b/packages/frontend/mobile/src/index.tsx index eeda0a5def..5235ae8c13 100644 --- a/packages/frontend/mobile/src/index.tsx +++ b/packages/frontend/mobile/src/index.tsx @@ -5,7 +5,6 @@ import './polyfill/request-idle-callback'; import '@affine/core/bootstrap/preload'; import { performanceLogger } from '@affine/core/shared'; -import { isDesktop } from '@affine/env/constant'; import { init, reactRouterV6BrowserTracingIntegration, @@ -24,36 +23,28 @@ import { App } from './app'; const performanceMainLogger = performanceLogger.namespace('main'); function main() { - performanceMainLogger.info('start'); - - // skip bootstrap setup for desktop onboarding - if (isDesktop && window.appInfo?.windowName === 'onboarding') { - performanceMainLogger.info('skip setup'); - } else { - performanceMainLogger.info('setup start'); - if (window.SENTRY_RELEASE || environment.isDebug) { - // https://docs.sentry.io/platforms/javascript/guides/react/#configure - init({ - dsn: process.env.SENTRY_DSN, - environment: process.env.BUILD_TYPE ?? 'development', - integrations: [ - reactRouterV6BrowserTracingIntegration({ - useEffect, - useLocation, - useNavigationType, - createRoutesFromChildren, - matchRoutes, - }), - ], - }); - setTags({ - appVersion: runtimeConfig.appVersion, - editorVersion: runtimeConfig.editorVersion, - }); - } - performanceMainLogger.info('setup done'); + performanceMainLogger.info('setup start'); + if (window.SENTRY_RELEASE || environment.isDebug) { + // https://docs.sentry.io/platforms/javascript/guides/react/#configure + init({ + dsn: process.env.SENTRY_DSN, + environment: process.env.BUILD_TYPE ?? 'development', + integrations: [ + reactRouterV6BrowserTracingIntegration({ + useEffect, + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, + }), + ], + }); + setTags({ + appVersion: runtimeConfig.appVersion, + editorVersion: runtimeConfig.editorVersion, + }); } - + performanceMainLogger.info('setup done'); mountApp(); } diff --git a/packages/frontend/mobile/src/pages/sign-in.tsx b/packages/frontend/mobile/src/pages/sign-in.tsx index 10a9ec3277..367bf2186d 100644 --- a/packages/frontend/mobile/src/pages/sign-in.tsx +++ b/packages/frontend/mobile/src/pages/sign-in.tsx @@ -1,7 +1,37 @@ -// Default route fallback for mobile -import { SignIn } from '@affine/core/pages/auth/sign-in'; +import { + RouteLogic, + useNavigateHelper, +} from '@affine/core/hooks/use-navigate-helper'; +import { AuthService } from '@affine/core/modules/cloud'; +import { useLiveData, useService } from '@toeverything/infra'; +import { useEffect } from 'react'; +import { useNavigate, useSearchParams } from 'react-router-dom'; + +import { MobileSignIn } from '../views/sign-in/mobile-sign-in'; export const Component = () => { - // placeholder impl - return ; + const session = useService(AuthService).session; + const status = useLiveData(session.status$); + const isRevalidating = useLiveData(session.isRevalidating$); + const navigate = useNavigate(); + const { jumpToIndex } = useNavigateHelper(); + const [searchParams] = useSearchParams(); + const isLoggedIn = status === 'authenticated' && !isRevalidating; + + useEffect(() => { + if (isLoggedIn) { + const redirectUri = searchParams.get('redirect_uri'); + if (redirectUri) { + navigate(redirectUri, { + replace: true, + }); + } else { + jumpToIndex(RouteLogic.REPLACE, { + search: searchParams.toString(), + }); + } + } + }, [jumpToIndex, navigate, isLoggedIn, searchParams]); + + return navigate('/')} />; }; diff --git a/packages/frontend/mobile/src/pages/workspace/detail/mobile-detail-page.css.ts b/packages/frontend/mobile/src/pages/workspace/detail/mobile-detail-page.css.ts index 98430208c5..8a7558bf17 100644 --- a/packages/frontend/mobile/src/pages/workspace/detail/mobile-detail-page.css.ts +++ b/packages/frontend/mobile/src/pages/workspace/detail/mobile-detail-page.css.ts @@ -4,7 +4,7 @@ import { globalStyle, style } from '@vanilla-extract/css'; export const root = style({ background: cssVarV2('layer/background/primary'), - minHeight: '100vh', + minHeight: '100dvh', display: 'flex', flexDirection: 'column', }); 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 15e9fac5f4..d6a882f70b 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,4 +1,5 @@ import { IconButton } 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'; import { useRegisterBlocksuiteEditorCommands } from '@affine/core/hooks/affine/use-register-blocksuite-editor-commands'; @@ -10,6 +11,7 @@ import { EditorService } from '@affine/core/modules/editor'; import { WorkbenchService } from '@affine/core/modules/workbench'; import { ViewService } from '@affine/core/modules/workbench/services/view'; import { DetailPageWrapper } from '@affine/core/pages/workspace/detail-page/detail-page-wrapper'; +import { WorkspaceFlavour } from '@affine/env/workspace'; import type { PageRootService } from '@blocksuite/blocks'; import { BookmarkBlockService, @@ -20,7 +22,7 @@ import { ImageBlockService, } from '@blocksuite/blocks'; import { DisposableGroup } from '@blocksuite/global/utils'; -import { ShareIcon } from '@blocksuite/icons/rc'; +import { ShareiOsIcon } from '@blocksuite/icons/rc'; import { type AffineEditorContainer } from '@blocksuite/presets'; import type { Doc as BlockSuiteDoc } from '@blocksuite/store'; import { @@ -28,6 +30,7 @@ import { FrameworkScope, GlobalContextService, useLiveData, + useService, useServices, WorkspaceService, } from '@toeverything/infra'; @@ -201,9 +204,24 @@ const DetailPageImpl = () => { ); }; +const skeleton = ( + <> + + + > +); + +const notFound = ( + <> + + Page Not Found (TODO) + > +); + export const Component = () => { const params = useParams(); const pageId = params.pageId; + const workspace = useService(WorkspaceService).workspace; if (!pageId) { return null; @@ -211,18 +229,23 @@ export const Component = () => { return ( - + - } - /> + {workspace.meta.flavour !== WorkspaceFlavour.LOCAL && ( + } + /> + )} > } diff --git a/packages/frontend/mobile/src/pages/workspace/layout.tsx b/packages/frontend/mobile/src/pages/workspace/layout.tsx index 97e466e049..5725752c98 100644 --- a/packages/frontend/mobile/src/pages/workspace/layout.tsx +++ b/packages/frontend/mobile/src/pages/workspace/layout.tsx @@ -77,7 +77,7 @@ export const WorkspaceLayout = ({ return ( - + {children} diff --git a/packages/frontend/mobile/src/provider/model-provider.tsx b/packages/frontend/mobile/src/provider/model-provider.tsx index 51247ce438..ad247186be 100644 --- a/packages/frontend/mobile/src/provider/model-provider.tsx +++ b/packages/frontend/mobile/src/provider/model-provider.tsx @@ -1,3 +1,4 @@ +import { NotificationCenter } from '@affine/component'; import { AiLoginRequiredModal } from '@affine/core/components/affine/auth/ai-login-required'; import { HistoryTipsModal } from '@affine/core/components/affine/history-tips-modal'; import { IssueFeedbackModal } from '@affine/core/components/affine/issue-feedback-modal'; @@ -8,12 +9,15 @@ import { import { StarAFFiNEModal } from '@affine/core/components/affine/star-affine-modal'; import { MoveToTrash } from '@affine/core/components/page-list'; import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-helper'; +import { CreateWorkspaceDialogProvider } from '@affine/core/modules/create-workspace'; import { PeekViewManagerModal } from '@affine/core/modules/peek-view'; +import { SignOutConfirmModal } from '@affine/core/providers/modal-provider'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { useService, WorkspaceService } from '@toeverything/infra'; import { useCallback } from 'react'; import { MobileSettingModal } from '../views'; +import { MobileSignInModal } from '../views/sign-in/modal'; export function MobileCurrentWorkspaceModals() { const currentWorkspace = useService(WorkspaceService).workspace; @@ -58,3 +62,15 @@ export function MobileCurrentWorkspaceModals() { > ); } + +// I don't like the name, but let's keep it for now +export const AllWorkspaceModals = () => { + return ( + <> + + + + + > + ); +}; diff --git a/packages/frontend/mobile/src/router.tsx b/packages/frontend/mobile/src/router.tsx index 3e35606927..3119fd6b3d 100644 --- a/packages/frontend/mobile/src/router.tsx +++ b/packages/frontend/mobile/src/router.tsx @@ -1,9 +1,12 @@ -import { RootRouter } from '@affine/core/router'; +import { NavigateContext } from '@affine/core/hooks/use-navigate-helper'; import { wrapCreateBrowserRouter } from '@sentry/react'; +import { useEffect, useState } from 'react'; import type { RouteObject } from 'react-router-dom'; import { createBrowserRouter as reactRouterCreateBrowserRouter, + Outlet, redirect, + useNavigate, } from 'react-router-dom'; import { Component as All } from './pages/workspace/all'; @@ -13,6 +16,25 @@ import { Component as Home } from './pages/workspace/home'; import { Component as Search } from './pages/workspace/search'; import { Component as Tag } from './pages/workspace/tag'; import { Component as TagDetail } from './pages/workspace/tag/detail'; +import { AllWorkspaceModals } from './provider/model-provider'; + +function RootRouter() { + const navigate = useNavigate(); + const [ready, setReady] = useState(false); + useEffect(() => { + // a hack to make sure router is ready + setReady(true); + }, []); + + return ( + ready && ( + + + + + ) + ); +} export const topLevelRoutes = [ { diff --git a/packages/frontend/mobile/src/views/sign-in/art-dark.inline.svg b/packages/frontend/mobile/src/views/sign-in/art-dark.inline.svg new file mode 100644 index 0000000000..e800c4d9f5 --- /dev/null +++ b/packages/frontend/mobile/src/views/sign-in/art-dark.inline.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/frontend/mobile/src/views/sign-in/art-light.inline.svg b/packages/frontend/mobile/src/views/sign-in/art-light.inline.svg new file mode 100644 index 0000000000..dfc195af14 --- /dev/null +++ b/packages/frontend/mobile/src/views/sign-in/art-light.inline.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/frontend/mobile/src/views/sign-in/background.css.ts b/packages/frontend/mobile/src/views/sign-in/background.css.ts new file mode 100644 index 0000000000..2e27e811ab --- /dev/null +++ b/packages/frontend/mobile/src/views/sign-in/background.css.ts @@ -0,0 +1,48 @@ +import { createVar, globalStyle, style } from '@vanilla-extract/css'; + +const bgColor = createVar(); +const dotColor = createVar(); + +export const root = style({ + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + pointerEvents: 'none', + zIndex: -1, +}); + +export const dotBg = style({ + height: '100%', + width: '100%', + position: 'absolute', + zIndex: -1, + top: 0, + left: 0, + backgroundImage: `linear-gradient(to bottom, transparent 0%, ${bgColor} 90%), + radial-gradient(${dotColor} 2px, transparent 2px), + radial-gradient(${dotColor} 2px, transparent 2px)`, + backgroundSize: '100% 100%, 20px 20px, 20px 20px', +}); + +export const arts = style({ + paddingTop: 60, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', +}); + +globalStyle(`[data-theme="light"] ${root}`, { + vars: { + [bgColor]: '#fff', + [dotColor]: '#d9d9d9', + }, +}); + +globalStyle(`[data-theme="dark"] ${root}`, { + vars: { + [bgColor]: '#141414', + [dotColor]: '#393939', + }, +}); diff --git a/packages/frontend/mobile/src/views/sign-in/background.tsx b/packages/frontend/mobile/src/views/sign-in/background.tsx new file mode 100644 index 0000000000..913661f53f --- /dev/null +++ b/packages/frontend/mobile/src/views/sign-in/background.tsx @@ -0,0 +1,19 @@ +import { useTheme } from 'next-themes'; + +import artsDark from './art-dark.inline.svg'; +import artsLight from './art-light.inline.svg'; +import * as styles from './background.css'; + +export const SignInBackground = () => { + const { resolvedTheme } = useTheme(); + + return ( + + + + + ); +}; diff --git a/packages/frontend/mobile/src/views/sign-in/layout.css.ts b/packages/frontend/mobile/src/views/sign-in/layout.css.ts new file mode 100644 index 0000000000..f9cedccf00 --- /dev/null +++ b/packages/frontend/mobile/src/views/sign-in/layout.css.ts @@ -0,0 +1,61 @@ +import { cssVar } from '@toeverything/theme'; +import { cssVarV2 } from '@toeverything/theme/v2'; +import { style } from '@vanilla-extract/css'; + +export const root = style({ + padding: '40px', + justifyContent: 'flex-end', + minHeight: '100dvh', + display: 'flex', + flexDirection: 'column', + position: 'relative', + zIndex: 0, +}); + +export const content = style({ + display: 'flex', + flexDirection: 'column', + alignItems: 'stretch', + gap: 24, +}); + +export const skipDivider = style({ + display: 'flex', + gap: 12, + alignItems: 'center', + height: 20, + marginTop: 12, + marginBottom: 12, +}); + +export const skipDividerLine = style({ + flex: 1, + height: 0, + borderBottom: `1px solid ${cssVarV2('layer/insideBorder/border')}`, +}); + +export const skipDividerText = style({ + color: cssVarV2('text/secondary'), + fontSize: cssVar('fontXs'), +}); + +export const skipText = style({ + color: cssVarV2('text/primary'), + fontSize: cssVar('fontXs'), + fontWeight: 500, +}); + +export const skipLink = style({ + color: cssVarV2('text/link'), + fontSize: cssVar('fontXs'), +}); + +export const skipLinkIcon = style({ + color: cssVarV2('text/link'), +}); + +export const skipSection = style({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', +}); diff --git a/packages/frontend/mobile/src/views/sign-in/layout.tsx b/packages/frontend/mobile/src/views/sign-in/layout.tsx new file mode 100644 index 0000000000..91a3810a36 --- /dev/null +++ b/packages/frontend/mobile/src/views/sign-in/layout.tsx @@ -0,0 +1,40 @@ +import { Button } from '@affine/component'; +import { useI18n } from '@affine/i18n'; +import { ArrowRightBigIcon } from '@blocksuite/icons/rc'; +import type { PropsWithChildren } from 'react'; + +import { SignInBackground } from './background'; +import * as styles from './layout.css'; + +export const MobileSignInLayout = ({ + children, + onSkip, +}: PropsWithChildren<{ + onSkip: () => void; +}>) => { + const t = useI18n(); + return ( + + + {children} + + + or + + + + + {t['com.affine.mobile.sign-in.skip.hint']()} + + } + > + {t['com.affine.mobile.sign-in.skip.link']()} + + + + ); +}; diff --git a/packages/frontend/mobile/src/views/sign-in/mobile-sign-in.tsx b/packages/frontend/mobile/src/views/sign-in/mobile-sign-in.tsx new file mode 100644 index 0000000000..0a35891bb3 --- /dev/null +++ b/packages/frontend/mobile/src/views/sign-in/mobile-sign-in.tsx @@ -0,0 +1,11 @@ +import { AuthPanel } from '@affine/core/components/affine/auth'; + +import { MobileSignInLayout } from './layout'; + +export const MobileSignIn = ({ onSkip }: { onSkip: () => void }) => { + return ( + + + + ); +}; diff --git a/packages/frontend/mobile/src/views/sign-in/modal.tsx b/packages/frontend/mobile/src/views/sign-in/modal.tsx new file mode 100644 index 0000000000..fde00eda18 --- /dev/null +++ b/packages/frontend/mobile/src/views/sign-in/modal.tsx @@ -0,0 +1,40 @@ +import { Modal } from '@affine/component'; +import { authAtom } from '@affine/core/atoms'; +import { cssVarV2 } from '@toeverything/theme/v2'; +import { useAtom } from 'jotai'; +import { useCallback } from 'react'; + +import { MobileSignIn } from './mobile-sign-in'; + +export const MobileSignInModal = () => { + const [authAtomValue, setAuthAtom] = useAtom(authAtom); + const setOpen = useCallback( + (open: boolean) => { + setAuthAtom(prev => ({ ...prev, openModal: open })); + }, + [setAuthAtom] + ); + + const closeModal = useCallback(() => { + setOpen(false); + }, [setOpen]); + + return ( + + + + ); +}; diff --git a/tools/cli/src/webpack/config.ts b/tools/cli/src/webpack/config.ts index 6b69c67e6a..a3674855c0 100644 --- a/tools/cli/src/webpack/config.ts +++ b/tools/cli/src/webpack/config.ts @@ -293,7 +293,11 @@ export const createConfiguration: ( }, { test: /\.txt$/, - loader: 'raw-loader', + type: 'asset/source', + }, + { + test: /\.inline\.svg$/, + type: 'asset/inline', }, { test: /\.css$/,