mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
✨ feat: Add browser compatibility detection and fallback page (#11309)
* ✨ feat: Add browser compatibility detection and fallback page - Add automatic browser compatibility check in app layout - Create standalone not-compatible.html fallback page with modern responsive design - Support dark mode via prefers-color-scheme - Include browser download links (Chrome, Firefox, Edge, Safari, Arc) - Display minimum browser requirements - Update feature development documentation * 📝 docs(CLAUDE): Update PR Linear Issue Association guidelines - Clarify the requirement to include magic keywords in PR body for Linear issues. - Add instruction to summarize work done in the Linear issue comment and update the issue status to "In Review". Signed-off-by: Innei <tukon479@gmail.com> * ✨ feat: Update browser compatibility page and layout - Change favicon link to absolute path in not-compatible.html. - Add Safari browser support with corresponding icon and link. - Update minimum browser requirements to Chrome 99+, Safari 16.4+, and Edge 99+. - Fix typo in layout.tsx comments from "serveral" to "several". Signed-off-by: Innei <tukon479@gmail.com> --------- Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
@@ -80,7 +80,7 @@ When creating new Linear issues using `mcp__linear-server__create_issue`, **MUST
|
||||
|
||||
### PR Linear Issue Association (REQUIRED)
|
||||
|
||||
**When creating PRs for Linear issues, MUST include magic keywords in PR body:** `Fixes LOBE-123`, `Closes LOBE-123`, or `Resolves LOBE-123`
|
||||
**When creating PRs for Linear issues, MUST include magic keywords in PR body:** `Fixes LOBE-123`, `Closes LOBE-123`, or `Resolves LOBE-123`, and summarize the work done in the linear issue comment and update the issue status to "In Review".
|
||||
|
||||
### IMPORTANT: Per-Issue Completion Rule
|
||||
|
||||
|
||||
@@ -254,14 +254,14 @@ Let's take the subcomponent `OpeningQuestion.tsx` as an example. Component imple
|
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { Flexbox, SortableList } from '@lobehub/ui';
|
||||
import { Button, Empty, Input } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useStore } from '../store';
|
||||
import { selectors } from '../store/selectors';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
empty: css`
|
||||
margin-block: 24px;
|
||||
margin-inline: 0;
|
||||
@@ -270,7 +270,7 @@ const useStyles = createStyles(({ css, token }) => ({
|
||||
margin-block-end: 8px;
|
||||
padding-block: 2px;
|
||||
padding-inline: 10px 0;
|
||||
background: ${token.colorBgContainer};
|
||||
background: ${cssVar.colorBgContainer};
|
||||
`,
|
||||
questionItemContent: css`
|
||||
flex: 1;
|
||||
@@ -281,7 +281,7 @@ const useStyles = createStyles(({ css, token }) => ({
|
||||
`,
|
||||
repeatError: css`
|
||||
margin: 0;
|
||||
color: ${token.colorErrorText};
|
||||
color: ${cssVar.colorErrorText};
|
||||
`,
|
||||
}));
|
||||
|
||||
@@ -292,7 +292,6 @@ interface QuestionItem {
|
||||
|
||||
const OpeningQuestions = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const { styles } = useStyles();
|
||||
const [questionInput, setQuestionInput] = useState('');
|
||||
|
||||
// Use selector to access corresponding configuration
|
||||
|
||||
@@ -254,14 +254,14 @@ lobe-chat 是个国际化项目,新加的文案需要更新默认的 `locale`
|
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { Flexbox, SortableList } from '@lobehub/ui';
|
||||
import { Button, Empty, Input } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useStore } from '../store';
|
||||
import { selectors } from '../store/selectors';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
empty: css`
|
||||
margin-block: 24px;
|
||||
margin-inline: 0;
|
||||
@@ -270,7 +270,7 @@ const useStyles = createStyles(({ css, token }) => ({
|
||||
margin-block-end: 8px;
|
||||
padding-block: 2px;
|
||||
padding-inline: 10px 0;
|
||||
background: ${token.colorBgContainer};
|
||||
background: ${cssVar.colorBgContainer};
|
||||
`,
|
||||
questionItemContent: css`
|
||||
flex: 1;
|
||||
@@ -281,7 +281,7 @@ const useStyles = createStyles(({ css, token }) => ({
|
||||
`,
|
||||
repeatError: css`
|
||||
margin: 0;
|
||||
color: ${token.colorErrorText};
|
||||
color: ${cssVar.colorErrorText};
|
||||
`,
|
||||
}));
|
||||
|
||||
@@ -292,7 +292,6 @@ interface QuestionItem {
|
||||
|
||||
const OpeningQuestions = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const { styles } = useStyles();
|
||||
const [questionInput, setQuestionInput] = useState('');
|
||||
|
||||
// 使用 selector 访问对应配置
|
||||
|
||||
1296
public/not-compatible.html
Normal file
1296
public/not-compatible.html
Normal file
File diff suppressed because it is too large
Load Diff
@@ -50,6 +50,9 @@ const RootLayout = async ({ children, params }: RootLayoutProps) => {
|
||||
return (
|
||||
<html dir={direction} lang={locale} suppressHydrationWarning>
|
||||
<head>
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-use-before-define */}
|
||||
<script dangerouslySetInnerHTML={{ __html: `(${outdateBrowserScript.toString()})();` }} />
|
||||
|
||||
{/* <script dangerouslySetInnerHTML={{ __html: 'setTimeout(() => {debugger}, 16)' }} /> */}
|
||||
{process.env.DEBUG_REACT_SCAN === '1' && (
|
||||
<Script
|
||||
@@ -74,6 +77,52 @@ const RootLayout = async ({ children, params }: RootLayoutProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
function outdateBrowserScript() {
|
||||
// eslint-disable-next-line unicorn/consistent-function-scoping
|
||||
function supportsImportMaps(): boolean {
|
||||
return (
|
||||
typeof HTMLScriptElement !== 'undefined' &&
|
||||
typeof (HTMLScriptElement as any).supports === 'function' &&
|
||||
(HTMLScriptElement as any).supports('importmap')
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line unicorn/consistent-function-scoping
|
||||
function supportsCascadeLayers(): boolean {
|
||||
if (typeof document === 'undefined') return false;
|
||||
|
||||
const el = document.createElement('div');
|
||||
el.className = '__layer_test__';
|
||||
el.style.position = 'absolute';
|
||||
el.style.left = '-99999px';
|
||||
el.style.top = '-99999px';
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@layer a, b;
|
||||
@layer a { .__layer_test__ { color: rgb(1, 2, 3); } }
|
||||
@layer b { .__layer_test__ { color: rgb(4, 5, 6); } }
|
||||
`;
|
||||
|
||||
document.documentElement.append(style);
|
||||
document.documentElement.append(el);
|
||||
|
||||
const color = getComputedStyle(el).color;
|
||||
|
||||
el.remove();
|
||||
style.remove();
|
||||
|
||||
return color === 'rgb(4, 5, 6)';
|
||||
}
|
||||
|
||||
const isOutdateBrowser = !(supportsImportMaps() && supportsCascadeLayers());
|
||||
if (isOutdateBrowser) {
|
||||
window.location.href = '/not-compatible.html';
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export default RootLayout;
|
||||
|
||||
export { generateMetadata } from './metadata';
|
||||
@@ -99,7 +148,7 @@ export const generateViewport = async (props: DynamicLayoutProps): ResolvingView
|
||||
|
||||
export const generateStaticParams = () => {
|
||||
const mobileOptions = isDesktop ? [false] : [true, false];
|
||||
// only static for serveral page, other go to dynamtic
|
||||
// only static for several page, other go to dynamic
|
||||
const staticLocales: Locales[] = [DEFAULT_LANG, 'zh-CN'];
|
||||
|
||||
const variants: { variants: string }[] = [];
|
||||
|
||||
Reference in New Issue
Block a user