Ranking|Claude Code Mods

ModsCode / Claude Code Mods / Ranking / painel-sessao

painel-sessao

Session panel for Claude Code: the steps of the session with the current one marked, the decisions waiting for you, a queue of next prompts with auto-send, your last messages, notes, and the context window and plan limits. In Portuguese (B…

Code you can checkTerminal + desktopPaneBandToastBoxButtonClientInputSvgText
drives Claude, draws

What it does

  • Draws a band above the prompt, a pane, toasts
  • Watches mcp__painel-sessao__atualizar calls and can refuse them
  • Hooks classic.SessionStart, classic.UserPromptSubmit, prompt.submit, session.append and 10 more
  • Keeps its own state between turns

Good points

  • No network calls
  • Starts no processes
  • Written in TypeScript
  • Has a desktop-specific branch

Code to start from

A starter ModsCode writes from the drawing it read, and the mod's own lines that draw, where there are any.

Starter (register.tsx)

register.tsx
import type { Register } from 'claude-code'

// Drawn like painel-sessao by guilhermebvdm (pane, band, toast), as ModsCode read it from the mod's code:
// https://modscode.com/claude-mods/mods/guilhermebvdm-painel-sessao/
// Written by ModsCode from that reading, not copied from the mod: use it as you like.
// Each value below stands for one the mod works out at run time; put in your own.
export const register: Register = on => {
  // the band above the prompt; what else goes there is drawn under it
  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
    const { Box, Text, Button } = $.ui.resolve(e)
    const at = '…'
    const m = '…'
    const length = '…'
    const time = '…'
    return (
      <Box flexDirection="column">
        <Box flexDirection="row" columnGap={1}>
          <Text color="#D97757" bold>
            {`● Step ${at}1 of ${m}`}
          </Text>
          <Text wrap="truncate-end" />
          <Text dimColor>
            {"· "}
            {length}
            {" "}
            queued prompt
          </Text>
          <Text color="#3FA66B">
            ●
          </Text>
          <Text dimColor>
            auto-send on
          </Text>
          <Text color="#D97757">
            {`Auto-send at ${time}`}
          </Text>
          <Button label="✕ Cancel" />
          <Button label="Panel" dimColor />
        </Box>
        {await next(e)}
      </Box>
    )
  })

  // the pane: open it with $.ui.open({ id: 'my-pane', title: "‹title›" })
  on('ui.render', { component: 'Pane', requestId: 'my-pane' }, async ($, e) => {
    const { Box, Button, Text, Client, Input } = $.ui.resolve(e)
    const text = '…'
    const time = '…'
    const n = '…'
    const m = '…'
    const title = '…'
    const label = '…'
    const beside = '…'
    const decision = '…'
    const decision2 = '…'
    const decision3 = '…'
    const beside2 = '…'
    const number = '…'
    const title2 = '…'
    const number2 = '…'
    const title3 = '…'
    const number3 = '…'
    const title4 = '…'
    const number4 = '…'
    const title5 = '…'
    const number5 = '…'
    const title6 = '…'
    const number6 = '…'
    const title7 = '…'
    const number7 = '…'
    const title8 = '…'
    const number8 = '…'
    const title9 = '…'
    const number9 = '…'
    const title10 = '…'
    const beside3 = '…'
    const why = '…'
    const what = '…'
    const fieldValue = '…'
    const i = '…'
    const beside4 = '…'
    const text2 = '…'
    const label2 = '…'
    const text3 = '…'
    const label3 = '…'
    const text4 = '…'
    const beside5 = '…'
    const point = '…'
    const point2 = '…'
    const point3 = '…'
    const term = '…'
    const meaning = '…'
    const term2 = '…'
    const meaning2 = '…'
    const term3 = '…'
    const meaning3 = '…'
    const beside6 = '…'
    const amount = '…'
    const limitName = '…'
    const decimal = '…'
    const limitName2 = '…'
    const limitName3 = '…'
    const compactions = '…'
    const input = '…'
    const output = '…'
    const read = '…'
    const written = '…'
    return (
      <Box flexDirection="column" rowGap={1}>
        <Box flexDirection="row" columnGap={1} alignItems="center">
          <Button label="This session" variant="secondary" />
          <Button label="Sessions" variant="primary" />
        </Box>
        <Box flexDirection="column" borderStyle="round" paddingX={1}>
          <Box flexDirection="row" justifyContent="space-between" columnGap={2}>
            <Text>
              {text}
            </Text>
            <Text dimColor>
              {`answered ${time}`}
            </Text>
          </Box>
          <Text dimColor>
            {`Step ${n} of ${m}`}
          </Text>
          <Text bold>
            {title}
          </Text>
          <Box flexDirection="column">
            <Box flexDirection="row">
              <Text color="#D97757">
                ━━━━━━━━
              </Text>
              <Text dimColor>
                ━━━━━━━━
              </Text>
            </Box>
          </Box>
        </Box>
        <Box flexDirection="column" borderStyle="round" borderDimColor paddingX={1}>
          <Box flexDirection="row" justifyContent="space-between" alignItems="center" columnGap={2} marginBottom={0}>
            <Box flexDirection="row" columnGap={1} alignItems="center" flexShrink={0}>
              <Text color="#D97757" bold>
                ▍
              </Text>
              <Button label={`▾ ${label}`} plain />
            </Box>
            <Text dimColor wrap="truncate-end">
              {beside}
            </Text>
          </Box>
          <Box flexDirection="column">
            <Text dimColor italic>
              No pending decisions.
            </Text>
            <Box flexDirection="row" columnGap={1} alignItems="flex-start" justifyContent="space-between">
              <Box flexDirection="row" columnGap={1} alignItems="flex-start">
                <Text color="#D97757" bold>
                  ?
                </Text>
                <Text>
                  {decision}
                </Text>
              </Box>
              <Box flexShrink={0}>
                <Button label="✓ Resolved" plain dimColor />
              </Box>
            </Box>
            <Box flexDirection="row" columnGap={1} alignItems="flex-start" justifyContent="space-between">
              <Box flexDirection="row" columnGap={1} alignItems="flex-start">
                <Text color="#D97757" bold>
                  ?
                </Text>
                <Text>
                  {decision2}
                </Text>
              </Box>
              <Box flexShrink={0}>
                <Button label="✓ Resolved" plain dimColor />
              </Box>
            </Box>
            <Box flexDirection="row" columnGap={1} alignItems="flex-start" justifyContent="space-between">
              <Box flexDirection="row" columnGap={1} alignItems="flex-start">
                <Text color="#D97757" bold>
                  ?
                </Text>
                <Text>
                  {decision3}
                </Text>
              </Box>
              <Box flexShrink={0}>
                <Button label="✓ Resolved" plain dimColor />
              </Box>
            </Box>
          </Box>
        </Box>
        <Box flexDirection="column" borderStyle="round" borderDimColor paddingX={1}>
          <Box flexDirection="row" justifyContent="space-between" alignItems="center" columnGap={2} marginBottom={0}>
            <Box flexDirection="row" columnGap={1} alignItems="center" flexShrink={0}>
              <Text color="#D97757" bold>
                ▍
              </Text>
              <Button label={`▾ ${label}`} plain />
            </Box>
            <Text dimColor wrap="truncate-end">
              {beside2}
            </Text>
          </Box>
          <Box flexDirection="column">
            <Text dimColor italic>
              No steps recorded in this session.
            </Text>
            <Text dimColor>
              {`⋮ ${n} earlier step done`}
            </Text>
            <Box flexDirection="row" columnGap={1}>
              <Text color="#3FA66B">
                ✓
              </Text>
              <Text dimColor>
                {number}
                {". "}
                {title2}
              </Text>
            </Box>
            <Box flexDirection="row" columnGap={1}>
              <Text color="#3FA66B">
                ✓
              </Text>
              <Text dimColor>
                {number2}
                {". "}
                {title3}
              </Text>
            </Box>
            <Box flexDirection="row" columnGap={1}>
              <Text color="#3FA66B">
                ✓
              </Text>
              <Text dimColor>
                {number3}
                {". "}
                {title4}
              </Text>
            </Box>
            <Box flexDirection="row" columnGap={1}>
              <Text color="#D97757" bold>
                ●
              </Text>
              <Text bold>
                {number4}
                {". "}
                {title5}
              </Text>
              <Box flexShrink={0}>
                <Text color="#D97757" inverse bold>
                  {" NOW "}
                </Text>
              </Box>
            </Box>
            <Box flexDirection="row" columnGap={1}>
              <Text color="#D97757" bold>
                ●
              </Text>
              <Text bold>
                {number5}
                {". "}
                {title6}
              </Text>
              <Box flexShrink={0}>
                <Text color="#D97757" inverse bold>
                  {" NOW "}
                </Text>
              </Box>
            </Box>
            <Box flexDirection="row" columnGap={1}>
              <Text color="#D97757" bold>
                ●
              </Text>
              <Text bold>
                {number6}
                {". "}
                {title7}
              </Text>
              <Box flexShrink={0}>
                <Text color="#D97757" inverse bold>
                  {" NOW "}
                </Text>
              </Box>
            </Box>
            <Box flexDirection="row" columnGap={1}>
              <Text dimColor>
                ○
              </Text>
              <Text>
                {number7}
                {". "}
                {title8}
              </Text>
            </Box>
            <Box flexDirection="row" columnGap={1}>
              <Text dimColor>
                ○
              </Text>
              <Text>
                {number8}
                {". "}
                {title9}
              </Text>
            </Box>
            <Box flexDirection="row" columnGap={1}>
              <Text dimColor>
                ○
              </Text>
              <Text>
                {number9}
                {". "}
                {title10}
              </Text>
            </Box>
            <Text dimColor>
              {`⋮ ${n} more step`}
            </Text>
            <Box flexDirection="row" marginTop={1}>
              <Button label="Complete current step" variant="primary" />
            </Box>
          </Box>
        </Box>
        <Box flexDirection="column" borderStyle="round" borderDimColor paddingX={1}>
          <Box flexDirection="row" justifyContent="space-between" alignItems="center" columnGap={2} marginBottom={0}>
            <Box flexDirection="row" columnGap={1} alignItems="center" flexShrink={0}>
              <Text color="#D97757" bold>
                ▍
              </Text>
              <Button label={`▾ ${label}`} plain />
            </Box>
            <Text dimColor wrap="truncate-end">
              {beside3}
            </Text>
          </Box>
          <Box flexDirection="column" rowGap={1}>
            <Box flexDirection="row" columnGap={1} alignItems="center" flexWrap="wrap">
              <Text color="#3FA66B" bold>
                ●
              </Text>
              <Button label="Auto-send: on" />
              <Text dimColor>
                After each answer, the first prompt enters in 5 seconds.
              </Text>
              <Text color="#D97757">
                Waiting for the app survey to leave the screen before sending.
              </Text>
              <Text color="#D97757">
                {`The first prompt enters at ${time}.`}
              </Text>
              <Button label="✕ Cancel" />
            </Box>
            <Text color="#E5484D">
              {`Turned off at ${time}: ${why}.`}
            </Text>
            <Text dimColor wrap="truncate-end">
              {`Last auto-send event: ${time} · ${what}`}
            </Text>
            <Client module="./textarea.tsx" width="100%" />
            <Box flexDirection="column">
              <Box flexDirection="column" borderStyle="round" borderDimColor paddingX={1}>
                <Text dimColor>
                  Lines of this prompt so far
                </Text>
                <Text>
                  {" "}
                </Text>
                <Text>
                  {" "}
                </Text>
                <Text>
                  {" "}
                </Text>
              </Box>
              <Box flexDirection="column" alignItems="stretch" width="100%">
                <Input placeholder="Write a prompt to send later and press Enter to queue it. For several lines, use New line." value={`${fieldValue}`} submitLabel="Queue" />
              </Box>
              <Box flexDirection="row" columnGap={1} flexWrap="wrap">
                <Button label="↵ New line" />
                <Button label="✕ Clear lines" dimColor />
              </Box>
            </Box>
            <Box flexDirection="row" columnGap={1} alignItems="center" flexWrap="wrap">
              <Button label="Back to the one-line field" dimColor />
            </Box>
            <Text dimColor italic>
              No queued prompts.
            </Text>
            <Box flexDirection="column">
              <Box flexDirection="row" columnGap={1} alignItems="flex-start">
                <Box flexShrink={0}>
                  <Text color="#D97757" bold>
                    {`${i}1`}
                  </Text>
                </Box>
                <Box flexDirection="column">
                  <Text>
                    {" "}
                  </Text>
                  <Text>
                    {" "}
                  </Text>
                  <Text>
                    {" "}
                  </Text>
                  <Text dimColor>
                    {`⋮ ${n} more line`}
                  </Text>
                </Box>
              </Box>
              <Box flexDirection="row" columnGap={1} marginLeft={2}>
                <Text color="#D97757">
                  Sent. It enters the conversation when the current answer ends.
                </Text>
              </Box>
              <Box flexDirection="row" columnGap={1} flexWrap="wrap" marginLeft={2}>
                <Button label="➤ Send" variant="primary" />
                <Button label="▴ Show less" />
                <Button label="⋯" />
                <Button label="↑ Move up" />
                <Button label="↓ Move down" />
                <Button label="✎ Move to the prompt box" />
                <Button label="⧉ Copy" />
                <Button label="✕ Delete" dimColor />
              </Box>
            </Box>
            <Box flexDirection="column">
              <Box flexDirection="row" columnGap={1} alignItems="flex-start">
                <Box flexShrink={0}>
                  <Text color="#D97757" bold>
                    {`${i}1`}
                  </Text>
                </Box>
                <Box flexDirection="column">
                  <Text>
                    {" "}
                  </Text>
                  <Text>
                    {" "}
                  </Text>
                  <Text>
                    {" "}
                  </Text>
                  <Text dimColor>
                    {`⋮ ${n} more line`}
                  </Text>
                </Box>
              </Box>
              <Box flexDirection="row" columnGap={1} marginLeft={2}>
                <Text color="#D97757">
                  Sent. It enters the conversation when the current answer ends.
                </Text>
              </Box>
              <Box flexDirection="row" columnGap={1} flexWrap="wrap" marginLeft={2}>
                <Button label="➤ Send" variant="primary" />
                <Button label="▴ Show less" />
                <Button label="⋯" />
                <Button label="↑ Move up" />
                <Button label="↓ Move down" />
                <Button label="✎ Move to the prompt box" />
                <Button label="⧉ Copy" />
                <Button label="✕ Delete" dimColor />
              </Box>
            </Box>
            <Box flexDirection="column">
              <Box flexDirection="row" columnGap={1} alignItems="flex-start">
                <Box flexShrink={0}>
                  <Text color="#D97757" bold>
                    {`${i}1`}
                  </Text>
                </Box>
                <Box flexDirection="column">
                  <Text>
                    {" "}
                  </Text>
                  <Text>
                    {" "}
                  </Text>
                  <Text>
                    {" "}
                  </Text>
                  <Text dimColor>
                    {`⋮ ${n} more line`}
                  </Text>
                </Box>
              </Box>
              <Box flexDirection="row" columnGap={1} marginLeft={2}>
                <Text color="#D97757">
                  Sent. It enters the conversation when the current answer ends.
                </Text>
              </Box>
              <Box flexDirection="row" columnGap={1} flexWrap="wrap" marginLeft={2}>
                <Button label="➤ Send" variant="primary" />
                <Button label="▴ Show less" />
                <Button label="⋯" />
                <Button label="↑ Move up" />
                <Button label="↓ Move down" />
                <Button label="✎ Move to the prompt box" />
                <Button label="⧉ Copy" />
                <Button label="✕ Delete" dimColor />
              </Box>
            </Box>
          </Box>
        </Box>
        <Box flexDirection="column" borderStyle="round" borderDimColor paddingX={1}>
          <Box flexDirection="row" justifyContent="space-between" alignItems="center" columnGap={2} marginBottom={0}>
            <Box flexDirection="row" columnGap={1} alignItems="center" flexShrink={0}>
              <Text color="#D97757" bold>
                ▍
              </Text>
              <Button label={`▾ ${label}`} plain />
            </Box>
            <Text dimColor wrap="truncate-end">
              {beside4}
            </Text>
          </Box>
          <Box flexDirection="column">
            <Text dimColor italic>
              No messages sent since the panel was turned on.
            </Text>
            <Text dimColor bold>
              {label}
            </Text>
            <Box flexDirection="row" columnGap={1} alignItems="center">
              <Box flexShrink={0}>
                <Button label="earlier ↗" plain />
              </Box>
              <Text wrap="truncate-end">
                {text2}
              </Text>
              <Box flexShrink={0}>
                <Button label="⧉" plain dimColor />
              </Box>
            </Box>
            <Text dimColor bold>
              {label2}
            </Text>
            <Box flexDirection="row" columnGap={1} alignItems="center">
              <Box flexShrink={0}>
                <Button label="earlier ↗" plain />
              </Box>
              <Text wrap="truncate-end">
                {text3}
              </Text>
              <Box flexShrink={0}>
                <Button label="⧉" plain dimColor />
              </Box>
            </Box>
            <Text dimColor bold>
              {label3}
            </Text>
            <Box flexDirection="row" columnGap={1} alignItems="center">
              <Box flexShrink={0}>
                <Button label="earlier ↗" plain />
              </Box>
              <Text wrap="truncate-end">
                {text4}
              </Text>
              <Box flexShrink={0}>
                <Button label="⧉" plain dimColor />
              </Box>
            </Box>
            <Box flexDirection="row" columnGap={1}>
              <Button label={`▾ Load ${n} more`} />
              <Button label="▴ Show only 5" dimColor />
            </Box>
          </Box>
        </Box>
        <Box flexDirection="column" borderStyle="round" borderDimColor paddingX={1}>
          <Box flexDirection="row" justifyContent="space-between" alignItems="center" columnGap={2} marginBottom={0}>
            <Box flexDirection="row" columnGap={1} alignItems="center" flexShrink={0}>
              <Text color="#D97757" bold>
                ▍
              </Text>
              <Button label={`▾ ${label}`} plain />
            </Box>
            <Text dimColor wrap="truncate-end">
              {beside5}
            </Text>
          </Box>
          <Box flexDirection="column">
            <Text bold>
              Important points
            </Text>
            <Text dimColor italic>
              No points recorded.
            </Text>
            <Box flexDirection="row" columnGap={1}>
              <Text color="#D97757">
                ◆
              </Text>
              <Text>
                {point}
              </Text>
            </Box>
            <Box flexDirection="row" columnGap={1}>
              <Text color="#D97757">
                ◆
              </Text>
              <Text>
                {point2}
              </Text>
            </Box>
            <Box flexDirection="row" columnGap={1}>
              <Text color="#D97757">
                ◆
              </Text>
              <Text>
                {point3}
              </Text>
            </Box>
            <Box marginTop={1}>
              <Text bold>
                Terms
              </Text>
            </Box>
            <Text dimColor italic>
              No terms recorded.
            </Text>
            <Box flexDirection="column">
              <Text color="#D97757" bold>
                {term}
              </Text>
              <Text dimColor>
                {meaning}
              </Text>
            </Box>
            <Box flexDirection="column">
              <Text color="#D97757" bold>
                {term2}
              </Text>
              <Text dimColor>
                {meaning2}
              </Text>
            </Box>
            <Box flexDirection="column">
              <Text color="#D97757" bold>
                {term3}
              </Text>
              <Text dimColor>
                {meaning3}
              </Text>
            </Box>
            <Box marginTop={1} flexDirection="row" justifyContent="space-between">
              <Text bold>
                Your notes
              </Text>
              <Text dimColor />
            </Box>
            <Text dimColor italic>
              No notes in this session. What you write here stays in the panel only.
            </Text>
            <Box flexDirection="row" columnGap={1} alignItems="flex-start" justifyContent="space-between">
              <Text color="#D97757">
                {" "}
              </Text>
              <Box flexDirection="row" columnGap={1} flexShrink={0}>
                <Button label="✎" plain dimColor />
                <Button label="✕" plain dimColor />
              </Box>
            </Box>
            <Box flexDirection="row" columnGap={1} alignItems="flex-start" justifyContent="space-between">
              <Text color="#D97757">
                {" "}
              </Text>
              <Box flexDirection="row" columnGap={1} flexShrink={0}>
                <Button label="✎" plain dimColor />
                <Button label="✕" plain dimColor />
              </Box>
            </Box>
            <Box flexDirection="row" columnGap={1} alignItems="flex-start" justifyContent="space-between">
              <Text color="#D97757">
                {" "}
              </Text>
              <Box flexDirection="row" columnGap={1} flexShrink={0}>
                <Button label="✎" plain dimColor />
                <Button label="✕" plain dimColor />
              </Box>
            </Box>
            <Box flexDirection="column" alignItems="stretch" width="100%" marginTop={0}>
              <Input placeholder="Write a note and press Enter. Each Enter starts a line." value={`${fieldValue}`} submitLabel="Write" />
            </Box>
            <Box flexDirection="row" columnGap={1} flexWrap="wrap" alignItems="center">
              <Button label="↑ Move line up" />
              <Button label="↓ Move line down" />
              <Button label="Cancel the change" />
              <Button label="⧉ Copy all" />
              <Button label="✕ Clear all" dimColor />
              <Text color="#E5484D">
                Delete all notes?
              </Text>
              <Button label="Delete" variant="primary" />
              <Button label="Keep" />
            </Box>
          </Box>
        </Box>
        <Box flexDirection="column" borderStyle="round" borderDimColor paddingX={1}>
          <Box flexDirection="row" justifyContent="space-between" alignItems="center" columnGap={2} marginBottom={0}>
            <Box flexDirection="row" columnGap={1} alignItems="center" flexShrink={0}>
              <Text color="#D97757" bold>
                ▍
              </Text>
              <Button label={`▾ ${label}`} plain />
            </Box>
            <Text dimColor wrap="truncate-end">
              {beside6}
            </Text>
          </Box>
          <Box flexDirection="column">
            <Text dimColor italic>
              Not measured yet. The figures appear after the next answer.
            </Text>
            <Box flexDirection="column">
              <Box flexDirection="row">
                <Text>
                  ━━━━━━━━
                </Text>
                <Text dimColor>
                  ━━━━━━━━
                </Text>
              </Box>
            </Box>
            <Box flexDirection="row" columnGap={3} flexWrap="wrap">
              <Box flexDirection="row" columnGap={1}>
                <Text dimColor>
                  Free context
                </Text>
                <Text bold>
                  {amount}
                </Text>
                <Text dimColor />
              </Box>
              <Box flexDirection="row" columnGap={1}>
                <Text dimColor>
                  {limitName}
                </Text>
                <Text bold>
                  {`${decimal}%`}
                </Text>
                <Text dimColor />
              </Box>
              <Box flexDirection="row" columnGap={1}>
                <Text dimColor>
                  {limitName2}
                </Text>
                <Text bold>
                  {`${decimal}%`}
                </Text>
                <Text dimColor />
              </Box>
              <Box flexDirection="row" columnGap={1}>
                <Text dimColor>
                  {limitName3}
                </Text>
                <Text bold>
                  {`${decimal}%`}
                </Text>
                <Text dimColor />
              </Box>
              <Box flexDirection="row" columnGap={1}>
                <Text dimColor>
                  Compactions
                </Text>
                <Text bold>
                  {compactions}
                </Text>
                <Text dimColor />
              </Box>
            </Box>
            <Text dimColor wrap="truncate-end">
              {`Last turn: input ${input} · output ${output} · cache read ${read} · cache written ${written}`}
            </Text>
          </Box>
        </Box>
        <Box flexDirection="row" columnGap={1} alignItems="center" flexWrap="wrap">
          <Text color="#D97757">
            Reading the conversation to fill the panel. It takes a few seconds.
          </Text>
          <Button label="⧉ Copy steps, decisions, points and terms" dimColor />
        </Box>
      </Box>
    )
  })

  on('session.start', async ($, e, next) => {
    const n = '…'
    await $.ui.toast(`Your notes already have 200 lines, which is the limit. Delete one before writing another.`)
    await $.ui.toast(`The prompt has ${n} characters and the limit is 4000. It was not queued.`)
    return next(e)
  })
}

