-
❓ .gitignoreUnknown, 37 B⬇
-
❓ README.mdUnknown, 2.94 kB⬇
-
🌐 admin_panel.phpWeb, 1.54 kB⬇
-
🌐 api.jsWeb, 22.21 kB⬇
-
🌐 api.phpWeb, 9.62 kB⬇
-
🌐 app.jsWeb, 61.42 kB⬇
-
🌐 attachments.jsWeb, 7.30 kB⬇
-
🌐 base.cssWeb, 2.23 kB⬇
-
🌐 choices-editor.jsWeb, 28.11 kB⬇
-
🌐 choices.cssWeb, 1.14 kB⬇
-
🌐 content.jsWeb, 31.08 kB⬇
-
🌐 context.jsWeb, 9.26 kB⬇
-
🌐 conversation-index.jsWeb, 10.74 kB⬇
-
🌐 conversation.cssWeb, 16.33 kB⬇
-
🌐 conversation.jsWeb, 1.11 kB⬇
-
🌐 dialogs.cssWeb, 11.43 kB⬇
-
🌐 diction.jsWeb, 948 B⬇
-
🌐 document-index.jsWeb, 1.04 kB⬇
-
🌐 document-model.jsWeb, 12.88 kB⬇
-
🌐 document.cssWeb, 6.74 kB⬇
-
🌐 document.jsWeb, 44.96 kB⬇
-
🌐 dynamic.phpWeb, 533 B⬇
-
📷 favicon-lg.pngImage, 9.11 kB⬇
-
📷 favicon.pngImage, 3.12 kB⬇
-
🌐 index-lists.cssWeb, 4.77 kB⬇
-
🌐 index.phpWeb, 49.72 kB⬇
-
🌐 indexjs.jsWeb, 377 B⬇
-
🌐 instant-answer.cssWeb, 5.17 kB⬇
-
🌐 layout.cssWeb, 11.37 kB⬇
-
🌐 login.phpWeb, 997 B⬇
-
❓ manifest.webmanifestUnknown, 262 B⬇
-
🌐 memory.jsWeb, 407 B⬇
-
🌐 mermaid.cssWeb, 6.16 kB⬇
-
🌐 model-presets.jsonWeb, 5.51 kB⬇
-
🌐 models.jsonWeb, 8.05 kB⬇
-
🌐 notes.cssWeb, 11.97 kB⬇
-
🌐 overlays.cssWeb, 13.97 kB⬇
-
⚠️ php.iniOS File, 424 B⬇
-
🌐 rankings-view.jsWeb, 5.67 kB⬇
-
🌐 rankings.cssWeb, 3.35 kB⬇
-
🌐 scratchpad-conversation.jsWeb, 4.87 kB⬇
-
🌐 scratchpad-index.jsWeb, 15.55 kB⬇
-
🌐 scratchpad-model.jsWeb, 13.29 kB⬇
-
🌐 scratchpad.cssWeb, 3.12 kB⬇
-
🌐 settings.cssWeb, 4.65 kB⬇
-
🌐 shared-view.cssWeb, 3.48 kB⬇
-
🌐 shared.cssWeb, 16.26 kB⬇
-
🌐 shared.jsWeb, 14.63 kB⬇
-
🌐 shared.phpWeb, 3.28 kB⬇
-
🌐 sticky-notes-editor.jsWeb, 64.84 kB⬇
-
🌐 storage.jsWeb, 36.73 kB⬇
-
🌐 style.cssWeb, 12.06 kB⬇
-
🌐 sw.jsWeb, 1.69 kB⬇
-
🌐 sync.jsWeb, 19.02 kB⬇
-
🌐 tools-index.jsWeb, 15.00 kB⬇
-
🌐 users.jsWeb, 49.86 kB⬇
-
🌐 utility-models.jsonWeb, 1.04 kB⬇
-
🌐 voice-models.jsonWeb, 4.81 kB⬇
import { escapeHtml } from './util/format-utils.js';
import { formatSuggestedChipsHtml, formatRelatedChipsHtml } from './util/format-utils.js';
/**
* Class RankingsView
* Read-only renderer for the Rankings tab of ranking conversations: one card
* per recorded ranking, showing the prompt, the ranked items in order (with
* their copied text, score, and reason), optional additional item
* suggestions, query chips, and the reply. The items are copies taken at
* rank time, so later edits to the choices never alter a recorded ranking.
*/
class RankingsView {
/**
* Renders the rank history blocks.
* @param {Array<Object>} [blocks=[]] - The recorded rank blocks.
* @param {object} [settings={}] - Conversation flags { show_suggested, show_related }.
* @returns {string} The html string.
*/
render(blocks = [], settings = {}) {
if (!Array.isArray(blocks) || blocks.length === 0) {
return '<p class="p-rankings-empty">No rankings yet. Use Rank to order the choices by relevance to a prompt.</p>';
}
const existing = Array.isArray(settings.existing_choices) ? settings.existing_choices : [];
return blocks.map(block => this.#block_html(block, settings, existing)).join('');
}
/**
* Builds the html for one rank block.
* @param {Object} block - The rank block.
* @param {object} settings - Conversation flags.
* @returns {string} The block html.
*/
#block_html(block, settings, existing = []) {
const prompt = escapeHtml(block.prompt || '');
const timestamp = block.timestamp ? new Date(block.timestamp).toLocaleString() : '';
const items = (Array.isArray(block.items) ? block.items : [])
.map(item => this.#item_html(item))
.join('');
const additional = this.#additional_html(block.additional_items, existing, settings);
const reply = block.reply
? `<div class="div-rankings-explanation"><div class="div-rankings-section-label">Explanation</div><div class="div-rankings-reply">${block.reply}</div></div>`
: '';
const queries = this.#queries_html(block, settings);
const actions = settings.read_only ? '' : `
<button type="button" class="as-icon div-rankings-revisit" data-guid="${escapeHtml(block.guid || '')}" title="Revisit: load these choices and their prompt">↻</button>
<button type="button" class="as-icon div-rankings-delete" data-guid="${escapeHtml(block.guid || '')}" title="Delete this ranking">🗑</button>`;
return `
<div class="div-rankings-block" data-guid="${escapeHtml(block.guid || '')}">
<div class="div-rankings-block-header">
<div class="div-rankings-prompt">${prompt}</div>
<span class="span-rankings-timestamp">${escapeHtml(timestamp)}</span>${actions}
</div>
<div class="div-rankings-items">${items}</div>
${additional}
${reply}
${queries}
</div>
`;
}
/**
* Builds the html for one ranked item.
* @param {Object} item - The ranked item { index, text, score, reason }.
* @returns {string} The item html.
*/
#item_html(item) {
const index = Number(item.index) || 0;
const score = Number(item.score) || 0;
const text = escapeHtml(item.text || '');
const reason = escapeHtml(item.reason || '');
return `
<div class="div-rankings-item" data-guid="${escapeHtml(item.guid || '')}">
<span class="span-rankings-index">${index}</span>
<div class="div-rankings-item-body">
<div class="div-rankings-item-text">${text}</div>
<div class="div-rankings-item-reason">${reason}</div>
</div>
<span class="span-rankings-score" title="Ranking confidence">${score}</span>
</div>
`;
}
/**
* Builds the additional-items section when the model proposed new
* candidates that would outrank the first item. Each row carries an add
* button that prepends the choice to the current list; it is disabled
* when the choice is already present (case-insensitive).
* @param {string[]} [items=[]] - The suggested additional items.
* @param {string[]} [existing=[]] - The current choices as lowercase plain text.
* @param {object} [settings={}] - Conversation flags; read_only omits the add buttons.
* @returns {string} The section html, empty when there are none.
*/
#additional_html(items = [], existing = [], settings = {}) {
if (!Array.isArray(items) || items.length === 0) return '';
const rows = items
.map(item => {
const present = existing.includes(item.trim().toLowerCase());
const button = settings.read_only ? '' : `<button type="button" class="as-icon div-rankings-add"${present ? ' disabled="disabled" title="Already in the list"' : ' title="Add to the top of the list"'} data-choice="${escapeHtml(item)}">➕</button>`;
return `<div class="div-rankings-additional-item">${escapeHtml(item)}${button}</div>`;
})
.join('');
return `
<div class="div-rankings-additional">
<div class="div-rankings-section-label">Additional items that could outrank the first</div>
${rows}
</div>
`;
}
/**
* Builds the suggested/related query chip block, gated by the
* conversation flags like chat responses.
* @param {Object} block - The rank block.
* @param {object} settings - Conversation flags.
* @returns {string} The queries html, empty when nothing to show.
*/
#queries_html(block, settings = {}) {
let html = '';
if (settings.show_suggested !== false && Array.isArray(block.suggestions) && block.suggestions.length > 0) {
html += `<p><strong>Suggestions:</strong> <i>${formatSuggestedChipsHtml(block.suggestions)}</i></p>`;
}
if (settings.show_related !== false && Array.isArray(block.related) && block.related.length > 0) {
html += `<p><strong>Related:</strong> <i>${formatRelatedChipsHtml(block.related)}</i></p>`;
}
return html ? `<div class="div-rankings-queries">${html}</div>` : '';
}
}
export default RankingsView;
rankings-view.js
×
Type: Web, text/plain
5.67 Kilobytes
Last Modified 2026-11-04 09:48:28
⬇ Download File
Type: Web, text/plain
5.67 Kilobytes
Last Modified 2026-11-04 09:48:28
⬇ Download File