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
) : (
No drafts reviewed yet.
)}
tone
harsh
); } // ---------- Devil's Advocate widget ---------- export function DevilsAdvocateWidget({ state, setState, openModal }) { return ( <>
Your claim
"{state.claim}"
Strongest counters · {state.counters.length}
{state.counters.slice(0, 3).map((c, i) => (
{c.lbl}
{c.text}
))}
); } // ---------- Scope realism widget ---------- export function ScopeRealismWidget({ state, openModal }) { return (
{state.score.toFixed(1)}
Feasibility
{state.label}
target: {state.target}
{state.factors.map(f => (
{f.label} {f.val}
))}
); } // =================================================================== // 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.