Add "Loved it" modifier to rating picker

Any rating can now be combined with a "Loved it" toggle. The suffix |loved
is appended to the rating string and parsed in normalize_refiner_v2_rating.

When active: reward boosted to max(base, 0.85), axis pressure softened 50%,
conditioning direction delta raised to 0.45, preferred context applied at
0.30 base_delta. Disabled for Perfect (redundant) and -Just forget it-.
Button label shows ♥ when modifier is active.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
digital-garbage
2026-05-30 11:55:17 +03:00
co-authored by Claude Sonnet 4.6
parent d7a1400239
commit e9a1510db0
2 changed files with 60 additions and 7 deletions
+21 -1
View File
@@ -5719,10 +5719,18 @@ V2_RATING_ALIASES = {
def normalize_refiner_v2_rating(value):
loved_modifier = False
if isinstance(value, str) and value.endswith("|loved"):
value = value[:-6]
loved_modifier = True
if isinstance(value, str):
cleaned = V2_RATING_ALIASES.get(value.strip(), value.strip())
if cleaned in V2_RATING_PROFILES:
return dict(V2_RATING_PROFILES[cleaned], label=cleaned)
profile = dict(V2_RATING_PROFILES[cleaned], label=cleaned)
if loved_modifier:
profile["loved_modifier"] = True
profile["reward"] = max(float(profile.get("reward", 0.0)), 0.85)
return profile
try:
value = int(float(cleaned))
except (TypeError, ValueError):
@@ -8210,6 +8218,8 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
elif rating_key == "nailed_it":
delta = -0.10
entry["forgiven_count"] = int(entry.get("forgiven_count", 0)) + 1
elif rating_profile.get("loved_modifier"):
delta *= 0.5 # soften axis pressure — quality was good, still try to fix it
elif rating_key == "loved_it":
continue # axis-blind quality endorsement — don't learn prompt adherence
elif rating_key == "awful":
@@ -8421,6 +8431,9 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
elif rating_profile.get("key") == "nailed_it":
positive_axes = {"action", "details"}
base_delta = 0.65
elif rating_profile.get("loved_modifier"):
positive_axes = {"action", "details"}
base_delta = 0.30
elif rating_profile.get("key") == "loved_it":
return "Preferred context: skipped for axis-blind quality endorsement."
else:
@@ -8612,6 +8625,13 @@ class FunPackVideoRefinerV2(FunPackVideoRefiner):
else:
delta = 0.0
count_category_evidence = False
elif rating_profile.get("loved_modifier"):
delta = 0.45 * kind_scale
entry["liked_count"] = int(entry.get("liked_count", 0)) + 1
rating_evidence["liked"] = int(rating_evidence.get("liked", 0)) + 1
for category, score in effective_before.items():
if float(score) >= 0.28:
weights[category] = float(weights.get(category, 0.0)) + 0.10 * kind_scale
elif rating_profile.get("key") == "nailed_it":
delta = 0.35 * kind_scale
entry["liked_count"] = int(entry.get("liked_count", 0)) + 1
+39 -6
View File
@@ -108,7 +108,16 @@ function injectStyles() {
.fp-picker-opt-reward { font-size: 10px; font-weight: 700; font-variant-numeric: tabular-nums; }
.fp-picker-opt-hint { font-size: 9px; color: #888; line-height: 1.3; }
.fp-picker-opt:hover .fp-picker-opt-hint { color: #aaa; }
.fp-picker-nuclear { padding: 8px 10px; display: grid; grid-template-columns: 1fr 1fr; gap: 5px; }
.fp-picker-nuclear { padding: 8px 10px 4px; display: grid; grid-template-columns: 1fr 1fr; gap: 5px; }
.fp-picker-loved-row { padding: 6px 10px 8px; border-top: 1px solid rgba(255,255,255,0.06); }
.fp-picker-loved-toggle {
width: 100%; padding: 7px 10px; border-radius: 6px; cursor: pointer;
font: 11px sans-serif; font-weight: 600; text-align: left;
background: rgba(255,255,255,0.04); border: 1px solid rgba(255,120,180,0.25); color: #aaa;
transition: background 0.1s, border-color 0.1s, color 0.1s;
}
.fp-picker-loved-toggle:hover { background: rgba(255,120,180,0.10); border-color: rgba(255,120,180,0.5); color: #eee; }
.fp-picker-loved-toggle.active { background: rgba(255,120,180,0.18); border-color: rgba(255,120,180,0.75); color: #ffb3d4; }
`;
document.head.append(s);
}
@@ -157,6 +166,8 @@ function makeOption(r, currentValue, accent, onPick) {
return btn;
}
const NO_LOVED_LABELS = new Set(["Perfect", FORGET_LABEL]);
function openPicker(ratingWidget, event, onPick) {
closePicker();
injectStyles();
@@ -176,7 +187,13 @@ function openPicker(ratingWidget, event, onPick) {
header.append(closeBtn);
picker.append(header);
const current = ratingWidget.value;
const rawCurrent = ratingWidget.value || "";
const currentLoved = rawCurrent.endsWith("|loved");
const currentBase = currentLoved ? rawCurrent.slice(0, -6) : rawCurrent;
let lovedOn = currentLoved;
const buildLabel = (base) => NO_LOVED_LABELS.has(base) ? base : (lovedOn ? base + "|loved" : base);
// Category sections
for (const cat of CATEGORIES) {
@@ -191,7 +208,7 @@ function openPicker(ratingWidget, event, onPick) {
const grid = document.createElement("div");
grid.className = cat.id === "positive" ? "fp-picker-positive-grid" : "fp-picker-grid";
for (const r of cat.ratings) grid.append(makeOption(r, current, cat.accent, onPick));
for (const r of cat.ratings) grid.append(makeOption(r, currentBase, cat.accent, (base) => onPick(buildLabel(base))));
section.append(grid);
picker.append(section);
}
@@ -200,9 +217,22 @@ function openPicker(ratingWidget, event, onPick) {
const nuclearSection = document.createElement("div");
nuclearSection.className = "fp-picker-nuclear";
nuclearSection.style.setProperty("--accent", "140,100,100");
for (const r of NUCLEAR) nuclearSection.append(makeOption(r, current, "140,100,100", onPick));
for (const r of NUCLEAR) nuclearSection.append(makeOption(r, currentBase, "140,100,100", (base) => onPick(buildLabel(base))));
picker.append(nuclearSection);
// Loved it toggle
const lovedRow = document.createElement("div");
lovedRow.className = "fp-picker-loved-row";
const lovedToggle = document.createElement("button");
lovedToggle.className = "fp-picker-loved-toggle" + (lovedOn ? " active" : "");
lovedToggle.textContent = "♥ Loved it — great output regardless of prompt adherence";
lovedToggle.addEventListener("click", () => {
lovedOn = !lovedOn;
lovedToggle.classList.toggle("active", lovedOn);
});
lovedRow.append(lovedToggle);
picker.append(lovedRow);
document.body.append(picker);
// Position at mouse, clamped to viewport
@@ -250,8 +280,10 @@ function setupRatingPicker(node) {
hideWidget(ratingWidget);
const getLabel = () => {
const v = ratingWidget.value;
return (!v || v === FORGET_LABEL) ? "Waiting for rating..." : `Rating: ${v}`;
const v = ratingWidget.value || "";
if (!v || v === FORGET_LABEL) return "Waiting for rating...";
if (v.endsWith("|loved")) return `Rating: ${v.slice(0, -6)} ♥`;
return `Rating: ${v}`;
};
node.widgets = (node.widgets || []).filter((w) => w.__fpPickerWidget !== true);
@@ -280,6 +312,7 @@ function resetRating(node) {
node.__fpPickerWidgetRef.name = "Waiting for rating...";
node.setDirtyCanvas?.(true, true);
}
closePicker();
}
// ─── Extension ────────────────────────────────────────────────────────────────