fix: 修改兼容问题

This commit is contained in:
speakeloudest 2025-09-02 06:17:20 -07:00
parent 3bbf9b00c2
commit 135d78fc0d
14 changed files with 537 additions and 486 deletions

View File

@ -3,6 +3,7 @@ import { useQuery } from '@tanstack/react-query';
import { AiroButton } from '@workspace/airo-ui/components/AiroButton';
import { Card, CardContent } from '@workspace/airo-ui/components/card';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
@ -10,7 +11,6 @@ import {
} from '@workspace/airo-ui/components/dialog';
import { default as Airo_Empty } from '@workspace/airo-ui/custom-components/empty';
import { formatDate } from '@workspace/airo-ui/utils';
import { Dialog } from '@workspace/ui/components/dialog';
import { useTranslations } from 'next-intl';
import { useState } from 'react';

View File

@ -11,8 +11,8 @@
"rsc": true,
"style": "new-york",
"tailwind": {
"config": "../../packages/ui/tailwind.config.ts",
"css": "../../packages/ui/src/styles/globals.css",
"config": "../../packages/airo-ui/tailwind.config.ts",
"css": "../../packages/airo-ui/src/styles/globals.css",
"baseColor": "zinc",
"cssVariables": true
},

View File

@ -37,7 +37,10 @@ export default function Header() {
{!user && (
<Link
href='#'
onClick={() => openLoginDialog()}
onClick={() => {
console.log('登录');
openLoginDialog();
}}
className={cn(
buttonVariants({
size: 'lg',

View File

@ -11,7 +11,7 @@ export default function FooterCopyright() {
const t = useTranslations('auth');
return (
<footer className={'fixed bottom-6 z-[1] w-full text-xs sm:text-sm'}>
<footer className={'fixed bottom-6 z-40 w-full text-xs sm:text-sm'}>
<div className={'container relative flex items-center justify-center text-right sm:block'}>
<Image
src={'./logo.png'}

View File

@ -2,7 +2,7 @@
const FullScreenVideoBackground = ({}: {}) => {
return (
<div className='h-full w-full overflow-hidden'>
<div className='fixed h-full w-full overflow-hidden indent-0'>
<video
autoPlay
muted

View File

@ -1,3 +1,4 @@
import { withSentryConfig } from '@sentry/nextjs';
import type { NextConfig } from 'next';
import createNextIntlPlugin from 'next-intl/plugin';
@ -56,4 +57,17 @@ const nextConfig: NextConfig = {
},
};
export default withNextIntl(nextConfig);
const sentryWebpackPluginOptions = {
// For all available options, see:
// https://github.com/getsentry/sentry-webpack-plugin#options
org: '你的组织名称',
project: '你的项目名称',
authToken: '你的 Sentry Auth Token',
silent: true, // Suppresses all logs
// For all available options, see:
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/#use-sentrywebpackplugin
};
// 组合两个插件
export default withSentryConfig(withNextIntl(nextConfig), sentryWebpackPluginOptions);

View File

@ -9,9 +9,17 @@
"openapi": "openapi2ts",
"start": "next start"
},
"browserslist": [
"defaults",
"not dead",
"last 2 versions",
"ios 14",
"Android >= 10"
],
"dependencies": {
"@react-spring/three": "^10.0.1",
"@react-three/fiber": "^9.2.0",
"@sentry/nextjs": "^10.8.0",
"@shadergradient/react": "^2.1.2",
"@stripe/react-stripe-js": "^3.4.0",
"@stripe/stripe-js": "^6.0.0",

658
bun.lock

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,6 @@
"@iconify-json/simple-icons": "^1.2.20",
"@iconify-json/uil": "^1.2.3",
"@iconify/react": "^5.2.0",
"@monaco-editor/react": "^4.6.0",
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-alert-dialog": "^1.1.4",
"@radix-ui/react-aspect-ratio": "^1.1.1",

View File

@ -1,38 +0,0 @@
'use client';
import {
MonacoEditor,
MonacoEditorProps,
} from '@workspace/airo-ui/custom-components/editor/monaco-editor';
import { useEffect, useRef } from 'react';
export function HTMLEditor(props: MonacoEditorProps) {
return (
<MonacoEditor
title='HTML Editor'
description='Support HTML'
{...props}
language='markdown'
render={(value) => <HTMLPreview value={value} />}
/>
);
}
interface HTMLPreviewProps {
value?: string;
}
function HTMLPreview({ value }: HTMLPreviewProps) {
const iframeRef = useRef<HTMLIFrameElement>(null);
useEffect(() => {
const iframeDocument = iframeRef.current?.contentDocument;
if (iframeDocument) {
iframeDocument.open();
iframeDocument.write(value || '');
iframeDocument.close();
}
}, [value]);
return <iframe ref={iframeRef} title='HTML Preview' className='h-full w-full border-0' />;
}

View File

@ -1,3 +0,0 @@
export { HTMLEditor } from '@workspace/airo-ui/custom-components/editor/html';
export { JSONEditor } from '@workspace/airo-ui/custom-components/editor/json';
export { MarkdownEditor } from '@workspace/airo-ui/custom-components/editor/markdown';

View File

@ -1,91 +0,0 @@
'use client';
import {
MonacoEditor,
MonacoEditorProps,
} from '@workspace/airo-ui/custom-components/editor/monaco-editor';
import { useMemo } from 'react';
interface JSONEditorProps extends Omit<MonacoEditorProps, 'placeholder' | 'value' | 'onChange'> {
schema?: Record<string, unknown>;
placeholder?: Record<string, unknown>;
value?: Record<string, unknown> | string;
onChange?: (value: Record<string, unknown> | string | undefined) => void;
}
export function JSONEditor(props: JSONEditorProps) {
const { schema, placeholder = {}, ...rest } = props;
const editorKey = useMemo(() => JSON.stringify({ schema, placeholder }), [schema, placeholder]);
return (
<MonacoEditor
key={editorKey}
title='Edit JSON'
{...rest}
value={
typeof props.value === 'string'
? props.value
: props.value
? JSON.stringify(props.value, null, 2)
: ''
}
onChange={(value) => {
if (props.onChange && typeof value === 'string') {
try {
props.onChange(
props.value && typeof props.value === 'string' ? value : JSON.parse(value),
);
} catch (e) {
console.log('Invalid JSON input:', e);
}
}
}}
placeholder={placeholder ? JSON.stringify(placeholder, null, 2) : ''}
language='json'
onMount={(editor, monaco) => {
if (props.onMount) props.onMount(editor, monaco);
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
schemas: [
{
uri: '',
fileMatch: ['*'],
schema: schema || {
type: 'object',
properties: generateSchema(placeholder),
},
},
],
});
}}
/>
);
}
const generateSchema = (obj: Record<string, unknown>): Record<string, SchemaProperty> => {
const properties: Record<string, SchemaProperty> = {};
for (const [key, value] of Object.entries(obj)) {
if (Array.isArray(value)) {
properties[key] = {
type: 'array',
items: value.length > 0 ? generateSchema({ item: value[0] }).item : { type: 'null' },
};
} else if (typeof value === 'object' && value !== null) {
properties[key] = {
type: 'object',
properties: generateSchema(value as Record<string, unknown>),
};
} else {
properties[key] = { type: typeof value as SchemaType };
}
}
return properties;
};
type SchemaType = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'null';
interface SchemaProperty {
type: SchemaType;
items?: SchemaProperty;
properties?: Record<string, SchemaProperty>;
}

View File

@ -1,19 +0,0 @@
'use client';
import {
MonacoEditor,
MonacoEditorProps,
} from '@workspace/airo-ui/custom-components/editor/monaco-editor';
import { Markdown } from '@workspace/airo-ui/custom-components/markdown';
export function MarkdownEditor(props: MonacoEditorProps) {
return (
<MonacoEditor
title='Markdown Editor'
description='Support markdwon and html syntax'
{...props}
language='markdown'
render={(value) => <Markdown>{value || ''}</Markdown>}
/>
);
}

View File

@ -1,174 +0,0 @@
'use client';
import { Editor, type Monaco, type OnMount } from '@monaco-editor/react';
import { Button } from '@workspace/airo-ui/components/button';
import { cn } from '@workspace/airo-ui/lib/utils';
import { useSize } from 'ahooks';
import { EyeIcon, EyeOff, FullscreenIcon, MinimizeIcon } from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
export interface MonacoEditorProps {
value?: string;
onChange?: (value: string | undefined) => void;
onBlur?: (value: string | undefined) => void;
title?: string;
description?: string;
placeholder?: string;
render?: (value?: string) => React.ReactNode;
onMount?: OnMount;
language?: string;
className?: string;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function debounce<T extends (...args: any[]) => void>(func: T, delay: number) {
let timeoutId: ReturnType<typeof setTimeout>;
return function (...args: Parameters<T>) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func(...args), delay);
};
}
export function MonacoEditor({
value: propValue,
onChange,
onBlur,
title = 'Editor Title',
description,
placeholder = 'Start typing...',
render,
onMount,
language = 'markdown',
className,
}: MonacoEditorProps) {
const [internalValue, setInternalValue] = useState<string | undefined>(propValue);
const [isFullscreen, setIsFullscreen] = useState(false);
const [isPreviewVisible, setIsPreviewVisible] = useState(false);
const ref = useRef<HTMLDivElement>(null);
const size = useSize(ref);
useEffect(() => {
setInternalValue(propValue);
}, [propValue]);
const debouncedOnChange = useRef(
debounce((newValue: string | undefined) => {
if (onChange) {
onChange(newValue);
}
}, 300),
).current;
const handleEditorDidMount: OnMount = (editor, monaco) => {
if (onMount) onMount(editor, monaco);
editor.onDidChangeModelContent(() => {
const newValue = editor.getValue();
setInternalValue(newValue);
debouncedOnChange(newValue);
});
editor.onDidBlurEditorWidget(() => {
if (onBlur) {
onBlur(editor.getValue());
}
});
};
const toggleFullscreen = () => setIsFullscreen(!isFullscreen);
const togglePreview = () => setIsPreviewVisible(!isPreviewVisible);
return (
<div ref={ref} className='size-full'>
<div style={size}>
<div
className={cn('flex size-full min-h-96 flex-col rounded-md border', className, {
'bg-background fixed inset-0 z-50 !mt-0 h-screen': isFullscreen,
})}
>
<div className='flex items-center justify-between border-b p-2'>
<div>
<h1 className='text-left text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'>
{title}
</h1>
<p className='text-muted-foreground text-[0.8rem]'>{description}</p>
</div>
<div className='flex items-center space-x-2'>
{render && (
<Button variant='outline' size='icon' type='button' onClick={togglePreview}>
{isPreviewVisible ? <EyeOff /> : <EyeIcon />}
</Button>
)}
<Button variant='outline' size='icon' type='button' onClick={toggleFullscreen}>
{isFullscreen ? <MinimizeIcon /> : <FullscreenIcon />}
</Button>
</div>
</div>
<div className={cn('relative flex flex-1 overflow-hidden')}>
<div
className={cn('flex-1 overflow-hidden p-4 invert dark:invert-0', {
'w-1/2': isPreviewVisible,
})}
>
<Editor
language={language}
value={internalValue}
onChange={(newValue) => {
setInternalValue(newValue);
debouncedOnChange(newValue);
}}
onMount={handleEditorDidMount}
className=''
options={{
automaticLayout: true,
contextmenu: false,
folding: false,
fontSize: 14,
formatOnPaste: true,
formatOnType: true,
glyphMargin: false,
lineNumbers: 'off',
minimap: { enabled: false },
overviewRulerLanes: 0,
renderLineHighlight: 'none',
scrollBeyondLastLine: false,
scrollbar: {
useShadows: false,
vertical: 'hidden',
},
tabSize: 2,
wordWrap: 'off',
}}
theme='transparentTheme'
beforeMount={(monaco: Monaco) => {
monaco.editor.defineTheme('transparentTheme', {
base: 'vs-dark',
inherit: true,
rules: [],
colors: {
'editor.background': '#00000000',
},
});
}}
/>
{!internalValue?.trim() && placeholder && (
<pre
className='text-muted-foreground pointer-events-none absolute left-7 top-4 text-sm'
style={{ userSelect: 'none' }}
>
{placeholder}
</pre>
)}
</div>
{render && isPreviewVisible && (
<div className='w-1/2 flex-1 overflow-auto border-l p-4'>{render(internalValue)}</div>
)}
</div>
</div>
</div>
</div>
);
}