Official

chat

Built-in chat UI (JavaScript UI plugin).

The app opens the listing; nothing installs until an agent in your Plugins workspace has read the files and you enable the plugin. In a terminal: cvg install convergence/chat@0.2.0

Permissions in 0.2.0

  • Control chats chats.controlHighCreates chats, sends prompts and cancels runs.Send prompts, answer agent questions, and create, change, fork, revert, stop and group chats
  • Run other plugins' commands commands.runHighRuns commands of other plugins.Run the sidebar's New chat command from the title bar
  • Read chats chats.readMediumReads your transcripts and chat lists.Show workspaces, tasks, chats and transcripts, and follow them as agents work
  • Read files fs.readMediumReads files in the listed places.Suggest workspace files for @ mentionsPlaces: the open workspace
  • Show panels ui.slotsLowShows views in the listed parts of the window.Draw the chat grid and the title barSlots: centertitle

Files

views/styles.ts4.4 KB
// Shared template metrics and tones preserve the existing chat surface.
// Chat disables embedded HTML formatting: template spaces become real UI nodes.
import { html } from "convergence";
import type { ComboMark, Workspace } from "convergence";
import type { AnchorProps } from "../state.ts";
export const READING_WIDTH = 880;
export const FIELD_INSET_X = 10;
export const PILL_INSET_X = 5;
export const column = { display: "flex", flexDirection: "column" };
export const row = { display: "flex", flexDirection: "row", alignItems: "center" };
export const fill = { flexGrow: 1, flexShrink: 1, flexBasis: "0%", minWidth: 0, minHeight: 0 };
export const full = { width: "100%", height: "100%" };
export const HOVER = { background: "hover" };
export const clickable = { group: "hover", cursor: "pointer" };
export const revealOnHover = { visibility: "hidden", groupHover: { group: "hover", visibility: "visible" } };
export const centred = { width: "100%", marginX: "auto" };
export const BORDER = { width: 1, color: "border" };
export const TINT: Record<string, string> = {
  foreground: "foreground",
  muted: "mutedForeground",
  faint: "faint",
  primary: "primary",
  info: "info",
  success: "success",
  warning: "warning",
  danger: "danger",
};
export const tint = (tone: string | undefined) => TINT[tone ?? "muted"];
export const ROW_TEXT = { size: "xs", weight: 560, lineHeight: 14.6 };
export const TOOL_ICONS = {
  read: "file-text",
  edit: "file-pen-line",
  delete: "delete",
  move: "arrow-right",
  search: "search",
  execute: "square-terminal",
  think: "brain",
  fetch: "globe",
  task: "layout-dashboard",
  switch_mode: "toggle-left",
  other: "cpu",
};
export const TOOL_LABELS = {
  read: "Read",
  edit: "Edit",
  delete: "Delete",
  move: "Move",
  search: "Search",
  execute: "Run",
  think: "Think",
  fetch: "Fetch",
  task: "Task",
  switch_mode: "Mode",
  other: "",
};
export const CARD_SPACE = 8;
export const PHASE_MARK = {
  done: { icon: "circle-check", tone: "success" },
  failed: { icon: "circle-x", tone: "danger" },
  waiting: { icon: "circle-pause", tone: "warning" },
  queued: { icon: "circle-dashed", tone: "muted" },
  pending: { icon: "circle-dashed", tone: "faint" },
  stopped: { icon: "circle-stop", tone: "muted" },
};
export const AGENT_MARK = {
  completed: { icon: "circle-check", tone: "success" },
  idle: { icon: "circle-check", tone: "success" },
  failed: { icon: "circle-x", tone: "danger" },
  waiting: { icon: "circle-pause", tone: "warning" },
  queued: { icon: "circle-dashed", tone: "faint" },
  cancelled: { icon: "circle-stop", tone: "muted" },
};
// A workspace's mark: its project icon, else the icon the app gives it
// (the Plugins puzzle), else a folder in its colour; the coloured folder
// too when the user chose it over the icon.
export const markOf = (w: Workspace | undefined): ComboMark => {
  if (w?.icon && !w.useColor) return { image: w.icon };
  if (w?.mark && !w.useColor) return { icon: w.mark };
  return w?.color ? { icon: "folder-solid", color: w.color } : { icon: "folder" };
};
const markIcon = (mark: ComboMark, size: number, small = false) =>
  mark.image
    ? html`<image path=${mark.image} width=${size} height=${size} radius=${3} />`
    : html`<icon name=${mark.icon ?? "folder"} color=${mark.color} small=${small} tone="muted" />`;
// The triggers of the workspace picker; the kernel's combobox around them
// opens the list on the press.
export const headlineAnchor =
  (headline: { size: string; bold: boolean }) =>
  ({ name, mark, can }: AnchorProps) =>
    html`
        <div ...${row} ...${can ? clickable : null} gap=${6} padding="small" paddingX=${6} paddingY=${2} borderRadius="md" hover=${can ? HOVER : undefined}>
          ${markIcon(mark, 20)}
          <text style="body" ...${headline}>${name}</text>
          ${can ? html`<icon name="chevron-down" small tone="muted" />` : null}
        </div>`;
export const chipAnchor = ({ name, mark, can }: AnchorProps, small = true) =>
  html`
    <div ...${row} ...${can ? clickable : null} gap=${5} padding="small" paddingX=${8} paddingY=${2} maxWidth=${220} background="subtle"
      borderRadius=${11} hover=${can ? HOVER : undefined}>
      ${markIcon(mark, small ? 12 : 14, small)}
      <text style="body" size="xs" tone="muted" truncate>${name}</text>
      ${can ? html`<icon name="chevron-down" small=${small} tone="faint" />` : null}
    </div>`;
export const titleChipAnchor = (props: AnchorProps) => chipAnchor(props, false);

Versions

VersionPublishedPlugin APISizePermissionsStatus
0.2.0latestOct 5, 2026>=2 <3105.1 KB5 permissionsListed

Reviews and comments

0 threads · 0 reviews

No comments yet.