openvibers/openvibe.live Commits
Channel lines: never cut after an introduced noun (#783)
Changes
@@@ -27,6 +27,8 @@ const MAX_CONTENT = MAX_LEN - 'Streams '.length - 1; // 81// Joining words a cut may not end on ("… sessions and", "… with a").const TRAIL_WORD = /^(?:a|an|the|and|or|but|with|from|in|on|at|by|for|of|to|as|that|while|when|often|usually|frequently|typically|mostly|generally|sometimes|featuring|including|presenting)$/i;++// A cut may not end on an introduced noun ("… and a dark", "… with a focus"): the phrase continues.++const WEAK_END = /\b(?:a|an|the)\s+\S+$/i;// The verb the card line opens with, wherever the overview puts it early ("the flaming feline streams …").const VERB = /\b(?:streams?|broadcasts?|presents?|plays?|hosts?|covers?|shows?|builds?|creates?|chats?)\b/i;// A description in the past tense is not the card's present tense: use the fallback shape instead.@@@ -130,7 +132,7 @@ function fitContent(content) {if (phrases.length > 1) {let out = phrases[0], best = '';for (let i = 1; i < phrases.length; i++) {-- if (out.length <= MAX_CONTENT && (out.length >= 50 || NEXT_CLAUSE.test(phrases[i]))) best = out;++ if (out.length <= MAX_CONTENT && (out.length >= 50 || NEXT_CLAUSE.test(phrases[i])) && !WEAK_END.test(out)) best = out;const next = `${out}, ${phrases[i]}`;if (next.length > MAX_CONTENT) break;out = next;@@@ -161,7 +163,8 @@ function shapeChannelLine({ username = null, displayName = null, overview = '' }// A colon never reaches a card line; a comma keeps the sentence reading.content = fitContent(content.replace(/:/g, ',')).replace(/[.!?…]+$/g, '').trim();-- if (!content) continue;++ // "as a car-streamer" is a complete role; other trailing "a …" is an unfinished phrase.++ if (!content || (!/^as\s+(?:a|an)\s/i.test(content) && WEAK_END.test(content))) continue;const line = `Streams ${content}.`;if (line.length > MAX_LEN || !CHANNEL_LINE_RE.test(line)) continue;// The owner's line (AGENTS.md, server/ai/explicit.js): people may be described; sex acts never are.@@@ -103,6 +103,7 @@ const TRIM = [{ overview: 'mrcodygraves streams as an IRL creator, often centered in a dark, minimalist setup with a headset and a Champion shirt.', line: 'Streams as an IRL creator, often centered in a dark, minimalist setup.' },{ overview: '1n1xx streams a mix of fast-paced first-person shooters and arcade-style Roblox-style obstacle courses, with on-screen chat.', line: 'Streams a mix of fast-paced first-person shooters.' },{ overview: 'Bobdope streams as an irl content creator sharing underwater marine life footage, turtle and reef scenes, and other real-world aquatic views.', line: 'Streams as an irl content creator sharing underwater marine life footage.' },++ { overview: 'Telegram_a1z69 streams in a desktop-focused setup, often featuring a dim mobile screen and a dark, indistinct background.', line: 'Streams in a desktop-focused setup, often featuring a dim mobile screen.' },];for (const t of TRIM) {const line = shapeChannelLine({ username: 'x', displayName: 'x', overview: t.overview });
@@ -27,6 +27,8 @@ const MAX_CONTENT = MAX_LEN - 'Streams '.length - 1; // 81
@@ -27,6 +27,8 @@ const MAX_CONTENT = MAX_LEN - 'Streams '.length - 1; // 81
// Joining words a cut may not end on ("… sessions and", "… with a").
// Joining words a cut may not end on ("… sessions and", "… with a").
const TRAIL_WORD = /^(?:a|an|the|and|or|but|with|from|in|on|at|by|for|of|to|as|that|while|when|often|usually|frequently|typically|mostly|generally|sometimes|featuring|including|presenting)$/i;
const TRAIL_WORD = /^(?:a|an|the|and|or|but|with|from|in|on|at|by|for|of|to|as|that|while|when|often|usually|frequently|typically|mostly|generally|sometimes|featuring|including|presenting)$/i;
+// A cut may not end on an introduced noun ("… and a dark", "… with a focus"): the phrase continues.
+const WEAK_END = /\b(?:a|an|the)\s+\S+$/i;
// The verb the card line opens with, wherever the overview puts it early ("the flaming feline streams …").
// The verb the card line opens with, wherever the overview puts it early ("the flaming feline streams …").
const VERB = /\b(?:streams?|broadcasts?|presents?|plays?|hosts?|covers?|shows?|builds?|creates?|chats?)\b/i;
const VERB = /\b(?:streams?|broadcasts?|presents?|plays?|hosts?|covers?|shows?|builds?|creates?|chats?)\b/i;
// A description in the past tense is not the card's present tense: use the fallback shape instead.
// A description in the past tense is not the card's present tense: use the fallback shape instead.
@@ -130,7 +132,7 @@ function fitContent(content) {
@@ -130,7 +132,7 @@ function fitContent(content) {
if (phrases.length > 1) {
if (phrases.length > 1) {
let out = phrases[0], best = '';
let out = phrases[0], best = '';
for (let i = 1; i < phrases.length; i++) {
for (let i = 1; i < phrases.length; i++) {
- if (out.length <= MAX_CONTENT && (out.length >= 50 || NEXT_CLAUSE.test(phrases[i]))) best = out;
+ if (out.length <= MAX_CONTENT && (out.length >= 50 || NEXT_CLAUSE.test(phrases[i])) && !WEAK_END.test(out)) best = out;
const next = `${out}, ${phrases[i]}`;
const next = `${out}, ${phrases[i]}`;
if (next.length > MAX_CONTENT) break;
if (next.length > MAX_CONTENT) break;
out = next;
out = next;
@@ -161,7 +163,8 @@ function shapeChannelLine({ username = null, displayName = null, overview = '' }
@@ -161,7 +163,8 @@ function shapeChannelLine({ username = null, displayName = null, overview = '' }
// A colon never reaches a card line; a comma keeps the sentence reading.
// A colon never reaches a card line; a comma keeps the sentence reading.
content = fitContent(content.replace(/:/g, ',')).replace(/[.!?…]+$/g, '').trim();
content = fitContent(content.replace(/:/g, ',')).replace(/[.!?…]+$/g, '').trim();
- if (!content) continue;
+ // "as a car-streamer" is a complete role; other trailing "a …" is an unfinished phrase.
+ if (!content || (!/^as\s+(?:a|an)\s/i.test(content) && WEAK_END.test(content))) continue;
const line = `Streams ${content}.`;
const line = `Streams ${content}.`;
if (line.length > MAX_LEN || !CHANNEL_LINE_RE.test(line)) continue;
if (line.length > MAX_LEN || !CHANNEL_LINE_RE.test(line)) continue;
// The owner's line (AGENTS.md, server/ai/explicit.js): people may be described; sex acts never are.
// The owner's line (AGENTS.md, server/ai/explicit.js): people may be described; sex acts never are.
@@ -103,6 +103,7 @@ const TRIM = [
@@ -103,6 +103,7 @@ const TRIM = [
{ overview: 'mrcodygraves streams as an IRL creator, often centered in a dark, minimalist setup with a headset and a Champion shirt.', line: 'Streams as an IRL creator, often centered in a dark, minimalist setup.' },
{ overview: 'mrcodygraves streams as an IRL creator, often centered in a dark, minimalist setup with a headset and a Champion shirt.', line: 'Streams as an IRL creator, often centered in a dark, minimalist setup.' },
{ overview: '1n1xx streams a mix of fast-paced first-person shooters and arcade-style Roblox-style obstacle courses, with on-screen chat.', line: 'Streams a mix of fast-paced first-person shooters.' },
{ overview: '1n1xx streams a mix of fast-paced first-person shooters and arcade-style Roblox-style obstacle courses, with on-screen chat.', line: 'Streams a mix of fast-paced first-person shooters.' },
{ overview: 'Bobdope streams as an irl content creator sharing underwater marine life footage, turtle and reef scenes, and other real-world aquatic views.', line: 'Streams as an irl content creator sharing underwater marine life footage.' },
{ overview: 'Bobdope streams as an irl content creator sharing underwater marine life footage, turtle and reef scenes, and other real-world aquatic views.', line: 'Streams as an irl content creator sharing underwater marine life footage.' },
+ { overview: 'Telegram_a1z69 streams in a desktop-focused setup, often featuring a dim mobile screen and a dark, indistinct background.', line: 'Streams in a desktop-focused setup, often featuring a dim mobile screen.' },
];
];
for (const t of TRIM) {
for (const t of TRIM) {
const line = shapeChannelLine({ username: 'x', displayName: 'x', overview: t.overview });
const line = shapeChannelLine({ username: 'x', displayName: 'x', overview: t.overview });