The mod's own code

Shown under the mod's licence (MIT); its text is below. Keep the notice if you copy these lines.

plugins/painel-sessao/hooks/register.tsx lines 1403–1457 · Band
  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
    // The app's survey about the session holds the automatic sending while it is on the screen
    if (e.props.hasSurvey) {
      if (surveyAt === 0) surveyAt = await $.clock.now()
    } else if (surveyAt !== 0) {
      surveyAt = 0
      void arm($).catch(() => {})
    }
    const b = await read($, board)
    const at = b.steps.findIndex(step => step.status === 'current')
    if (e.props.hasSurvey || (at < 0 && b.queue.length === 0)) return next(e)

    const { Box, Text, Button } = $.ui.resolve(e)
    const theirs = await next(e)

    return (
      <Box flexDirection="column">
        {theirs}
        <Box flexDirection="row" columnGap={1}>
          {at >= 0 && (
            <Text color={ACCENT} bold>
              {t('● Passo {n} de {m}', { n: at + 1, m: b.steps.length })}
            </Text>
          )}
          {at >= 0 && <Text wrap="truncate-end">{b.steps[at]?.title ?? ''}</Text>}
          {b.queue.length > 0 && (
            <Text dimColor>
              {at >= 0 ? '· ' : ''}
              {b.queue.length} {b.queue.length === 1 ? t('prompt anotado') : t('prompts anotados')}
            </Text>
          )}
          {b.queue.length > 0 && b.auto === true && b.pending === undefined && <Text color={DONE}>●</Text>}
          {b.queue.length > 0 && b.auto === true && b.pending === undefined && (
            <Text dimColor>{t('envio automático ligado')}</Text>
          )}
          {b.pending !== undefined && (
            <Text color={ACCENT}>{t('Envio automático às {time}', { time: clockSeconds(b.pending.at) })}</Text>
          )}
          {b.pending !== undefined && (
            <Button
              key="cancelar-envio-faixa"
              label={t('✕ Cancelar')}
              onPress={() => void commit($, now => ({ ...now, auto: false, pending: undefined, hold: undefined }))}
            />
          )}
          <Button
            key="abrir-painel"
            label={t('Painel')}
            dimColor
            onPress={() => void $.ui.open({ id: PANE, title: title() })}
          />
        </Box>
      </Box>
    )
  })
