import React, { useState } from 'react';
import Icon from './CanvasIcon';
const fireToast = (msg, kind = 'success') =>
window.dispatchEvent(new CustomEvent('canvas-toast', { detail: { msg, kind } }));
// Critic widgets are scripted in canvas; the *real* critique should happen in
// the main chat so message history lives in one place (per Daniel's review).
// "Open in chat" stashes a draft prompt + persona hint then asks CanvasPage to
// navigate to chat — the chat page can read `canvas-chat-handoff` from localStorage.
const handoffToChat = (persona, prompt, context = {}) => {
try {
localStorage.setItem('canvas-chat-handoff', JSON.stringify({
at: Date.now(), persona, prompt, ...context,
}));
} catch { /* ignore */ }
window.dispatchEvent(new CustomEvent('canvas-open-in-chat', { detail: { persona, prompt } }));
fireToast(`Opening ${persona} in chat — full history will be there.`);
};
// ---------- Reviewer 2 widget ----------
export function Reviewer2Widget({ state, setState, openModal }) {
return (
<>
"Paste a draft paragraph, abstract, or section. I'll respond as the most uncharitable but technically competent reviewer your work will ever see."
{state.lastReview ? (
Last critique · {state.lastReview.severity}/10 severity
Major: {state.lastReview.major}
+{state.lastReview.minorCount} minor issues, {state.lastReview.suggestionCount} suggestions
);
}
// ===================================================================
// Critic modals
// ===================================================================
const REVIEW_TEMPLATES = [
{
severity: 8,
major: 'The hypothesis is presented before its operationalization. You write that L2/3 spiking "encodes" prediction error without specifying what spike-pattern feature you will measure (rate? latency? variance?), what range of values would count as "encoding," or what would falsify the claim.',
minor: [
'Sample size of n=4 animals is described as "preliminary" without a power calculation or a stopping rule.',
'GLM with history kernel is presented as the analysis but no mention of how its outputs map to the proposed predictive-coding interpretation.',
'No engagement with adaptation as a confound — the obvious alternative explanation for any oddball-driven decrease in firing.',
'Reference to "predictive coding" is loose. Rao & Ballard, Bastos, and Friston make different commitments. Which one are you testing?',
],
suggestions: [
'Add a single sentence specifying the measurable signature you predict, with directionality.',
'Either control for arousal/pupil or acknowledge it as a limit upfront.',
'Add a stopping rule and target effect size before scaling beyond n=4.',
],
},
{
severity: 7,
major: 'You claim the GLM analyses are "consistent with" the hypothesis. This phrase is doing too much work. Consistency with PE encoding is also consistency with at least three alternative explanations (adaptation, arousal, attention). Without a positive test that PE encoding predicts but the alternatives do not, "consistent with" is unfalsifiable.',
minor: [
'Mouse V1 is justified by convention rather than by what makes it the right model for this question.',
'No statement of what would change your mind.',
'Figure-free abstract for an empirical claim is a red flag for reviewers.',
],
suggestions: [
'Replace "consistent with" with a specific signed prediction the data either matches or doesn\'t.',
'List 1-2 results that, if observed, would refute the hypothesis.',
],
},
{
severity: 9,
major: 'This reads like an introduction, not an abstract. There is no result. There is no number. The strongest claim is that your "preliminary analyses are consistent" with your hypothesis — which is the lowest possible bar in empirical neuroscience. If the actual finding is interesting, lead with the finding, not with the framing.',
minor: [
'Word "preliminary" appears three times in three sentences. Cut two.',
'"Oddball stimulus paradigm" is jargon-without-citation; one sentence of definition or one citation, not zero.',
'No mention of layer-specificity, despite L2/3 being the core claim.',
],
suggestions: [
'Lead sentence: "We find that " — even if the effect is small, name it.',
'Cut "we hypothesize that" entirely. Hypotheses go in the intro of the paper, not the abstract.',
],
},
];
export function ReviewerModal({ data, onClose }) {
const [draft, setDraft] = useState(data.initial || '');
const [running, setRunning] = useState(false);
const [review, setReview] = useState(null);
const run = () => {
if (!draft.trim()) return;
setRunning(true);
setReview(null);
setTimeout(() => {
const idx = (draft.length + draft.split(' ').length) % REVIEW_TEMPLATES.length;
const r = REVIEW_TEMPLATES[idx];
setReview(r);
setRunning(false);
}, 900);
};
const accept = () => {
if (!review) return;
data.onComplete({
draft,
severity: review.severity,
major: review.major,
minorCount: review.minor.length,
suggestionCount: review.suggestions.length,
});
fireToast('Critique saved · severity ' + review.severity + '/10', 'critic');
onClose();
};
return (
e.stopPropagation()}>
Reviewer 2 Simulator
Paste a draft. Get the harshest competent peer review you'll ever read — before a real reviewer does.
{!review && !running && (
What this is: a simulated peer review tuned to push back hard. It will name unstated operationalizations, rival explanations, and weak phrasings. It will not flatter you. That's the point.
{review.minor.length} minor · {review.suggestions.length} suggestions
Major issue
{review.major}
Minor issues
{review.minor.map((m, i) =>
{m}
)}
Suggestions
{review.suggestions.map((s, i) =>
{s}
)}
)}
{review ? (
<>
>
) : (
)}
);
}
const HARDER_COUNTERS = [
{ lbl: 'Reverse causation', text: 'You assume PE drives spike changes. The opposite mapping — that some intrinsic cortical state drives both the spike pattern and the perceived "surprise" — is observationally indistinguishable in your design.' },
{ lbl: 'Definition shift', text: 'You will be tempted, when results don\'t fit, to redefine "prediction error" until they do. Pre-register your operationalization or you cannot honestly claim to have tested PC.' },
{ lbl: 'Wrong layer', text: 'Most predictive-coding accounts place PE signaling in L4 or L5b, not L2/3. Your prior for finding PE in L2/3 should be lower than you\'re writing.' },
{ lbl: 'Mouse vs. theory', text: 'Predictive coding theories were built on primate visual hierarchies with rich top-down attention. Mouse V1 lacks several of the assumed circuits. You may be testing the theory on a substrate it doesn\'t apply to.' },
];
export function DevilsModal({ data, onClose }) {
const [counters, setCounters] = useState(data.counters);
const [pushing, setPushing] = useState(false);
const pushHarder = () => {
setPushing(true);
setTimeout(() => {
const next = HARDER_COUNTERS.find(c => !counters.find(x => x.lbl === c.lbl));
if (next) {
const nc = [...counters, next];
setCounters(nc);
data.onUpdate({ counters: nc });
fireToast('Stronger counter added: "' + next.lbl + '"', 'critic');
} else {
fireToast('No more counters — your hypothesis is more robust than I thought.');
}
setPushing(false);
}, 800);
};
return (
e.stopPropagation()}>
Devil's Advocate
The strongest counter-arguments to your hypothesis, ranked by how much they should worry you.
Your claim
"{data.claim}"
{counters.map((c, i) => (
{c.lbl}
{c.text}
))}
);
}
export function ScopeModal({ data, onClose }) {
const s = data.state;
return (
e.stopPropagation()}>
Scope Realism Check
A brutal feasibility verdict on "{s.target}" given your current pace.