fix: 修改兼容问题
This commit is contained in:
parent
3bbf9b00c2
commit
135d78fc0d
@ -3,6 +3,7 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import { AiroButton } from '@workspace/airo-ui/components/AiroButton';
|
import { AiroButton } from '@workspace/airo-ui/components/AiroButton';
|
||||||
import { Card, CardContent } from '@workspace/airo-ui/components/card';
|
import { Card, CardContent } from '@workspace/airo-ui/components/card';
|
||||||
import {
|
import {
|
||||||
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
@ -10,7 +11,6 @@ import {
|
|||||||
} from '@workspace/airo-ui/components/dialog';
|
} from '@workspace/airo-ui/components/dialog';
|
||||||
import { default as Airo_Empty } from '@workspace/airo-ui/custom-components/empty';
|
import { default as Airo_Empty } from '@workspace/airo-ui/custom-components/empty';
|
||||||
import { formatDate } from '@workspace/airo-ui/utils';
|
import { formatDate } from '@workspace/airo-ui/utils';
|
||||||
import { Dialog } from '@workspace/ui/components/dialog';
|
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
|||||||
@ -11,8 +11,8 @@
|
|||||||
"rsc": true,
|
"rsc": true,
|
||||||
"style": "new-york",
|
"style": "new-york",
|
||||||
"tailwind": {
|
"tailwind": {
|
||||||
"config": "../../packages/ui/tailwind.config.ts",
|
"config": "../../packages/airo-ui/tailwind.config.ts",
|
||||||
"css": "../../packages/ui/src/styles/globals.css",
|
"css": "../../packages/airo-ui/src/styles/globals.css",
|
||||||
"baseColor": "zinc",
|
"baseColor": "zinc",
|
||||||
"cssVariables": true
|
"cssVariables": true
|
||||||
},
|
},
|
||||||
|
|||||||
@ -37,7 +37,10 @@ export default function Header() {
|
|||||||
{!user && (
|
{!user && (
|
||||||
<Link
|
<Link
|
||||||
href='#'
|
href='#'
|
||||||
onClick={() => openLoginDialog()}
|
onClick={() => {
|
||||||
|
console.log('登录');
|
||||||
|
openLoginDialog();
|
||||||
|
}}
|
||||||
className={cn(
|
className={cn(
|
||||||
buttonVariants({
|
buttonVariants({
|
||||||
size: 'lg',
|
size: 'lg',
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export default function FooterCopyright() {
|
|||||||
const t = useTranslations('auth');
|
const t = useTranslations('auth');
|
||||||
|
|
||||||
return (
|
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'}>
|
<div className={'container relative flex items-center justify-center text-right sm:block'}>
|
||||||
<Image
|
<Image
|
||||||
src={'./logo.png'}
|
src={'./logo.png'}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
const FullScreenVideoBackground = ({}: {}) => {
|
const FullScreenVideoBackground = ({}: {}) => {
|
||||||
return (
|
return (
|
||||||
<div className='h-full w-full overflow-hidden'>
|
<div className='fixed h-full w-full overflow-hidden indent-0'>
|
||||||
<video
|
<video
|
||||||
autoPlay
|
autoPlay
|
||||||
muted
|
muted
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { withSentryConfig } from '@sentry/nextjs';
|
||||||
import type { NextConfig } from 'next';
|
import type { NextConfig } from 'next';
|
||||||
import createNextIntlPlugin from 'next-intl/plugin';
|
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);
|
||||||
|
|||||||
@ -9,9 +9,17 @@
|
|||||||
"openapi": "openapi2ts",
|
"openapi": "openapi2ts",
|
||||||
"start": "next start"
|
"start": "next start"
|
||||||
},
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"defaults",
|
||||||
|
"not dead",
|
||||||
|
"last 2 versions",
|
||||||
|
"ios 14",
|
||||||
|
"Android >= 10"
|
||||||
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-spring/three": "^10.0.1",
|
"@react-spring/three": "^10.0.1",
|
||||||
"@react-three/fiber": "^9.2.0",
|
"@react-three/fiber": "^9.2.0",
|
||||||
|
"@sentry/nextjs": "^10.8.0",
|
||||||
"@shadergradient/react": "^2.1.2",
|
"@shadergradient/react": "^2.1.2",
|
||||||
"@stripe/react-stripe-js": "^3.4.0",
|
"@stripe/react-stripe-js": "^3.4.0",
|
||||||
"@stripe/stripe-js": "^6.0.0",
|
"@stripe/stripe-js": "^6.0.0",
|
||||||
|
|||||||
@ -27,7 +27,6 @@
|
|||||||
"@iconify-json/simple-icons": "^1.2.20",
|
"@iconify-json/simple-icons": "^1.2.20",
|
||||||
"@iconify-json/uil": "^1.2.3",
|
"@iconify-json/uil": "^1.2.3",
|
||||||
"@iconify/react": "^5.2.0",
|
"@iconify/react": "^5.2.0",
|
||||||
"@monaco-editor/react": "^4.6.0",
|
|
||||||
"@radix-ui/react-accordion": "^1.2.2",
|
"@radix-ui/react-accordion": "^1.2.2",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.4",
|
"@radix-ui/react-alert-dialog": "^1.1.4",
|
||||||
"@radix-ui/react-aspect-ratio": "^1.1.1",
|
"@radix-ui/react-aspect-ratio": "^1.1.1",
|
||||||
|
|||||||
@ -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' />;
|
|
||||||
}
|
|
||||||
@ -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';
|
|
||||||
@ -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>;
|
|
||||||
}
|
|
||||||
@ -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>}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user