plugins/painel-sessao/hooks/register.tsx lines 1459–1618 · Pane
  on('ui.render', { component: 'Pane', requestId: 'painel-sessao' }, async ($, e) => {
    const table = $.ui.resolve(e)
    const { Box, Text, Button } = table
    const Input = 'Input' in table ? table.Input : null
    // The text box that receives each key is drawn in the terminal only: on 02/10/2026 the desktop app
    // (Claude Code 2.1.286) drew nothing for a Client and never ran its file. Elsewhere the prompt is
    // written line by line in the app's own field. On 06/10/2026, with 2.1.288, the desktop app did run
    // a Client's file; whether the keys reach it there was not checked, so this choice stands.
    // On 07/10/2026 the user can turn the box on in the desktop app to try it there ("Testar caixa larga"),
    // because the app gives its own field a width the mod cannot change.
    const b = await read($, board)
    paneSurface = e.surface
    const isBoxOn = e.surface === 'terminal' || (e.surface === 'desktop' && b.wideBox === true)
    const Client = isBoxOn && 'Client' in table ? table.Client : null
    // The desktop app keeps what the user types in a field across drawings by itself, as long as the mod
    // draws no value for it or the same one; elsewhere the mod draws what was typed, as before
    const keepsTyping = e.surface === 'desktop'
    const fieldValue = (key: string, typedNow: string): string | undefined =>
      keepsTyping ? fieldSet.get(key) : typedNow
    // The user typed in a field the mod had set: from now on the mod draws no value for it. The drawing asked
    // here takes the value drawn before out of the app's page, so the next time the mod sets the field (to
    // empty it after Enter) the app sees a value that changed.
    const typedIn = (key: string): void => {
      if (fieldSet.delete(key) && keepsTyping) {
        void update($, view, now => ({ ...now, fieldTick: (now.fieldTick ?? 0) + 1 }))
      }
    }
    const draft = b.draft ?? []

    const numbered = b.steps.map((step, i) => ({ ...step, number: i + 1 }))
    const done = numbered.filter(step => step.status === 'done')
    const current = numbered.filter(step => step.status === 'current')
    const upcoming = numbered.filter(step => step.status === 'next')
    const v = await read($, view)
    const asks = b.asks.slice(-v.shownAsks).reverse()

    const remove = (item: Queued) => commit($, now => ({ ...now, queue: now.queue.filter(one => one.id !== item.id) }))

    const send = (item: Queued) => dispatch($, item)

    // Turns the automatic sending on or off; either way, a sending that was waiting is called off
    const setAuto = async (isOn: boolean) => {
      autoFailures = 0
      await commit($, now => ({ ...now, auto: isOn, pending: undefined, hold: undefined, stopped: undefined }))
      // With no answer running, the first prompt does not wait for one: its 5 seconds start now
      if (isOn) await arm($)
    }

    // Moves a noted prompt one place up or down: the automatic sending sends the first of the list
    const move = (item: Queued, by: -1 | 1) =>
      commit($, now => {
        const from = now.queue.findIndex(one => one.id === item.id)
        const to = from + by
        if (from < 0 || to < 0 || to >= now.queue.length) return now
        const queue = [...now.queue]
        const [taken] = queue.splice(from, 1)
        if (taken === undefined) return now
        queue.splice(to, 0, taken)

        // A wait that was counting for the prompt that was first is counted again for the one that is first now
        const isSameFirst = now.pending === undefined || queue[0]?.id === now.pending.id

        return { ...now, queue, pending: isSameFirst ? now.pending : undefined }
      }).then(() => arm($))

    // Adds the noted prompt to what the prompt box already holds, without replacing it
    const toBox = async (item: Queued) => {
      const filled = await $.prompt.fill({ text: item.text, mode: 'append' })
      if (filled.isFilled) await remove(item)
      else $.ui.toast(t('A caixa de prompt não aceitou o texto. O prompt continua em Próximos prompts.'))
    }

    // Notes the lines already kept plus the line in the field as one prompt
    const note = async (line: string) => {
      const text = [...draft, line].join('\n')
      // When the panel keeps as many prompts as it may, the text stays in the field and in the kept lines
      if (text.trim() !== '' && !(await noted($, text))) return
      await emptied('novo-prompt')
      await commit($, now => ({ ...now, draft: [] }))
    }

    // The field is empty from its next drawing on, and so is the record of what was typed in it
    const emptied = async (key: 'novo-prompt' | 'nova-anotacao') => {
      fieldSet.set(key, '')
      if (key === 'novo-prompt') typing = ''
      else noteTyping = ''
      await update($, typed, now => (key === 'novo-prompt' ? { ...now, prompt: '' } : { ...now, note: '' }))
    }

    // Keeps the line in the field as a line of the prompt being written, and empties the field. After a
    // reload of the mod the module's own memory of the field is empty, and the session's state has the line.
    const breakLine = async () => {
      const line = typing !== '' ? typing : (await read($, typed)).prompt
      await emptied('novo-prompt')
      await commit($, now => ({ ...now, draft: [...(now.draft ?? []), line].slice(-40) }))
    }

    const clearDraft = async () => {
      await emptied('novo-prompt')
      await commit($, now => ({ ...now, draft: [] }))
    }

    const notes = b.notes ?? []
    // Enter in the field of the notes: replaces the line being changed, or adds what was typed as new lines
    const writeNote = async (text: string) => {
      const at = v.editingNote
      if (at !== undefined) {
        await emptied('nova-anotacao')
        await commit($, now => ({
          ...now,
          notes: (now.notes ?? []).map((line, i) => (i === at ? text.trimEnd().slice(0, NOTE_LINE_LIMIT) : line)),
        }))
        await update($, view, now => ({ ...now, editingNote: undefined }))

        return
      }
      if (await withNote($, text)) {
        await emptied('nova-anotacao')
        // A blank text changed no note, so nothing would draw the field again, empty: this value does.
        // It is a state write because a redraw asked with $.ui.invalidate needed a second click (02/10/2026).
        if (text.trim() === '') await update($, view, now => ({ ...now, fieldTick: (now.fieldTick ?? 0) + 1 }))
      }
    }
    // Puts a line of the notes in the field, to be changed there
    const editNote = (at: number) => {
      noteTyping = notes[at] ?? ''
      fieldSet.set('nova-anotacao', noteTyping)

      return update($, view, now => ({ ...now, editingNote: at, isClearingNotes: undefined }))
    }
    const dropNote = async (at: number) => {
      // A change under way is called off, and the text it had put in the field goes with it
      if (v.editingNote !== undefined) await emptied('nova-anotacao')
      await update($, view, now => ({ ...now, editingNote: undefined }))
      await commit($, now => ({ ...now, notes: (now.notes ?? []).filter((_, i) => i !== at) }))
    }
    const clearNotes = async () => {
      await emptied('nova-anotacao')
      await update($, view, now => ({ ...now, editingNote: undefined, isClearingNotes: undefined }))
      await commit($, now => ({ ...now, notes: [] }))
    }
    // Moves the line of the notes that is being changed one place up or down; the change goes on, on that line
    const moveNote = async (by: -1 | 1) => {
      const from = v.editingNote
      const to = (from ?? 0) + by
      if (from === undefined || to < 0 || to >= notes.length) return
      await commit($, now => {
        const lines = [...(now.notes ?? [])]
        const [taken] = lines.splice(from, 1)
        if (taken === undefined) return now
        lines.splice(to, 0, taken)

        return { ...now, notes: lines }
      })
      await update($, view, now => ({ ...now, editingNote: to }))
    }

    // The progress bar is as wide as the card's body: the pane less the border and the padding
    const barWidth = Math.max(10, Math.min(60, e.props.bodyColumns - 4))
    const hiddenDone = done.length - 2
Licence text (MIT)
MIT License

Copyright (c) 2026 guilhermebvdm

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

How to build this: Pane (panel), Deny a tool call

What its code calls

drives Claudetool callsremembers

What its code shows

Read from the source at commit 4dfd3f57ee2d on 2026-10-11. The first group decides whether a mod is listed; the rest is for you to weigh. A later commit is not read until the next crawl.

Decides the listing: code you can check
Entry readEvery module hooks/hooks.json names was read.plugins/painel-sessao/hooks/register.tsx
ParsesEvery file parses as TypeScript or JavaScript.3 files parsed
All its code is in the modEvery import is a file of the mod or the engine's own module; no packages, no require.all its code is in the mod
ReadableNo minified lines, no eval or new Function, no import of a computed path, no encoded blobs.no minified code, no eval, no encoded blobs
Calls in the openNetwork and programs are reached as $.http.fetch and $.process.run in place, never passed around.every call outside goes through $ in the open
For you to weigh before installing
Where it drawsThe terminal, the desktop app, both, or nothing (hooks only: a guard or a prompt rewriter), as its code says. Shown, never a reason to leave a mod out.draws in the terminal and the desktop app, with its own branch for the desktop
Install lineWhether its repository's own .claude-plugin/marketplace.json lists it, so it installs from that marketplace with the lines on its page.listed in its repository's .claude-plugin/marketplace.json: installs from there
Programs on WindowsWhether every program it starts through $.process exists on Windows (tail, date, open, osascript, /usr/… do not).runs no program a Windows PC lacks
NetworkWhether every fetch names its https host in the code.no network
ProgramsWhether every program it starts is named in the code and is not a shell, an interpreter or a network tool.starts no programs
Reads and sendsWhether it both reads something of yours (files, environment variables, the transcript, settings) and reaches outside (the network, an MCP server, another session, telemetry).reads the transcript, but reaches nothing outside
Keys in the sourceWhether the source holds what looks like an API key or a private key.no keys in the source
LicenceAn open licence (MIT, Apache-2.0, BSD, ISC, Unlicense, 0BSD, CC0) with its text lets ModsCode show its code; otherwise the code is linked.MIT
Settings hooksHooks declared in hooks/hooks.json or plugin.json beside the module: a command (a shell when it has no args), an HTTP request, an MCP tool or a model prompt at an event. They run outside $.no settings hooks: its function hooks alone
Servers it shipsMCP servers (a program, or a URL; a headersHelper is a shell command; a .mcpb bundle is unpacked at run time), LSP servers, monitors (a shell command for the whole session) and bin/ (on the PATH of the Bash tool).ships no MCP or LSP server, no monitor, no bin/
Skills, commands, agentsWhat its Markdown declares beyond text: allowed-tools granted for the turn, hooks added when a skill is invoked, a command run before Claude reads the skill.no skills, commands, agents or workflows
Tools and MCPWhether every tool it calls through $.tool.call and $.mcp.call is named in the code, and none is a shell (Bash through the tool runs a shell without $.process).calls no tool, MCP server or command
Asks of the modelCompletions it runs ($.model.complete, fork, classify), subagents it spawns, prompts it submits as you, and the tools, commands and agents it adds.prompt.submit (as the person), model.fork (sends its own transcript), adds command ?, adds tool atualizar
Events it hooksBy what a hook there can do: gates (refuse a tool call, a prompt, a setting, another plugin), shapes (what the model reads: the system prompt, tool descriptions, each request), reads (your input, the events of the settings hooks, every event with *), and the rest.gates: classic.UserPromptSubmit, command.run, prompt.submit, session.append, tool.check (mcp__painel-sessao__atu…), tool.call (mcp__painel-sessao__atu…) · shapes: tool.describe (mcp__painel-sessao__atu…) · reads: classic.SessionStart, ui.message, ui.press (Pane) · also: session.start, session.end, turn.complete, turn.start (+5) · timers: clock.after, clock.every
Reads, by nameThe environment variables and files it names, and whether it reads the transcript, settings.json, the prompt box or the mouse selection.the transcript
Writes, by nameThe files and environment variables it names, and whether it changes settings, the prompt box, the clipboard, the conversation (a row the model reads) or another session.the conversation (a row the model reads); the prompt box; the clipboard

This is a reading of the code text, not a security review: it does not run the mod, and it cannot see what a program the mod starts does. The check before installing is yours. How mods are chosen

Install it

Check it yourself before you install it. Mods run unsandboxed, with your permissions, and ModsCode does not vouch for what this code does. Read its code (or run claude plugin validate . in its folder), weigh what it reaches, and install the commit you read.

From its marketplace

Its repository is its marketplace. Add that marketplace (in the terminal with these commands, or in the desktop app's plugin settings) and install the mod from it:

/plugin marketplace add guilhermebvdm/claude-code-mods-gbvdm
/plugin install painel-sessao@gbvdm-mods
/reload-plugins

This installs the marketplace's latest commit, which may not be the one read here (4dfd3f57ee2d). Compare them before you install. /reload-plugins loads it into a session that is already open.

Try the commit ModsCode read

No marketplace needed. In a terminal, clone the repository, check out the commit read, validate the mod, and load it for one session:

git clone https://github.com/guilhermebvdm/claude-code-mods-gbvdm
git -C claude-code-mods-gbvdm checkout 4dfd3f57ee2de61a0f33ab3b848a80f607fa371d
claude plugin validate claude-code-mods-gbvdm/plugins/painel-sessao
claude --plugin-dir claude-code-mods-gbvdm/plugins/painel-sessao

The validate line lists the events the mod hooks and what it asks Claude Code to do, without running it. The last line loads the mod for this session only and writes nothing to your settings. If the author has rewritten history, the commit may no longer exist.

Ask Claude about it

With the ModsCode connector added, Claude can read this mod's code and what it reaches, and build on them. Ask it like this:

Read the ModsCode mod "guilhermebvdm-painel-sessao" with its code and make me one like it

Search them from Claude: the MCP connector

Similar mods

Badge for your README

If this is your mod, paste this into your README. It links to this page.

README.md
[![ModsCode](https://modscode.com/badge/guilhermebvdm-painel-sessao.svg)](https://modscode.com/claude-mods/mods/guilhermebvdm-painel-sessao/)