# UI screen examples (/start/interface-examples)



<Callout type="info" name="AI">
  You can use the button above to create your own AI UI screen.
</Callout>

<Callout type="info">
  All examples were created using basic HTML elements, you will need to replace them with UI components from your favorite library to improve the graphics.
</Callout>

<Accordions>
  <Accordion title="ui_narrative_dialogue" id="narrative-dialogue">
    <CodeBlockTabs defaultValue="React">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="React">
          React
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Vue">
          Vue
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="hooks/useQueryInterface.ts">
          hooks/useQueryInterface.ts
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="React">
        ```tsx
        import Markdown from "react-markdown";
        import rehypeRaw from "rehype-raw";
        import remarkGfm from "remark-gfm";
        import { useQueryDialogue } from "../hooks/useQueryInterface";
        import ChoiceMenu from "./ChoiceMenu";

        export default function NarrationScreen() {
            const { data: { text, character } = {} } = useQueryDialogue();

            return (
                <div
                    style={{
                        position: "absolute",
                        display: "flex",
                        flexDirection: "column",
                        height: "100%",
                        width: "100%",
                    }}
                >
                    <div style={{ flex: 1, minHeight: 0 }}>
                        <ChoiceMenu />
                    </div>
                    {text && (
                        <div
                            style={{
                                flex: "0 0 auto",
                                height: "30%",
                                minHeight: 0,
                                pointerEvents: "auto",
                                backgroundColor: "white",
                                display: "flex",
                                flexDirection: "column",
                                overflow: "hidden",
                            }}
                        >
                            {character && character.name && <b>{`${character?.name || ""} ${character?.surname || ""}`}</b>}
                            <div
                                style={{
                                    display: "flex",
                                    flexDirection: "row",
                                    height: "100%",
                                    minHeight: 0,
                                    overflow: "hidden",
                                }}
                            >
                                {character?.icon && (
                                    <img
                                        src={character?.icon}
                                        loading='lazy'
                                        alt=''
                                        style={{
                                            maxWidth: "30%",
                                            height: "auto",
                                            objectFit: "contain",
                                            display: "block",
                                        }}
                                    />
                                )}
                                <div style={{ flex: 1, minHeight: 0, overflow: "auto", height: "100%" }}>
                                    <Markdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
                                        {text}
                                    </Markdown>
                                </div>
                            </div>
                        </div>
                    )}
                </div>
            );
        }
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Vue">
        ```vue
        <template>
            <div style="position: absolute; display: flex; flex-direction: column; height: 100%; width: 100%;">
                <div style="flex: 1; min-height: 0;">
                    <ChoiceMenu />
                </div>
                <div
                    v-if="text"
                    style="flex: 0 0 auto; height: 30%; min-height: 0; pointer-events: auto; background-color: white; display: flex; flex-direction: column; overflow: hidden;"
                >
                    <b v-if="character && (character.name || character.surname)">{{ character?.name || '' }} {{ character?.surname || '' }}</b>
                    <div style="display: flex; flex-direction: row; height: 100%; min-height: 0; overflow: hidden;">
                        <img
                            v-if="character && character.icon"
                            :src="character.icon"
                            loading="lazy"
                            alt=""
                            style="max-width: 30%; height: auto; object-fit: contain; display: block;"
                        />
                        <div style="flex: 1; min-height: 0; overflow: auto; height: 100%;">
                            <Markdown v-if="text" :source="text" />
                        </div>
                    </div>
                </div>
            </div>
        </template>

        <script setup>
        import { computed } from 'vue'
        import Markdown from 'vue3-markdown-it'
        import { useQueryDialogue } from '../hooks/useQueryInterface'
        import ChoiceMenu from './ChoiceMenu.vue'

        const { data } = useQueryDialogue()
        const text = computed(() => data.value?.text)
        const character = computed(() => data.value?.character)
        </script>

        ```
      </CodeBlockTab>

      <CodeBlockTab value="hooks/useQueryInterface.ts">
        ```ts
        import { CharacterBaseModel, narration } from "@drincs/pixi-vn";
        import { useQuery } from "@tanstack/react-query";

        export const INTERFACE_DATA_USE_QUEY_KEY = "interface_data_use_quey_key";

        const DIALOGUE_USE_QUEY_KEY = "dialogue_use_quey_key";
        export function useQueryDialogue() {
            return useQuery({
                queryKey: [INTERFACE_DATA_USE_QUEY_KEY, DIALOGUE_USE_QUEY_KEY],
                queryFn: async ({ queryKey }) => {
                    let dialogue = narration.dialogue;
                    let text = dialogue?.text;
                    if (Array.isArray(text)) {
                        text = text.join(" ");
                    }
                    let character = dialogue?.character as string | CharacterBaseModel | undefined;
                    if (typeof character === "string") {
                        character = new CharacterBaseModel(character, {
                            name: character,
                        });
                    }

                    return {
                        text,
                        character,
                    };
                },
            });
        }
        ```
      </CodeBlockTab>
    </CodeBlockTabs>
  </Accordion>

  <Accordion title="ui_choice_menu" id="choice-menu">
    <CodeBlockTabs defaultValue="React">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="React">
          React
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Vue">
          Vue
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="hooks/useQueryInterface.ts">
          hooks/useQueryInterface.ts
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="hooks/useNarrationFunctions.ts">
          hooks/useNarrationFunctions.ts
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="React">
        ```tsx
        import useNarrationFunctions from "../hooks/useNarrationFunctions";
        import { useQueryChoiceMenuOptions } from "../hooks/useQueryInterface";

        export default function ChoiceMenu() {
            const { data: menu = [] } = useQueryChoiceMenuOptions();
            const { selectChoice } = useNarrationFunctions();

            return (
                <div
                    style={{
                        display: "flex",
                        flexDirection: "column",
                        justifyContent: "center",
                        alignItems: "center",
                        width: "100%",
                        height: "100%",
                        overflow: "auto",
                        gap: "8px",
                        maxHeight: "100%",
                        margin: 0,
                        pointerEvents: menu?.length > 0 ? "auto" : "none",
                    }}
                >
                    {menu?.map((item, index) => (
                        <button
                            key={"choice-" + index}
                            style={{
                                justifyContent: "center",
                                alignItems: "center",
                            }}
                            onClick={() => selectChoice(item)}
                        >
                            {item.text}
                        </button>
                    ))}
                </div>
            );
        }
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Vue">
        ```vue
        <template>
            <div
                :style="{
                    display: 'flex',
                    flexDirection: 'column',
                    justifyContent: 'center',
                    alignItems: 'center',
                    width: '100%',
                    height: '100%',
                    overflow: 'auto',
                    gap: '8px',
                    maxHeight: '100%',
                    margin: 0,
                }"
                :pointer-events="menu.length > 0 ? 'auto' : 'none'">
                <button
                    v-for="(item, index) in menu"
                    :key="'choice-' + index"
                    :style="{ justifyContent: 'center', alignItems: 'center' }"
                    @click="selectChoice(item)"
                >
                    {{ item.text }}
                </button>
            </div>
        </template>

        <script setup>
        import { computed } from 'vue'
        import useNarrationFunctions from '../hooks/useNarrationFunctions'
        import { useQueryChoiceMenuOptions } from '../hooks/useQueryInterface'

        const { data } = useQueryChoiceMenuOptions()
        const menu = computed(() => data.value || [])
        const { selectChoice } = useNarrationFunctions()
        </script>

        ```
      </CodeBlockTab>

      <CodeBlockTab value="hooks/useQueryInterface.ts">
        ```ts
        import { CharacterBaseModel, narration } from "@drincs/pixi-vn";
        import { useQuery } from "@tanstack/react-query";

        export const INTERFACE_DATA_USE_QUEY_KEY = "interface_data_use_quey_key";

        const CHOICE_MENU_OPTIONS_USE_QUEY_KEY = "choice_menu_options_use_quey_key";
        export function useQueryChoiceMenuOptions() {
            return useQuery({
                queryKey: [INTERFACE_DATA_USE_QUEY_KEY, CHOICE_MENU_OPTIONS_USE_QUEY_KEY],
                queryFn: async () =>
                    narration.choices?.map((option) => ({
                        ...option,
                        text: typeof option.text === "string" ? option.text : option.text.join(" "),
                    })) || [],
            });
        }
        ```
      </CodeBlockTab>

      <CodeBlockTab value="hooks/useNarrationFunctions.ts">
        ```ts
        import { narration, StoredIndexedChoiceInterface } from "@drincs/pixi-vn";
        import { useQueryClient } from "@tanstack/react-query";
        import { useCallback } from "react";
        import { INTERFACE_DATA_USE_QUEY_KEY } from "./useQueryInterface";

        export default function useNarrationFunctions() {
            const queryClient = useQueryClient();
            const gameProps = {};

            const selectChoice = useCallback(
                async (item: StoredIndexedChoiceInterface) => {
                return narration
                    .selectChoice(item, gameProps)
                    .then(() => queryClient.invalidateQueries({ queryKey: [INTERFACE_DATA_USE_QUEY_KEY] }))
                    .catch((e) => console.error(e));
                },
                [gameProps, queryClient]
            );

            return {
                selectChoice,
            };
        }
        ```
      </CodeBlockTab>
    </CodeBlockTabs>
  </Accordion>

  <Accordion title="ui_go_next" id="go-next">
    <CodeBlockTabs defaultValue="React">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="React">
          React
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Vue">
          Vue
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="hooks/useQueryInterface.ts">
          hooks/useQueryInterface.ts
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="hooks/useNarrationFunctions.ts">
          hooks/useNarrationFunctions.ts
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="React">
        ```tsx
        import { useState } from "react";
        import useNarrationFunctions from "../hooks/useNarrationFunctions";
        import { useQueryCanGoNext } from "../hooks/useQueryInterface";

        export default function NextButton() {
            const { data: canGoNext = false } = useQueryCanGoNext();
            const [loading, setLoading] = useState(false);
            const { goNext } = useNarrationFunctions();

            if (!canGoNext) {
                return null;
            }

            return (
                <button
                    onClick={() => {
                        setLoading(true);
                        goNext()
                            .then(() => setLoading(false))
                            .catch(() => setLoading(false));
                    }}
                    disabled={loading}
                    style={{
                        width: 40,
                        height: 20,
                        pointerEvents: "auto",
                    }}
                >
                    Next
                </button>
            );
        }
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Vue">
        ```vue
        <template>
            <button
                v-if="canGoNext"
                :disabled="loading"
                :style="{ width: '40px', height: '20px', pointerEvents: 'auto' }"
                @click="handleClick"
            >
                Next
            </button>
        </template>

        <script setup>
        import { ref, computed } from 'vue'
        import useNarrationFunctions from '../hooks/useNarrationFunctions'
        import { useQueryCanGoNext } from '../hooks/useQueryInterface'

        const { data } = useQueryCanGoNext()
        const canGoNext = computed(() => data.value ?? false)
        const loading = ref(false)
        const { goNext } = useNarrationFunctions()

        function handleClick() {
            loading.value = true
            goNext()
                .then(() => (loading.value = false))
                .catch(() => (loading.value = false))
        }
        </script>

        ```
      </CodeBlockTab>

      <CodeBlockTab value="hooks/useQueryInterface.ts">
        ```ts
        import { narration } from "@drincs/pixi-vn";
        import { useQuery } from "@tanstack/react-query";

        export const INTERFACE_DATA_USE_QUEY_KEY = "interface_data_use_quey_key";

        const CAN_GO_NEXT_USE_QUEY_KEY = "can_go_next_use_quey_key";
        export function useQueryCanGoNext() {
            return useQuery({
                queryKey: [INTERFACE_DATA_USE_QUEY_KEY, CAN_GO_NEXT_USE_QUEY_KEY],
                queryFn: async () => narration.canContinue && !narration.isRequiredInput,
            });
        }
        ```
      </CodeBlockTab>

      <CodeBlockTab value="hooks/useNarrationFunctions.ts">
        ```ts
        import { narration } from "@drincs/pixi-vn";
        import { useQueryClient } from "@tanstack/react-query";
        import { useCallback } from "react";
        import { INTERFACE_DATA_USE_QUEY_KEY } from "./useQueryInterface";

        export default function useNarrationFunctions() {
            const queryClient = useQueryClient();
            const gameProps = {};

            const goNext = useCallback(async () => {
                try {
                    if (!narration.canContinue) {
                        return;
                    }
                    return narration
                        .continue(gameProps)
                        .then(() => queryClient.invalidateQueries({ queryKey: [INTERFACE_DATA_USE_QUEY_KEY] }))
                        .catch((e) => console.error(e));
                } catch (e) {
                    console.error(e);
                    return;
                }
            }, [gameProps, queryClient]);

            return {
                goNext,
            };
        }
        ```
      </CodeBlockTab>
    </CodeBlockTabs>
  </Accordion>

  <Accordion title="ui_go_back" id="go-back">
    <CodeBlockTabs defaultValue="React">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="React">
          React
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Vue">
          Vue
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="hooks/useQueryInterface.ts">
          hooks/useQueryInterface.ts
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="hooks/useNarrationFunctions.ts">
          hooks/useNarrationFunctions.ts
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="React">
        ```tsx
        import { useState } from "react";
        import useNarrationFunctions from "../hooks/useNarrationFunctions";
        import { useQueryCanGoBack } from "../hooks/useQueryInterface";

        export default function BackButton() {
            const { data: canGoBack = false } = useQueryCanGoBack();
            const [loading, setLoading] = useState(false);
            const { goBack } = useNarrationFunctions();

            if (!canGoBack) {
                return null;
            }

            return (
                <button
                    onClick={() => {
                        setLoading(true);
                        goBack()
                            .then(() => setLoading(false))
                            .catch(() => setLoading(false));
                    }}
                    disabled={loading}
                    style={{
                        width: 40,
                        height: 20,
                        pointerEvents: "auto",
                    }}
                >
                    Back
                </button>
            );
        }
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Vue">
        ```vue
        <template>
            <button
                v-if="canGoBack"
                :disabled="loading"
                :style="{ width: '40px', height: '20px', pointerEvents: 'auto' }"
                @click="handleClick"
            >
                Back
            </button>
        </template>

        <script setup>
        import { ref, computed } from 'vue'
        import useNarrationFunctions from '../hooks/useNarrationFunctions'
        import { useQueryCanGoBack } from '../hooks/useQueryInterface'

        const { data } = useQueryCanGoBack()
        const canGoBack = computed(() => data.value ?? false)
        const loading = ref(false)
        const { goBack } = useNarrationFunctions()

        function handleClick() {
            loading.value = true
            goBack()
                .then(() => (loading.value = false))
                .catch(() => (loading.value = false))
        }
        </script>

        ```
      </CodeBlockTab>

      <CodeBlockTab value="hooks/useQueryInterface.ts">
        ```ts
        import { stepHistory } from "@drincs/pixi-vn";
        import { useQuery } from "@tanstack/react-query";

        export const INTERFACE_DATA_USE_QUEY_KEY = "interface_data_use_quey_key";

        const CAN_GO_BACK_USE_QUEY_KEY = "can_go_back_use_quey_key";
        export function useQueryCanGoBack() {
            return useQuery({
                queryKey: [INTERFACE_DATA_USE_QUEY_KEY, CAN_GO_BACK_USE_QUEY_KEY],
                queryFn: async () => stepHistory.canGoBack,
            });
        }
        ```
      </CodeBlockTab>

      <CodeBlockTab value="hooks/useNarrationFunctions.ts">
        ```ts
        import { stepHistory } from "@drincs/pixi-vn";
        import { useQueryClient } from "@tanstack/react-query";
        import { useCallback } from "react";
        import { INTERFACE_DATA_USE_QUEY_KEY } from "./useQueryInterface";

        export default function useNarrationFunctions() {
            const queryClient = useQueryClient();
            const gameProps = {};

            const goBack = useCallback(async () => {
                return stepHistory
                    .back(gameProps)
                    .then(() => queryClient.invalidateQueries({ queryKey: [INTERFACE_DATA_USE_QUEY_KEY] }))
                    .catch((e) => console.error(e));
            }, [gameProps, queryClient]);

            return {
                goBack,
            };
        }
        ```
      </CodeBlockTab>
    </CodeBlockTabs>
  </Accordion>

  <Accordion title="ui_input_prompt" id="input-prompt">
    <CodeBlockTabs defaultValue="React">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="React">
          React
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Vue">
          Vue
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="hooks/useQueryInterface.ts">
          hooks/useQueryInterface.ts
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="React">
        ```tsx
        import { narration } from "@drincs/pixi-vn";
        import { useQueryClient } from "@tanstack/react-query";
        import { useState } from "react";
        import { INTERFACE_DATA_USE_QUEY_KEY, useQueryDialogue, useQueryInputValue } from "../../hooks/useQueryInterface";

        export default function TextInput() {
            const { data: { text } = {} } = useQueryDialogue();
            const {
                data: { isRequired: open, currentValue } = {
                    currentValue: undefined,
                    isRequired: false,
                },
            } = useQueryInputValue<string | number>();
            const [tempValue, setTempValue] = useState<string | number>();
            const queryClient = useQueryClient();

            return (
                <dialog
                    open={open}
                    style={{
                        pointerEvents: "auto",
                    }}
                >
                    <p>{text}</p>
                    {open && (
                        <input
                            defaultValue={currentValue || ""}
                            onChange={(e) => {
                                if (e && e.target && "value" in e.target) {
                                    let value: any = e.target.value;
                                    if ("type" in e.target && e.target.type === "number" && "valueAsNumber" in e.target) {
                                        value = e.target.valueAsNumber;
                                    }
                                    setTempValue(value);
                                }
                            }}
                        />
                    )}
                    <button
                        onClick={() => {
                            narration.inputValue = tempValue || currentValue;
                            setTempValue(undefined);
                            queryClient.invalidateQueries({
                                queryKey: [INTERFACE_DATA_USE_QUEY_KEY],
                            });
                        }}
                    >
                        Confirm
                    </button>
                </dialog>
            );
        }
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Vue">
        ```vue
        <template>
            <dialog :open="open" style="pointer-events: auto;">
                <p>{{ text }}</p>
                <input
                    v-if="open"
                    :value="currentValue || ''"
                    @input="onInput"
                />
                <button @click="confirm">Confirm</button>
            </dialog>
        </template>

        <script setup>
        import { ref, computed } from 'vue'
        import { useQueryClient } from '@tanstack/vue-query'
        import { narration } from '@drincs/pixi-vn'
        import { INTERFACE_DATA_USE_QUEY_KEY, useQueryDialogue, useQueryInputValue } from '../../hooks/useQueryInterface'

        const { data: dialogueData } = useQueryDialogue()
        const text = computed(() => dialogueData.value?.text)

        const { data: inputData } = useQueryInputValue()
        const open = computed(() => inputData.value?.isRequired ?? false)
        const currentValue = computed(() => inputData.value?.currentValue)

        const tempValue = ref(undefined)
        const queryClient = useQueryClient()

        function onInput(e) {
            tempValue.value = e.target?.value
        }

        function confirm() {
            narration.inputValue = tempValue.value ?? currentValue.value
            tempValue.value = undefined
            queryClient.invalidateQueries({ queryKey: [INTERFACE_DATA_USE_QUEY_KEY] })
        }
        </script>

        ```
      </CodeBlockTab>

      <CodeBlockTab value="hooks/useQueryInterface.ts">
        ```ts
        import { CharacterBaseModel, narration } from "@drincs/pixi-vn";
        import { useQuery } from "@tanstack/react-query";

        export const INTERFACE_DATA_USE_QUEY_KEY = "interface_data_use_quey_key";

        const INPUT_VALUE_USE_QUEY_KEY = "input_value_use_quey_key";
        export function useQueryInputValue<T>() {
            return useQuery({
                queryKey: [INTERFACE_DATA_USE_QUEY_KEY, INPUT_VALUE_USE_QUEY_KEY],
                queryFn: async () => ({
                    isRequired: narration.isRequiredInput,
                    type: narration.inputType,
                    currentValue: narration.inputValue as T | undefined,
                }),
            });
        }

        const DIALOGUE_USE_QUEY_KEY = "dialogue_use_quey_key";
        export function useQueryDialogue() {
            return useQuery({
                queryKey: [INTERFACE_DATA_USE_QUEY_KEY, DIALOGUE_USE_QUEY_KEY],
                queryFn: async ({ queryKey }) => {
                    let dialogue = narration.dialogue;
                    let text = dialogue?.text;
                    if (Array.isArray(text)) {
                        text = text.join(" ");
                    }
                    let character = dialogue?.character as string | CharacterBaseModel | undefined;
                    if (typeof character === "string") {
                        character = new CharacterBaseModel(character, {
                            name: character,
                        });
                    }

                    return {
                        text,
                        character,
                    };
                },
            });
        }
        ```
      </CodeBlockTab>
    </CodeBlockTabs>
  </Accordion>

  <Accordion title="ui_history" id="history">
    <CodeBlockTabs defaultValue="React">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="React">
          React
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Vue">
          Vue
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="hooks/useQueryInterface.ts">
          hooks/useQueryInterface.ts
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="React">
        ```tsx
        import { Box, Stack } from "@mui/system";
        import React from "react";
        import { useQueryNarrativeHistory } from "../use_query/useQueryInterface";

        export default function HistoryScreen() {
            const { data = [] } = useQueryNarrativeHistory();

            return (
                <dialog
                    open={true}
                    style={{
                        height: "80%",
                    }}
                >
                    <Box
                        sx={{
                            display: "flex",
                            flex: 1,
                            minHeight: 0,
                            px: 2,
                            py: 3,
                            overflowY: "scroll",
                            flexDirection: "column-reverse",
                            pointerEvents: "auto",
                            overflow: "auto",
                            height: "80%",
                        }}
                    >
                        <Stack spacing={2} justifyContent="flex-end">
                        {data.map((data, index) => {
                            return (
                            <React.Fragment key={"history" + index}>
                                <Stack direction="row" spacing={1.5}>
                                    <img
                                        src={data.icon}
                                        loading="lazy"
                                        alt=""
                                        style={{
                                            verticalAlign: "middle",
                                            maxWidth: "50px",
                                            maxHeight: "50px",
                                            borderRadius: "50%",
                                        }}
                                    />
                                    <Box sx={{ flex: 1 }}>
                                        {data.character && data.character}
                                        <div />
                                        {data.text}
                                    </Box>
                                </Stack>
                                <Stack direction="row" spacing={0.5}>
                                    <Box sx={{ flex: 1 }}>
                                        {data.choices &&
                                        data.choices.map((choice, index) => {
                                            if (choice.hidden) {
                                                return null;
                                            }
                                            if (choice.isResponse) {
                                                return (
                                                    <div
                                                        key={"choices-success" + index}
                                                        style={{
                                                            display: "inline-block",
                                                            padding: "5px 5px",
                                                            fontSize: "12px",
                                                            borderRadius: "25px",
                                                            backgroundColor: "#21ff3e",
                                                        }}
                                                    >
                                                        {choice.text}
                                                    </div>
                                                );
                                            }
                                            return (
                                                <div
                                                    key={"choices" + index}
                                                    style={{
                                                        display: "inline-block",
                                                        padding: "5px 5px",
                                                        fontSize: "12px",
                                                        borderRadius: "25px",
                                                        backgroundColor: "#bcfdff",
                                                    }}
                                                >
                                                    {choice.text}
                                                </div>
                                            );
                                        })}
                                        {data.inputValue && (
                                            <div
                                                key={"choices-success" + index}
                                                style={{
                                                    display: "inline-block",
                                                    padding: "5px 5px",
                                                    fontSize: "12px",
                                                    borderRadius: "25px",
                                                    backgroundColor: "#b0c2b2",
                                                }}
                                            >
                                                {data.inputValue.toString()}
                                            </div>
                                        )}
                                    </Box>
                                </Stack>
                            </React.Fragment>
                            );
                        })}
                        </Stack>
                    </Box>
                </dialog>
            );
        }
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Vue">
        ```vue
        <template>
            <dialog open style="height: 80%;">
                <div
                    style="display:flex;flex:1;min-height:0;padding:0 16px;overflow-y:scroll;flex-direction:column-reverse;pointer-events:auto;height:80%;"
                >
                    <div v-for="(item, index) in data" :key="'history' + index" style="margin-bottom: 12px;">
                        <div style="display:flex;gap:12px;align-items:center;">
                            <img :src="item.icon" loading="lazy" alt="" style="vertical-align:middle;max-width:50px;max-height:50px;border-radius:50%;" />
                            <div style="flex:1;">
                                <div>{{ item.character }}</div>
                                <div />
                                <div>{{ item.text }}</div>
                            </div>
                        </div>
                        <div style="display:flex;gap:8px;margin-top:8px;">
                            <div style="flex:1;">
                                <template v-if="item.choices">
                                    <template v-for="(choice, i) in item.choices" :key="'choice-' + i">
                                        <span v-if="!choice.hidden" :style="choice.isResponse ? responseStyle : choiceStyle">{{ choice.text }}</span>
                                    </template>
                                </template>
                                <div v-if="item.inputValue" :style="inputStyle">{{ item.inputValue.toString() }}</div>
                            </div>
                        </div>
                    </div>
                </div>
            </dialog>
        </template>

        <script setup>
        import { computed } from 'vue'
        import { useQueryNarrativeHistory } from '../use_query/useQueryInterface'

        const { data } = useQueryNarrativeHistory()
        const responseStyle = { display: 'inline-block', padding: '5px 5px', fontSize: '12px', borderRadius: '25px', backgroundColor: '#21ff3e' }
        const choiceStyle = { display: 'inline-block', padding: '5px 5px', fontSize: '12px', borderRadius: '25px', backgroundColor: '#bcfdff' }
        const inputStyle = { display: 'inline-block', padding: '5px 5px', fontSize: '12px', borderRadius: '25px', backgroundColor: '#b0c2b2' }
        </script>

        ```
      </CodeBlockTab>

      <CodeBlockTab value="hooks/useQueryInterface.ts">
        ```ts
        import {
            CharacterBaseModel,
            getCharacterById,
            narration,
        } from "@drincs/pixi-vn";
        import { useQuery } from "@tanstack/react-query";

        export const INTERFACE_DATA_USE_QUEY_KEY = "interface_data_use_quey_key";

        const NARRATIVE_HISTORY_USE_QUEY_KEY = "narrative_history_use_quey_key";
        export function useQueryNarrativeHistory() {
            return useQuery({
                queryKey: [INTERFACE_DATA_USE_QUEY_KEY, NARRATIVE_HISTORY_USE_QUEY_KEY],
                queryFn: () => {
                    return narration.narrativeHistory.map((step) => {
                        let character = step.dialoge?.character
                            ? getCharacterById(step.dialoge?.character) ??
                                new CharacterBaseModel(step.dialoge?.character, {
                                    name: step.dialoge?.character,
                                })
                            : undefined;
                        return {
                            character: character?.name
                                ? character.name +
                                (character.surname ? " " + character.surname : "")
                                : undefined,
                            text: step.dialoge?.text || "",
                            icon: character?.icon,
                            choices: step.choices,
                            inputValue: step.inputValue,
                        };
                    });
                },
            });
        }
        ```
      </CodeBlockTab>
    </CodeBlockTabs>
  </Accordion>
</Accordions>
