Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Q
qjclaw-dmg
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
AI-甘富林
qjclaw-dmg
Commits
c9ab97c3
Commit
c9ab97c3
authored
May 14, 2026
by
edy
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(ui): scope settings actions
parent
68462773
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
1296 additions
and
498 deletions
+1296
-498
App.tsx
apps/ui/src/App.tsx
+109
-12
SettingsPanels.tsx
apps/ui/src/features/settings/SettingsPanels.tsx
+415
-246
SettingsView.tsx
apps/ui/src/features/settings/SettingsView.tsx
+4
-1
settingsDrafts.ts
apps/ui/src/features/settings/settingsDrafts.ts
+109
-0
useSaveSettings.ts
apps/ui/src/features/settings/useSaveSettings.ts
+155
-68
settings.css
apps/ui/src/styles/settings.css
+305
-171
settingsDrafts.test.ts
apps/ui/test/settingsDrafts.test.ts
+132
-0
settingsPanelsSource.test.ts
apps/ui/test/settingsPanelsSource.test.ts
+67
-0
No files found.
apps/ui/src/App.tsx
View file @
c9ab97c3
...
...
@@ -71,6 +71,15 @@ import {
}
from
"./features/shell/startupStatus"
;
import
{
SettingsPanels
}
from
"./features/settings/SettingsPanels"
;
import
{
SettingsView
}
from
"./features/settings/SettingsView"
;
import
{
getHasPendingSettingsChange
,
getResetCopywritingSettingsDrafts
,
getResetDigitalHumanSettingsDrafts
,
getResetDouyinRuntimeSettingsDrafts
,
getResetImageSettingsDrafts
,
getResetVideoSettingsDrafts
,
getResetXhsFeishuSettingsDrafts
}
from
"./features/settings/settingsDrafts"
;
import
{
useSaveSettings
}
from
"./features/settings/useSaveSettings"
;
import
{
useSettingsState
}
from
"./features/settings/useSettingsState"
;
import
{
useSmokeActionHandlers
}
from
"./features/smoke/useSmokeActionHandlers"
;
...
...
@@ -397,8 +406,7 @@ export default function App() {
saving
,
setSaving
,
hasPendingLobsterKey
,
hasPendingXhsFeishuConfig
,
hasPendingModelKeys
hasPendingXhsFeishuConfig
}
=
useSettingsState
(
config
);
const
{
messageTraces
,
...
...
@@ -411,8 +419,13 @@ export default function App() {
}
=
useMessageTraces
();
const
{
saveConfig
,
saveWorkspaceDirectory
,
restoreWorkspaceDirectory
,
saveLobsterKey
,
saveXhsFeishuConfig
,
saveCopywritingConfig
,
saveImageConfig
,
saveVideoConfig
,
saveDigitalHumanConfig
,
saveDouyinRuntimeConfig
,
pickWorkspaceDirectory
}
=
useSaveSettings
({
config
,
...
...
@@ -491,6 +504,76 @@ export default function App() {
const
savedWorkspacePath
=
config
?.
workspacePath
??
""
;
const
displayedWorkspacePath
=
workspacePathDraft
.
trim
()
||
savedWorkspacePath
||
ui
.
none
;
const
hasPendingWorkspacePathChange
=
Boolean
(
config
&&
workspacePathDraft
.
trim
()
&&
workspacePathDraft
.
trim
()
!==
savedWorkspacePath
);
const
hasPendingBasicConfig
=
hasPendingLobsterKey
||
hasPendingWorkspacePathChange
;
const
hasPendingCopywritingConfig
=
Boolean
(
copywritingModelApiKeyDraft
.
trim
());
const
hasPendingImageConfig
=
Boolean
(
imageModelApiKeyDraft
.
trim
());
const
hasPendingVideoConfig
=
Boolean
(
videoModelApiKeyDraft
.
trim
());
const
hasPendingDigitalHumanConfig
=
Boolean
(
digitalHumanVolcAccessKeyDraft
.
trim
()
||
digitalHumanVolcSecretKeyDraft
.
trim
()
||
digitalHumanQiniuAccessKeyDraft
.
trim
()
||
digitalHumanQiniuSecretKeyDraft
.
trim
()
);
const
hasPendingDouyinRuntimeConfig
=
Boolean
(
videoAnalyzerBaseUrlDraft
.
trim
()
!==
(
config
?.
douyinRuntimeConfig
.
videoAnalyzer
.
baseUrl
??
""
).
trim
()
||
videoAnalyzerModelIdDraft
.
trim
()
!==
(
config
?.
douyinRuntimeConfig
.
videoAnalyzer
.
modelId
??
""
).
trim
()
||
videoAnalyzerApiKeyDraft
.
trim
()
||
replicationBriefBaseUrlDraft
.
trim
()
!==
(
config
?.
douyinRuntimeConfig
.
replicationBrief
.
baseUrl
??
""
).
trim
()
||
replicationBriefModelIdDraft
.
trim
()
!==
(
config
?.
douyinRuntimeConfig
.
replicationBrief
.
modelId
??
""
).
trim
()
||
replicationBriefApiKeyDraft
.
trim
()
||
vectcutBaseUrlDraft
.
trim
()
!==
(
config
?.
douyinRuntimeConfig
.
vectcut
.
baseUrl
??
""
).
trim
()
||
vectcutFileBaseUrlDraft
.
trim
()
!==
(
config
?.
douyinRuntimeConfig
.
vectcut
.
fileBaseUrl
??
""
).
trim
()
||
vectcutApiKeyDraft
.
trim
()
);
const
hasPendingSettingsChange
=
getHasPendingSettingsChange
({
hasPendingBasicConfig
,
hasPendingXhsFeishuConfig
,
hasPendingCopywritingConfig
,
hasPendingImageConfig
,
hasPendingVideoConfig
,
hasPendingDigitalHumanConfig
,
hasPendingDouyinRuntimeConfig
});
void
hasPendingSettingsChange
;
const
resetXhsFeishuSettingsDrafts
=
useCallback
(()
=>
{
const
drafts
=
getResetXhsFeishuSettingsDrafts
();
setXhsFeishuAppIdDraft
(
drafts
.
xhsFeishuAppId
);
setXhsFeishuAppSecretDraft
(
drafts
.
xhsFeishuAppSecret
);
setXhsFeishuAppTokenDraft
(
drafts
.
xhsFeishuAppToken
);
setXhsFeishuTableIdDraft
(
drafts
.
xhsFeishuTableId
);
},
[]);
const
resetCopywritingSettingsDrafts
=
useCallback
(()
=>
{
setCopywritingModelApiKeyDraft
(
getResetCopywritingSettingsDrafts
().
copywritingModelApiKey
);
},
[]);
const
resetImageSettingsDrafts
=
useCallback
(()
=>
{
setImageModelApiKeyDraft
(
getResetImageSettingsDrafts
().
imageModelApiKey
);
},
[]);
const
resetVideoSettingsDrafts
=
useCallback
(()
=>
{
setVideoModelApiKeyDraft
(
getResetVideoSettingsDrafts
().
videoModelApiKey
);
},
[]);
const
resetDigitalHumanSettingsDrafts
=
useCallback
(()
=>
{
const
drafts
=
getResetDigitalHumanSettingsDrafts
();
setDigitalHumanVolcAccessKeyDraft
(
drafts
.
digitalHumanVolcAccessKey
);
setDigitalHumanVolcSecretKeyDraft
(
drafts
.
digitalHumanVolcSecretKey
);
setDigitalHumanQiniuAccessKeyDraft
(
drafts
.
digitalHumanQiniuAccessKey
);
setDigitalHumanQiniuSecretKeyDraft
(
drafts
.
digitalHumanQiniuSecretKey
);
},
[]);
const
resetDouyinRuntimeSettingsDrafts
=
useCallback
(()
=>
{
if
(
!
config
)
{
return
;
}
const
drafts
=
getResetDouyinRuntimeSettingsDrafts
(
config
);
setVideoAnalyzerBaseUrlDraft
(
drafts
.
videoAnalyzerBaseUrl
);
setVideoAnalyzerModelIdDraft
(
drafts
.
videoAnalyzerModelId
);
setVideoAnalyzerApiKeyDraft
(
drafts
.
videoAnalyzerApiKey
);
setReplicationBriefBaseUrlDraft
(
drafts
.
replicationBriefBaseUrl
);
setReplicationBriefModelIdDraft
(
drafts
.
replicationBriefModelId
);
setReplicationBriefApiKeyDraft
(
drafts
.
replicationBriefApiKey
);
setVectcutBaseUrlDraft
(
drafts
.
vectcutBaseUrl
);
setVectcutFileBaseUrlDraft
(
drafts
.
vectcutFileBaseUrl
);
setVectcutApiKeyDraft
(
drafts
.
vectcutApiKey
);
},
[
config
]);
const
startupProgress
=
getStartupProgress
(
startupPhase
);
const
startupCurtainStatus
=
getStartupCurtainStatus
(
startupPhase
,
chatLaunchState
);
const
startupCurtainFootnote
=
getStartupCurtainFootnote
(
chatLaunchState
);
...
...
@@ -1230,7 +1313,7 @@ export default function App() {
saving
,
bindingLabel
:
ui
.
binding
,
onLobsterKeyChange
:
setLobsterKeyDraft
,
onSave
:
()
=>
void
save
Config
({
lobsterKey
:
lobsterKeyDraft
}
)
onSave
:
()
=>
void
save
LobsterKey
(
)
},
hasExpertProjects
:
Boolean
(
expertPageProjects
.
length
),
noExpertsLabel
:
expertsPageCopy
.
noExperts
,
...
...
@@ -1306,12 +1389,16 @@ export default function App() {
config
,
workspaceApiKeyConfigured
:
Boolean
(
workspace
?.
apiKeyConfigured
),
displayedWorkspacePath
,
hasPendingWorkspacePathChange
,
saving
,
hasPendingLobsterKey
,
hasPendingWorkspacePathChange
,
hasPendingXhsFeishuConfig
,
hasPendingModelKeys
,
saving
,
labels
:
{
saving
:
ui
.
saving
,
save
:
ui
.
save
,
export
:
ui
.
export
},
hasPendingCopywritingConfig
,
hasPendingImageConfig
,
hasPendingVideoConfig
,
hasPendingDigitalHumanConfig
,
hasPendingDouyinRuntimeConfig
,
labels
:
{
export
:
ui
.
export
},
drafts
:
{
lobsterKey
:
lobsterKeyDraft
,
xhsFeishuAppId
:
xhsFeishuAppIdDraft
,
...
...
@@ -1358,9 +1445,19 @@ export default function App() {
setVectcutFileBaseUrl
:
setVectcutFileBaseUrlDraft
,
setVectcutApiKey
:
setVectcutApiKeyDraft
},
saveConfig
,
saveWorkspaceDirectory
,
restoreWorkspaceDirectory
,
onSaveLobsterKey
:
()
=>
void
saveLobsterKey
(),
onSaveXhsFeishuConfig
:
()
=>
void
saveXhsFeishuConfig
(),
onResetXhsFeishuConfig
:
resetXhsFeishuSettingsDrafts
,
onSaveCopywritingConfig
:
()
=>
void
saveCopywritingConfig
(),
onResetCopywritingConfig
:
resetCopywritingSettingsDrafts
,
onSaveImageConfig
:
()
=>
void
saveImageConfig
(),
onResetImageConfig
:
resetImageSettingsDrafts
,
onSaveVideoConfig
:
()
=>
void
saveVideoConfig
(),
onResetVideoConfig
:
resetVideoSettingsDrafts
,
onSaveDigitalHumanConfig
:
()
=>
void
saveDigitalHumanConfig
(),
onResetDigitalHumanConfig
:
resetDigitalHumanSettingsDrafts
,
onSaveDouyinRuntimeConfig
:
()
=>
void
saveDouyinRuntimeConfig
(),
onResetDouyinRuntimeConfig
:
resetDouyinRuntimeSettingsDrafts
,
pickWorkspaceDirectory
,
exportDiagnostics
}
satisfies
ComponentProps
<
typeof
SettingsPanels
>
;
...
...
apps/ui/src/features/settings/SettingsPanels.tsx
View file @
c9ab97c3
This diff is collapsed.
Click to expand it.
apps/ui/src/features/settings/SettingsView.tsx
View file @
c9ab97c3
...
...
@@ -5,7 +5,10 @@ interface SettingsViewProps {
children
:
ReactNode
}
export
function
SettingsView
({
statusHint
,
children
}:
SettingsViewProps
)
{
export
function
SettingsView
({
statusHint
,
children
}:
SettingsViewProps
)
{
return
(
<
div
className=
"page-stack settings-page-stack settings-page-shell"
>
{
statusHint
}
...
...
apps/ui/src/features/settings/settingsDrafts.ts
0 → 100644
View file @
c9ab97c3
import
type
{
AppConfig
}
from
"@qjclaw/shared-types"
export
interface
PendingSettingsFlags
{
hasPendingBasicConfig
:
boolean
hasPendingXhsFeishuConfig
:
boolean
hasPendingCopywritingConfig
:
boolean
hasPendingImageConfig
:
boolean
hasPendingVideoConfig
:
boolean
hasPendingDigitalHumanConfig
:
boolean
hasPendingDouyinRuntimeConfig
:
boolean
}
export
interface
BasicResetSettingsDrafts
{
lobsterKey
:
string
workspacePath
:
string
}
export
interface
DigitalHumanResetSettingsDrafts
{
digitalHumanVolcAccessKey
:
string
digitalHumanVolcSecretKey
:
string
digitalHumanQiniuAccessKey
:
string
digitalHumanQiniuSecretKey
:
string
}
export
interface
DouyinRuntimeResetSettingsDrafts
{
videoAnalyzerBaseUrl
:
string
videoAnalyzerModelId
:
string
videoAnalyzerApiKey
:
string
replicationBriefBaseUrl
:
string
replicationBriefModelId
:
string
replicationBriefApiKey
:
string
vectcutBaseUrl
:
string
vectcutFileBaseUrl
:
string
vectcutApiKey
:
string
}
export
interface
XhsFeishuResetSettingsDrafts
{
xhsFeishuAppId
:
string
xhsFeishuAppSecret
:
string
xhsFeishuAppToken
:
string
xhsFeishuTableId
:
string
}
export
function
getHasPendingSettingsChange
(
flags
:
PendingSettingsFlags
)
{
return
flags
.
hasPendingBasicConfig
||
flags
.
hasPendingXhsFeishuConfig
||
flags
.
hasPendingCopywritingConfig
||
flags
.
hasPendingImageConfig
||
flags
.
hasPendingVideoConfig
||
flags
.
hasPendingDigitalHumanConfig
||
flags
.
hasPendingDouyinRuntimeConfig
}
export
function
getResetBasicSettingsDrafts
(
config
:
AppConfig
):
BasicResetSettingsDrafts
{
return
{
lobsterKey
:
""
,
workspacePath
:
config
.
workspacePath
}
}
export
function
getResetCopywritingSettingsDrafts
()
{
return
{
copywritingModelApiKey
:
""
}
}
export
function
getResetImageSettingsDrafts
()
{
return
{
imageModelApiKey
:
""
}
}
export
function
getResetVideoSettingsDrafts
()
{
return
{
videoModelApiKey
:
""
}
}
export
function
getResetDigitalHumanSettingsDrafts
():
DigitalHumanResetSettingsDrafts
{
return
{
digitalHumanVolcAccessKey
:
""
,
digitalHumanVolcSecretKey
:
""
,
digitalHumanQiniuAccessKey
:
""
,
digitalHumanQiniuSecretKey
:
""
}
}
export
function
getResetDouyinRuntimeSettingsDrafts
(
config
:
AppConfig
):
DouyinRuntimeResetSettingsDrafts
{
return
{
videoAnalyzerBaseUrl
:
config
.
douyinRuntimeConfig
.
videoAnalyzer
.
baseUrl
,
videoAnalyzerModelId
:
config
.
douyinRuntimeConfig
.
videoAnalyzer
.
modelId
??
""
,
videoAnalyzerApiKey
:
""
,
replicationBriefBaseUrl
:
config
.
douyinRuntimeConfig
.
replicationBrief
.
baseUrl
,
replicationBriefModelId
:
config
.
douyinRuntimeConfig
.
replicationBrief
.
modelId
??
""
,
replicationBriefApiKey
:
""
,
vectcutBaseUrl
:
config
.
douyinRuntimeConfig
.
vectcut
.
baseUrl
,
vectcutFileBaseUrl
:
config
.
douyinRuntimeConfig
.
vectcut
.
fileBaseUrl
,
vectcutApiKey
:
""
}
}
export
function
getResetXhsFeishuSettingsDrafts
():
XhsFeishuResetSettingsDrafts
{
return
{
xhsFeishuAppId
:
""
,
xhsFeishuAppSecret
:
""
,
xhsFeishuAppToken
:
""
,
xhsFeishuTableId
:
""
}
}
apps/ui/src/features/settings/useSaveSettings.ts
View file @
c9ab97c3
This diff is collapsed.
Click to expand it.
apps/ui/src/styles/settings.css
View file @
c9ab97c3
This diff is collapsed.
Click to expand it.
apps/ui/test/settingsDrafts.test.ts
0 → 100644
View file @
c9ab97c3
import
test
from
"node:test"
import
assert
from
"node:assert/strict"
import
type
{
AppConfig
}
from
"@qjclaw/shared-types"
import
{
getHasPendingSettingsChange
,
getResetBasicSettingsDrafts
,
getResetCopywritingSettingsDrafts
,
getResetDigitalHumanSettingsDrafts
,
getResetDouyinRuntimeSettingsDrafts
,
getResetImageSettingsDrafts
,
getResetVideoSettingsDrafts
,
getResetXhsFeishuSettingsDrafts
}
from
"../src/features/settings/settingsDrafts.ts"
const
config
=
{
setupMode
:
"employee-key"
,
provider
:
"openai"
,
baseUrl
:
"https://example.test/v1"
,
apiKeyConfigured
:
true
,
gatewayTokenConfigured
:
false
,
authTokenConfigured
:
false
,
defaultModel
:
"qwen-max"
,
workspacePath
:
"/workspace/current"
,
gatewayUrl
:
"ws://127.0.0.1:8765"
,
cloudApiBaseUrl
:
"https://cloud.example.test"
,
runtimeCloudApiBaseUrl
:
"https://runtime.example.test"
,
runtimeMode
:
"bundled-runtime"
,
expertModelConfig
:
{
image
:
{
baseUrl
:
"https://image.example.test"
,
modelId
:
"image-model"
,
apiKeyConfigured
:
true
},
video
:
{
baseUrl
:
"https://video.example.test"
,
modelId
:
"video-model"
,
apiKeyConfigured
:
true
},
copywriting
:
{
baseUrl
:
"https://copy.example.test"
,
modelId
:
"copy-model"
,
apiKeyConfigured
:
true
},
digitalHuman
:
{
volcRegion
:
"cn-north-1"
,
volcService
:
"cv"
,
volcHost
:
"visual.volcengineapi.com"
,
volcScheme
:
"https"
,
ttsVoice
:
"zh-CN-YunxiNeural"
,
qiniuBucket
:
"bucket"
,
qiniuDomain
:
"https://cdn.example.test"
,
qiniuKeyPrefix
:
"omnihuman"
,
volcAccessKeyConfigured
:
true
,
volcSecretKeyConfigured
:
true
,
qiniuAccessKeyConfigured
:
true
,
qiniuSecretKeyConfigured
:
true
}
},
douyinRuntimeConfig
:
{
videoAnalyzer
:
{
baseUrl
:
"https://video-analyzer.example.test"
,
modelId
:
"va-model"
,
apiKeyConfigured
:
true
},
replicationBrief
:
{
baseUrl
:
"https://brief.example.test"
,
modelId
:
"brief-model"
,
apiKeyConfigured
:
true
},
vectcut
:
{
baseUrl
:
"https://vectcut.example.test"
,
fileBaseUrl
:
"https://files.example.test"
,
apiKeyConfigured
:
true
}
},
xhsFeishuConfig
:
{
appIdConfigured
:
true
,
appSecretConfigured
:
true
,
appTokenConfigured
:
true
,
tableIdConfigured
:
true
}
}
satisfies
AppConfig
test
(
"detects any pending settings change"
,
()
=>
{
assert
.
equal
(
getHasPendingSettingsChange
({
hasPendingBasicConfig
:
false
,
hasPendingXhsFeishuConfig
:
false
,
hasPendingCopywritingConfig
:
false
,
hasPendingImageConfig
:
false
,
hasPendingVideoConfig
:
false
,
hasPendingDigitalHumanConfig
:
false
,
hasPendingDouyinRuntimeConfig
:
false
}),
false
)
assert
.
equal
(
getHasPendingSettingsChange
({
hasPendingBasicConfig
:
false
,
hasPendingXhsFeishuConfig
:
false
,
hasPendingCopywritingConfig
:
false
,
hasPendingImageConfig
:
false
,
hasPendingVideoConfig
:
false
,
hasPendingDigitalHumanConfig
:
false
,
hasPendingDouyinRuntimeConfig
:
true
}),
true
)
})
test
(
"basic reset only affects lobster key and workspace path"
,
()
=>
{
assert
.
deepEqual
(
getResetBasicSettingsDrafts
(
config
),
{
lobsterKey
:
""
,
workspacePath
:
"/workspace/current"
})
})
test
(
"single model resets only clear the current module key"
,
()
=>
{
assert
.
deepEqual
(
getResetCopywritingSettingsDrafts
(),
{
copywritingModelApiKey
:
""
,
})
assert
.
deepEqual
(
getResetImageSettingsDrafts
(),
{
imageModelApiKey
:
""
,
})
assert
.
deepEqual
(
getResetVideoSettingsDrafts
(),
{
videoModelApiKey
:
""
,
})
})
test
(
"xhs feishu reset only clears its own drafts"
,
()
=>
{
assert
.
deepEqual
(
getResetXhsFeishuSettingsDrafts
(),
{
xhsFeishuAppId
:
""
,
xhsFeishuAppSecret
:
""
,
xhsFeishuAppToken
:
""
,
xhsFeishuTableId
:
""
})
})
test
(
"digital human reset only clears its own secrets"
,
()
=>
{
assert
.
deepEqual
(
getResetDigitalHumanSettingsDrafts
(),
{
digitalHumanVolcAccessKey
:
""
,
digitalHumanVolcSecretKey
:
""
,
digitalHumanQiniuAccessKey
:
""
,
digitalHumanQiniuSecretKey
:
""
})
})
test
(
"douyin runtime reset restores saved base urls and model ids while clearing runtime secrets"
,
()
=>
{
assert
.
deepEqual
(
getResetDouyinRuntimeSettingsDrafts
(
config
),
{
videoAnalyzerBaseUrl
:
"https://video-analyzer.example.test"
,
videoAnalyzerModelId
:
"va-model"
,
videoAnalyzerApiKey
:
""
,
replicationBriefBaseUrl
:
"https://brief.example.test"
,
replicationBriefModelId
:
"brief-model"
,
replicationBriefApiKey
:
""
,
vectcutBaseUrl
:
"https://vectcut.example.test"
,
vectcutFileBaseUrl
:
"https://files.example.test"
,
vectcutApiKey
:
""
})
})
apps/ui/test/settingsPanelsSource.test.ts
0 → 100644
View file @
c9ab97c3
import
test
from
"node:test"
import
assert
from
"node:assert/strict"
import
{
readFileSync
}
from
"node:fs"
const
settingsPanelsSource
=
readFileSync
(
new
URL
(
"../src/features/settings/SettingsPanels.tsx"
,
import
.
meta
.
url
),
"utf8"
)
const
settingsStylesSource
=
readFileSync
(
new
URL
(
"../src/styles/settings.css"
,
import
.
meta
.
url
),
"utf8"
)
test
(
"settings panels remove per-module status chips"
,
()
=>
{
assert
.
doesNotMatch
(
settingsPanelsSource
,
/StatusChip/
)
assert
.
doesNotMatch
(
settingsPanelsSource
,
/已配置/
)
assert
.
doesNotMatch
(
settingsPanelsSource
,
/未配置/
)
})
test
(
"settings page buttons use the unified action button classes"
,
()
=>
{
assert
.
match
(
settingsPanelsSource
,
/settings-action-button settings-action-button-secondary/
)
assert
.
match
(
settingsPanelsSource
,
/settings-action-button settings-action-button-primary/
)
assert
.
doesNotMatch
(
settingsPanelsSource
,
/settings-card-action-button/
)
assert
.
doesNotMatch
(
settingsPanelsSource
,
/settings-secondary-button/
)
assert
.
doesNotMatch
(
settingsStylesSource
,
/
\.
settings-panel
\.
status-chip/
)
assert
.
doesNotMatch
(
settingsStylesSource
,
/
\.
settings-card-action-button/
)
assert
.
match
(
settingsStylesSource
,
/
\.
settings-action-button
\b
/
)
assert
.
match
(
settingsStylesSource
,
/
\.
settings-actions-row
\b
/
)
})
test
(
"basic config action buttons share the same width rule"
,
()
=>
{
assert
.
match
(
settingsStylesSource
,
/
\.
settings-inline-save-button,
\s
*
\n\.
settings-basic-directory-actions
\.
settings-action-button
\s
*
\{\s
*
\n\s
*width:
\s
*96px;
\s
*
\n\s
*min-width:
\s
*96px;/m
)
})
test
(
"workspace directory keeps only export diagnostics and change directory actions stacked"
,
()
=>
{
assert
.
doesNotMatch
(
settingsPanelsSource
,
/>
\s
*恢复当前
\s
*</
)
assert
.
doesNotMatch
(
settingsPanelsSource
,
/>
\s
*保存目录
\s
*</
)
assert
.
match
(
settingsPanelsSource
,
/onClick=
\{\(\)
=> void exportDiagnostics
\(\)\}
/
)
assert
.
match
(
settingsPanelsSource
,
/onClick=
\{\(\)
=> void pickWorkspaceDirectory
\(\)\}
/
)
assert
.
match
(
settingsStylesSource
,
/
\.
settings-basic-directory-actions
\s
*
\{[\s\S]
*
?
flex-direction:
\s
*column;
[\s\S]
*
?
align-items:
\s
*stretch;/m
)
})
test
(
"xhs feishu actions are anchored inside the input block"
,
()
=>
{
assert
.
match
(
settingsPanelsSource
,
/className="settings-xhs-feishu-form"/
)
assert
.
match
(
settingsPanelsSource
,
/renderActions
\(\{\s
*hasPending:
\s
*hasPendingXhsFeishuConfig,
\s
*onReset:
\s
*
\(\)\s
*=>
\s
*void onResetXhsFeishuConfig
\(\)
,
\s
*onSave:
\s
*
\(\)\s
*=>
\s
*void onSaveXhsFeishuConfig
\(\)
,
\s
*className:
\s
*"settings-xhs-feishu-actions"/m
)
assert
.
match
(
settingsStylesSource
,
/
\.
settings-xhs-feishu-form
\s
*
\{[\s\S]
*
?
display:
\s
*grid;
[\s\S]
*
?
gap:
\s
*8px;/m
)
assert
.
match
(
settingsStylesSource
,
/
\.
settings-xhs-feishu-actions
\s
*
\{[\s\S]
*
?
justify-content:
\s
*flex-end;
[\s\S]
*
?
margin-top:
\s
*0;/m
)
})
test
(
"settings panel actions are wired per section instead of global handlers"
,
()
=>
{
assert
.
doesNotMatch
(
settingsPanelsSource
,
/
\b
hasPendingSettingsChange
\b
/
)
assert
.
doesNotMatch
(
settingsPanelsSource
,
/
\b
onSaveAll
\b
/
)
assert
.
match
(
settingsPanelsSource
,
/
\b
onSaveLobsterKey
\b
/
)
assert
.
doesNotMatch
(
settingsPanelsSource
,
/
\b
saveWorkspaceDirectory
\b
/
)
assert
.
doesNotMatch
(
settingsPanelsSource
,
/
\b
restoreWorkspaceDirectory
\b
/
)
assert
.
match
(
settingsPanelsSource
,
/
\b
onSaveCopywritingConfig
\b
/
)
assert
.
match
(
settingsPanelsSource
,
/
\b
onSaveImageConfig
\b
/
)
assert
.
match
(
settingsPanelsSource
,
/
\b
onSaveVideoConfig
\b
/
)
assert
.
match
(
settingsPanelsSource
,
/
\b
onSaveDigitalHumanConfig
\b
/
)
assert
.
match
(
settingsPanelsSource
,
/
\b
onSaveDouyinRuntimeConfig
\b
/
)
})
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment