Refocus Ranker as Prioritix idea-layer sorting
This commit is contained in:
@@ -448,19 +448,70 @@ function hits(text, words) {
|
||||
return words.reduce((count, word) => count + (lower.includes(word) ? 1 : 0), 0);
|
||||
}
|
||||
|
||||
const sortableTextHeadings = /^(features?|functionality|functions?|options?|variants?|claims?|promises?|risks?|assumptions?|experiments?|tests?|proof steps?|validation questions?|questions?|feedback themes?|feedback|audiences?|segments?|use cases?|possible next moves?|next moves?|moves?|candidates?|things to sort|sortable layer|xyz)\s*:\s*$/i;
|
||||
const contextTextHeadings = /^(idea|context|constraints?|instructions?|instruction|notes?|non[- ]?goals?|avoid|do not sort|don't sort|dont sort|background|goal|goals?)\s*:\s*$/i;
|
||||
const inlineSortablePrefix = /^(features?|functionality|functions?|options?|variants?|claims?|promises?|risks?|assumptions?|experiments?|tests?|proof steps?|validation questions?|questions?|feedback themes?|feedback|audiences?|segments?|use cases?|possible next moves?|next moves?|moves?|candidates?|things to sort|sortable layer|xyz)\s*:\s*/i;
|
||||
|
||||
function normalizeSortableTextLine(line = '') {
|
||||
return cleanText(line, 900)
|
||||
.replace(/^\s*(?:[-*•]|\d+[.)]|[a-z][.)])\s*/i, '')
|
||||
.replace(/^\s*(?:rough\s+idea|idea|option|feature|function|variant|claim|risk|experiment|test|question|feedback|audience|segment|move)\s+[a-z0-9]+\s*[:.)-]\s*/i, '')
|
||||
.replace(/^\s*(maybe|possibly|also|plus|and|or|then|later|eventually|build|add|include|some kind of|kind of)\b\s*/i, '')
|
||||
.replace(/\?+$/i, '')
|
||||
.trim();
|
||||
}
|
||||
|
||||
function splitOptionTitleDescription(line = '') {
|
||||
const normalized = normalizeSortableTextLine(line);
|
||||
const [rawTitle, ...rest] = normalized.split(/\s[-–—]\s/);
|
||||
return {
|
||||
title: cleanText(rawTitle || normalized || line, 140),
|
||||
description: cleanText(rest.join(' — '), 420),
|
||||
};
|
||||
}
|
||||
|
||||
function parseOptionsFromText(value, sourceSection = 'optionsText') {
|
||||
const text = cleanMultiline(value, 12000);
|
||||
const candidateText = /\b(maybe|possibly|options?|features?|ideas?|next moves?|functionality|backlog)\b[:\s]/i.test(text)
|
||||
? text.replace(/^[\s\S]*?\b(maybe|possibly|options?|features?|ideas?|next moves?|functionality|backlog)\b[:\s]*/i, '')
|
||||
const lines = text.split('\n').map(line => line.trim()).filter(Boolean);
|
||||
const hasExplicitSortableHeading = lines.some(line => sortableTextHeadings.test(line));
|
||||
const extracted = [];
|
||||
let activeSection = hasExplicitSortableHeading ? 'context' : 'sortable';
|
||||
|
||||
for (const rawLine of lines) {
|
||||
const line = rawLine.trim();
|
||||
if (sortableTextHeadings.test(line)) {
|
||||
activeSection = 'sortable';
|
||||
continue;
|
||||
}
|
||||
if (contextTextHeadings.test(line)) {
|
||||
activeSection = 'context';
|
||||
continue;
|
||||
}
|
||||
const inlineSortable = line.match(inlineSortablePrefix);
|
||||
if (inlineSortable) {
|
||||
activeSection = 'sortable';
|
||||
const rest = line.replace(inlineSortablePrefix, '').trim();
|
||||
if (rest) extracted.push(rest);
|
||||
continue;
|
||||
}
|
||||
if (activeSection !== 'sortable') continue;
|
||||
if (/^(constraints?|instructions?|context|notes?|goal|goals?)\s*:/i.test(line)) continue;
|
||||
extracted.push(line);
|
||||
}
|
||||
|
||||
const looseText = !hasExplicitSortableHeading && /\b(maybe|possibly|options?|features?|functionality|variants?|claims?|risks?|experiments?|validation questions?|feedback themes?|audiences?|next moves?)\b[:\s]/i.test(text)
|
||||
? text.replace(/^[\s\S]*?\b(maybe|possibly|options?|features?|functionality|variants?|claims?|risks?|experiments?|validation questions?|feedback themes?|audiences?|next moves?)\b[:\s]*/i, '')
|
||||
: text;
|
||||
const cleanedLines = candidateText.split('\n').map(line => line.replace(/^\s*[-*•\d.)]+\s*/, '').trim()).filter(Boolean);
|
||||
const candidateText = (hasExplicitSortableHeading ? extracted.join('\n').trim() : '') || looseText
|
||||
.replace(/\b(maybe|possibly|options?|features?|functionality|variants?|claims?|risks?|experiments?|validation questions?|feedback themes?|audiences?|next moves?)\s*:/gi, '\n')
|
||||
.trim();
|
||||
const cleanedLines = candidateText.split('\n').map(normalizeSortableTextLine).filter(Boolean);
|
||||
const sentenceList = candidateText
|
||||
.replace(/\b(maybe|possibly|options?|features?|ideas?|next moves?|functionality|backlog)\s*:/gi, '\n')
|
||||
.split(/\n|;|\|/)
|
||||
.map(part => part.replace(/^\s*[-*•\d.)]+\s*/, '').trim())
|
||||
.map(normalizeSortableTextLine)
|
||||
.filter(Boolean);
|
||||
const commaList = sentenceList.length === 1
|
||||
? sentenceList[0].split(/,|\s+and\s+|[.!?]\s+/i).map(part => part.trim()).filter(Boolean)
|
||||
? sentenceList[0].split(/,|\s+and\s+|[.!?]\s+/i).map(normalizeSortableTextLine).filter(Boolean)
|
||||
: [];
|
||||
const optionLines = cleanedLines.length >= 2
|
||||
? cleanedLines
|
||||
@@ -468,20 +519,16 @@ function parseOptionsFromText(value, sourceSection = 'optionsText') {
|
||||
? sentenceList
|
||||
: commaList.length >= 2
|
||||
? commaList
|
||||
: candidateText.split(/[;|]/).map(part => part.trim()).filter(Boolean);
|
||||
: candidateText.split(/[;|]/).map(normalizeSortableTextLine).filter(Boolean);
|
||||
return optionLines.slice(0, 24).map((line, index) => {
|
||||
const normalized = line
|
||||
.replace(/^\s*(maybe|possibly|also|plus|and|or|then|later|eventually|build|add|include|some kind of|kind of)\b\s*/i, '')
|
||||
.replace(/\?+$/, '')
|
||||
.trim();
|
||||
const [rawTitle, ...rest] = normalized.split(/\s*[-–—:]\s+/);
|
||||
const { title, description } = splitOptionTitleDescription(line);
|
||||
return {
|
||||
id: `option-${index + 1}`,
|
||||
title: cleanText(rawTitle || normalized || line, 140),
|
||||
description: cleanText(rest.join(' — '), 420),
|
||||
title,
|
||||
description,
|
||||
provenance: { sourceSection },
|
||||
};
|
||||
}).filter(item => item.title && !/^(i('| a)?m|i am|we are|i only|want|need)\b/i.test(item.title));
|
||||
}).filter(item => item.title && !/^(i('| a)?m|i am|we are|i only|want|need|rank the|sort the|do not|don't|dont)\b/i.test(item.title));
|
||||
}
|
||||
|
||||
function objectFrom(value) {
|
||||
|
||||
Reference in New Issue
Block a user