feat: 角色视觉体系(头像按角色取)+ 时间注入(loop 每轮当前时间)
【角色视觉体系(红线:每个角色自己的头像/背景,绝不串用昔涟)】
- persona.go:Persona 加 Visual{avatar, chatBackground{landscape,portrait}} + DefaultAvatar;每个角色各自配。
- cyrene.yaml:昔涟配 avatar(3F-Q-Happy-1) + chatBackground(横/竖屏)。
- PersonaStore 加 Persona(id);PersonaSummary 加 avatar/chatBackground;collectPersonas 按角色取
Persona(id) 视觉(无视觉→空,前端回退默认占位,绝不串角色)。
- 前端:types PersonaRecord 加 avatar/chatBackground;persona.ts personaAvatar(p)=p?.avatar||默认占位
(殊无昔涟 fallback);store 加 currentPersona;App 挂载 fetchPersonas 取默认角色;
TopBar/chat 头像改 personaAvatar(currentPersona)(写死昔涟头像已清除)。
【时间注入(她自然知道现在几点,深夜催睡交给人格/capacity)】
- options.go Options 加 Timezone;loop.go Loop 加 timeLocation/timeZoneName + New 解析 + loadTimezone;
time.go currentTimeBlock()(当前时间+周几+时区,每轮刷新);memory.go assembleContext 在「工作记忆」
前注入时间 system(keyed 在 timeLocation!=nil,无时区不注入不破坏测试);wire.go Timezone=a.cfg.Timezone。
go build/vet/test 全绿(gateway/loop/persona);前端 tsc/vite build 75模块绿。红线无写死昔涟头像。
This commit is contained in:
1 parent
950bea9758
commit
7455d09d05
20 files changed
+177
-6
No files matched your search
@@ -6,12 +6,15 @@ import { AdminApp } from "./admin/AdminApp";
|
||||
import { parseAdminHash } from "./admin/router";
|
||||
import { connectWs, disconnectWs } from "./lib/ws";
|
||||
import { initTheme } from "./lib/theme";
|
||||
import { getAuthToken } from "./lib/api";
|
||||
import { fetchPersonas, getAuthToken } from "./lib/api";
|
||||
import { useAppStore } from "./store/useAppStore";
|
||||
|
||||
export default function App(): React.ReactNode {
|
||||
// Apply the persisted (dark-first) theme and open the realtime channel only
|
||||
// when we hold a token — otherwise the gateway rejects the handshake with 401.
|
||||
const [route, setRoute] = useState(() => parseAdminHash(window.location.hash));
|
||||
const setCurrentPersona = useAppStore((s) => s.setCurrentPersona);
|
||||
|
||||
useEffect(() => {
|
||||
initTheme();
|
||||
if (getAuthToken()) connectWs();
|
||||
@@ -23,6 +26,25 @@ export default function App(): React.ReactNode {
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Load personas once on mount and pick the default one as the web main-session
|
||||
// persona. On failure currentPersona stays null and components fall back to the
|
||||
// neutral default placeholder (never another role's avatar).
|
||||
useEffect(() => {
|
||||
if (!getAuthToken()) return;
|
||||
let cancelled = false;
|
||||
fetchPersonas()
|
||||
.then((list) => {
|
||||
if (cancelled) return;
|
||||
setCurrentPersona(list.find((p) => p.isDefault) ?? list[0] ?? null);
|
||||
})
|
||||
.catch(() => {
|
||||
// Keep currentPersona null → neutral default placeholder.
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [setCurrentPersona]);
|
||||
|
||||
// The M9 modal UI and the admin subsystem (design/10) are two separate shells.
|
||||
// `/admin` is reached via `#/admin`; every admin module is a hash segment.
|
||||
if (route.admin) {
|
||||
|
||||
Reference in new issue
Block a user