feat:启动时检查webui版本并尝试自动更新
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "maibot-dashboard",
|
||||
"private": true,
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"type": "module",
|
||||
"main": "./out/main/index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -84,7 +84,7 @@ function DynamicConfigSection({
|
||||
values: Record<string, unknown>
|
||||
}) {
|
||||
return (
|
||||
<Card>
|
||||
<Card className="min-w-0">
|
||||
<CardHeader className="border-b border-border/50 pb-4">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="space-y-1">
|
||||
@@ -296,15 +296,15 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
|
||||
row.length > 1 ? (
|
||||
<div
|
||||
key={row.map((field) => field.name).join('|')}
|
||||
className="grid gap-4 py-1 md:grid-cols-[repeat(var(--field-row-count),minmax(0,1fr))]"
|
||||
className="grid min-w-0 gap-4 py-1 md:grid-cols-[repeat(var(--field-row-count),minmax(0,1fr))]"
|
||||
style={{ '--field-row-count': row.length } as React.CSSProperties}
|
||||
>
|
||||
{row.map((field) => (
|
||||
<div key={field.name}>{renderField(field)}</div>
|
||||
<div key={field.name} className="min-w-0">{renderField(field)}</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div key={row[0].name} className="py-1">{renderField(row[0])}</div>
|
||||
<div key={row[0].name} className="min-w-0 py-1">{renderField(row[0])}</div>
|
||||
)
|
||||
))}
|
||||
</>
|
||||
@@ -322,7 +322,7 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="min-w-0 space-y-6">
|
||||
{visibleFields.length > 0 && (
|
||||
<div>
|
||||
{renderFieldList(visibleFields)}
|
||||
@@ -353,7 +353,7 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
|
||||
const HookComponent = hookEntry.component
|
||||
if (hookEntry.type === 'replace') {
|
||||
return (
|
||||
<div key={key}>
|
||||
<div key={key} className="min-w-0">
|
||||
<HookComponent
|
||||
fieldPath={nestedFieldPath}
|
||||
value={values[key]}
|
||||
@@ -368,7 +368,7 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={key}>
|
||||
<div key={key} className="min-w-0">
|
||||
<HookComponent
|
||||
fieldPath={nestedFieldPath}
|
||||
value={values[key]}
|
||||
@@ -416,7 +416,7 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<Card key={key} className="border-border/70 bg-muted/20 shadow-none">
|
||||
<Card key={key} className="min-w-0 border-border/70 bg-muted/20 shadow-none">
|
||||
<CardHeader className="border-b border-border/50 px-4 py-3">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="space-y-1">
|
||||
@@ -449,7 +449,7 @@ export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
|
||||
|
||||
if (level === 0 && sectionColumns === 2 && visibleNestedSections.length > 1) {
|
||||
return (
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<div className="grid min-w-0 gap-4 md:grid-cols-2">
|
||||
{visibleNestedSections}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -158,7 +158,7 @@ export const DynamicField: React.FC<DynamicFieldProps> = ({
|
||||
const label = (
|
||||
<Label
|
||||
className={cn(
|
||||
"inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap text-[15px] leading-6",
|
||||
"inline-flex min-w-0 items-center gap-1.5 text-[15px] leading-6",
|
||||
descriptionDisplay === 'label-hover' && fieldDescription && "cursor-help",
|
||||
schema.advanced
|
||||
? "text-sky-700 dark:text-sky-300"
|
||||
@@ -166,7 +166,7 @@ export const DynamicField: React.FC<DynamicFieldProps> = ({
|
||||
)}
|
||||
>
|
||||
{renderIcon()}
|
||||
<span>{fieldLabel}</span>
|
||||
<span className="break-words">{fieldLabel}</span>
|
||||
{schema.required && <span className="text-destructive">*</span>}
|
||||
</Label>
|
||||
)
|
||||
@@ -281,8 +281,8 @@ export const DynamicField: React.FC<DynamicFieldProps> = ({
|
||||
const renderSwitch = () => {
|
||||
const checked = Boolean(value)
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-4 py-2">
|
||||
<div className="pr-4">
|
||||
<div className="flex min-w-0 items-center justify-between gap-4 py-2">
|
||||
<div className="min-w-0 pr-4">
|
||||
{renderFieldHeader()}
|
||||
</div>
|
||||
<Switch
|
||||
@@ -303,7 +303,7 @@ export const DynamicField: React.FC<DynamicFieldProps> = ({
|
||||
const step = schema.step ?? 1
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="min-w-0 space-y-2">
|
||||
<Slider
|
||||
value={[numValue]}
|
||||
onValueChange={(values) => onChange(values[0])}
|
||||
@@ -466,7 +466,7 @@ export const DynamicField: React.FC<DynamicFieldProps> = ({
|
||||
className="flex flex-col gap-2 py-2 sm:flex-row sm:items-center"
|
||||
style={{ '--field-input-width': schema['x-input-width'] ?? '12rem' } as React.CSSProperties}
|
||||
>
|
||||
<div className="shrink-0">
|
||||
<div className="min-w-0 sm:shrink-0">
|
||||
{renderFieldHeader()}
|
||||
</div>
|
||||
<div className="min-w-20 flex-1 sm:ml-auto sm:max-w-[var(--field-input-width)]">
|
||||
@@ -477,7 +477,7 @@ export const DynamicField: React.FC<DynamicFieldProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="min-w-0 space-y-2">
|
||||
{renderFieldHeader()}
|
||||
|
||||
{/* Input component */}
|
||||
|
||||
@@ -87,12 +87,12 @@ export function Header({
|
||||
return (
|
||||
<header
|
||||
className={cn(
|
||||
'sticky top-0 isolate z-10 flex h-16 items-center justify-between border-b px-4 backdrop-blur-md',
|
||||
'sticky top-0 isolate z-10 flex h-16 min-w-0 items-center justify-between gap-2 border-b px-3 backdrop-blur-md sm:px-4',
|
||||
inheritsPageBackground ? 'bg-transparent' : 'bg-card/80'
|
||||
)}
|
||||
>
|
||||
{!inheritsPageBackground && <BackgroundLayer config={headerBg} layerId="header" />}
|
||||
<div className="relative z-10 flex items-center gap-4">
|
||||
<div className="relative z-10 flex min-w-0 shrink-0 items-center gap-2 sm:gap-4">
|
||||
{/* 移动端菜单按钮 */}
|
||||
<button
|
||||
onClick={onMobileMenuToggle}
|
||||
@@ -122,7 +122,7 @@ export function Header({
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex items-center gap-2">
|
||||
<div className="relative z-10 flex min-w-0 flex-1 items-center justify-end gap-1 sm:gap-2">
|
||||
{/* 工作区切换:复用 Tabs 组件 + Motion 动画指示器 */}
|
||||
<LayoutGroup id="workspace-switcher">
|
||||
<Tabs value={workspaceMode} aria-label={t('workspace.switcherLabel')}>
|
||||
@@ -165,7 +165,7 @@ export function Header({
|
||||
</Tabs>
|
||||
</LayoutGroup>
|
||||
|
||||
<div className="bg-border h-6 w-px" />
|
||||
<div className="bg-border hidden h-6 w-px sm:block" />
|
||||
{/* 后端切换按钮(仅 Electron) */}
|
||||
{isElectron() && (
|
||||
<>
|
||||
@@ -211,7 +211,7 @@ export function Header({
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => window.open('https://docs.mai-mai.org', '_blank')}
|
||||
className="gap-2"
|
||||
className="hidden gap-2 sm:inline-flex"
|
||||
title={t('header.viewDocs')}
|
||||
>
|
||||
<BookOpen className="h-4 w-4" />
|
||||
@@ -221,7 +221,7 @@ export function Header({
|
||||
{/* 语言切换 */}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="sm" className="gap-2">
|
||||
<Button variant="ghost" size="sm" className="gap-2 px-2 sm:px-3">
|
||||
<Globe className="h-4 w-4" />
|
||||
<span className="hidden text-xs sm:inline">
|
||||
{LANGUAGE_NAMES[currentLang.split('-')[0] as 'zh' | 'en' | 'ja' | 'ko'] ??
|
||||
@@ -259,14 +259,14 @@ export function Header({
|
||||
</button>
|
||||
|
||||
{/* 分隔线 */}
|
||||
<div className="bg-border h-6 w-px" />
|
||||
<div className="bg-border hidden h-6 w-px sm:block" />
|
||||
|
||||
{/* 登出按钮 */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleLogout}
|
||||
className="gap-2"
|
||||
className="gap-2 px-2 sm:px-3"
|
||||
title={t('header.logout')}
|
||||
>
|
||||
<LogOut className="h-4 w-4" />
|
||||
|
||||
@@ -178,7 +178,7 @@ export function Layout({ children }: LayoutProps) {
|
||||
)}
|
||||
</AnimatePresence>
|
||||
{/* Main content */}
|
||||
<div className="flex flex-1 flex-col overflow-hidden">
|
||||
<div className="flex min-w-0 flex-1 flex-col overflow-hidden">
|
||||
{/* HTTP 安全警告横幅 */}
|
||||
<HttpWarningBanner />
|
||||
|
||||
@@ -211,7 +211,7 @@ export function Layout({ children }: LayoutProps) {
|
||||
<AnimatePresence mode="wait" initial={false}>
|
||||
<motion.div
|
||||
key={workspaceMode}
|
||||
className="relative z-10 h-full"
|
||||
className="relative z-10 h-full min-w-0"
|
||||
initial={{ opacity: 0, x: isChatWorkspace ? 32 : -32, filter: 'blur(6px)' }}
|
||||
animate={{ opacity: 1, x: 0, filter: 'blur(0px)' }}
|
||||
exit={{ opacity: 0, x: isChatWorkspace ? -32 : 32, filter: 'blur(6px)' }}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 修改此处的版本号后,所有展示版本的地方都会自动更新
|
||||
*/
|
||||
|
||||
export const APP_VERSION = '1.0.6'
|
||||
export const APP_VERSION = '1.0.7'
|
||||
export const APP_NAME = 'MaiBot Dashboard'
|
||||
export const APP_FULL_NAME = `${APP_NAME} v${APP_VERSION}`
|
||||
|
||||
|
||||
@@ -766,8 +766,8 @@ function BotConfigPageContent() {
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollArea className="h-full">
|
||||
<div className="space-y-4 sm:space-y-6 p-4 sm:p-6">
|
||||
<ScrollArea className="h-full min-w-0" scrollbars="vertical">
|
||||
<div className="max-w-full space-y-4 overflow-x-hidden p-4 sm:space-y-6 sm:p-6">
|
||||
{/* 页面标题 */}
|
||||
<div className="flex flex-col gap-3 sm:gap-4">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
|
||||
@@ -776,11 +776,11 @@ function BotConfigPageContent() {
|
||||
<p className="text-muted-foreground mt-1 text-xs sm:text-sm">管理麦麦的核心功能和行为设置</p>
|
||||
</div>
|
||||
{/* 按钮组 - 桌面端靠右 */}
|
||||
<div className="flex flex-wrap gap-2 flex-shrink-0 sm:justify-end">
|
||||
<div className="flex w-full min-w-0 flex-wrap gap-2 sm:w-auto sm:flex-shrink-0 sm:justify-end">
|
||||
<Tabs
|
||||
value={editMode}
|
||||
onValueChange={(v) => handleModeChange(v as 'visual' | 'source')}
|
||||
className="w-full min-w-[13rem] sm:w-[14rem]"
|
||||
className="w-full min-w-0 sm:w-[14rem]"
|
||||
>
|
||||
<TabsList className="grid h-8 w-full grid-cols-2 sm:h-9">
|
||||
<TabsTrigger value="visual" className="px-2 text-xs">
|
||||
@@ -798,7 +798,7 @@ function BotConfigPageContent() {
|
||||
disabled={saving || autoSaving || isRestarting}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="w-20 sm:w-24"
|
||||
className="min-w-0 flex-1 sm:w-24 sm:flex-none"
|
||||
>
|
||||
<RefreshCw className="h-3 w-3 sm:h-4 sm:w-4 mr-1" />
|
||||
刷新
|
||||
@@ -808,7 +808,7 @@ function BotConfigPageContent() {
|
||||
disabled={saving || autoSaving || !hasUnsavedChanges || isRestarting}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="w-20 sm:w-24"
|
||||
className="min-w-0 flex-1 sm:w-24 sm:flex-none"
|
||||
>
|
||||
<Save className="h-4 w-4 flex-shrink-0" strokeWidth={2} fill="none" />
|
||||
<span className="ml-1 truncate text-xs sm:text-sm">
|
||||
@@ -820,7 +820,7 @@ function BotConfigPageContent() {
|
||||
<Button
|
||||
disabled={saving || autoSaving || isRestarting}
|
||||
size="sm"
|
||||
className="w-20 sm:w-28"
|
||||
className="min-w-0 flex-1 sm:w-28 sm:flex-none"
|
||||
>
|
||||
<Power className="h-4 w-4 flex-shrink-0" />
|
||||
<span className="ml-1 truncate text-xs sm:text-sm">
|
||||
@@ -1041,7 +1041,7 @@ function DynamicConfigTabs(props: DynamicConfigTabsProps) {
|
||||
|
||||
return (
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
|
||||
<TabsList className="flex flex-wrap h-auto gap-1 p-1 transition-all duration-300 ease-out">
|
||||
<TabsList className="flex h-auto max-w-full justify-start gap-1 overflow-x-auto p-1 transition-all duration-300 ease-out sm:flex-wrap sm:overflow-x-visible">
|
||||
{visibleTabGroups.map((tab) => {
|
||||
const isExpandedOnlyTab = !DEFAULT_VISIBLE_TAB_IDS.has(tab.id)
|
||||
return (
|
||||
@@ -1052,7 +1052,7 @@ function DynamicConfigTabs(props: DynamicConfigTabsProps) {
|
||||
<TabsTrigger
|
||||
value={tab.id}
|
||||
className={cn(
|
||||
"px-2 py-1.5 text-sm transition-all duration-200 ease-out sm:px-3 sm:py-2 data-[state=active]:shadow-sm",
|
||||
"shrink-0 px-2 py-1.5 text-sm transition-all duration-200 ease-out sm:px-3 sm:py-2 data-[state=active]:shadow-sm",
|
||||
isExpandedOnlyTab &&
|
||||
"border border-dashed border-border/70 bg-background/45 text-muted-foreground/80 motion-safe:animate-[config-tab-enter_180ms_ease-out_both] hover:bg-background/70 data-[state=active]:border-primary/45 data-[state=active]:bg-primary/10 data-[state=active]:text-primary data-[state=active]:shadow-none"
|
||||
)}
|
||||
@@ -1067,7 +1067,7 @@ function DynamicConfigTabs(props: DynamicConfigTabsProps) {
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="group h-8 px-2 text-xs transition-all duration-200 ease-out sm:h-9 sm:px-3"
|
||||
className="group h-8 shrink-0 px-2 text-xs transition-all duration-200 ease-out sm:h-9 sm:px-3"
|
||||
onClick={toggleExpanded}
|
||||
>
|
||||
{expanded ? (
|
||||
@@ -1082,7 +1082,7 @@ function DynamicConfigTabs(props: DynamicConfigTabsProps) {
|
||||
type="button"
|
||||
variant={advancedVisible ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
className="ml-auto h-8 px-2 text-xs transition-all duration-200 ease-out sm:h-9 sm:px-3"
|
||||
className="h-8 shrink-0 px-2 text-xs transition-all duration-200 ease-out sm:ml-auto sm:h-9 sm:px-3"
|
||||
onClick={() => setAdvancedVisible((current) => !current)}
|
||||
>
|
||||
高级设置
|
||||
|
||||
Reference in New Issue
Block a user