openvibers/openvibe.design Commits
Research first: keep a proposed rule in the register's areas (#286)
Changes
@@@ -111,7 +111,19 @@else if (input.name === 'models' || input.name === 'takes') fitModels();else if (input.name === 'start_url') setText('start', startUrl() ? (() => { try { return new URL(startUrl()).hostname; } catch { return 'A page'; } })() : 'Blank');else if (input.name === 'who') { all('input[name="models"]').forEach((x) => { x.checked = false; }); fitModels(); show(input); setTimeout(() => closeMods(null), 160); }-- else if (input.name === 'research' || input.name === 'research_depth' || input.name === 'research_id' || input.name === 'research_ref') { fitResearch(); }++ else if (input.name === 'research') {++ if (input.checked) { const id = q('select[name="research_id"]'); if (id) id.value = ''; const ref = q('input[name="research_ref"]'); if (ref) ref.value = ''; }++ fitResearch();++ }++ else if (input.name === 'research_id') {++ if (input.value) { const tick = q('input[name="research"]'); if (tick) tick.checked = false; const ref = q('input[name="research_ref"]'); if (ref) ref.value = ''; }++ fitResearch();++ }++ else if (input.name === 'research_ref') {++ if (input.value.trim()) { const tick = q('input[name="research"]'); if (tick) tick.checked = false; const id = q('select[name="research_id"]'); if (id) id.value = ''; }++ fitResearch();++ }++ else if (input.name === 'research_depth') fitResearch();else if (input.closest('.dv-mod')) { show(input); setTimeout(() => closeMods(null), 160); }save(); askCost();});@@@ -121,7 +133,10 @@});prompt.addEventListener('blur', askCost);const researchRef = q('input[name="research_ref"]');-- if (researchRef) researchRef.addEventListener('input', () => { fitResearch(); save(); askCost(); });++ if (researchRef) researchRef.addEventListener('input', () => {++ if (researchRef.value.trim()) { const tick = q('input[name="research"]'); if (tick) tick.checked = false; const id = q('select[name="research_id"]'); if (id) id.value = ''; }++ fitResearch(); save(); askCost();++ });mods.forEach((m) => m.addEventListener('toggle', () => { if (m.open) closeMods(m); }));document.addEventListener('click', (event) => { const open = mods.find((m) => m.open); if (open && (!open.contains(event.target) || event.target === open)) closeMods(null); });document.addEventListener('keydown', (event) => { if (event.key === 'Escape') { const open = mods.find((m) => m.open); if (open) { closeMods(null); open.querySelector('summary').focus(); } } });@@@ -82,7 +82,8 @@ function createDeepDiveAi({ config, fetchImpl = globalThis.fetch, log = consolethrow new DeepDiveError(502, 'deep_dive.unreachable', err && err.name === 'TimeoutError'? 'OpenVibe.AI did not answer in time.' : 'OpenVibe.AI could not be reached.');}-- if (res.status === 401 && !token && !retried) {++ if ((res.status === 401 || res.status === 403) && !token && !retried) {++ // A grant that just changed (or a revoked token): ask Network for a fresh one once.tokenClient.invalidate();return call(method, path, body, { token, retried: true, baseUrl });}@@@ -121,11 +121,22 @@ function proposedRulesOf(report) {if (!(rule.status === 'would_change' || item.proposed_rule || item.proposes_rule)) continue;const text = str(item.proposed_rule || item.rule_text || item.what || (typeof item === 'string' ? item : ''), 300);if (!text) continue;++ // Keep the row in one of the register's areas, or the page's grouping would drop it. The cited rule's++ // own area is the best answer; a new rule lands in products, where most product rules live.++ let area = str(item.area || item.group, 40);++ if (!area) {++ try {++ const { RULES } = require('../rules/register');++ const cited = RULES.find((x) => (rule.id && x.id === rule.id) || (rule.text && x.rule === rule.text));++ if (cited && cited.area) area = cited.area;++ } catch { /* the register is not readable here; the default below is fine */ }++ }++ if (!area) area = 'products';out.push({rule: text,detail: str(rule.text || item.detail || item.description || item.what, 1200),why: str(item.why || item.reason || item.evidence_note, 1200),-- area: str(item.area || item.group, 40),++ area: area.slice(0, 40),evidence: evidenceOf(item),ruleId: str(rule.id, 120) || null,});@@@ -222,8 +222,10 @@ function createDeepDiveService({ s, config, fetchImpl = globalThis.fetch, log =if (!owner) return { component, count: 0, finds: [], reports: 0 };const reports = await db.allDone({ limit: 60, owner });const out = [];++ const contributed = new Set();for (const row of reports) {const found = findsFor(row.report, component, limit);++ if (found.length) contributed.add(row.id);for (const find of found) {const key = `${find.n || ''}|${find.title}|${find.crop || find.url || ''}`;if (out.some((x) => x.key === key)) continue;@@@ -231,7 +233,7 @@ function createDeepDiveService({ s, config, fetchImpl = globalThis.fetch, log =reportUrl: row.report_url || null, depth: row.depth, readAt: row.updated_at });}}-- return { component, count: out.length, finds: out.slice(0, limit), reports: reports.length };++ return { component, count: out.length, finds: out.slice(0, limit), reports: contributed.size };}async function resume() {@@@ -123,6 +123,7 @@ const pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));assert.ok(!/<style|<html|class=/.test(n.brief), 'the brief is words, never a site\'s markup');assert.equal(n.proposedRules.length, 2);assert.match(n.proposedRules[0].rule, /pressed state|48 px/);++ assert.ok(n.proposedRules.every((p) => p.area), 'a proposed rule keeps one of the register\'s areas');assert.match(findsFor(report, 'button')[0].title, /button/i);assert.equal(findsFor(report, 'carousel').length, 0);});
@@ -111,7 +111,19 @@
@@ -111,7 +111,19 @@
else if (input.name === 'models' || input.name === 'takes') fitModels();
else if (input.name === 'models' || input.name === 'takes') fitModels();
else if (input.name === 'start_url') setText('start', startUrl() ? (() => { try { return new URL(startUrl()).hostname; } catch { return 'A page'; } })() : 'Blank');
else if (input.name === 'start_url') setText('start', startUrl() ? (() => { try { return new URL(startUrl()).hostname; } catch { return 'A page'; } })() : 'Blank');
else if (input.name === 'who') { all('input[name="models"]').forEach((x) => { x.checked = false; }); fitModels(); show(input); setTimeout(() => closeMods(null), 160); }
else if (input.name === 'who') { all('input[name="models"]').forEach((x) => { x.checked = false; }); fitModels(); show(input); setTimeout(() => closeMods(null), 160); }
- else if (input.name === 'research' || input.name === 'research_depth' || input.name === 'research_id' || input.name === 'research_ref') { fitResearch(); }
+ else if (input.name === 'research') {
+ if (input.checked) { const id = q('select[name="research_id"]'); if (id) id.value = ''; const ref = q('input[name="research_ref"]'); if (ref) ref.value = ''; }
+ fitResearch();
+ }
+ else if (input.name === 'research_id') {
+ if (input.value) { const tick = q('input[name="research"]'); if (tick) tick.checked = false; const ref = q('input[name="research_ref"]'); if (ref) ref.value = ''; }
+ fitResearch();
+ }
+ else if (input.name === 'research_ref') {
+ if (input.value.trim()) { const tick = q('input[name="research"]'); if (tick) tick.checked = false; const id = q('select[name="research_id"]'); if (id) id.value = ''; }
+ fitResearch();
+ }
+ else if (input.name === 'research_depth') fitResearch();
else if (input.closest('.dv-mod')) { show(input); setTimeout(() => closeMods(null), 160); }
else if (input.closest('.dv-mod')) { show(input); setTimeout(() => closeMods(null), 160); }
save(); askCost();
save(); askCost();
});
});
@@ -121,7 +133,10 @@
@@ -121,7 +133,10 @@
});
});
prompt.addEventListener('blur', askCost);
prompt.addEventListener('blur', askCost);
const researchRef = q('input[name="research_ref"]');
const researchRef = q('input[name="research_ref"]');
- if (researchRef) researchRef.addEventListener('input', () => { fitResearch(); save(); askCost(); });
+ if (researchRef) researchRef.addEventListener('input', () => {
+ if (researchRef.value.trim()) { const tick = q('input[name="research"]'); if (tick) tick.checked = false; const id = q('select[name="research_id"]'); if (id) id.value = ''; }
+ fitResearch(); save(); askCost();
+ });
mods.forEach((m) => m.addEventListener('toggle', () => { if (m.open) closeMods(m); }));
mods.forEach((m) => m.addEventListener('toggle', () => { if (m.open) closeMods(m); }));
document.addEventListener('click', (event) => { const open = mods.find((m) => m.open); if (open && (!open.contains(event.target) || event.target === open)) closeMods(null); });
document.addEventListener('click', (event) => { const open = mods.find((m) => m.open); if (open && (!open.contains(event.target) || event.target === open)) closeMods(null); });
document.addEventListener('keydown', (event) => { if (event.key === 'Escape') { const open = mods.find((m) => m.open); if (open) { closeMods(null); open.querySelector('summary').focus(); } } });
document.addEventListener('keydown', (event) => { if (event.key === 'Escape') { const open = mods.find((m) => m.open); if (open) { closeMods(null); open.querySelector('summary').focus(); } } });
@@ -82,7 +82,8 @@ function createDeepDiveAi({ config, fetchImpl = globalThis.fetch, log = console
@@ -82,7 +82,8 @@ function createDeepDiveAi({ config, fetchImpl = globalThis.fetch, log = console
throw new DeepDiveError(502, 'deep_dive.unreachable', err && err.name === 'TimeoutError'
throw new DeepDiveError(502, 'deep_dive.unreachable', err && err.name === 'TimeoutError'
? 'OpenVibe.AI did not answer in time.' : 'OpenVibe.AI could not be reached.');
? 'OpenVibe.AI did not answer in time.' : 'OpenVibe.AI could not be reached.');
}
}
- if (res.status === 401 && !token && !retried) {
+ if ((res.status === 401 || res.status === 403) && !token && !retried) {
+ // A grant that just changed (or a revoked token): ask Network for a fresh one once.
tokenClient.invalidate();
tokenClient.invalidate();
return call(method, path, body, { token, retried: true, baseUrl });
return call(method, path, body, { token, retried: true, baseUrl });
}
}
@@ -121,11 +121,22 @@ function proposedRulesOf(report) {
@@ -121,11 +121,22 @@ function proposedRulesOf(report) {
if (!(rule.status === 'would_change' || item.proposed_rule || item.proposes_rule)) continue;
if (!(rule.status === 'would_change' || item.proposed_rule || item.proposes_rule)) continue;
const text = str(item.proposed_rule || item.rule_text || item.what || (typeof item === 'string' ? item : ''), 300);
const text = str(item.proposed_rule || item.rule_text || item.what || (typeof item === 'string' ? item : ''), 300);
if (!text) continue;
if (!text) continue;
+ // Keep the row in one of the register's areas, or the page's grouping would drop it. The cited rule's
+ // own area is the best answer; a new rule lands in products, where most product rules live.
+ let area = str(item.area || item.group, 40);
+ if (!area) {
+ try {
+ const { RULES } = require('../rules/register');
+ const cited = RULES.find((x) => (rule.id && x.id === rule.id) || (rule.text && x.rule === rule.text));
+ if (cited && cited.area) area = cited.area;
+ } catch { /* the register is not readable here; the default below is fine */ }
+ }
+ if (!area) area = 'products';
out.push({
out.push({
rule: text,
rule: text,
detail: str(rule.text || item.detail || item.description || item.what, 1200),
detail: str(rule.text || item.detail || item.description || item.what, 1200),
why: str(item.why || item.reason || item.evidence_note, 1200),
why: str(item.why || item.reason || item.evidence_note, 1200),
- area: str(item.area || item.group, 40),
+ area: area.slice(0, 40),
evidence: evidenceOf(item),
evidence: evidenceOf(item),
ruleId: str(rule.id, 120) || null,
ruleId: str(rule.id, 120) || null,
});
});
@@ -222,8 +222,10 @@ function createDeepDiveService({ s, config, fetchImpl = globalThis.fetch, log =
@@ -222,8 +222,10 @@ function createDeepDiveService({ s, config, fetchImpl = globalThis.fetch, log =
if (!owner) return { component, count: 0, finds: [], reports: 0 };
if (!owner) return { component, count: 0, finds: [], reports: 0 };
const reports = await db.allDone({ limit: 60, owner });
const reports = await db.allDone({ limit: 60, owner });
const out = [];
const out = [];
+ const contributed = new Set();
for (const row of reports) {
for (const row of reports) {
const found = findsFor(row.report, component, limit);
const found = findsFor(row.report, component, limit);
+ if (found.length) contributed.add(row.id);
for (const find of found) {
for (const find of found) {
const key = `${find.n || ''}|${find.title}|${find.crop || find.url || ''}`;
const key = `${find.n || ''}|${find.title}|${find.crop || find.url || ''}`;
if (out.some((x) => x.key === key)) continue;
if (out.some((x) => x.key === key)) continue;
@@ -231,7 +233,7 @@ function createDeepDiveService({ s, config, fetchImpl = globalThis.fetch, log =
@@ -231,7 +233,7 @@ function createDeepDiveService({ s, config, fetchImpl = globalThis.fetch, log =
reportUrl: row.report_url || null, depth: row.depth, readAt: row.updated_at });
reportUrl: row.report_url || null, depth: row.depth, readAt: row.updated_at });
}
}
}
}
- return { component, count: out.length, finds: out.slice(0, limit), reports: reports.length };
+ return { component, count: out.length, finds: out.slice(0, limit), reports: contributed.size };
}
}
async function resume() {
async function resume() {
@@ -123,6 +123,7 @@ const pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -123,6 +123,7 @@ const pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
assert.ok(!/<style|<html|class=/.test(n.brief), 'the brief is words, never a site\'s markup');
assert.ok(!/<style|<html|class=/.test(n.brief), 'the brief is words, never a site\'s markup');
assert.equal(n.proposedRules.length, 2);
assert.equal(n.proposedRules.length, 2);
assert.match(n.proposedRules[0].rule, /pressed state|48 px/);
assert.match(n.proposedRules[0].rule, /pressed state|48 px/);
+ assert.ok(n.proposedRules.every((p) => p.area), 'a proposed rule keeps one of the register\'s areas');
assert.match(findsFor(report, 'button')[0].title, /button/i);
assert.match(findsFor(report, 'button')[0].title, /button/i);
assert.equal(findsFor(report, 'carousel').length, 0);
assert.equal(findsFor(report, 'carousel').length, 0);
});
});