{"version":3,"file":"web.mjs","names":["toolRead"],"sources":["../../src/extensions/workspaces/sessions/(frontend)/tool/_lib/readToolView.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/ReadToolMessage.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/read.web.tsx","../../generated/web.ts"],"sourcesContent":["import { compactDetails } from '@agimon-ai/doompi-web-components';\n\nexport interface ReadCallView {\n  readonly path: string;\n  /** `from N` and `N lines`, as the TUI's call heading lists them. */\n  readonly details: readonly string[];\n}\n\nfunction optionalNumber(value: unknown): number | undefined {\n  return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\n/** What the read call heading shows: the path, then the offset and limit when given. */\nexport function readCallView(args: Readonly<Record<string, unknown>>): ReadCallView {\n  const offset = optionalNumber(args.offset);\n  const limit = optionalNumber(args.limit);\n  return {\n    path: typeof args.path === 'string' ? args.path : '',\n    details: compactDetails([\n      offset === undefined ? undefined : `from ${offset}`,\n      limit === undefined ? undefined : `${limit} lines`,\n    ]),\n  };\n}\n","import type { ToolMessageRenderProps } from '@agimon-ai/doompi-core/web';\nimport {\n  HashlineLines,\n  hashlineBody,\n  MessageItem,\n  MessageItemBody,\n  MessageItemHeader,\n  MessageItemStatus,\n  resultTextLines,\n  ToolPathLink,\n  toolTone,\n} from '@agimon-ai/doompi-web-components';\n\nimport { readCallView } from '../_lib/readToolView';\n\nconst IMAGE_MIME_TYPES = new Set(['image/png', 'image/jpeg', 'image/gif', 'image/webp']);\n\nfunction resultImages(content: readonly unknown[]): Array<{ data: string; mimeType: string }> {\n  return content.flatMap((block) => {\n    if (typeof block !== 'object' || block === null || Array.isArray(block)) return [];\n    const value = block as Record<string, unknown>;\n    return value.type === 'image' &&\n      typeof value.data === 'string' &&\n      typeof value.mimeType === 'string' &&\n      IMAGE_MIME_TYPES.has(value.mimeType)\n      ? [{ data: value.data, mimeType: value.mimeType }]\n      : [];\n  });\n}\n/**\n * The read tool's timeline item, the web half of its renderCall and\n * renderResult: the call summary in the header and, when expanded, the\n * anchored lines with a line-number gutter in the body. A failure shows the\n * tool's message in red inside the expanded body instead.\n */\nexport function ReadToolMessage({\n  args,\n  result,\n  output,\n  running,\n  isError,\n  fileTabFor,\n  openTransientTab,\n}: ToolMessageRenderProps) {\n  const view = readCallView(args);\n  const images = resultImages(result?.content ?? []);\n  // A read is the one call whose file the session usually has not changed, so\n  // this is the link that opens it read-only rather than in its history.\n  const tab = fileTabFor(view.path);\n  const body = result === null || isError ? null : hashlineBody(result, output, 'read', true);\n  return (\n    <MessageItem tone={toolTone({ running, isError })} expandable defaultExpanded={false}>\n      {({ expanded }) => {\n        return (\n          <>\n            <MessageItemHeader title=\"read\">\n              <span data-testid=\"tool-call-read\" className=\"flex min-w-0 flex-1 items-center gap-2\">\n                <ToolPathLink\n                  path={view.path}\n                  {...(tab === undefined ? {} : { onOpen: () => openTransientTab(tab) })}\n                />\n                {view.details.length > 0 ? (\n                  <span className=\"shrink-0 text-doom-faint\">· {view.details.join(' · ')}</span>\n                ) : null}\n              </span>\n            </MessageItemHeader>\n            {expanded ? (\n              isError ? (\n                <MessageItemBody data-testid=\"tool-result-read\" className=\"flex flex-col\">\n                  <MessageItemStatus tone=\"error\">failed</MessageItemStatus>\n                  {resultTextLines(result, output).map((line, index) => (\n                    <span key={`${String(index)}-${line}`} className=\"whitespace-pre-wrap break-words text-doom-red\">\n                      {line}\n                    </span>\n                  ))}\n                </MessageItemBody>\n              ) : body === null ? (\n                running ? (\n                  <MessageItemBody data-testid=\"tool-result-read\">\n                    <MessageItemStatus tone=\"running\">running</MessageItemStatus>\n                  </MessageItemBody>\n                ) : null\n              ) : body.shown.length === 0 && body.notice === undefined && images.length === 0 ? null : (\n                <MessageItemBody data-testid=\"tool-result-read\" className=\"flex flex-col gap-1\">\n                  {/* A read body is one file with no heading of its own, so the\n                      call's path is what names the grammar for it. */}\n                  <HashlineLines lines={body.shown} gutter={body.gutter} path={view.path} />\n                  {images.map((image, index) => {\n                    const url = `data:${image.mimeType};base64,${image.data}`;\n                    return (\n                      <a\n                        key={`${image.mimeType}:${String(index)}`}\n                        href={url}\n                        target=\"_blank\"\n                        rel=\"noreferrer\"\n                        className=\"self-start\"\n                      >\n                        <img\n                          src={url}\n                          alt={`Read result ${String(index + 1)}`}\n                          data-testid=\"tool-result-read-image\"\n                          className=\"h-auto max-h-96 max-w-full rounded-md border border-doom-border-soft object-contain\"\n                        />\n                      </a>\n                    );\n                  })}\n                  {body.notice !== undefined ? <span className=\"text-doom-faint\">{body.notice}</span> : null}\n                </MessageItemBody>\n              )\n            ) : null}\n          </>\n        );\n      }}\n    </MessageItem>\n  );\n}\n","import { defineToolRenderer } from '@agimon-ai/doompi-core/web';\n\nimport { ReadToolMessage } from './_components/ReadToolMessage';\n\nexport default defineToolRenderer({ message: ReadToolMessage });\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/doompi-core/web';\n\nimport toolRead from '../src/extensions/workspaces/sessions/(frontend)/tool/read.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T => ({ ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n  id: 'read',\n  session: {\n    get toolRenderers(): WebPluginContributions['toolRenderers'] { return [via({ tools: ['read'] }, toolRead)]; },\n  },\n});\n"],"mappings":"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"}