9 directories, 58 files

tinai

Home / tinai
import {
	formatContentBlock,
	formatAnnotationsBlock,
	formatSuggestions,
	formatCostSummary
} from './util/conversation-format-utils.js';
import { formatThinkingBlockHtml } from './util/format-utils.js';

/**
 * Class ScratchpadConversation
 * Handles formatting and rendering specifically for scratchpad conversation
 * revisions. Each history entry is a revision holding the full scratchpad
 * state (items with guids, table of contents, changes); only the most recent
 * revision is displayed, and pending revisions render live so tool-driven
 * edits appear as they apply.
 */
class ScratchpadConversation {

	/**
	 * Initializes a new instance of ScratchpadConversation.
	 */
	constructor() {
	}

	/**
	 * Formats a single scratchpad revision as HTML.
	 * @param {Object} data - The revision (history item) data.
	 * @param {number} index - The index of the revision in history.
	 * @param {Object} [conversation_settings={}] - Settings for the conversation.
	 * @returns {string} The formatted HTML string.
	 */
	format_scratchpad_item(data, index, conversation_settings = {}) {
		if (!data) return '';

		let html = '';

		if (!data.pending) {
			html += this.format_scratchpad_header(index, !!conversation_settings.read_only);
		}

		html += formatThinkingBlockHtml(data.thinking, !!data.pending, index);

		const items = Array.isArray(data.items) ? data.items : (Array.isArray(data.content) ? data.content : []);
		let response_html = formatContentBlock(items);
		response_html += formatAnnotationsBlock(data.annotations);

		html += `<div class="div-response-content" data-index="${index}">${response_html}</div>`;

		if (data.pending) {
			return html;
		}

		html += formatSuggestions(data, conversation_settings);
		html += formatCostSummary(data);

		return html;
	}

	/**
	 * Formats the scratchpad revision header toolbar.
	 * @param {number} index - The index of the revision in history.
	 * @param {boolean} [read_only=false] - When true, renders only the Copy
	 *   button (used by the shared viewer).
	 * @returns {string} The toolbar HTML string.
	 */
	format_scratchpad_header(index, read_only = false) {
		const left_buttons = '<button class="btn-copy-scratchpad btn-compact" data-index="' + index + '">Copy</button>';
		if (read_only) {
			return `
				<div class="div-chat-response-buttons div-chat-scratchpad-buttons">
					<div class="div-chat-response-buttons-left">${left_buttons}</div>
				</div>
			`;
		}
		return `
			<div class="div-chat-response-buttons div-chat-scratchpad-buttons">
				<div class="div-chat-response-buttons-left">${left_buttons}</div>
				<div class="div-chat-read-controls">
					<button class="btn-read-prev btn-compact as-icon" data-index="${index}" title="Previous item">&#9198;</button>
					<button class="btn-read-toggle btn-compact as-icon" data-index="${index}" title="Read aloud">&#9654;</button>
					<button class="btn-read-next btn-compact as-icon" data-index="${index}" title="Next item">&#9199;</button>
					<label class="label-sync-switch label-read-option" title="Auto-advance">
						<input type="checkbox" class="input-read-auto" role="switch" aria-label="Auto-advance">
						<span class="span-sync-switch"></span>
						<span class="span-read-repeat as-icon" aria-hidden="true">&#x1F501;</span>
					</label>
					<label class="label-sync-switch label-read-option" title="Auto-play new replies">
						<input type="checkbox" class="input-read-autoplay" role="switch" aria-label="Auto-play new replies">
						<span class="span-sync-switch"></span>
						<span class="span-read-repeat as-icon" aria-hidden="true">&#9210;&#65038;</span>
					</label>
					<span class="span-read-loading index-item-indicator as-icon" data-index="${index}" style="display: none;" title="Loading audio...">&#9679;</span>
				</div>
				<div class="div-chat-response-buttons-right">
					<button class="btn-redo-scratchpad btn-compact" data-index="${index}">Redo</button>
					<button class="btn-undo-scratchpad btn-compact" data-index="${index}">Undo</button>
					<button class="btn-delete-scratchpad btn-compact" data-index="${index}">Delete</button>
				</div>
			</div>
		`;
	}

	/**
	 * Renders the scratchpad, displaying only the most recent revision.
	 * @param {Object} conversation - The conversation object containing HISTORY array.
	 * @param {Object} [options={}] - Render options (e.g. show_suggested, show_related).
	 * @returns {string} The complete HTML string for the scratchpad conversation.
	 */
	render(conversation, options = {}) {
		if (!conversation) return '<p>Enter a query to get started.</p>';
		const history = conversation.HISTORY || conversation.history || (Array.isArray(conversation) ? conversation : []);
		if (!Array.isArray(history) || history.length === 0) return '<p>Enter a query to get started.</p>';

		const lastIndex = history.length - 1;
		const lastItem = history[lastIndex];
		return this.format_scratchpad_item(lastItem, lastIndex, options);
	}
}

export default ScratchpadConversation;
🌐
scratchpad-conversation.js ×
Type: Web, text/plain
4.87 Kilobytes
Last Modified 2026-11-04 09:48:28
⬇ Download File