local status = require 'bugabinga.pivi.status' local VARIABLE = 'pivi' local EVENT = 'User' local STATUS_PATTERN = 'PiviStatus' local ACTIVITY_PATTERN = 'PiviActivity' local read = function() local value = vim.g[VARIABLE] if type( value ) ~= 'table' then return { active = nil, sessions = {}, } end value.sessions = value.sessions or {} return value end local write = function( value ) vim.g[VARIABLE] = value end --- Removes ANSI control sequences from passthrough payload text. --- Extension status and widget text arrives already formatted, with styling; --- native surfaces render plain text, so the styling is stripped by contract. local strip_ansi strip_ansi = function( value ) if type( value ) == 'string' then return ( value:gsub( '\27%[[0-9;?]*%a', '' ) ) end if type( value ) == 'table' then local out = {} for key, inner in pairs( value ) do out[key] = strip_ansi( inner ) end return out end return value end --- Extension status and widget entries, keyed generically, never per extension. --- pivi passes them through opaque: it never parses their text into fields. local normalize_ext = function( ext ) if type( ext ) ~= 'table' then return nil end local normal = {} if type( ext.status ) == 'table' and next( ext.status ) then normal.status = strip_ansi( ext.status ) end if type( ext.widget ) == 'table' and next( ext.widget ) then normal.widget = strip_ansi( ext.widget ) end if not next( normal ) then return nil end return normal end local emit = function( pattern, data ) pcall( vim.api.nvim_exec_autocmds, EVENT, { pattern = pattern, data = data, modeline = false, } ) end --- Caches one session's semantic status and announces the change. --- Consumers read the cached variable; they never perform RPC. local session_status = function( name, state, ext ) vim.validate( 'name', name, 'string' ) vim.validate( 'state', state, 'table' ) local value = read() value.sessions[name] = { name = name, connection = state.connection, run = state.run, tool = state.tool, queue = state.queue, compacting = state.compacting, retry = state.retry, error = state.error, model = state.model, thinking = state.thinking, session = state.session, context = state.context, cost = state.cost, tokens = state.tokens, ext = normalize_ext( ext ), summary = status.summary( state ), } value.active = value.active or name write( value ) emit( STATUS_PATTERN, { session = name, status = value.sessions[name], } ) end local set_active = function( name ) local value = read() value.active = name write( value ) emit( STATUS_PATTERN, { session = name, status = value.sessions[name], } ) end local remove = function( name ) local value = read() value.sessions[name] = nil if value.active == name then value.active = next( value.sessions ) end write( value ) emit( STATUS_PATTERN, { session = name, status = nil, } ) end local activity = function( payload ) emit( ACTIVITY_PATTERN, payload ) end --- Cached statusline text. Safe when pivi never started. local summary = function( name ) local value = read() local key = name or value.active if not key then return '' end local entry = value.sessions[key] if not entry then return '' end return entry.summary or '' end local reset = function() write( { active = nil, sessions = {}, } ) end return { variable = VARIABLE, status_pattern = STATUS_PATTERN, activity_pattern = ACTIVITY_PATTERN, read = read, session_status = session_status, set_active = set_active, remove = remove, activity = activity, summary = summary, reset = reset, }