1 directories, 28 files

tinai

Home / testing / ai / tinai
/**
 * class Conversation
 * Handles the formatting and rendering of conversation history items into HTML.
 * Converts various API response content types (text, code, tables, diagrams, etc.) into styled DOM elements.
 */
class Conversation {

	//region Initialization

	/**
	 * Initializes a new instance of the Conversation class.
	 */
	constructor() {
	}

	//endregion

	//region Formatting Helpers

	/**
	 * Formats a header item.
	 * @param {Object} item - The content item object.
	 * @returns {string} HTML string for the header.
	 */
	format_header(item) {
		return '<h4>' + item.value + '</h4>';
	}

	/**
	 * Formats a text paragraph.
	 * @param {Object} item - The content item object.
	 * @returns {string} HTML string for the paragraph.
	 */
	format_text(item) {
		return '<p>' + item.value + '</p>';
	}

	/**
	 * Formats a code block with syntax highlighting support and escaping.
	 * @param {Object} item - The content item object containing code and language.
	 * @returns {string} HTML string for the code block.
	 */
	format_code(item) {
		let escaped = item.value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
		let lang_class = item.language ? ' class="code-block language-' + item.language + '"' : '';
		let html = '';
		if (item.caption) html += '<h7>' + item.caption + '</h7>';
		html += '<code' + lang_class + '><pre>' + escaped + '</pre></code>';
		return html;
	}

	/**
	 * Formats a blockquote.
	 * @param {Object} item - The content item object.
	 * @returns {string} HTML string for the blockquote.
	 */
	format_quote(item) {
		let html = '';
		if (item.caption) html += '<h7>' + item.caption + '</h7>';
		html += '<blockquote>' + item.value + '</blockquote>';
		return html;
	}

	/**
	 * Formats a hyperlink, using source metadata if available.
	 * @param {Object} item - The content item object.
	 * @returns {string} HTML string for the link.
	 */
	format_link(item) {
		const link_title = item.source?.[0]?.title;
		const link_href = item.source?.[0]?.url;
		if(link_title && link_href){
			return '<a target="_blank" href="' + link_href + '">' + link_title + '↗</a>';
		}
		return '<a>' + item.value + '</a>';
	}

	/**
	 * Formats a color preview.
	 * @param {Object} item - The content item object containing a hex color.
	 * @returns {string} HTML string for the color span.
	 */
	format_color(item) {
		let html = '';
		if (item.caption) html += '<h7>' + item.caption + '</h7>';
		html += '<span style="color: ' + item.value + '">' + item.value + '</span>';
		return html;
	}

	/**
	 * Formats a list item (ordered or unordered).
	 * @param {Object} item - The content item object.
	 * @returns {string} HTML string for the list item.
	 */
	format_list(item) {
		if(item.ordered){
			return '<ol><li value="' + item.index + '">' + item.value + '</li></ol>';
		}
		return '<ul><li>' + item.value + '</li></ul>';
	}

	/**
	 * Formats a Mermaid diagram container.
	 * @param {Object} item - The content item object containing Mermaid syntax.
	 * @returns {string} HTML string for the diagram.
	 */
	format_mermaid(item) {
		let html = '';
		if (item.caption) html += '<h7>' + item.caption + '</h7><br/>';
		html += '<pre class="div-diagram-mermaid">' + item.value + '</pre>';
		return html;
	}

	/**
	 * Formats a data table.
	 * @param {Object} item - The content item object containing table rows and configuration.
	 * @returns {string} HTML string for the table.
	 */
	format_table(item) {
		let html = '';
		if(item.caption) html += '<h7>' + item.caption + '</h7>';
		const has_th = item['has-header'];
		const rows = [...item['table-rows']];
		html += '<table>';
		if(has_th){
			const row = rows.shift();
			html += '<thead><tr>';
			for(const title of row) html += '<th>' + title + '</th>';
			html += '</tr></thead>';
		}
		html += '<tbody>';
		for(const row of rows){
			html += '<tr>';
			for(const cell of row) html += '<td>' + cell + '</td>';
			html += '</tr>';
		}
		html += '</tbody></table>';
		return html;
	}

	/**
	 * Formats a TeX Equation container.
	 * @param {Object} item - The content item object containing TeX syntax.
	 * @returns {string} HTML string for the equation.
	 */
	format_equation(item) {
		let html = '';
		if (item.caption) html += '<h7>' + item.caption + '</h7>';
		html += '$$ ' + item.value + ' $$';
		return html;
	}

	/**
	 * Fallback formatter for unknown content types.
	 * @param {Object} item - The content item object.
	 * @returns {string} HTML string for the preformatted value.
	 */
	format_default(item) {
		return '<pre>' + item.value + '</pre>';
	}

	//endregion

	//region Core Routing

