/** * Estúdio de criação (/criacao/*): expressões, atividades e figurinhas feitas por IA. * O estado chega por SSE ('criacao') e é recarregado ao abrir a tela. */ import { pedir, postar } from './api'; import { avisos, tentar } from './avisos.svelte'; import { ouvir } from './estado.svelte'; export type TipoCriacao = 'expressao' | 'atividade' | 'figurinha'; export type IaCriacao = 'gemini' | 'local' | 's10'; export interface ItemCriado { id: number; horario: number; tipo: TipoCriacao; nome: string; emoji: string; receita: any; ia: IaCriacao; pedido: string; origem?: 'pedido' | 'dia'; motivo?: string; aprendi?: string; } export interface Provedor { nome: string; pronto: boolean; detalhe: string; motivo: string; url?: string; modelo?: string; chave?: boolean; } export interface StatusCriacao { ocupado: { tipo: TipoCriacao; ia: IaCriacao; desde: number; pedido: string } | null; erro: string; provedores: Record; criacoes: ItemCriado[]; bases: Record<'expressao' | 'atividade', { id: string; nome: string; emoji: string }[]>; } export const TIPOS: { id: TipoCriacao; nome: string; emoji: string; exemplo: string }[] = [ { id: 'expressao', nome: 'Expressão', emoji: '😊', exemplo: 'Ex.: desconfiado, olhando de lado com um olho meio fechado' }, { id: 'atividade', nome: 'Atividade', emoji: '🤹', exemplo: 'Ex.: fazendo malabarismo com 3 bolinhas, concentrado' }, { id: 'figurinha', nome: 'Figurinha', emoji: '🎨', exemplo: 'Ex.: um sol de óculos escuros sorrindo' }, ]; class Criacao { status = $state(null); async carregar() { try { this.status = await pedir('/criacao/status'); } catch (e) { avisos.erro(e); } } item(id: number) { return this.status?.criacoes.find((c) => c.id === id) ?? null; } async criar(tipo: TipoCriacao, pedido: string, ia: IaCriacao, base: string) { const r = await tentar(() => postar('/criacao/criar', { tipo, pedido, ia, base: base || null })); if (r) this.status = r; return !!r; } tocar(id: number) { return tentar(() => postar('/criacao/tocar', { id })); } async apagar(id: number) { await tentar(() => postar('/criacao/apagar', { id }), 'Criação apagada.'); } async configurar(local: { url?: string; modelo?: string; chave?: string }) { const r = await tentar(() => postar('/criacao/config', { local }), 'IA local salva.'); if (r) this.status = r; } async testar(ia: IaCriacao) { const r = await tentar(() => postar<{ mensagem: string }>('/criacao/testar', { ia })); if (r) avisos.ok(r.mensagem); } } export const criacao = new Criacao(); ouvir('criacao', (d: StatusCriacao) => { const antes = criacao.status; criacao.status = d; // terminou: avisa o resultado if (antes?.ocupado && !d.ocupado) { if (d.erro) avisos.erro(new Error(d.erro)); else if (d.criacoes[0]) avisos.ok(`Criado: ${d.criacoes[0].emoji} ${d.criacoes[0].nome}`); } }); /** Favoritos podem guardar criações ("criado:12"): tocar no celular. */ export const ehCriado = (g: string) => g.startsWith('criado:'); export const idCriado = (g: string) => Number(g.slice(7));