feat: lots 430-434 — /upscale, /count, /speak, emoji health, /flip
Lot 430: /upscale placeholder (needs ESRGAN model) Lot 431: /count — file counts for all data directories Lot 432: /speak <persona> <text> — force TTS as any persona Lot 433: Emoji health indicators in admin dashboard Lot 434: /flip enhanced (PILE/FACE + emoji) 132 commands, 211 tests. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
088decd1ae
commit
4959539bfa
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"topics": {},
|
||||
"pins": {},
|
||||
"savedAt": "2026-03-21T22:17:08.574Z"
|
||||
"savedAt": "2026-03-21T22:22:08.578Z"
|
||||
}
|
||||
@@ -2,5 +2,5 @@
|
||||
"nick": "Pharmacius",
|
||||
"facts": [],
|
||||
"summary": "",
|
||||
"lastUpdated": "2026-03-21T22:20:02.284Z"
|
||||
"lastUpdated": "2026-03-21T22:23:01.597Z"
|
||||
}
|
||||
@@ -23,6 +23,7 @@ export const CHAT_COMMANDS = new Set([
|
||||
"/haiku",
|
||||
"/timer",
|
||||
"/story",
|
||||
"/speak",
|
||||
]);
|
||||
|
||||
export function createChatCommandHandler(deps: CommandHandlerDeps) {
|
||||
@@ -821,6 +822,19 @@ export function createChatCommandHandler(deps: CommandHandlerDeps) {
|
||||
return;
|
||||
}
|
||||
|
||||
case "/speak": {
|
||||
// /speak <persona> <texte> — force TTS for text as persona
|
||||
const speakMatch = text.match(/^\/speak\s+(\S+)\s+(.+)$/);
|
||||
if (!speakMatch) { send(ws, { type: "system", text: "Usage: /speak <persona> <texte>" }); return; }
|
||||
const [, speakNick, speakText] = speakMatch;
|
||||
const persona = getPersonas().find(p => p.nick.toLowerCase() === speakNick.toLowerCase());
|
||||
if (!persona) { send(ws, { type: "system", text: `Persona "${speakNick}" inconnue.` }); return; }
|
||||
|
||||
const { synthesizeTTS } = await import("./ws-multimodal.js");
|
||||
await synthesizeTTS(persona.nick, speakText, info.channel, broadcast);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
send(ws, { type: "system", text: `Commande inconnue: ${cmd}. Tape /help.` });
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ export const GENERATE_COMMANDS = new Set([
|
||||
"/drone", "/grain", "/circus", "/honk", "/kokoro",
|
||||
"/variations",
|
||||
"/imagine-queue",
|
||||
"/upscale",
|
||||
]);
|
||||
|
||||
export function createGenerateCommandHandler(deps: CommandHandlerDeps) {
|
||||
@@ -1547,6 +1548,12 @@ export function createGenerateCommandHandler(deps: CommandHandlerDeps) {
|
||||
return;
|
||||
}
|
||||
|
||||
case "/upscale": {
|
||||
// /upscale — upscale last generated image 2x (placeholder, needs ESRGAN model)
|
||||
send(ws, { type: "system", text: "Upscale non implemente (necessite modele ESRGAN). Utilisez /imagine avec un prompt plus detaille." });
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
send(ws, { type: "system", text: `Commande inconnue: ${cmd}. Tape /help.` });
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ export const INFO_COMMANDS = new Set([
|
||||
"/about",
|
||||
"/benchmark",
|
||||
"/ping",
|
||||
"/count",
|
||||
]);
|
||||
|
||||
export function createInfoCommandHandler(deps: CommandHandlerDeps) {
|
||||
@@ -618,8 +619,9 @@ export function createInfoCommandHandler(deps: CommandHandlerDeps) {
|
||||
}
|
||||
|
||||
case "/flip": {
|
||||
const result = Math.random() < 0.5 ? "pile" : "face";
|
||||
broadcast(info.channel, { type: "system", text: `\u{1FA99} ${info.nick}: ${result}!` });
|
||||
const result = Math.random() < 0.5 ? "PILE" : "FACE";
|
||||
const emoji = result === "PILE" ? "\u{1FA99}" : "\u{1F451}";
|
||||
broadcast(info.channel, { type: "system", text: `${emoji} ${info.nick} lance une piece: ${result}!` });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -629,6 +631,25 @@ export function createInfoCommandHandler(deps: CommandHandlerDeps) {
|
||||
return;
|
||||
}
|
||||
|
||||
case "/count": {
|
||||
const fs = await import("node:fs");
|
||||
const countFiles = (dir: string) => {
|
||||
try { return fs.readdirSync(path.join(process.cwd(), "data", dir)).filter((f: string) => !f.startsWith(".")).length; } catch { return 0; }
|
||||
};
|
||||
const lines = [
|
||||
"=== COMPTEURS ===",
|
||||
` Personas: ${getPersonas().length}`,
|
||||
` Images: ${countFiles("media/images")}`,
|
||||
` Audio: ${countFiles("media/audio")}`,
|
||||
` Samples DAW: ${countFiles("daw-samples")}`,
|
||||
` Compositions: ${countFiles("compositions")}`,
|
||||
` Exports: ${countFiles("exports")}`,
|
||||
` Feedback: ${countFiles("feedback")}`,
|
||||
];
|
||||
send(ws, { type: "system", text: lines.join("\n") });
|
||||
return;
|
||||
}
|
||||
|
||||
case "/uptime": {
|
||||
const secs = Math.floor(process.uptime());
|
||||
const h = Math.floor(secs / 3600);
|
||||
|
||||
@@ -61,7 +61,7 @@ function ServiceHealth() {
|
||||
className={`admin-health-card ${s.ok ? "admin-health-ok" : "admin-health-fail"}`}
|
||||
>
|
||||
<div className="admin-health-name">{name.toUpperCase()}</div>
|
||||
<div className="admin-health-status">{s.ok ? "\u25CF" : "\u25CB"}</div>
|
||||
<div className="admin-health-status">{s.ok ? "\u{1F7E2}" : "\u{1F534}"}</div>
|
||||
{s.detail && <div className="admin-health-detail">{s.detail}</div>}
|
||||
</div>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user