Embed workflow UI and isolate workspace assets

This commit is contained in:
boos
2026-05-26 17:00:10 +08:00
parent 0f9fe4a81d
commit e0bd9b4194
7 changed files with 529 additions and 148 deletions
+43
View File
@@ -138,6 +138,20 @@ function resolveStoredProfile(): UserProfile {
return { name: '管理员', avatarSeed, avatarStyle: pickDicebearStyle(avatarSeed) };
}
}
function profileFromUsername(username: string, current?: UserProfile): UserProfile {
const name = username.trim();
const avatarSeed = current?.avatarSeed?.trim() || createRandomAvatarSeed();
const avatarStyle = current?.avatarStyle?.trim() || pickDicebearStyle(avatarSeed);
return {
name: name || current?.name || '用户',
avatarSeed,
avatarStyle: DICEBEAR_STYLES.includes(avatarStyle as DicebearStyle)
? avatarStyle
: pickDicebearStyle(avatarSeed),
};
}
function UserProfileModal({
open,
profile,
@@ -560,6 +574,35 @@ function AppShell() {
cancelled = true;
};
}, [location.pathname, userAuthed]);
useEffect(() => {
if (
!userAuthed
|| !location.pathname.startsWith('/user/')
|| ['/user/login', '/user/register'].includes(location.pathname)
) return;
let cancelled = false;
const syncUserProfile = async () => {
try {
const response = await userApi.me();
if (cancelled) return;
const user = (response as any)?.user || (response as any)?.data || {};
const username = typeof user.username === 'string' ? user.username.trim() : '';
if (!username) return;
setUserProfile((current) => {
if ((current.name || '').trim() === username) return current;
const next = profileFromUsername(username, current);
localStorage.setItem(USER_PROFILE_STORAGE_KEY, JSON.stringify(next));
return next;
});
} catch {
// Keep the local profile if the user profile endpoint is temporarily unavailable.
}
};
void syncUserProfile();
return () => {
cancelled = true;
};
}, [location.pathname, userAuthed]);
useEffect(() => {
const handler = (e: MouseEvent) => {
if (userMenuRef.current && !userMenuRef.current.contains(e.target as Node)) {
+3 -1
View File
@@ -425,7 +425,9 @@ export default function Workspace() {
</button>
)}
<div style={detailMetaStyle}>
<div><span>用户</span><strong>{selected.userId ?? '管理员/系统'}</strong></div>
{audience !== 'user' && (
<div><span>用户</span><strong>{selected.userId ?? '管理员/系统'}</strong></div>
)}
<div><span>模型</span><strong>{selected.model || '未记录'}</strong></div>
<div><span>大小</span><strong>{formatBytes(selected.sizeBytes)}</strong></div>
<div><span>文件</span><strong>{selected.hasLocalFile ? '本地归档' : '远程引用'}</strong></div>