funifier-story-player
v0.1.0
Published
Player de Histórias Interativas do Funifier
Maintainers
Readme
funifier-story-player
Player de Histórias Interativas do Funifier
Install
npm install funifier-story-playerreact e react-dom (>= 18) são peerDependencies (o app já os tem). Importe a folha de
estilo uma vez na aplicação:
import "funifier-story-player/styles.css";Quick start
import { StoryPlayer } from "funifier-story-player";
import "funifier-story-player/styles.css";
<div style={{ position: "relative", width: 360, height: 640 }}>
<StoryPlayer bundle={bundle} onComplete={(r) => {}} />
</div>;Casos de uso
1. Dados prontos + UI pronta
Você já tem o bundle (buscou você mesmo, ou no server do Next).
<StoryPlayer bundle={bundle} onComplete={fn} />2. O player busca + UI pronta
Passe storyId + transport; o player busca sob demanda.
<StoryPlayer storyId="s1" transport={transport} onComplete={fn} />3. Motor headless (UI própria)
useStoryEngine entrega estado + ações, sem UI.
const engine = useStoryEngine({ bundle }); // ou { storyId, transport }
// engine.phase, engine.currentScene, engine.chooseOption, engine.advance, ...Fonte de dados
Forneça bundle ou transport.
| prop | tipo | descrição |
| --- | --- | --- |
| bundle | StoryBundle | { story, characters, scenes } — dados prontos |
| transport | (storyId: string) => Promise<StoryBundle> | função que busca sob demanda |
| storyId | string | id passado ao transport |
Props (root)
Além de bundle / transport / storyId (ver Fonte de dados acima):
| prop | tipo | default | descrição |
| --- | --- | --- | --- |
| lang | "pt" \| "es" \| "en" | "pt" | idioma dos textos da UI (botões, rótulos) |
| theme | StoryTheme | paleta neutra | 3 cores + radius/font (ver Theme) |
| className | string | — | classe extra no elemento raiz |
| onComplete | (result: StoryCompletionResult) => void | — | história concluída (ver Callbacks) |
| onExit | () => void | — | usuário saiu ou fechou a tela final |
| onAspectRatio | (ratio: "16:9" \| "9:16" \| "1:1") => void | — | proporção da história (dimensionar o container) |
Configuração de exibição (capa, controles, slots) é feita por componentes-filho — ver Compound components.
Compound components
A capa e os controles são configurados por componentes-filho.
Como as props funcionam: todos os elementos são visíveis por padrão. Você esconde o que não quer passando
={false}. Passartrue(ex.:<StoryPlayer.Cover play />) não muda nada — já é o padrão.
<StoryPlayer bundle={bundle} lang="es" theme={theme} onComplete={fn}>
<StoryPlayer.Cover synopsis={false} /> {/* capa sem sinopse */}
<StoryPlayer.Controls volume={false} skip={false} /> {/* sem volume, sem pular */}
<StoryPlayer.Slots topLeft={<Badge />} /> {/* injeta UI própria */}
</StoryPlayer><StoryPlayer.Cover> — o que aparece na capa
Todas boolean, default true. Passe ={false} para esconder.
| prop | controla |
| --- | --- |
| title | título da história |
| synopsis | sinopse |
| cast | botão Elenco + avatares + nomes |
| play | botão Reproduzir |
| enabled | mostra a capa — false inicia a história direto |
<StoryPlayer.Cover synopsis={false} /> // esconde a sinopse
<StoryPlayer.Cover cast={false} title={false} /> // sinopse + play
<StoryPlayer.Cover title={false} synopsis={false} cast={false} /> // só o botão Reproduzir
<StoryPlayer.Cover enabled={false} /> // pula a capa<StoryPlayer.Controls> — controles durante a cena
Todas boolean, default true. Passe ={false} para esconder.
| prop | controla |
| --- | --- |
| title | título no topo |
| playPause | botão play/pause central |
| subtitle | legendas |
| sceneProgress | barra de progresso da cena |
| storyProgress | barra de progresso da história |
| cast | botão Elenco |
| skip | botão Pular fala |
| volume | controle de volume da música |
<StoryPlayer.Controls volume={false} /> // esconde o volume
<StoryPlayer.Controls subtitle={false} skip={false} /> // sem legenda nem pular<StoryPlayer.Slots> — injeta UI própria nas bordas
Cada prop recebe um ReactNode (só aparece se você passar algo).
| prop | posição |
| --- | --- |
| topLeft | canto superior esquerdo |
| topCenter | topo centralizado |
| topRight | canto superior direito |
| bottomLeft | canto inferior esquerdo |
| bottomRight | canto inferior direito |
| overlay | camada livre sobre tudo |
<StoryPlayer.Slots topLeft={<span>Módulo I</span>} />Theme
<StoryPlayer
bundle={bundle}
theme={{
primary: "#6d5ae6",
secondary: "#94a3b8",
tertiary: "#22d3ee",
radius: 14,
font: '"Poppins", sans-serif',
}}
/>| campo | aplica em |
| --- | --- |
| primary | CTA, progresso, palavra ativa do karaokê |
| secondary | controles de apoio, nome do personagem |
| tertiary | hotspot, respiração, acentos |
| radius | number (px) ou string CSS |
| font | família de fonte |
Callbacks
onComplete(result) recebe StoryCompletionResult:
| campo | tipo |
| --- | --- |
| story_id | string |
| score | number |
| passing_score | number \| undefined |
| passed | boolean \| undefined |
| end_label | string \| undefined |
| decisions_taken | StoryDecisionTaken[] |
| variables | Record<string, unknown> |
Transports Funifier (./funifier)
Helpers que buscam a história no Funifier. Você injeta a URL base e o token/apikey — nada disso fica embutido no pacote.
No .env do seu app:
REACT_APP_API_URL=https://your-instance.funifier.com/v3Em Next, o prefixo é
NEXT_PUBLIC_(ex.:NEXT_PUBLIC_API_URL).
Autenticado (usuário logado)
O getToken devolve o token do player. Normalmente você lê o token de um contexto/estado
global do seu app (auth/sessão):
import { StoryPlayer } from "funifier-story-player";
import { funifierTransport } from "funifier-story-player/funifier";
import { useAuth } from "context/AuthContext"; // seu contexto global
function Story() {
const { token } = useAuth(); // token da sessão, vindo do contexto global
const transport = funifierTransport({
service: process.env.REACT_APP_API_URL!,
getToken: () => token,
});
return <StoryPlayer storyId="s1" transport={transport} />;
}Anônimo (sem login)
import { funifierPublicTransport } from "funifier-story-player/funifier";
const transport = funifierPublicTransport({
service: process.env.REACT_APP_API_URL!,
apiKey: process.env.REACT_APP_APIKEY!, // identificador público do tenant
});
<StoryPlayer storyId="s1" transport={transport} />;Registrar a conclusão (opcional)
Se a história tem uma ação de conclusão configurada, dispare-a no onComplete (o pacote
não faz isso sozinho):
onComplete={(result) =>
fireStoryOnComplete({
service: process.env.REACT_APP_API_URL!,
getToken: () => token,
story: bundle.story,
result,
})
}Exports
| export | o que faz |
| --- | --- |
| funifierTransport({ service, getToken }) | busca a história autenticado (token do player) |
| funifierPublicTransport({ service, apiKey }) | busca a história anônimo (apikey pública) |
| fireStoryOnComplete({ service, getToken, story, result }) | dispara a ação de conclusão da história (se houver): monta os atributos e faz o POST |
| buildCompletionAttributes(story, result) | só monta { actionId, attributes } a partir das variáveis — pra você fazer o POST você mesmo |
| funifierActionLog({ service, getToken, actionId, attributes? }) | POST de action log genérico |
Backend (endpoint anônimo, ação de conclusão): docs/backend-setup.md.
Headless
import { useStoryEngine } from "funifier-story-player";
const engine = useStoryEngine({ bundle });Exports
StoryPlayer (.Cover, .Controls, .Slots) · useStoryEngine · useTts ·
getStrings · useStrings · STORY_STRINGS. Subpath ./funifier: transports + action log.
Tipos: StoryBundle, Story, StoryScene, StoryCharacter, StoryCompletionResult,
StoryTheme, StoryCoverConfig, StoryControlsConfig, StorySlots, StoryLang.
Security
- Zero segredo/env/token embutido; sem telemetria.
- Auth injetada pelo consumidor; o pacote não lê
process.env. - Zero dependência de runtime além de
react/react-dom(peer). - CSS escopado sob
.fstory.
License
Proprietary — Funifier. Ver LICENSE.