	/**
	 * Routes a content item to its specific formatter based on its type.
	 * @param {Object} item - The content item object.
	 * @returns {string} The formatted HTML string.
	 */
	format_item(item) {
		switch (item.type) {
			case 'header':
				return this.format_header(item);
			case 'text':
				return this.format_text(item);
			case 'code':
				return this.format_code(item);
			case 'quote':
				return this.format_quote(item);
			case 'link':
				return this.format_link(item);
			case 'color-hex':
				return this.format_color(item);
			case 'list-step':
				return this.format_list(item);
			case 'diagram-mermaid':
				return this.format_mermaid(item);
			case 'table':
				return this.format_table(item);
			case 'equation':
				return this.format_equation(item);
			default:
				return this.format_default(item);
		}
	}

	//endregion

	//region Response Metadata & Layout

	/**
	 * Returns a standard horizontal divider for chat history.
	 * @param {string|number} [id] - Optional ID to assign to the divider.
	 * @returns {string} HTML string for the divider.
	 */
	format_divider(id = null) {
		const id_attr = id !== null ? ' id="chat-item-' + id + '"' : '';
		return '<hr class="hr-chat-response-divider"' + id_attr + '/>';
	}

	/**
	 * Formats the related topics and suggested follow-up prompts.
	 * @param {Object} data - The response data object.
	 * @param {Object} conversation_settings - Object containing settings for the current conversation (e.g., showRelatedQueries, showSuggestedQueries).
	 * @returns {string} HTML string for the related/suggestions section.
	 */
	format_suggestions(data, conversation_settings = {}) {
		let html = '';
		let has_content = false;

		const show_related = conversation_settings.showRelatedQueries;
		const show_suggestions = conversation_settings.showSuggestedQueries;

		if (show_related && data.related && data.related.length > 0) {
			html += this._get_related_html(data.related);
			has_content = true;
		}
		if (show_suggestions && data.suggestions && data.suggestions.length > 0) {
			html += this._get_suggestions_html(data.suggestions);
			has_content = true;
		}
		if (has_content) {
			html += this.format_divider();
		}
		return html;
	}

	/**
	 * Generates the HTML string for related topics.
	 * @param {Array<string>} related_topics - An array of related topic strings.
	 * @returns {string} The HTML string for related topics.
	 */
	_get_related_html(related_topics) {
		const related_spans = related_topics.map(topic => `<span class="span-query-chip span-related-query span-clickable-query">${topic}</span>`).join('&nbsp;');
		return `<p><strong>Related:</strong> <i>${related_spans}</i></p>`;
	}

	/**
	 * Generates the HTML string for suggested follow-up prompts.
	 * @param {Array<string>} suggestions - An array of suggested prompt strings.
	 * @returns {string} The HTML string for suggested prompts.
	 */
	_get_suggestions_html(suggestions) {
		const suggestion_spans = suggestions.map(suggestion => `<span class="span-query-chip span-suggested-query span-clickable-query">${suggestion}</span>`).join('&nbsp;');
		return `<p><strong>Suggestions:</strong> <i>${suggestion_spans}</i></p>`;
	}

	/**
	 * Formats the cost and token usage summary for a response.
	 * @param {Object} data - The response data containing model, tokens, and costs.
	 * @returns {string} HTML string for the cost summary.
	 */
	format_cost_summary(data) {
		if (!data.costs || !data.tokens) {
			return '';
		}
		const total_cost = data.costs.total.toFixed(6);
		const op = data.costs.op !== undefined ? data.costs.op.toFixed(6) : '?';
		return `<p style="opacity: 0.5;"><strong>${data.model}:</strong> Input ${data.tokens.prompt}, replied ${data.tokens.reply}, thought ${data.tokens.thought}, totalling ${data.tokens.total}, costing ≈ $${total_cost} (op: $${op}).</p>`;
	}

	/**
	 * Formats an entire history item from the API, including the query, response content,
	 * suggestions, and token/cost metadata.
	 * @param {Object} data - The complete response object for a single turn.
	 * @param {number} [index=0] - The index of the item in the conversation history.
	 * @param {Object} conversation_settings - Object containing settings for the current conversation (e.g., showRelatedQueries, showSuggestedQueries).
	 * @returns {string} The complete HTML representation of the history item.
	 */
	format_history_item(data, index = 0, conversation_settings = {}) {
		let html = '';

		let related_to_text = data.chain ? 'As related to the previous query.' : 'As a new conversational direction.';

		html += this.format_divider(index);

		html += this._get_history_item_header_html(data.query, related_to_text);
		html += this.format_divider();

		if(data.content){
			Object.values(data.content).forEach(item => {
				html += this.format_item(item);
				html += this.format_divider();
			});
		}

		html += this.format_suggestions(data, conversation_settings);
		html += this.format_cost_summary(data);

		return html;
	}

	/**
	 * Generates the HTML string for the history item header (query and related text).
	 * @param {string} query The user's query.
	 * @param {string} related_to_text Text indicating if the query is related or new.
	 * @returns {string} The HTML string for the history item header.
	 */
	_get_history_item_header_html(query, related_to_text) {
		return `<h5><strong>Query:</strong> ${query}</h5><h6>${related_to_text}</h6>`;
	}

	//endregion

}

export default Conversation;
🌐
conversation.js ×
Type: Web, text/plain
9.87 Kilobytes
Last Modified 2026-05-04 01:35:56
⬇ Download File