jspsych-tangram 0.0.19 → 0.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -56,6 +56,7 @@ function GridView({ params }) {
56
56
  primitiveColorIndices = [0, 1, 2, 3, 4],
57
57
  onTrialEnd
58
58
  } = params;
59
+ const { onTrialEnd: _, ...trialParams } = params;
59
60
  const trialStartTime = React.useRef(Date.now());
60
61
  const [response, setResponse] = React.useState("");
61
62
  const [cellSize, setCellSize] = React.useState(100);
@@ -168,12 +169,10 @@ function GridView({ params }) {
168
169
  const trialData = {
169
170
  response,
170
171
  rt,
171
- n_rows,
172
- n_cols,
173
- tangram_ids: processedTangrams.map((t) => t.tangramId),
174
- show_tangram_decomposition,
175
- use_primitive_colors: usePrimitiveColors,
176
- primitive_color_indices: primitiveColorIndices
172
+ trialParams: {
173
+ ...trialParams,
174
+ tangramIDs: processedTangrams.map((t) => t.tangramId)
175
+ }
177
176
  };
178
177
  if (onTrialEnd) {
179
178
  onTrialEnd(trialData);
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/core/config/config.ts","../../src/plugins/tangram-grid/GridApp.tsx","../../src/plugins/tangram-grid/index.ts"],"sourcesContent":["// src/core/config/config.ts\nexport type Config = {\n color: {\n background: string;\n bands: {\n silhouette: { fillEven: string; fillOdd: string; stroke: string };\n workspace: { fillEven: string; fillOdd: string; stroke: string };\n };\n completion: { fill: string; stroke: string };\n silhouetteMask: string;\n anchors: { invalid: string; valid: string };\n piece: { draggingFill: string; validFill: string; invalidFill: string; invalidStroke: string; selectedStroke: string; allGreenStroke: string; borderStroke: string };\n ui: { light: string; dark: string };\n blueprint: { fill: string; selectedStroke: string; badgeFill: string; labelFill: string };\n tangramDecomposition: { stroke: string };\n primitiveColors: string[];\n };\n opacity: {\n blueprint: number;\n silhouetteMask: number;\n anchors: { invalid: number; valid: number };\n piece: { invalid: number; dragging: number; locked: number; normal: number };\n };\n size: {\n stroke: { bandPx: number; pieceSelectedPx: number; allGreenStrokePx: number; pieceBorderPx: number; tangramDecompositionPx: number };\n anchorRadiusPx: { valid: number; invalid: number };\n badgeFontPx: number;\n centerBadge: { fractionOfOuterR: number; minPx: number; marginPx: number };\n invalidMarker: { sizePx: number; strokePx: number };\n };\n layout: {\n grid: { stepPx: number; unitPx: number };\n paddingPx: number;\n viewportScale: number;\n /** renamed from capacity → constraints */\n constraints: {\n workspaceDiamAnchors: number;\n quickstashDiamAnchors: number;\n primitiveDiamAnchors: number;\n };\n defaults: { maxQuickstashSlots: number };\n };\n game: {\n snapRadiusPx: number;\n showBorders: boolean;\n hideTouchingBorders: boolean;\n silhouettesBelowPieces: boolean;\n };\n};\n\nexport const CONFIG: Config = {\n color: {\n background: \"#fff7e0ff\",\n bands: {\n silhouette: { fillEven: \"#ffffff\", fillOdd: \"#ffffff\", stroke: \"#b1b1b1\" },\n workspace: { fillEven: \"#ffffff\", fillOdd: \"#ffffff\", stroke: \"#b1b1b1\" }\n },\n completion: { fill: \"#ccffcc\", stroke: \"#13da57\" },\n silhouetteMask: \"#374151\",\n anchors: { invalid: \"#7dd3fc\", valid: \"#475569\" },\n // validFill used here for placed composites\n piece: { draggingFill: \"#8e7cc3\", validFill: \"#8e7cc3\", invalidFill: \"#d55c00\", invalidStroke: \"#dc2626\", selectedStroke: \"#674ea7\", allGreenStroke: \"#86efac\", borderStroke: \"#674ea7\" },\n ui: { light: \"#60a5fa\", dark: \"#1d4ed8\" },\n blueprint: { fill: \"#374151\", selectedStroke: \"#111827\", badgeFill: \"#000000\", labelFill: \"#ffffff\" },\n tangramDecomposition: { stroke: \"#fef2cc\" },\n primitiveColors: [ // from seaborn \"colorblind\" palette, 6 colors, with red omitted\n '#0173b2',\n '#de8f05',\n '#029e73',\n '#cc78bc',\n '#ca9161'\n ]\n },\n opacity: {\n blueprint: 0.6,\n silhouetteMask: 0.25,\n //anchors: { valid: 0.80, invalid: 0.50 },\n anchors: { invalid: 0.0, valid: 0.0 },\n piece: { invalid: 1, dragging: 1, locked: 1, normal: 1 },\n },\n size: {\n stroke: { bandPx: 5, pieceSelectedPx: 5, allGreenStrokePx: 10, pieceBorderPx: 2, tangramDecompositionPx: 1 },\n anchorRadiusPx: { valid: 1.0, invalid: 1.0 },\n badgeFontPx: 16,\n centerBadge: { fractionOfOuterR: 0.15, minPx: 20, marginPx: 4 },\n invalidMarker: { sizePx: 10, strokePx: 4 }\n },\n layout: {\n grid: { stepPx: 20, unitPx: 40 },\n paddingPx: 1,\n viewportScale: 0.8,\n constraints: {\n workspaceDiamAnchors: 10, // num anchors req'd to be on diagonal\n quickstashDiamAnchors: 7, // num anchors req'd to be in single quickstash slot\n primitiveDiamAnchors: 5,\n },\n defaults: { maxQuickstashSlots: 1 }\n },\n game: {\n snapRadiusPx: 15,\n showBorders: false,\n hideTouchingBorders: true,\n silhouettesBelowPieces: true\n }\n};","/**\n * GridApp.tsx - React wrapper for tangram grid display with text input\n *\n * This component displays a grid of tangrams with a text input field\n * and submit button for collecting free-text responses.\n */\n\nimport React, { useRef, useState, useMemo, useEffect } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { JsPsych } from \"jspsych\";\nimport type { Poly, TanKind } from \"../../core/domain/types\";\nimport { CONFIG } from \"../../core/config/config\";\n\nexport interface StartGridTrialParams {\n tangrams: any[];\n n_rows: number;\n n_cols: number;\n prompt_text: string;\n button_text: string;\n show_tangram_decomposition?: boolean;\n usePrimitiveColors?: boolean;\n primitiveColorIndices?: number[];\n onTrialEnd?: (data: any) => void;\n}\n\n/**\n * Start a grid trial by rendering the GridView component\n *\n * REQUIRES: display_element is a valid HTMLElement\n * MODIFIES: display_element (renders React into it)\n * EFFECTS: Creates a React root and renders GridView with the given params\n */\nexport function startGridTrial(\n display_element: HTMLElement,\n params: StartGridTrialParams,\n _jsPsych: JsPsych\n) {\n const root = createRoot(display_element);\n root.render(React.createElement(GridView, { params }));\n return { root, display_element, jsPsych: _jsPsych };\n}\n\ninterface GridViewProps {\n params: StartGridTrialParams;\n}\n\n/**\n * Compute bounding box for an array of polygons\n *\n * REQUIRES: polys is an array of polygons with {x, y} points\n * MODIFIES: nothing\n * EFFECTS: Returns {minX, minY, maxX, maxY, width, height} of bounding box\n */\nfunction computeBounds(polys: Poly[]) {\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n for (const poly of polys) {\n for (const p of poly) {\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n }\n }\n return { minX, minY, maxX, maxY, width: maxX - minX, height: maxY - minY };\n}\n\n/**\n * GridView - Main React component for the grid trial\n *\n * REQUIRES: params contains valid tangram specs and grid dimensions\n * MODIFIES: nothing\n * EFFECTS: Renders a grid of tangrams, text input, and submit button;\n * calls onTrialEnd with response data when submitted\n */\nfunction GridView({ params }: GridViewProps) {\n const {\n tangrams,\n n_rows,\n n_cols,\n prompt_text,\n button_text,\n show_tangram_decomposition = false,\n usePrimitiveColors = false,\n primitiveColorIndices = [0, 1, 2, 3, 4],\n onTrialEnd\n } = params;\n\n const trialStartTime = useRef<number>(Date.now());\n const [response, setResponse] = useState<string>(\"\");\n const [cellSize, setCellSize] = useState<number>(100);\n const controlsRef = useRef<HTMLDivElement>(null);\n\n // Layout constants\n const GRID_GAP = 6;\n const CONTAINER_PADDING = 8;\n const CELL_MARGIN = 0.05; // 5% margin inside cell for tangram\n // jsPsych progress bar: 20px height + 8px top/bottom padding + 18px margin\n const PROGRESS_BAR_HEIGHT = 58;\n // Border width on each cell (from CONFIG.size.stroke.bandPx)\n const CELL_BORDER = CONFIG.size.stroke.bandPx ?? 1;\n // Extra buffer to prevent scrollbars from appearing\n const SAFETY_BUFFER = 8;\n\n // Inject style to override jspsych-content max-width constraint\n useEffect(() => {\n const styleId = \"tangram-grid-jspsych-override\";\n if (!document.getElementById(styleId)) {\n const style = document.createElement(\"style\");\n style.id = styleId;\n style.textContent = `\n .jspsych-content {\n max-width: 100% !important;\n width: 100% !important;\n }\n `;\n document.head.appendChild(style);\n }\n return () => {\n const style = document.getElementById(styleId);\n if (style) style.remove();\n };\n }, []);\n\n // Canonical piece names\n const CANON = new Set([\n \"square\",\n \"smalltriangle\",\n \"parallelogram\",\n \"medtriangle\",\n \"largetriangle\"\n ]);\n\n // Convert TangramSpec to internal format with mask and decomposition\n const processedTangrams = useMemo(() => {\n return tangrams.map((tangramSpec) => {\n const filteredTans = tangramSpec.solutionTans.filter((tan: any) => {\n const tanName = tan.name ?? tan.kind;\n return CANON.has(tanName);\n });\n\n const mask: Poly[] = filteredTans.map((tan: any) => {\n return tan.vertices.map(([x, y]: number[]) => ({\n x: x ?? 0,\n y: -(y ?? 0)\n }));\n });\n\n const primitiveDecomposition = filteredTans.map((tan: any) => ({\n kind: (tan.name ?? tan.kind) as TanKind,\n polygon: tan.vertices.map(([x, y]: number[]) => ({\n x: x ?? 0,\n y: -(y ?? 0)\n }))\n }));\n\n return {\n tangramId: tangramSpec.tangramID,\n mask,\n primitiveDecomposition\n };\n });\n }, [tangrams]);\n\n // Find the largest tangram dimensions to determine scaling\n const maxTangramExtent = useMemo(() => {\n let maxExtent = 0;\n for (const t of processedTangrams) {\n const bounds = computeBounds(t.mask);\n maxExtent = Math.max(maxExtent, bounds.width, bounds.height);\n }\n return maxExtent || 1;\n }, [processedTangrams]);\n\n // Calculate cell size based on available space\n useEffect(() => {\n const calculateCellSize = () => {\n // Use document.documentElement for more accurate viewport size\n const viewportWidth = document.documentElement.clientWidth;\n const viewportHeight = document.documentElement.clientHeight;\n\n // Reserve space for controls (prompt + input row) and progress bar\n const controlsHeight = controlsRef.current?.offsetHeight ?? 70;\n\n // Available space for the grid (subtract progress bar, controls, padding, buffer)\n const availableWidth =\n viewportWidth - CONTAINER_PADDING * 2 - SAFETY_BUFFER;\n const availableHeight =\n viewportHeight - PROGRESS_BAR_HEIGHT - controlsHeight -\n CONTAINER_PADDING * 2 - SAFETY_BUFFER;\n\n // Account for gaps between cells AND borders on each cell\n // Each cell has border on all sides, so total border per cell = 2 * CELL_BORDER\n const totalHorizontalGaps = GRID_GAP * (n_cols - 1);\n const totalVerticalGaps = GRID_GAP * (n_rows - 1);\n const totalHorizontalBorders = CELL_BORDER * 2 * n_cols;\n const totalVerticalBorders = CELL_BORDER * 2 * n_rows;\n\n // Calculate max cell size that fits in available space\n const maxCellWidth =\n (availableWidth - totalHorizontalGaps - totalHorizontalBorders) / n_cols;\n const maxCellHeight =\n (availableHeight - totalVerticalGaps - totalVerticalBorders) / n_rows;\n\n // Use the smaller dimension to keep cells square\n const newCellSize = Math.floor(Math.min(maxCellWidth, maxCellHeight));\n setCellSize(Math.max(newCellSize, 50)); // Minimum 50px\n };\n\n calculateCellSize();\n window.addEventListener(\"resize\", calculateCellSize);\n return () => window.removeEventListener(\"resize\", calculateCellSize);\n }, [n_rows, n_cols]);\n\n // Scale factor to fit largest tangram in cell with margin\n const tangramScale = useMemo(() => {\n const usableSize = cellSize * (1 - CELL_MARGIN * 2);\n return usableSize / maxTangramExtent;\n }, [cellSize, maxTangramExtent]);\n\n // Mapping from TanKind to color index\n const kindToIndex: Record<TanKind, number> = {\n square: 0,\n smalltriangle: 1,\n parallelogram: 2,\n medtriangle: 3,\n largetriangle: 4\n };\n\n // Helper to convert polygon to SVG path\n const pathD = (poly: Poly): string => {\n if (!poly || poly.length === 0) return \"\";\n const moves = poly.map(\n (p, i) => `${i === 0 ? \"M\" : \"L\"} ${p.x} ${p.y}`\n );\n return moves.join(\" \") + \" Z\";\n };\n\n // Handle submit\n const handleSubmit = () => {\n const rt = Date.now() - trialStartTime.current;\n const trialData = {\n response,\n rt,\n n_rows,\n n_cols,\n tangram_ids: processedTangrams.map((t) => t.tangramId),\n show_tangram_decomposition,\n use_primitive_colors: usePrimitiveColors,\n primitive_color_indices: primitiveColorIndices\n };\n if (onTrialEnd) {\n onTrialEnd(trialData);\n }\n };\n\n // Render a single tangram SVG\n const renderTangram = (\n tangramData: (typeof processedTangrams)[0],\n index: number\n ) => {\n const { mask, primitiveDecomposition } = tangramData;\n\n if (show_tangram_decomposition) {\n // Show individual primitives with optional coloring\n // Scale each primitive and fit to viewport while preserving relative positions\n const scaledPrimitives = primitiveDecomposition.map(\n (prim: { kind: TanKind; polygon: Poly }) => {\n const scaledPoly = prim.polygon.map((p: { x: number; y: number }) => ({\n x: p.x * tangramScale,\n y: p.y * tangramScale\n }));\n return { kind: prim.kind, polygon: scaledPoly };\n }\n );\n\n // Find bounds of all primitives together\n let minX = Infinity,\n minY = Infinity,\n maxX = -Infinity,\n maxY = -Infinity;\n for (const prim of scaledPrimitives) {\n for (const p of prim.polygon) {\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n }\n }\n\n // Compute translation to center all primitives together in viewport\n const width = maxX - minX;\n const height = maxY - minY;\n const tx = cellSize / 2 - (minX + width / 2);\n const ty = cellSize / 2 - (minY + height / 2);\n\n const translatedPrimitives = scaledPrimitives.map(\n (prim: { kind: TanKind; polygon: Poly }) => ({\n kind: prim.kind,\n polygon: prim.polygon.map((p: { x: number; y: number }) => ({\n x: p.x + tx,\n y: p.y + ty\n }))\n })\n );\n\n return (\n <svg\n key={index}\n width={cellSize}\n height={cellSize}\n viewBox={`0 0 ${cellSize} ${cellSize}`}\n style={{\n display: \"block\",\n background: CONFIG.color.bands.silhouette.fillEven,\n border: `${CONFIG.size.stroke.bandPx}px solid ${CONFIG.color.bands.silhouette.stroke}`,\n borderRadius: \"8px\"\n }}\n >\n {translatedPrimitives.map(\n (prim: { kind: TanKind; polygon: Poly }, i: number) => {\n let fillColor: string;\n\n if (usePrimitiveColors) {\n // Use primitive colors: map piece type to color via primitiveColorIndices\n const primitiveIndex = kindToIndex[prim.kind];\n if (\n primitiveIndex !== undefined &&\n primitiveColorIndices[primitiveIndex] !== undefined\n ) {\n const colorIndex = primitiveColorIndices[primitiveIndex];\n const color = CONFIG.color.primitiveColors[colorIndex];\n fillColor = color || CONFIG.color.piece.validFill;\n } else {\n fillColor = CONFIG.color.piece.validFill;\n }\n } else {\n // Use default piece color when not using primitive colors\n fillColor = CONFIG.color.piece.validFill;\n }\n\n return (\n <path\n key={`prim-${i}`}\n d={pathD(prim.polygon)}\n fill={fillColor}\n opacity={CONFIG.opacity.piece.normal}\n stroke={CONFIG.color.tangramDecomposition.stroke}\n strokeWidth={CONFIG.size.stroke.tangramDecompositionPx}\n />\n );\n }\n )}\n </svg>\n );\n } else {\n // Show as silhouette (merged shape) - scale and center\n const scaledMask = mask.map((poly) =>\n poly.map((p) => ({\n x: p.x * tangramScale,\n y: p.y * tangramScale\n }))\n );\n\n // Find bounds\n let minX = Infinity,\n minY = Infinity,\n maxX = -Infinity,\n maxY = -Infinity;\n for (const poly of scaledMask) {\n for (const p of poly) {\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n }\n }\n\n // Center in viewport\n const width = maxX - minX;\n const height = maxY - minY;\n const tx = cellSize / 2 - (minX + width / 2);\n const ty = cellSize / 2 - (minY + height / 2);\n\n const placedMask = scaledMask.map((poly) =>\n poly.map((p) => ({ x: p.x + tx, y: p.y + ty }))\n );\n\n return (\n <svg\n key={index}\n width={cellSize}\n height={cellSize}\n viewBox={`0 0 ${cellSize} ${cellSize}`}\n style={{\n display: \"block\",\n background: CONFIG.color.bands.silhouette.fillEven,\n border: `${CONFIG.size.stroke.bandPx}px solid ${CONFIG.color.bands.silhouette.stroke}`,\n borderRadius: \"8px\"\n }}\n >\n {placedMask.map((poly, i) => (\n <path\n key={`sil-${i}`}\n d={pathD(poly)}\n fill={CONFIG.color.piece.validFill}\n opacity={CONFIG.opacity.piece.normal}\n stroke=\"none\"\n />\n ))}\n </svg>\n );\n }\n };\n\n const isSubmitDisabled = response.trim().length === 0;\n\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n background: CONFIG.color.background,\n width: \"100%\",\n height: `calc(100vh - ${PROGRESS_BAR_HEIGHT}px)`,\n overflow: \"hidden\",\n fontFamily: \"Roboto, sans-serif\",\n boxSizing: \"border-box\",\n padding: `${CONTAINER_PADDING}px`\n }}\n >\n {/* Grid of tangrams - takes up available space */}\n <div\n style={{\n flex: \"1 1 auto\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: 0\n }}\n >\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(${n_cols}, ${cellSize}px)`,\n gridTemplateRows: `repeat(${n_rows}, ${cellSize}px)`,\n gap: `${GRID_GAP}px`\n }}\n >\n {processedTangrams.slice(0, n_rows * n_cols).map((t, i) =>\n renderTangram(t, i)\n )}\n </div>\n </div>\n\n {/* Controls section - fixed height at bottom */}\n <div\n ref={controlsRef}\n style={{\n flex: \"0 0 auto\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: \"4px\",\n paddingTop: \"4px\"\n }}\n >\n {/* Prompt text */}\n <div\n style={{\n fontSize: \"14px\",\n textAlign: \"center\",\n maxWidth: \"90vw\"\n }}\n >\n {prompt_text}\n </div>\n\n {/* Text input and submit button side by side */}\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"12px\"\n }}\n >\n <input\n type=\"text\"\n value={response}\n onChange={(e) => setResponse(e.target.value)}\n style={{\n width: \"min(400px, 50vw)\",\n padding: \"6px 10px\",\n fontSize: \"14px\",\n borderRadius: \"6px\",\n border: \"2px solid #ccc\",\n fontFamily: \"inherit\",\n boxSizing: \"border-box\"\n }}\n placeholder=\"Type your response here...\"\n />\n\n <button\n className=\"jspsych-btn\"\n onClick={handleSubmit}\n disabled={isSubmitDisabled}\n style={{\n padding: \"6px 16px\",\n fontSize: \"13px\",\n cursor: isSubmitDisabled ? \"not-allowed\" : \"pointer\",\n opacity: isSubmitDisabled ? 0.5 : 1,\n flexShrink: 0\n }}\n >\n {button_text}\n </button>\n </div>\n </div>\n </div>\n );\n}\n","import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\nimport { startGridTrial, StartGridTrialParams } from \"./GridApp\";\n\nconst info = {\n name: \"tangram-grid\",\n version: \"1.0.0\",\n parameters: {\n /** Array of tangram specifications to display in the grid */\n tangrams: {\n type: ParameterType.COMPLEX,\n default: undefined,\n description: \"Array of TangramSpec objects to display in the grid\"\n },\n /** Number of rows in the grid */\n n_rows: {\n type: ParameterType.INT,\n default: 1,\n description: \"Number of rows in the tangram grid\"\n },\n /** Number of columns in the grid */\n n_cols: {\n type: ParameterType.INT,\n default: 1,\n description: \"Number of columns in the tangram grid\"\n },\n /** Prompt text displayed above the text input */\n prompt_text: {\n type: ParameterType.STRING,\n default: \"\",\n description: \"Text displayed above the text input field\"\n },\n /** Label for the submit button */\n button_text: {\n type: ParameterType.STRING,\n default: \"Submit\",\n description: \"Text displayed on the submit button\"\n },\n /** Whether to show tangrams decomposed into primitives */\n show_tangram_decomposition: {\n type: ParameterType.BOOL,\n default: false,\n description:\n \"Whether to show tangrams decomposed into individual primitives\"\n },\n /** Whether to use distinct colors for each primitive type */\n use_primitive_colors: {\n type: ParameterType.BOOL,\n default: false,\n description:\n \"Whether each primitive shape type should have its own distinct color\"\n },\n /** Indices mapping primitives to colors */\n primitive_color_indices: {\n type: ParameterType.OBJECT,\n default: [0, 1, 2, 3, 4],\n description:\n \"Array of 5 integers indexing into primitiveColors array, mapping \" +\n \"[square, smalltriangle, parallelogram, medtriangle, largetriangle] \" +\n \"to colors\"\n },\n /** Callback fired when trial ends */\n onTrialEnd: {\n type: ParameterType.FUNCTION,\n default: undefined,\n description: \"Callback when trial completes with full data\"\n }\n },\n data: {\n /** The text response entered by the participant */\n response: {\n type: ParameterType.STRING,\n description: \"The text response entered by the participant\"\n },\n /** Reaction time from trial start to submit button click */\n rt: {\n type: ParameterType.INT,\n description: \"Time in milliseconds from trial start to submit\"\n }\n },\n citations: \"\"\n};\n\ntype Info = typeof info;\n\n/**\n * **tangram-grid**\n *\n * A jsPsych plugin that displays a grid of tangrams with a text input field\n * and submit button for collecting free-text responses.\n *\n * @author Sean Paul Anderson & Justin Yang\n * @see {@link https://github.com/cogtoolslab/tangram_construction.git}\n */\nclass TangramGridPlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n /**\n * Launches the trial by invoking startGridTrial with the display element,\n * parameters, and jsPsych instance.\n *\n * REQUIRES: display_element is a valid HTMLElement, trial contains valid\n * parameters\n * MODIFIES: display_element (renders React component)\n * EFFECTS: Starts the grid trial and handles cleanup on completion\n */\n trial(display_element: HTMLElement, trial: TrialType<Info>) {\n // Wrap onTrialEnd to handle React cleanup and jsPsych trial completion\n const wrappedOnTrialEnd = (data: any) => {\n // Call user-provided callback if exists\n if (trial.onTrialEnd) {\n trial.onTrialEnd(data);\n }\n\n // Clean up React first (before clearing DOM)\n const reactContext = (display_element as any).__reactContext;\n if (reactContext?.root) {\n reactContext.root.unmount();\n }\n\n // Clear display after React cleanup\n display_element.innerHTML = \"\";\n\n // Finish jsPsych trial with data\n this.jsPsych.finishTrial(data);\n };\n\n // Create parameter object for wrapper\n const params: StartGridTrialParams = {\n tangrams: trial.tangrams,\n n_rows: trial.n_rows,\n n_cols: trial.n_cols,\n prompt_text: trial.prompt_text,\n button_text: trial.button_text,\n show_tangram_decomposition: trial.show_tangram_decomposition,\n usePrimitiveColors: trial.use_primitive_colors,\n primitiveColorIndices: trial.primitive_color_indices,\n onTrialEnd: wrappedOnTrialEnd\n };\n\n // Use React wrapper to start the trial\n const { root, display_element: element, jsPsych } = startGridTrial(\n display_element,\n params,\n this.jsPsych\n );\n\n // Store React context for cleanup\n (element as any).__reactContext = { root, jsPsych };\n }\n}\n\nexport default TangramGridPlugin;\n"],"names":["createRoot","useRef","useState","useEffect","useMemo","ParameterType"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/core/config/config.ts","../../src/plugins/tangram-grid/GridApp.tsx","../../src/plugins/tangram-grid/index.ts"],"sourcesContent":["// src/core/config/config.ts\nexport type Config = {\n color: {\n background: string;\n bands: {\n silhouette: { fillEven: string; fillOdd: string; stroke: string };\n workspace: { fillEven: string; fillOdd: string; stroke: string };\n };\n completion: { fill: string; stroke: string };\n silhouetteMask: string;\n anchors: { invalid: string; valid: string };\n piece: { draggingFill: string; validFill: string; invalidFill: string; invalidStroke: string; selectedStroke: string; allGreenStroke: string; borderStroke: string };\n ui: { light: string; dark: string };\n blueprint: { fill: string; selectedStroke: string; badgeFill: string; labelFill: string };\n tangramDecomposition: { stroke: string };\n primitiveColors: string[];\n };\n opacity: {\n blueprint: number;\n silhouetteMask: number;\n anchors: { invalid: number; valid: number };\n piece: { invalid: number; dragging: number; locked: number; normal: number };\n };\n size: {\n stroke: { bandPx: number; pieceSelectedPx: number; allGreenStrokePx: number; pieceBorderPx: number; tangramDecompositionPx: number };\n anchorRadiusPx: { valid: number; invalid: number };\n badgeFontPx: number;\n centerBadge: { fractionOfOuterR: number; minPx: number; marginPx: number };\n invalidMarker: { sizePx: number; strokePx: number };\n };\n layout: {\n grid: { stepPx: number; unitPx: number };\n paddingPx: number;\n viewportScale: number;\n /** renamed from capacity → constraints */\n constraints: {\n workspaceDiamAnchors: number;\n quickstashDiamAnchors: number;\n primitiveDiamAnchors: number;\n };\n defaults: { maxQuickstashSlots: number };\n };\n game: {\n snapRadiusPx: number;\n showBorders: boolean;\n hideTouchingBorders: boolean;\n silhouettesBelowPieces: boolean;\n };\n};\n\nexport const CONFIG: Config = {\n color: {\n background: \"#fff7e0ff\",\n bands: {\n silhouette: { fillEven: \"#ffffff\", fillOdd: \"#ffffff\", stroke: \"#b1b1b1\" },\n workspace: { fillEven: \"#ffffff\", fillOdd: \"#ffffff\", stroke: \"#b1b1b1\" }\n },\n completion: { fill: \"#ccffcc\", stroke: \"#13da57\" },\n silhouetteMask: \"#374151\",\n anchors: { invalid: \"#7dd3fc\", valid: \"#475569\" },\n // validFill used here for placed composites\n piece: { draggingFill: \"#8e7cc3\", validFill: \"#8e7cc3\", invalidFill: \"#d55c00\", invalidStroke: \"#dc2626\", selectedStroke: \"#674ea7\", allGreenStroke: \"#86efac\", borderStroke: \"#674ea7\" },\n ui: { light: \"#60a5fa\", dark: \"#1d4ed8\" },\n blueprint: { fill: \"#374151\", selectedStroke: \"#111827\", badgeFill: \"#000000\", labelFill: \"#ffffff\" },\n tangramDecomposition: { stroke: \"#fef2cc\" },\n primitiveColors: [ // from seaborn \"colorblind\" palette, 6 colors, with red omitted\n '#0173b2',\n '#de8f05',\n '#029e73',\n '#cc78bc',\n '#ca9161'\n ]\n },\n opacity: {\n blueprint: 0.6,\n silhouetteMask: 0.25,\n //anchors: { valid: 0.80, invalid: 0.50 },\n anchors: { invalid: 0.0, valid: 0.0 },\n piece: { invalid: 1, dragging: 1, locked: 1, normal: 1 },\n },\n size: {\n stroke: { bandPx: 5, pieceSelectedPx: 5, allGreenStrokePx: 10, pieceBorderPx: 2, tangramDecompositionPx: 1 },\n anchorRadiusPx: { valid: 1.0, invalid: 1.0 },\n badgeFontPx: 16,\n centerBadge: { fractionOfOuterR: 0.15, minPx: 20, marginPx: 4 },\n invalidMarker: { sizePx: 10, strokePx: 4 }\n },\n layout: {\n grid: { stepPx: 20, unitPx: 40 },\n paddingPx: 1,\n viewportScale: 0.8,\n constraints: {\n workspaceDiamAnchors: 10, // num anchors req'd to be on diagonal\n quickstashDiamAnchors: 7, // num anchors req'd to be in single quickstash slot\n primitiveDiamAnchors: 5,\n },\n defaults: { maxQuickstashSlots: 1 }\n },\n game: {\n snapRadiusPx: 15,\n showBorders: false,\n hideTouchingBorders: true,\n silhouettesBelowPieces: true\n }\n};","/**\n * GridApp.tsx - React wrapper for tangram grid display with text input\n *\n * This component displays a grid of tangrams with a text input field\n * and submit button for collecting free-text responses.\n */\n\nimport React, { useRef, useState, useMemo, useEffect } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { JsPsych } from \"jspsych\";\nimport type { Poly, TanKind } from \"../../core/domain/types\";\nimport { CONFIG } from \"../../core/config/config\";\n\nexport interface StartGridTrialParams {\n tangrams: any[];\n n_rows: number;\n n_cols: number;\n prompt_text: string;\n button_text: string;\n show_tangram_decomposition?: boolean;\n usePrimitiveColors?: boolean;\n primitiveColorIndices?: number[];\n onTrialEnd?: (data: any) => void;\n}\n\n/**\n * Start a grid trial by rendering the GridView component\n *\n * REQUIRES: display_element is a valid HTMLElement\n * MODIFIES: display_element (renders React into it)\n * EFFECTS: Creates a React root and renders GridView with the given params\n */\nexport function startGridTrial(\n display_element: HTMLElement,\n params: StartGridTrialParams,\n _jsPsych: JsPsych\n) {\n const root = createRoot(display_element);\n root.render(React.createElement(GridView, { params }));\n return { root, display_element, jsPsych: _jsPsych };\n}\n\ninterface GridViewProps {\n params: StartGridTrialParams;\n}\n\n/**\n * Compute bounding box for an array of polygons\n *\n * REQUIRES: polys is an array of polygons with {x, y} points\n * MODIFIES: nothing\n * EFFECTS: Returns {minX, minY, maxX, maxY, width, height} of bounding box\n */\nfunction computeBounds(polys: Poly[]) {\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n for (const poly of polys) {\n for (const p of poly) {\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n }\n }\n return { minX, minY, maxX, maxY, width: maxX - minX, height: maxY - minY };\n}\n\n/**\n * GridView - Main React component for the grid trial\n *\n * REQUIRES: params contains valid tangram specs and grid dimensions\n * MODIFIES: nothing\n * EFFECTS: Renders a grid of tangrams, text input, and submit button;\n * calls onTrialEnd with response data when submitted\n */\nfunction GridView({ params }: GridViewProps) {\n const {\n tangrams,\n n_rows,\n n_cols,\n prompt_text,\n button_text,\n show_tangram_decomposition = false,\n usePrimitiveColors = false,\n primitiveColorIndices = [0, 1, 2, 3, 4],\n onTrialEnd\n } = params;\n\n // Extract non-callback params for trial data\n const { onTrialEnd: _, ...trialParams } = params;\n\n const trialStartTime = useRef<number>(Date.now());\n const [response, setResponse] = useState<string>(\"\");\n const [cellSize, setCellSize] = useState<number>(100);\n const controlsRef = useRef<HTMLDivElement>(null);\n\n // Layout constants\n const GRID_GAP = 6;\n const CONTAINER_PADDING = 8;\n const CELL_MARGIN = 0.05; // 5% margin inside cell for tangram\n // jsPsych progress bar: 20px height + 8px top/bottom padding + 18px margin\n const PROGRESS_BAR_HEIGHT = 58;\n // Border width on each cell (from CONFIG.size.stroke.bandPx)\n const CELL_BORDER = CONFIG.size.stroke.bandPx ?? 1;\n // Extra buffer to prevent scrollbars from appearing\n const SAFETY_BUFFER = 8;\n\n // Inject style to override jspsych-content max-width constraint\n useEffect(() => {\n const styleId = \"tangram-grid-jspsych-override\";\n if (!document.getElementById(styleId)) {\n const style = document.createElement(\"style\");\n style.id = styleId;\n style.textContent = `\n .jspsych-content {\n max-width: 100% !important;\n width: 100% !important;\n }\n `;\n document.head.appendChild(style);\n }\n return () => {\n const style = document.getElementById(styleId);\n if (style) style.remove();\n };\n }, []);\n\n // Canonical piece names\n const CANON = new Set([\n \"square\",\n \"smalltriangle\",\n \"parallelogram\",\n \"medtriangle\",\n \"largetriangle\"\n ]);\n\n // Convert TangramSpec to internal format with mask and decomposition\n const processedTangrams = useMemo(() => {\n return tangrams.map((tangramSpec) => {\n const filteredTans = tangramSpec.solutionTans.filter((tan: any) => {\n const tanName = tan.name ?? tan.kind;\n return CANON.has(tanName);\n });\n\n const mask: Poly[] = filteredTans.map((tan: any) => {\n return tan.vertices.map(([x, y]: number[]) => ({\n x: x ?? 0,\n y: -(y ?? 0)\n }));\n });\n\n const primitiveDecomposition = filteredTans.map((tan: any) => ({\n kind: (tan.name ?? tan.kind) as TanKind,\n polygon: tan.vertices.map(([x, y]: number[]) => ({\n x: x ?? 0,\n y: -(y ?? 0)\n }))\n }));\n\n return {\n tangramId: tangramSpec.tangramID,\n mask,\n primitiveDecomposition\n };\n });\n }, [tangrams]);\n\n // Find the largest tangram dimensions to determine scaling\n const maxTangramExtent = useMemo(() => {\n let maxExtent = 0;\n for (const t of processedTangrams) {\n const bounds = computeBounds(t.mask);\n maxExtent = Math.max(maxExtent, bounds.width, bounds.height);\n }\n return maxExtent || 1;\n }, [processedTangrams]);\n\n // Calculate cell size based on available space\n useEffect(() => {\n const calculateCellSize = () => {\n // Use document.documentElement for more accurate viewport size\n const viewportWidth = document.documentElement.clientWidth;\n const viewportHeight = document.documentElement.clientHeight;\n\n // Reserve space for controls (prompt + input row) and progress bar\n const controlsHeight = controlsRef.current?.offsetHeight ?? 70;\n\n // Available space for the grid (subtract progress bar, controls, padding, buffer)\n const availableWidth =\n viewportWidth - CONTAINER_PADDING * 2 - SAFETY_BUFFER;\n const availableHeight =\n viewportHeight - PROGRESS_BAR_HEIGHT - controlsHeight -\n CONTAINER_PADDING * 2 - SAFETY_BUFFER;\n\n // Account for gaps between cells AND borders on each cell\n // Each cell has border on all sides, so total border per cell = 2 * CELL_BORDER\n const totalHorizontalGaps = GRID_GAP * (n_cols - 1);\n const totalVerticalGaps = GRID_GAP * (n_rows - 1);\n const totalHorizontalBorders = CELL_BORDER * 2 * n_cols;\n const totalVerticalBorders = CELL_BORDER * 2 * n_rows;\n\n // Calculate max cell size that fits in available space\n const maxCellWidth =\n (availableWidth - totalHorizontalGaps - totalHorizontalBorders) / n_cols;\n const maxCellHeight =\n (availableHeight - totalVerticalGaps - totalVerticalBorders) / n_rows;\n\n // Use the smaller dimension to keep cells square\n const newCellSize = Math.floor(Math.min(maxCellWidth, maxCellHeight));\n setCellSize(Math.max(newCellSize, 50)); // Minimum 50px\n };\n\n calculateCellSize();\n window.addEventListener(\"resize\", calculateCellSize);\n return () => window.removeEventListener(\"resize\", calculateCellSize);\n }, [n_rows, n_cols]);\n\n // Scale factor to fit largest tangram in cell with margin\n const tangramScale = useMemo(() => {\n const usableSize = cellSize * (1 - CELL_MARGIN * 2);\n return usableSize / maxTangramExtent;\n }, [cellSize, maxTangramExtent]);\n\n // Mapping from TanKind to color index\n const kindToIndex: Record<TanKind, number> = {\n square: 0,\n smalltriangle: 1,\n parallelogram: 2,\n medtriangle: 3,\n largetriangle: 4\n };\n\n // Helper to convert polygon to SVG path\n const pathD = (poly: Poly): string => {\n if (!poly || poly.length === 0) return \"\";\n const moves = poly.map(\n (p, i) => `${i === 0 ? \"M\" : \"L\"} ${p.x} ${p.y}`\n );\n return moves.join(\" \") + \" Z\";\n };\n\n // Handle submit\n const handleSubmit = () => {\n const rt = Date.now() - trialStartTime.current;\n const trialData = {\n response,\n rt,\n trialParams: {\n ...trialParams,\n tangramIDs: processedTangrams.map((t) => t.tangramId)\n }\n };\n if (onTrialEnd) {\n onTrialEnd(trialData);\n }\n };\n\n // Render a single tangram SVG\n const renderTangram = (\n tangramData: (typeof processedTangrams)[0],\n index: number\n ) => {\n const { mask, primitiveDecomposition } = tangramData;\n\n if (show_tangram_decomposition) {\n // Show individual primitives with optional coloring\n // Scale each primitive and fit to viewport while preserving relative positions\n const scaledPrimitives = primitiveDecomposition.map(\n (prim: { kind: TanKind; polygon: Poly }) => {\n const scaledPoly = prim.polygon.map((p: { x: number; y: number }) => ({\n x: p.x * tangramScale,\n y: p.y * tangramScale\n }));\n return { kind: prim.kind, polygon: scaledPoly };\n }\n );\n\n // Find bounds of all primitives together\n let minX = Infinity,\n minY = Infinity,\n maxX = -Infinity,\n maxY = -Infinity;\n for (const prim of scaledPrimitives) {\n for (const p of prim.polygon) {\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n }\n }\n\n // Compute translation to center all primitives together in viewport\n const width = maxX - minX;\n const height = maxY - minY;\n const tx = cellSize / 2 - (minX + width / 2);\n const ty = cellSize / 2 - (minY + height / 2);\n\n const translatedPrimitives = scaledPrimitives.map(\n (prim: { kind: TanKind; polygon: Poly }) => ({\n kind: prim.kind,\n polygon: prim.polygon.map((p: { x: number; y: number }) => ({\n x: p.x + tx,\n y: p.y + ty\n }))\n })\n );\n\n return (\n <svg\n key={index}\n width={cellSize}\n height={cellSize}\n viewBox={`0 0 ${cellSize} ${cellSize}`}\n style={{\n display: \"block\",\n background: CONFIG.color.bands.silhouette.fillEven,\n border: `${CONFIG.size.stroke.bandPx}px solid ${CONFIG.color.bands.silhouette.stroke}`,\n borderRadius: \"8px\"\n }}\n >\n {translatedPrimitives.map(\n (prim: { kind: TanKind; polygon: Poly }, i: number) => {\n let fillColor: string;\n\n if (usePrimitiveColors) {\n // Use primitive colors: map piece type to color via primitiveColorIndices\n const primitiveIndex = kindToIndex[prim.kind];\n if (\n primitiveIndex !== undefined &&\n primitiveColorIndices[primitiveIndex] !== undefined\n ) {\n const colorIndex = primitiveColorIndices[primitiveIndex];\n const color = CONFIG.color.primitiveColors[colorIndex];\n fillColor = color || CONFIG.color.piece.validFill;\n } else {\n fillColor = CONFIG.color.piece.validFill;\n }\n } else {\n // Use default piece color when not using primitive colors\n fillColor = CONFIG.color.piece.validFill;\n }\n\n return (\n <path\n key={`prim-${i}`}\n d={pathD(prim.polygon)}\n fill={fillColor}\n opacity={CONFIG.opacity.piece.normal}\n stroke={CONFIG.color.tangramDecomposition.stroke}\n strokeWidth={CONFIG.size.stroke.tangramDecompositionPx}\n />\n );\n }\n )}\n </svg>\n );\n } else {\n // Show as silhouette (merged shape) - scale and center\n const scaledMask = mask.map((poly) =>\n poly.map((p) => ({\n x: p.x * tangramScale,\n y: p.y * tangramScale\n }))\n );\n\n // Find bounds\n let minX = Infinity,\n minY = Infinity,\n maxX = -Infinity,\n maxY = -Infinity;\n for (const poly of scaledMask) {\n for (const p of poly) {\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n }\n }\n\n // Center in viewport\n const width = maxX - minX;\n const height = maxY - minY;\n const tx = cellSize / 2 - (minX + width / 2);\n const ty = cellSize / 2 - (minY + height / 2);\n\n const placedMask = scaledMask.map((poly) =>\n poly.map((p) => ({ x: p.x + tx, y: p.y + ty }))\n );\n\n return (\n <svg\n key={index}\n width={cellSize}\n height={cellSize}\n viewBox={`0 0 ${cellSize} ${cellSize}`}\n style={{\n display: \"block\",\n background: CONFIG.color.bands.silhouette.fillEven,\n border: `${CONFIG.size.stroke.bandPx}px solid ${CONFIG.color.bands.silhouette.stroke}`,\n borderRadius: \"8px\"\n }}\n >\n {placedMask.map((poly, i) => (\n <path\n key={`sil-${i}`}\n d={pathD(poly)}\n fill={CONFIG.color.piece.validFill}\n opacity={CONFIG.opacity.piece.normal}\n stroke=\"none\"\n />\n ))}\n </svg>\n );\n }\n };\n\n const isSubmitDisabled = response.trim().length === 0;\n\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n background: CONFIG.color.background,\n width: \"100%\",\n height: `calc(100vh - ${PROGRESS_BAR_HEIGHT}px)`,\n overflow: \"hidden\",\n fontFamily: \"Roboto, sans-serif\",\n boxSizing: \"border-box\",\n padding: `${CONTAINER_PADDING}px`\n }}\n >\n {/* Grid of tangrams - takes up available space */}\n <div\n style={{\n flex: \"1 1 auto\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: 0\n }}\n >\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(${n_cols}, ${cellSize}px)`,\n gridTemplateRows: `repeat(${n_rows}, ${cellSize}px)`,\n gap: `${GRID_GAP}px`\n }}\n >\n {processedTangrams.slice(0, n_rows * n_cols).map((t, i) =>\n renderTangram(t, i)\n )}\n </div>\n </div>\n\n {/* Controls section - fixed height at bottom */}\n <div\n ref={controlsRef}\n style={{\n flex: \"0 0 auto\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: \"4px\",\n paddingTop: \"4px\"\n }}\n >\n {/* Prompt text */}\n <div\n style={{\n fontSize: \"14px\",\n textAlign: \"center\",\n maxWidth: \"90vw\"\n }}\n >\n {prompt_text}\n </div>\n\n {/* Text input and submit button side by side */}\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"12px\"\n }}\n >\n <input\n type=\"text\"\n value={response}\n onChange={(e) => setResponse(e.target.value)}\n style={{\n width: \"min(400px, 50vw)\",\n padding: \"6px 10px\",\n fontSize: \"14px\",\n borderRadius: \"6px\",\n border: \"2px solid #ccc\",\n fontFamily: \"inherit\",\n boxSizing: \"border-box\"\n }}\n placeholder=\"Type your response here...\"\n />\n\n <button\n className=\"jspsych-btn\"\n onClick={handleSubmit}\n disabled={isSubmitDisabled}\n style={{\n padding: \"6px 16px\",\n fontSize: \"13px\",\n cursor: isSubmitDisabled ? \"not-allowed\" : \"pointer\",\n opacity: isSubmitDisabled ? 0.5 : 1,\n flexShrink: 0\n }}\n >\n {button_text}\n </button>\n </div>\n </div>\n </div>\n );\n}\n","import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\nimport { startGridTrial, StartGridTrialParams } from \"./GridApp\";\n\nconst info = {\n name: \"tangram-grid\",\n version: \"1.0.0\",\n parameters: {\n /** Array of tangram specifications to display in the grid */\n tangrams: {\n type: ParameterType.COMPLEX,\n default: undefined,\n description: \"Array of TangramSpec objects to display in the grid\"\n },\n /** Number of rows in the grid */\n n_rows: {\n type: ParameterType.INT,\n default: 1,\n description: \"Number of rows in the tangram grid\"\n },\n /** Number of columns in the grid */\n n_cols: {\n type: ParameterType.INT,\n default: 1,\n description: \"Number of columns in the tangram grid\"\n },\n /** Prompt text displayed above the text input */\n prompt_text: {\n type: ParameterType.STRING,\n default: \"\",\n description: \"Text displayed above the text input field\"\n },\n /** Label for the submit button */\n button_text: {\n type: ParameterType.STRING,\n default: \"Submit\",\n description: \"Text displayed on the submit button\"\n },\n /** Whether to show tangrams decomposed into primitives */\n show_tangram_decomposition: {\n type: ParameterType.BOOL,\n default: false,\n description:\n \"Whether to show tangrams decomposed into individual primitives\"\n },\n /** Whether to use distinct colors for each primitive type */\n use_primitive_colors: {\n type: ParameterType.BOOL,\n default: false,\n description:\n \"Whether each primitive shape type should have its own distinct color\"\n },\n /** Indices mapping primitives to colors */\n primitive_color_indices: {\n type: ParameterType.OBJECT,\n default: [0, 1, 2, 3, 4],\n description:\n \"Array of 5 integers indexing into primitiveColors array, mapping \" +\n \"[square, smalltriangle, parallelogram, medtriangle, largetriangle] \" +\n \"to colors\"\n },\n /** Callback fired when trial ends */\n onTrialEnd: {\n type: ParameterType.FUNCTION,\n default: undefined,\n description: \"Callback when trial completes with full data\"\n }\n },\n data: {\n /** The text response entered by the participant */\n response: {\n type: ParameterType.STRING,\n description: \"The text response entered by the participant\"\n },\n /** Reaction time from trial start to submit button click */\n rt: {\n type: ParameterType.INT,\n description: \"Time in milliseconds from trial start to submit\"\n }\n },\n citations: \"\"\n};\n\ntype Info = typeof info;\n\n/**\n * **tangram-grid**\n *\n * A jsPsych plugin that displays a grid of tangrams with a text input field\n * and submit button for collecting free-text responses.\n *\n * @author Sean Paul Anderson & Justin Yang\n * @see {@link https://github.com/cogtoolslab/tangram_construction.git}\n */\nclass TangramGridPlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n /**\n * Launches the trial by invoking startGridTrial with the display element,\n * parameters, and jsPsych instance.\n *\n * REQUIRES: display_element is a valid HTMLElement, trial contains valid\n * parameters\n * MODIFIES: display_element (renders React component)\n * EFFECTS: Starts the grid trial and handles cleanup on completion\n */\n trial(display_element: HTMLElement, trial: TrialType<Info>) {\n // Wrap onTrialEnd to handle React cleanup and jsPsych trial completion\n const wrappedOnTrialEnd = (data: any) => {\n // Call user-provided callback if exists\n if (trial.onTrialEnd) {\n trial.onTrialEnd(data);\n }\n\n // Clean up React first (before clearing DOM)\n const reactContext = (display_element as any).__reactContext;\n if (reactContext?.root) {\n reactContext.root.unmount();\n }\n\n // Clear display after React cleanup\n display_element.innerHTML = \"\";\n\n // Finish jsPsych trial with data\n this.jsPsych.finishTrial(data);\n };\n\n // Create parameter object for wrapper\n const params: StartGridTrialParams = {\n tangrams: trial.tangrams,\n n_rows: trial.n_rows,\n n_cols: trial.n_cols,\n prompt_text: trial.prompt_text,\n button_text: trial.button_text,\n show_tangram_decomposition: trial.show_tangram_decomposition,\n usePrimitiveColors: trial.use_primitive_colors,\n primitiveColorIndices: trial.primitive_color_indices,\n onTrialEnd: wrappedOnTrialEnd\n };\n\n // Use React wrapper to start the trial\n const { root, display_element: element, jsPsych } = startGridTrial(\n display_element,\n params,\n this.jsPsych\n );\n\n // Store React context for cleanup\n (element as any).__reactContext = { root, jsPsych };\n }\n}\n\nexport default TangramGridPlugin;\n"],"names":["createRoot","useRef","useState","useEffect","useMemo","ParameterType"],"mappings":";;;;;;AAkDO,MAAM,MAAA,GAAiB;AAAA,EAC5B,KAAA,EAAO;AAAA,IACL,UAAA,EAAY,WAAA;AAAA,IACZ,KAAA,EAAO;AAAA,MACL,YAAY,EAAE,QAAA,EAAU,WAA+B,QAAQ,SAAA,EAEjE,CAAA;AAAA,IAGkD;AAAA,IAElD,KAAA,EAAO,EAA2B,WAAW,SAA2I,CAAA;AAAA,IAGxL,oBAAA,EAAsB,EAAE,MAAA,EAAQ,SAAA,EAAU;AAAA,IAC1C,eAAA,EAAiB;AAAA;AAAA,MACf,SAAA;AAAA,MACA,SAAA;AAAA,MACA,SAAA;AAAA,MACA,SAAA;AAAA,MACA;AAAA;AACJ,GACA;AAAA,EACA,OAAA,EAAS;AAAA,IAKP,KAAA,EAAO,EAAsC,MAAA,EAAQ,CAAA;AAAE,GACzD;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,MAAA,EAAQ,EAAE,MAAA,EAAQ,CAAA,EAA+D,sBAAA,EAAwB,CAAA,EAK3G,CAkBF,CAAA;;ACxEO,SAAS,cAAA,CACd,eAAA,EACA,MAAA,EACA,QAAA,EACA;AACA,EAAA,MAAM,IAAA,GAAOA,kBAAW,eAAe,CAAA;AACvC,EAAA,IAAA,CAAK,OAAO,KAAA,CAAM,aAAA,CAAc,UAAU,EAAE,MAAA,EAAQ,CAAC,CAAA;AACrD,EAAA,OAAO,EAAE,IAAA,EAAM,eAAA,EAAiB,OAAA,EAAS,QAAA,EAAS;AACpD;AAaA,SAAS,cAAc,KAAA,EAAe;AACpC,EAAA,IAAI,OAAO,QAAA,EAAU,IAAA,GAAO,QAAA,EAAU,IAAA,GAAO,WAAW,IAAA,GAAO,CAAA,QAAA;AAC/D,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,KAAA,MAAW,KAAK,IAAA,EAAM;AACpB,MAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,CAAC,CAAA;AACzB,MAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,CAAC,CAAA;AACzB,MAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,CAAC,CAAA;AACzB,MAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,CAAC,CAAA;AAAA,IAC3B;AAAA,EACF;AACA,EAAA,OAAO,EAAE,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,OAAO,IAAA,GAAO,IAAA,EAAM,MAAA,EAAQ,IAAA,GAAO,IAAA,EAAK;AAC3E;AAUA,SAAS,QAAA,CAAS,EAAE,MAAA,EAAO,EAAkB;AAC3C,EAAA,MAAM;AAAA,IACJ,QAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,WAAA;AAAA,IACA,WAAA;AAAA,IACA,0BAAA,GAA6B,KAAA;AAAA,IAC7B,kBAAA,GAAqB,KAAA;AAAA,IACrB,wBAAwB,CAAC,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,GAAG,CAAC,CAAA;AAAA,IACtC;AAAA,GACF,GAAI,MAAA;AAGJ,EAAA,MAAM,EAAE,UAAA,EAAY,CAAA,EAAG,GAAG,aAAY,GAAI,MAAA;AAE1C,EAAA,MAAM,cAAA,GAAiBC,YAAA,CAAe,IAAA,CAAK,GAAA,EAAK,CAAA;AAChD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIC,eAAiB,EAAE,CAAA;AACnD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIA,eAAiB,GAAG,CAAA;AACpD,EAAA,MAAM,WAAA,GAAcD,aAAuB,IAAI,CAAA;AAG/C,EAAA,MAAM,QAAA,GAAW,CAAA;AACjB,EAAA,MAAM,iBAAA,GAAoB,CAAA;AAC1B,EAAA,MAAM,WAAA,GAAc,IAAA;AAEpB,EAAA,MAAM,mBAAA,GAAsB,EAAA;AAE5B,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,IAAA,CAAK,MAAA,CAAO,MAAU;AAEjD,EAAA,MAAM,aAAA,GAAgB,CAAA;AAGtB,EAAAE,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,OAAA,GAAU,+BAAA;AAChB,IAAA,IAAI,CAAC,QAAA,CAAS,cAAA,CAAe,OAAO,CAAA,EAAG;AACrC,MAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AAC5C,MAAA,KAAA,CAAM,EAAA,GAAK,OAAA;AACX,MAAA,KAAA,CAAM,WAAA,GAAc;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA,CAAA;AAMpB,MAAA,QAAA,CAAS,IAAA,CAAK,YAAY,KAAK,CAAA;AAAA,IACjC;AACA,IAAA,OAAO,MAAM;AACX,MAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,cAAA,CAAe,OAAO,CAAA;AAC7C,MAAA,IAAI,KAAA,QAAa,MAAA,EAAO;AAAA,IAC1B,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAGL,EAAA,MAAM,KAAA,uBAAY,GAAA,CAAI;AAAA,IACpB,QAAA;AAAA,IACA,eAAA;AAAA,IACA,eAAA;AAAA,IACA,aAAA;AAAA,IACA;AAAA,GACD,CAAA;AAGD,EAAA,MAAM,iBAAA,GAAoBC,cAAQ,MAAM;AACtC,IAAA,OAAO,QAAA,CAAS,GAAA,CAAI,CAAC,WAAA,KAAgB;AACnC,MAAA,MAAM,YAAA,GAAe,WAAA,CAAY,YAAA,CAAa,MAAA,CAAO,CAAC,GAAA,KAAa;AACjE,QAAA,MAAM,OAAA,GAAU,GAAA,CAAI,IAAA,IAAQ,GAAA,CAAI,IAAA;AAChC,QAAA,OAAO,KAAA,CAAM,IAAI,OAAO,CAAA;AAAA,MAC1B,CAAC,CAAA;AAED,MAAA,MAAM,IAAA,GAAe,YAAA,CAAa,GAAA,CAAI,CAAC,GAAA,KAAa;AAClD,QAAA,OAAO,IAAI,QAAA,CAAS,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,MAAiB;AAAA,UAC7C,GAAG,CAAA,IAAK,CAAA;AAAA,UACR,CAAA,EAAG,EAAE,CAAA,IAAK,CAAA;AAAA,SACZ,CAAE,CAAA;AAAA,MACJ,CAAC,CAAA;AAED,MAAA,MAAM,sBAAA,GAAyB,YAAA,CAAa,GAAA,CAAI,CAAC,GAAA,MAAc;AAAA,QAC7D,IAAA,EAAO,GAAA,CAAI,IAAA,IAAQ,GAAA,CAAI,IAAA;AAAA,QACvB,OAAA,EAAS,IAAI,QAAA,CAAS,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,MAAiB;AAAA,UAC/C,GAAG,CAAA,IAAK,CAAA;AAAA,UACR,CAAA,EAAG,EAAE,CAAA,IAAK,CAAA;AAAA,SACZ,CAAE;AAAA,OACJ,CAAE,CAAA;AAEF,MAAA,OAAO;AAAA,QACL,WAAW,WAAA,CAAY,SAAA;AAAA,QACvB,IAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF,CAAC,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAGb,EAAA,MAAM,gBAAA,GAAmBA,cAAQ,MAAM;AACrC,IAAA,IAAI,SAAA,GAAY,CAAA;AAChB,IAAA,KAAA,MAAW,KAAK,iBAAA,EAAmB;AACjC,MAAA,MAAM,MAAA,GAAS,aAAA,CAAc,CAAA,CAAE,IAAI,CAAA;AACnC,MAAA,SAAA,GAAY,KAAK,GAAA,CAAI,SAAA,EAAW,MAAA,CAAO,KAAA,EAAO,OAAO,MAAM,CAAA;AAAA,IAC7D;AACA,IAAA,OAAO,SAAA,IAAa,CAAA;AAAA,EACtB,CAAA,EAAG,CAAC,iBAAiB,CAAC,CAAA;AAGtB,EAAAD,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,oBAAoB,MAAM;AAE9B,MAAA,MAAM,aAAA,GAAgB,SAAS,eAAA,CAAgB,WAAA;AAC/C,MAAA,MAAM,cAAA,GAAiB,SAAS,eAAA,CAAgB,YAAA;AAGhD,MAAA,MAAM,cAAA,GAAiB,WAAA,CAAY,OAAA,EAAS,YAAA,IAAgB,EAAA;AAG5D,MAAA,MAAM,cAAA,GACJ,aAAA,GAAgB,iBAAA,GAAoB,CAAA,GAAI,aAAA;AAC1C,MAAA,MAAM,eAAA,GACJ,cAAA,GAAiB,mBAAA,GAAsB,cAAA,GACvC,oBAAoB,CAAA,GAAI,aAAA;AAI1B,MAAA,MAAM,mBAAA,GAAsB,YAAY,MAAA,GAAS,CAAA,CAAA;AACjD,MAAA,MAAM,iBAAA,GAAoB,YAAY,MAAA,GAAS,CAAA,CAAA;AAC/C,MAAA,MAAM,sBAAA,GAAyB,cAAc,CAAA,GAAI,MAAA;AACjD,MAAA,MAAM,oBAAA,GAAuB,cAAc,CAAA,GAAI,MAAA;AAG/C,MAAA,MAAM,YAAA,GAAA,CACH,cAAA,GAAiB,mBAAA,GAAsB,sBAAA,IAA0B,MAAA;AACpE,MAAA,MAAM,aAAA,GAAA,CACH,eAAA,GAAkB,iBAAA,GAAoB,oBAAA,IAAwB,MAAA;AAGjE,MAAA,MAAM,cAAc,IAAA,CAAK,KAAA,CAAM,KAAK,GAAA,CAAI,YAAA,EAAc,aAAa,CAAC,CAAA;AACpE,MAAA,WAAA,CAAY,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,EAAE,CAAC,CAAA;AAAA,IACvC,CAAA;AAEA,IAAA,iBAAA,EAAkB;AAClB,IAAA,MAAA,CAAO,gBAAA,CAAiB,UAAU,iBAAiB,CAAA;AACnD,IAAA,OAAO,MAAM,MAAA,CAAO,mBAAA,CAAoB,QAAA,EAAU,iBAAiB,CAAA;AAAA,EACrE,CAAA,EAAG,CAAC,MAAA,EAAQ,MAAM,CAAC,CAAA;AAGnB,EAAA,MAAM,YAAA,GAAeC,cAAQ,MAAM;AACjC,IAAA,MAAM,UAAA,GAAa,QAAA,IAAY,CAAA,GAAI,WAAA,GAAc,CAAA,CAAA;AACjD,IAAA,OAAO,UAAA,GAAa,gBAAA;AAAA,EACtB,CAAA,EAAG,CAAC,QAAA,EAAU,gBAAgB,CAAC,CAAA;AAG/B,EAAA,MAAM,WAAA,GAAuC;AAAA,IAC3C,MAAA,EAAQ,CAAA;AAAA,IACR,aAAA,EAAe,CAAA;AAAA,IACf,aAAA,EAAe,CAAA;AAAA,IACf,WAAA,EAAa,CAAA;AAAA,IACb,aAAA,EAAe;AAAA,GACjB;AAGA,EAAA,MAAM,KAAA,GAAQ,CAAC,IAAA,KAAuB;AACpC,IAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,CAAK,MAAA,KAAW,GAAG,OAAO,EAAA;AACvC,IAAA,MAAM,QAAQ,IAAA,CAAK,GAAA;AAAA,MACjB,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,GAAA,GAAM,GAAG,CAAA,CAAA,EAAI,CAAA,CAAE,CAAC,CAAA,CAAA,EAAI,EAAE,CAAC,CAAA;AAAA,KAChD;AACA,IAAA,OAAO,KAAA,CAAM,IAAA,CAAK,GAAG,CAAA,GAAI,IAAA;AAAA,EAC3B,CAAA;AAGA,EAAA,MAAM,eAAe,MAAM;AACzB,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,GAAA,EAAI,GAAI,cAAA,CAAe,OAAA;AACvC,IAAA,MAAM,SAAA,GAAY;AAAA,MAChB,QAAA;AAAA,MACA,EAAA;AAAA,MACA,WAAA,EAAa;AAAA,QACX,GAAG,WAAA;AAAA,QACH,YAAY,iBAAA,CAAkB,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,SAAS;AAAA;AACtD,KACF;AACA,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,UAAA,CAAW,SAAS,CAAA;AAAA,IACtB;AAAA,EACF,CAAA;AAGA,EAAA,MAAM,aAAA,GAAgB,CACpB,WAAA,EACA,KAAA,KACG;AACH,IAAA,MAAM,EAAE,IAAA,EAAM,sBAAA,EAAuB,GAAI,WAAA;AAEzC,IAAA,IAAI,0BAAA,EAA4B;AAG9B,MAAA,MAAM,mBAAmB,sBAAA,CAAuB,GAAA;AAAA,QAC9C,CAAC,IAAA,KAA2C;AAC1C,UAAA,MAAM,UAAA,GAAa,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,MAAiC;AAAA,YACpE,CAAA,EAAG,EAAE,CAAA,GAAI,YAAA;AAAA,YACT,CAAA,EAAG,EAAE,CAAA,GAAI;AAAA,WACX,CAAE,CAAA;AACF,UAAA,OAAO,EAAE,IAAA,EAAM,IAAA,CAAK,IAAA,EAAM,SAAS,UAAA,EAAW;AAAA,QAChD;AAAA,OACF;AAGA,MAAA,IAAI,OAAO,QAAA,EACT,IAAA,GAAO,QAAA,EACP,IAAA,GAAO,WACP,IAAA,GAAO,CAAA,QAAA;AACT,MAAA,KAAA,MAAW,QAAQ,gBAAA,EAAkB;AACnC,QAAA,KAAA,MAAW,CAAA,IAAK,KAAK,OAAA,EAAS;AAC5B,UAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,CAAC,CAAA;AACzB,UAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,CAAC,CAAA;AACzB,UAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,CAAC,CAAA;AACzB,UAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,CAAC,CAAA;AAAA,QAC3B;AAAA,MACF;AAGA,MAAA,MAAM,QAAQ,IAAA,GAAO,IAAA;AACrB,MAAA,MAAM,SAAS,IAAA,GAAO,IAAA;AACtB,MAAA,MAAM,EAAA,GAAK,QAAA,GAAW,CAAA,IAAK,IAAA,GAAO,KAAA,GAAQ,CAAA,CAAA;AAC1C,MAAA,MAAM,EAAA,GAAK,QAAA,GAAW,CAAA,IAAK,IAAA,GAAO,MAAA,GAAS,CAAA,CAAA;AAE3C,MAAA,MAAM,uBAAuB,gBAAA,CAAiB,GAAA;AAAA,QAC5C,CAAC,IAAA,MAA4C;AAAA,UAC3C,MAAM,IAAA,CAAK,IAAA;AAAA,UACX,OAAA,EAAS,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,MAAiC;AAAA,YAC1D,CAAA,EAAG,EAAE,CAAA,GAAI,EAAA;AAAA,YACT,CAAA,EAAG,EAAE,CAAA,GAAI;AAAA,WACX,CAAE;AAAA,SACJ;AAAA,OACF;AAEA,MAAA,uBACE,KAAA,CAAA,aAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,GAAA,EAAK,KAAA;AAAA,UACL,KAAA,EAAO,QAAA;AAAA,UACP,MAAA,EAAQ,QAAA;AAAA,UACR,OAAA,EAAS,CAAA,IAAA,EAAO,QAAQ,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAA;AAAA,UACpC,KAAA,EAAO;AAAA,YACL,OAAA,EAAS,OAAA;AAAA,YACT,UAAA,EAAY,MAAA,CAAO,KAAA,CAAM,KAAA,CAAM,UAAA,CAAW,QAAA;AAAA,YAC1C,MAAA,EAAQ,CAAA,EAAG,MAAA,CAAO,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA,SAAA,EAAY,MAAA,CAAO,KAAA,CAAM,KAAA,CAAM,UAAA,CAAW,MAAM,CAAA,CAAA;AAAA,YACpF,YAAA,EAAc;AAAA;AAChB,SAAA;AAAA,QAEC,oBAAA,CAAqB,GAAA;AAAA,UACpB,CAAC,MAAwC,CAAA,KAAc;AACrD,YAAA,IAAI,SAAA;AAEJ,YAAA,IAAI,kBAAA,EAAoB;AAEtB,cAAA,MAAM,cAAA,GAAiB,WAAA,CAAY,IAAA,CAAK,IAAI,CAAA;AAC5C,cAAA,IACE,cAAA,KAAmB,MAAA,IACnB,qBAAA,CAAsB,cAAc,MAAM,MAAA,EAC1C;AACA,gBAAA,MAAM,UAAA,GAAa,sBAAsB,cAAc,CAAA;AACvD,gBAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,KAAA,CAAM,eAAA,CAAgB,UAAU,CAAA;AACrD,gBAAA,SAAA,GAAY,KAAA,IAAS,MAAA,CAAO,KAAA,CAAM,KAAA,CAAM,SAAA;AAAA,cAC1C,CAAA,MAAO;AACL,gBAAA,SAAA,GAAY,MAAA,CAAO,MAAM,KAAA,CAAM,SAAA;AAAA,cACjC;AAAA,YACF,CAAA,MAAO;AAEL,cAAA,SAAA,GAAY,MAAA,CAAO,MAAM,KAAA,CAAM,SAAA;AAAA,YACjC;AAEA,YAAA,uBACE,KAAA,CAAA,aAAA;AAAA,cAAC,MAAA;AAAA,cAAA;AAAA,gBACC,GAAA,EAAK,QAAQ,CAAC,CAAA,CAAA;AAAA,gBACd,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,OAAO,CAAA;AAAA,gBACrB,IAAA,EAAM,SAAA;AAAA,gBACN,OAAA,EAAS,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,MAAA;AAAA,gBAC9B,MAAA,EAAQ,MAAA,CAAO,KAAA,CAAM,oBAAA,CAAqB,MAAA;AAAA,gBAC1C,WAAA,EAAa,MAAA,CAAO,IAAA,CAAK,MAAA,CAAO;AAAA;AAAA,aAClC;AAAA,UAEJ;AAAA;AACF,OACF;AAAA,IAEJ,CAAA,MAAO;AAEL,MAAA,MAAM,aAAa,IAAA,CAAK,GAAA;AAAA,QAAI,CAAC,IAAA,KAC3B,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,UACf,CAAA,EAAG,EAAE,CAAA,GAAI,YAAA;AAAA,UACT,CAAA,EAAG,EAAE,CAAA,GAAI;AAAA,SACX,CAAE;AAAA,OACJ;AAGA,MAAA,IAAI,OAAO,QAAA,EACT,IAAA,GAAO,QAAA,EACP,IAAA,GAAO,WACP,IAAA,GAAO,CAAA,QAAA;AACT,MAAA,KAAA,MAAW,QAAQ,UAAA,EAAY;AAC7B,QAAA,KAAA,MAAW,KAAK,IAAA,EAAM;AACpB,UAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,CAAC,CAAA;AACzB,UAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,CAAC,CAAA;AACzB,UAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,CAAC,CAAA;AACzB,UAAA,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,CAAC,CAAA;AAAA,QAC3B;AAAA,MACF;AAGA,MAAA,MAAM,QAAQ,IAAA,GAAO,IAAA;AACrB,MAAA,MAAM,SAAS,IAAA,GAAO,IAAA;AACtB,MAAA,MAAM,EAAA,GAAK,QAAA,GAAW,CAAA,IAAK,IAAA,GAAO,KAAA,GAAQ,CAAA,CAAA;AAC1C,MAAA,MAAM,EAAA,GAAK,QAAA,GAAW,CAAA,IAAK,IAAA,GAAO,MAAA,GAAS,CAAA,CAAA;AAE3C,MAAA,MAAM,aAAa,UAAA,CAAW,GAAA;AAAA,QAAI,CAAC,IAAA,KACjC,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,MAAO,EAAE,CAAA,EAAG,CAAA,CAAE,IAAI,EAAA,EAAI,CAAA,EAAG,CAAA,CAAE,CAAA,GAAI,IAAG,CAAE;AAAA,OAChD;AAEA,MAAA,uBACE,KAAA,CAAA,aAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,GAAA,EAAK,KAAA;AAAA,UACL,KAAA,EAAO,QAAA;AAAA,UACP,MAAA,EAAQ,QAAA;AAAA,UACR,OAAA,EAAS,CAAA,IAAA,EAAO,QAAQ,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAA;AAAA,UACpC,KAAA,EAAO;AAAA,YACL,OAAA,EAAS,OAAA;AAAA,YACT,UAAA,EAAY,MAAA,CAAO,KAAA,CAAM,KAAA,CAAM,UAAA,CAAW,QAAA;AAAA,YAC1C,MAAA,EAAQ,CAAA,EAAG,MAAA,CAAO,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA,SAAA,EAAY,MAAA,CAAO,KAAA,CAAM,KAAA,CAAM,UAAA,CAAW,MAAM,CAAA,CAAA;AAAA,YACpF,YAAA,EAAc;AAAA;AAChB,SAAA;AAAA,QAEC,UAAA,CAAW,GAAA,CAAI,CAAC,IAAA,EAAM,CAAA,qBACrB,KAAA,CAAA,aAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,GAAA,EAAK,OAAO,CAAC,CAAA,CAAA;AAAA,YACb,CAAA,EAAG,MAAM,IAAI,CAAA;AAAA,YACb,IAAA,EAAM,MAAA,CAAO,KAAA,CAAM,KAAA,CAAM,SAAA;AAAA,YACzB,OAAA,EAAS,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,MAAA;AAAA,YAC9B,MAAA,EAAO;AAAA;AAAA,SAEV;AAAA,OACH;AAAA,IAEJ;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,gBAAA,GAAmB,QAAA,CAAS,IAAA,EAAK,CAAE,MAAA,KAAW,CAAA;AAEpD,EAAA,uBACE,KAAA,CAAA,aAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO;AAAA,QACL,OAAA,EAAS,MAAA;AAAA,QACT,aAAA,EAAe,QAAA;AAAA,QACf,UAAA,EAAY,QAAA;AAAA,QACZ,cAAA,EAAgB,eAAA;AAAA,QAChB,UAAA,EAAY,OAAO,KAAA,CAAM,UAAA;AAAA,QACzB,KAAA,EAAO,MAAA;AAAA,QACP,MAAA,EAAQ,gBAAgB,mBAAmB,CAAA,GAAA,CAAA;AAAA,QAC3C,QAAA,EAAU,QAAA;AAAA,QACV,UAAA,EAAY,oBAAA;AAAA,QACZ,SAAA,EAAW,YAAA;AAAA,QACX,OAAA,EAAS,GAAG,iBAAiB,CAAA,EAAA;AAAA;AAC/B,KAAA;AAAA,oBAGA,KAAA,CAAA,aAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAO;AAAA,UACL,IAAA,EAAM,UAAA;AAAA,UACN,OAAA,EAAS,MAAA;AAAA,UACT,UAAA,EAAY,QAAA;AAAA,UACZ,cAAA,EAAgB,QAAA;AAAA,UAChB,SAAA,EAAW;AAAA;AACb,OAAA;AAAA,sBAEA,KAAA,CAAA,aAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAO;AAAA,YACL,OAAA,EAAS,MAAA;AAAA,YACT,mBAAA,EAAqB,CAAA,OAAA,EAAU,MAAM,CAAA,EAAA,EAAK,QAAQ,CAAA,GAAA,CAAA;AAAA,YAClD,gBAAA,EAAkB,CAAA,OAAA,EAAU,MAAM,CAAA,EAAA,EAAK,QAAQ,CAAA,GAAA,CAAA;AAAA,YAC/C,GAAA,EAAK,GAAG,QAAQ,CAAA,EAAA;AAAA;AAClB,SAAA;AAAA,QAEC,iBAAA,CAAkB,KAAA,CAAM,CAAA,EAAG,MAAA,GAAS,MAAM,CAAA,CAAE,GAAA;AAAA,UAAI,CAAC,CAAA,EAAG,CAAA,KACnD,aAAA,CAAc,GAAG,CAAC;AAAA;AACpB;AACF,KACF;AAAA,oBAGA,KAAA,CAAA,aAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA,EAAK,WAAA;AAAA,QACL,KAAA,EAAO;AAAA,UACL,IAAA,EAAM,UAAA;AAAA,UACN,OAAA,EAAS,MAAA;AAAA,UACT,aAAA,EAAe,QAAA;AAAA,UACf,UAAA,EAAY,QAAA;AAAA,UACZ,GAAA,EAAK,KAAA;AAAA,UACL,UAAA,EAAY;AAAA;AACd,OAAA;AAAA,sBAGA,KAAA,CAAA,aAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAO;AAAA,YACL,QAAA,EAAU,MAAA;AAAA,YACV,SAAA,EAAW,QAAA;AAAA,YACX,QAAA,EAAU;AAAA;AACZ,SAAA;AAAA,QAEC;AAAA,OACH;AAAA,sBAGA,KAAA,CAAA,aAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,KAAA,EAAO;AAAA,YACL,OAAA,EAAS,MAAA;AAAA,YACT,UAAA,EAAY,QAAA;AAAA,YACZ,GAAA,EAAK;AAAA;AACP,SAAA;AAAA,wBAEA,KAAA,CAAA,aAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,MAAA;AAAA,YACL,KAAA,EAAO,QAAA;AAAA,YACP,UAAU,CAAC,CAAA,KAAM,WAAA,CAAY,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,YAC3C,KAAA,EAAO;AAAA,cACL,KAAA,EAAO,kBAAA;AAAA,cACP,OAAA,EAAS,UAAA;AAAA,cACT,QAAA,EAAU,MAAA;AAAA,cACV,YAAA,EAAc,KAAA;AAAA,cACd,MAAA,EAAQ,gBAAA;AAAA,cACR,UAAA,EAAY,SAAA;AAAA,cACZ,SAAA,EAAW;AAAA,aACb;AAAA,YACA,WAAA,EAAY;AAAA;AAAA,SACd;AAAA,wBAEA,KAAA,CAAA,aAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAU,aAAA;AAAA,YACV,OAAA,EAAS,YAAA;AAAA,YACT,QAAA,EAAU,gBAAA;AAAA,YACV,KAAA,EAAO;AAAA,cACL,OAAA,EAAS,UAAA;AAAA,cACT,QAAA,EAAU,MAAA;AAAA,cACV,MAAA,EAAQ,mBAAmB,aAAA,GAAgB,SAAA;AAAA,cAC3C,OAAA,EAAS,mBAAmB,GAAA,GAAM,CAAA;AAAA,cAClC,UAAA,EAAY;AAAA;AACd,WAAA;AAAA,UAEC;AAAA;AACH;AACF;AACF,GACF;AAEJ;;ACvgBA,MAAM,IAAA,GAAO;AAAA,EACX,IAAA,EAAM,cAAA;AAAA,EACN,OAAA,EAAS,OAAA;AAAA,EACT,UAAA,EAAY;AAAA;AAAA,IAEV,QAAA,EAAU;AAAA,MACR,MAAMC,qBAAA,CAAc,OAAA;AAAA,MACpB,OAAA,EAAS,MAAA;AAAA,MACT,WAAA,EAAa;AAAA,KACf;AAAA;AAAA,IAEA,MAAA,EAAQ;AAAA,MACN,MAAMA,qBAAA,CAAc,GAAA;AAAA,MACpB,OAAA,EAAS,CAAA;AAAA,MACT,WAAA,EAAa;AAAA,KACf;AAAA;AAAA,IAEA,MAAA,EAAQ;AAAA,MACN,MAAMA,qBAAA,CAAc,GAAA;AAAA,MACpB,OAAA,EAAS,CAAA;AAAA,MACT,WAAA,EAAa;AAAA,KACf;AAAA;AAAA,IAEA,WAAA,EAAa;AAAA,MACX,MAAMA,qBAAA,CAAc,MAAA;AAAA,MACpB,OAAA,EAAS,EAAA;AAAA,MACT,WAAA,EAAa;AAAA,KACf;AAAA;AAAA,IAEA,WAAA,EAAa;AAAA,MACX,MAAMA,qBAAA,CAAc,MAAA;AAAA,MACpB,OAAA,EAAS,QAAA;AAAA,MACT,WAAA,EAAa;AAAA,KACf;AAAA;AAAA,IAEA,0BAAA,EAA4B;AAAA,MAC1B,MAAMA,qBAAA,CAAc,IAAA;AAAA,MACpB,OAAA,EAAS,KAAA;AAAA,MACT,WAAA,EACE;AAAA,KACJ;AAAA;AAAA,IAEA,oBAAA,EAAsB;AAAA,MACpB,MAAMA,qBAAA,CAAc,IAAA;AAAA,MACpB,OAAA,EAAS,KAAA;AAAA,MACT,WAAA,EACE;AAAA,KACJ;AAAA;AAAA,IAEA,uBAAA,EAAyB;AAAA,MACvB,MAAMA,qBAAA,CAAc,MAAA;AAAA,MACpB,SAAS,CAAC,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,GAAG,CAAC,CAAA;AAAA,MACvB,WAAA,EACE;AAAA,KAGJ;AAAA;AAAA,IAEA,UAAA,EAAY;AAAA,MACV,MAAMA,qBAAA,CAAc,QAAA;AAAA,MACpB,OAAA,EAAS,MAAA;AAAA,MACT,WAAA,EAAa;AAAA;AACf,GACF;AAAA,EACA,IAAA,EAAM;AAAA;AAAA,IAEJ,QAAA,EAAU;AAAA,MACR,MAAMA,qBAAA,CAAc,MAAA;AAAA,MACpB,WAAA,EAAa;AAAA,KACf;AAAA;AAAA,IAEA,EAAA,EAAI;AAAA,MACF,MAAMA,qBAAA,CAAc,GAAA;AAAA,MACpB,WAAA,EAAa;AAAA;AACf,GACF;AAAA,EACA,SAAA,EAAW;AACb,CAAA;AAaA,MAAM,iBAAA,CAAiD;AAAA,EAGrD,YAAoB,OAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAAmB;AAAA,EAFvC;AAAA,IAAA,IAAA,CAAO,IAAA,GAAO,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAad,KAAA,CAAM,iBAA8B,KAAA,EAAwB;AAE1D,IAAA,MAAM,iBAAA,GAAoB,CAAC,IAAA,KAAc;AAEvC,MAAA,IAAI,MAAM,UAAA,EAAY;AACpB,QAAA,KAAA,CAAM,WAAW,IAAI,CAAA;AAAA,MACvB;AAGA,MAAA,MAAM,eAAgB,eAAA,CAAwB,cAAA;AAC9C,MAAA,IAAI,cAAc,IAAA,EAAM;AACtB,QAAA,YAAA,CAAa,KAAK,OAAA,EAAQ;AAAA,MAC5B;AAGA,MAAA,eAAA,CAAgB,SAAA,GAAY,EAAA;AAG5B,MAAA,IAAA,CAAK,OAAA,CAAQ,YAAY,IAAI,CAAA;AAAA,IAC/B,CAAA;AAGA,IAAA,MAAM,MAAA,GAA+B;AAAA,MACnC,UAAU,KAAA,CAAM,QAAA;AAAA,MAChB,QAAQ,KAAA,CAAM,MAAA;AAAA,MACd,QAAQ,KAAA,CAAM,MAAA;AAAA,MACd,aAAa,KAAA,CAAM,WAAA;AAAA,MACnB,aAAa,KAAA,CAAM,WAAA;AAAA,MACnB,4BAA4B,KAAA,CAAM,0BAAA;AAAA,MAClC,oBAAoB,KAAA,CAAM,oBAAA;AAAA,MAC1B,uBAAuB,KAAA,CAAM,uBAAA;AAAA,MAC7B,UAAA,EAAY;AAAA,KACd;AAGA,IAAA,MAAM,EAAE,IAAA,EAAM,eAAA,EAAiB,OAAA,EAAS,SAAQ,GAAI,cAAA;AAAA,MAClD,eAAA;AAAA,MACA,MAAA;AAAA,MACA,IAAA,CAAK;AAAA,KACP;AAGA,IAAC,OAAA,CAAgB,cAAA,GAAiB,EAAE,IAAA,EAAM,OAAA,EAAQ;AAAA,EACpD;AACF;;;;"}
@@ -54,6 +54,7 @@ function GridView({ params }) {
54
54
  primitiveColorIndices = [0, 1, 2, 3, 4],
55
55
  onTrialEnd
56
56
  } = params;
57
+ const { onTrialEnd: _, ...trialParams } = params;
57
58
  const trialStartTime = useRef(Date.now());
58
59
  const [response, setResponse] = useState("");
59
60
  const [cellSize, setCellSize] = useState(100);
@@ -166,12 +167,10 @@ function GridView({ params }) {
166
167
  const trialData = {
167
168
  response,
168
169
  rt,
169
- n_rows,
170
- n_cols,
171
- tangram_ids: processedTangrams.map((t) => t.tangramId),
172
- show_tangram_decomposition,
173
- use_primitive_colors: usePrimitiveColors,
174
- primitive_color_indices: primitiveColorIndices
170
+ trialParams: {
171
+ ...trialParams,
172
+ tangramIDs: processedTangrams.map((t) => t.tangramId)
173
+ }
175
174
  };
176
175
  if (onTrialEnd) {
177
176
  onTrialEnd(trialData);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/core/config/config.ts","../../src/plugins/tangram-grid/GridApp.tsx","../../src/plugins/tangram-grid/index.ts"],"sourcesContent":["// src/core/config/config.ts\nexport type Config = {\n color: {\n background: string;\n bands: {\n silhouette: { fillEven: string; fillOdd: string; stroke: string };\n workspace: { fillEven: string; fillOdd: string; stroke: string };\n };\n completion: { fill: string; stroke: string };\n silhouetteMask: string;\n anchors: { invalid: string; valid: string };\n piece: { draggingFill: string; validFill: string; invalidFill: string; invalidStroke: string; selectedStroke: string; allGreenStroke: string; borderStroke: string };\n ui: { light: string; dark: string };\n blueprint: { fill: string; selectedStroke: string; badgeFill: string; labelFill: string };\n tangramDecomposition: { stroke: string };\n primitiveColors: string[];\n };\n opacity: {\n blueprint: number;\n silhouetteMask: number;\n anchors: { invalid: number; valid: number };\n piece: { invalid: number; dragging: number; locked: number; normal: number };\n };\n size: {\n stroke: { bandPx: number; pieceSelectedPx: number; allGreenStrokePx: number; pieceBorderPx: number; tangramDecompositionPx: number };\n anchorRadiusPx: { valid: number; invalid: number };\n badgeFontPx: number;\n centerBadge: { fractionOfOuterR: number; minPx: number; marginPx: number };\n invalidMarker: { sizePx: number; strokePx: number };\n };\n layout: {\n grid: { stepPx: number; unitPx: number };\n paddingPx: number;\n viewportScale: number;\n /** renamed from capacity → constraints */\n constraints: {\n workspaceDiamAnchors: number;\n quickstashDiamAnchors: number;\n primitiveDiamAnchors: number;\n };\n defaults: { maxQuickstashSlots: number };\n };\n game: {\n snapRadiusPx: number;\n showBorders: boolean;\n hideTouchingBorders: boolean;\n silhouettesBelowPieces: boolean;\n };\n};\n\nexport const CONFIG: Config = {\n color: {\n background: \"#fff7e0ff\",\n bands: {\n silhouette: { fillEven: \"#ffffff\", fillOdd: \"#ffffff\", stroke: \"#b1b1b1\" },\n workspace: { fillEven: \"#ffffff\", fillOdd: \"#ffffff\", stroke: \"#b1b1b1\" }\n },\n completion: { fill: \"#ccffcc\", stroke: \"#13da57\" },\n silhouetteMask: \"#374151\",\n anchors: { invalid: \"#7dd3fc\", valid: \"#475569\" },\n // validFill used here for placed composites\n piece: { draggingFill: \"#8e7cc3\", validFill: \"#8e7cc3\", invalidFill: \"#d55c00\", invalidStroke: \"#dc2626\", selectedStroke: \"#674ea7\", allGreenStroke: \"#86efac\", borderStroke: \"#674ea7\" },\n ui: { light: \"#60a5fa\", dark: \"#1d4ed8\" },\n blueprint: { fill: \"#374151\", selectedStroke: \"#111827\", badgeFill: \"#000000\", labelFill: \"#ffffff\" },\n tangramDecomposition: { stroke: \"#fef2cc\" },\n primitiveColors: [ // from seaborn \"colorblind\" palette, 6 colors, with red omitted\n '#0173b2',\n '#de8f05',\n '#029e73',\n '#cc78bc',\n '#ca9161'\n ]\n },\n opacity: {\n blueprint: 0.6,\n silhouetteMask: 0.25,\n //anchors: { valid: 0.80, invalid: 0.50 },\n anchors: { invalid: 0.0, valid: 0.0 },\n piece: { invalid: 1, dragging: 1, locked: 1, normal: 1 },\n },\n size: {\n stroke: { bandPx: 5, pieceSelectedPx: 5, allGreenStrokePx: 10, pieceBorderPx: 2, tangramDecompositionPx: 1 },\n anchorRadiusPx: { valid: 1.0, invalid: 1.0 },\n badgeFontPx: 16,\n centerBadge: { fractionOfOuterR: 0.15, minPx: 20, marginPx: 4 },\n invalidMarker: { sizePx: 10, strokePx: 4 }\n },\n layout: {\n grid: { stepPx: 20, unitPx: 40 },\n paddingPx: 1,\n viewportScale: 0.8,\n constraints: {\n workspaceDiamAnchors: 10, // num anchors req'd to be on diagonal\n quickstashDiamAnchors: 7, // num anchors req'd to be in single quickstash slot\n primitiveDiamAnchors: 5,\n },\n defaults: { maxQuickstashSlots: 1 }\n },\n game: {\n snapRadiusPx: 15,\n showBorders: false,\n hideTouchingBorders: true,\n silhouettesBelowPieces: true\n }\n};","/**\n * GridApp.tsx - React wrapper for tangram grid display with text input\n *\n * This component displays a grid of tangrams with a text input field\n * and submit button for collecting free-text responses.\n */\n\nimport React, { useRef, useState, useMemo, useEffect } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { JsPsych } from \"jspsych\";\nimport type { Poly, TanKind } from \"../../core/domain/types\";\nimport { CONFIG } from \"../../core/config/config\";\n\nexport interface StartGridTrialParams {\n tangrams: any[];\n n_rows: number;\n n_cols: number;\n prompt_text: string;\n button_text: string;\n show_tangram_decomposition?: boolean;\n usePrimitiveColors?: boolean;\n primitiveColorIndices?: number[];\n onTrialEnd?: (data: any) => void;\n}\n\n/**\n * Start a grid trial by rendering the GridView component\n *\n * REQUIRES: display_element is a valid HTMLElement\n * MODIFIES: display_element (renders React into it)\n * EFFECTS: Creates a React root and renders GridView with the given params\n */\nexport function startGridTrial(\n display_element: HTMLElement,\n params: StartGridTrialParams,\n _jsPsych: JsPsych\n) {\n const root = createRoot(display_element);\n root.render(React.createElement(GridView, { params }));\n return { root, display_element, jsPsych: _jsPsych };\n}\n\ninterface GridViewProps {\n params: StartGridTrialParams;\n}\n\n/**\n * Compute bounding box for an array of polygons\n *\n * REQUIRES: polys is an array of polygons with {x, y} points\n * MODIFIES: nothing\n * EFFECTS: Returns {minX, minY, maxX, maxY, width, height} of bounding box\n */\nfunction computeBounds(polys: Poly[]) {\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n for (const poly of polys) {\n for (const p of poly) {\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n }\n }\n return { minX, minY, maxX, maxY, width: maxX - minX, height: maxY - minY };\n}\n\n/**\n * GridView - Main React component for the grid trial\n *\n * REQUIRES: params contains valid tangram specs and grid dimensions\n * MODIFIES: nothing\n * EFFECTS: Renders a grid of tangrams, text input, and submit button;\n * calls onTrialEnd with response data when submitted\n */\nfunction GridView({ params }: GridViewProps) {\n const {\n tangrams,\n n_rows,\n n_cols,\n prompt_text,\n button_text,\n show_tangram_decomposition = false,\n usePrimitiveColors = false,\n primitiveColorIndices = [0, 1, 2, 3, 4],\n onTrialEnd\n } = params;\n\n const trialStartTime = useRef<number>(Date.now());\n const [response, setResponse] = useState<string>(\"\");\n const [cellSize, setCellSize] = useState<number>(100);\n const controlsRef = useRef<HTMLDivElement>(null);\n\n // Layout constants\n const GRID_GAP = 6;\n const CONTAINER_PADDING = 8;\n const CELL_MARGIN = 0.05; // 5% margin inside cell for tangram\n // jsPsych progress bar: 20px height + 8px top/bottom padding + 18px margin\n const PROGRESS_BAR_HEIGHT = 58;\n // Border width on each cell (from CONFIG.size.stroke.bandPx)\n const CELL_BORDER = CONFIG.size.stroke.bandPx ?? 1;\n // Extra buffer to prevent scrollbars from appearing\n const SAFETY_BUFFER = 8;\n\n // Inject style to override jspsych-content max-width constraint\n useEffect(() => {\n const styleId = \"tangram-grid-jspsych-override\";\n if (!document.getElementById(styleId)) {\n const style = document.createElement(\"style\");\n style.id = styleId;\n style.textContent = `\n .jspsych-content {\n max-width: 100% !important;\n width: 100% !important;\n }\n `;\n document.head.appendChild(style);\n }\n return () => {\n const style = document.getElementById(styleId);\n if (style) style.remove();\n };\n }, []);\n\n // Canonical piece names\n const CANON = new Set([\n \"square\",\n \"smalltriangle\",\n \"parallelogram\",\n \"medtriangle\",\n \"largetriangle\"\n ]);\n\n // Convert TangramSpec to internal format with mask and decomposition\n const processedTangrams = useMemo(() => {\n return tangrams.map((tangramSpec) => {\n const filteredTans = tangramSpec.solutionTans.filter((tan: any) => {\n const tanName = tan.name ?? tan.kind;\n return CANON.has(tanName);\n });\n\n const mask: Poly[] = filteredTans.map((tan: any) => {\n return tan.vertices.map(([x, y]: number[]) => ({\n x: x ?? 0,\n y: -(y ?? 0)\n }));\n });\n\n const primitiveDecomposition = filteredTans.map((tan: any) => ({\n kind: (tan.name ?? tan.kind) as TanKind,\n polygon: tan.vertices.map(([x, y]: number[]) => ({\n x: x ?? 0,\n y: -(y ?? 0)\n }))\n }));\n\n return {\n tangramId: tangramSpec.tangramID,\n mask,\n primitiveDecomposition\n };\n });\n }, [tangrams]);\n\n // Find the largest tangram dimensions to determine scaling\n const maxTangramExtent = useMemo(() => {\n let maxExtent = 0;\n for (const t of processedTangrams) {\n const bounds = computeBounds(t.mask);\n maxExtent = Math.max(maxExtent, bounds.width, bounds.height);\n }\n return maxExtent || 1;\n }, [processedTangrams]);\n\n // Calculate cell size based on available space\n useEffect(() => {\n const calculateCellSize = () => {\n // Use document.documentElement for more accurate viewport size\n const viewportWidth = document.documentElement.clientWidth;\n const viewportHeight = document.documentElement.clientHeight;\n\n // Reserve space for controls (prompt + input row) and progress bar\n const controlsHeight = controlsRef.current?.offsetHeight ?? 70;\n\n // Available space for the grid (subtract progress bar, controls, padding, buffer)\n const availableWidth =\n viewportWidth - CONTAINER_PADDING * 2 - SAFETY_BUFFER;\n const availableHeight =\n viewportHeight - PROGRESS_BAR_HEIGHT - controlsHeight -\n CONTAINER_PADDING * 2 - SAFETY_BUFFER;\n\n // Account for gaps between cells AND borders on each cell\n // Each cell has border on all sides, so total border per cell = 2 * CELL_BORDER\n const totalHorizontalGaps = GRID_GAP * (n_cols - 1);\n const totalVerticalGaps = GRID_GAP * (n_rows - 1);\n const totalHorizontalBorders = CELL_BORDER * 2 * n_cols;\n const totalVerticalBorders = CELL_BORDER * 2 * n_rows;\n\n // Calculate max cell size that fits in available space\n const maxCellWidth =\n (availableWidth - totalHorizontalGaps - totalHorizontalBorders) / n_cols;\n const maxCellHeight =\n (availableHeight - totalVerticalGaps - totalVerticalBorders) / n_rows;\n\n // Use the smaller dimension to keep cells square\n const newCellSize = Math.floor(Math.min(maxCellWidth, maxCellHeight));\n setCellSize(Math.max(newCellSize, 50)); // Minimum 50px\n };\n\n calculateCellSize();\n window.addEventListener(\"resize\", calculateCellSize);\n return () => window.removeEventListener(\"resize\", calculateCellSize);\n }, [n_rows, n_cols]);\n\n // Scale factor to fit largest tangram in cell with margin\n const tangramScale = useMemo(() => {\n const usableSize = cellSize * (1 - CELL_MARGIN * 2);\n return usableSize / maxTangramExtent;\n }, [cellSize, maxTangramExtent]);\n\n // Mapping from TanKind to color index\n const kindToIndex: Record<TanKind, number> = {\n square: 0,\n smalltriangle: 1,\n parallelogram: 2,\n medtriangle: 3,\n largetriangle: 4\n };\n\n // Helper to convert polygon to SVG path\n const pathD = (poly: Poly): string => {\n if (!poly || poly.length === 0) return \"\";\n const moves = poly.map(\n (p, i) => `${i === 0 ? \"M\" : \"L\"} ${p.x} ${p.y}`\n );\n return moves.join(\" \") + \" Z\";\n };\n\n // Handle submit\n const handleSubmit = () => {\n const rt = Date.now() - trialStartTime.current;\n const trialData = {\n response,\n rt,\n n_rows,\n n_cols,\n tangram_ids: processedTangrams.map((t) => t.tangramId),\n show_tangram_decomposition,\n use_primitive_colors: usePrimitiveColors,\n primitive_color_indices: primitiveColorIndices\n };\n if (onTrialEnd) {\n onTrialEnd(trialData);\n }\n };\n\n // Render a single tangram SVG\n const renderTangram = (\n tangramData: (typeof processedTangrams)[0],\n index: number\n ) => {\n const { mask, primitiveDecomposition } = tangramData;\n\n if (show_tangram_decomposition) {\n // Show individual primitives with optional coloring\n // Scale each primitive and fit to viewport while preserving relative positions\n const scaledPrimitives = primitiveDecomposition.map(\n (prim: { kind: TanKind; polygon: Poly }) => {\n const scaledPoly = prim.polygon.map((p: { x: number; y: number }) => ({\n x: p.x * tangramScale,\n y: p.y * tangramScale\n }));\n return { kind: prim.kind, polygon: scaledPoly };\n }\n );\n\n // Find bounds of all primitives together\n let minX = Infinity,\n minY = Infinity,\n maxX = -Infinity,\n maxY = -Infinity;\n for (const prim of scaledPrimitives) {\n for (const p of prim.polygon) {\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n }\n }\n\n // Compute translation to center all primitives together in viewport\n const width = maxX - minX;\n const height = maxY - minY;\n const tx = cellSize / 2 - (minX + width / 2);\n const ty = cellSize / 2 - (minY + height / 2);\n\n const translatedPrimitives = scaledPrimitives.map(\n (prim: { kind: TanKind; polygon: Poly }) => ({\n kind: prim.kind,\n polygon: prim.polygon.map((p: { x: number; y: number }) => ({\n x: p.x + tx,\n y: p.y + ty\n }))\n })\n );\n\n return (\n <svg\n key={index}\n width={cellSize}\n height={cellSize}\n viewBox={`0 0 ${cellSize} ${cellSize}`}\n style={{\n display: \"block\",\n background: CONFIG.color.bands.silhouette.fillEven,\n border: `${CONFIG.size.stroke.bandPx}px solid ${CONFIG.color.bands.silhouette.stroke}`,\n borderRadius: \"8px\"\n }}\n >\n {translatedPrimitives.map(\n (prim: { kind: TanKind; polygon: Poly }, i: number) => {\n let fillColor: string;\n\n if (usePrimitiveColors) {\n // Use primitive colors: map piece type to color via primitiveColorIndices\n const primitiveIndex = kindToIndex[prim.kind];\n if (\n primitiveIndex !== undefined &&\n primitiveColorIndices[primitiveIndex] !== undefined\n ) {\n const colorIndex = primitiveColorIndices[primitiveIndex];\n const color = CONFIG.color.primitiveColors[colorIndex];\n fillColor = color || CONFIG.color.piece.validFill;\n } else {\n fillColor = CONFIG.color.piece.validFill;\n }\n } else {\n // Use default piece color when not using primitive colors\n fillColor = CONFIG.color.piece.validFill;\n }\n\n return (\n <path\n key={`prim-${i}`}\n d={pathD(prim.polygon)}\n fill={fillColor}\n opacity={CONFIG.opacity.piece.normal}\n stroke={CONFIG.color.tangramDecomposition.stroke}\n strokeWidth={CONFIG.size.stroke.tangramDecompositionPx}\n />\n );\n }\n )}\n </svg>\n );\n } else {\n // Show as silhouette (merged shape) - scale and center\n const scaledMask = mask.map((poly) =>\n poly.map((p) => ({\n x: p.x * tangramScale,\n y: p.y * tangramScale\n }))\n );\n\n // Find bounds\n let minX = Infinity,\n minY = Infinity,\n maxX = -Infinity,\n maxY = -Infinity;\n for (const poly of scaledMask) {\n for (const p of poly) {\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n }\n }\n\n // Center in viewport\n const width = maxX - minX;\n const height = maxY - minY;\n const tx = cellSize / 2 - (minX + width / 2);\n const ty = cellSize / 2 - (minY + height / 2);\n\n const placedMask = scaledMask.map((poly) =>\n poly.map((p) => ({ x: p.x + tx, y: p.y + ty }))\n );\n\n return (\n <svg\n key={index}\n width={cellSize}\n height={cellSize}\n viewBox={`0 0 ${cellSize} ${cellSize}`}\n style={{\n display: \"block\",\n background: CONFIG.color.bands.silhouette.fillEven,\n border: `${CONFIG.size.stroke.bandPx}px solid ${CONFIG.color.bands.silhouette.stroke}`,\n borderRadius: \"8px\"\n }}\n >\n {placedMask.map((poly, i) => (\n <path\n key={`sil-${i}`}\n d={pathD(poly)}\n fill={CONFIG.color.piece.validFill}\n opacity={CONFIG.opacity.piece.normal}\n stroke=\"none\"\n />\n ))}\n </svg>\n );\n }\n };\n\n const isSubmitDisabled = response.trim().length === 0;\n\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n background: CONFIG.color.background,\n width: \"100%\",\n height: `calc(100vh - ${PROGRESS_BAR_HEIGHT}px)`,\n overflow: \"hidden\",\n fontFamily: \"Roboto, sans-serif\",\n boxSizing: \"border-box\",\n padding: `${CONTAINER_PADDING}px`\n }}\n >\n {/* Grid of tangrams - takes up available space */}\n <div\n style={{\n flex: \"1 1 auto\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: 0\n }}\n >\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(${n_cols}, ${cellSize}px)`,\n gridTemplateRows: `repeat(${n_rows}, ${cellSize}px)`,\n gap: `${GRID_GAP}px`\n }}\n >\n {processedTangrams.slice(0, n_rows * n_cols).map((t, i) =>\n renderTangram(t, i)\n )}\n </div>\n </div>\n\n {/* Controls section - fixed height at bottom */}\n <div\n ref={controlsRef}\n style={{\n flex: \"0 0 auto\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: \"4px\",\n paddingTop: \"4px\"\n }}\n >\n {/* Prompt text */}\n <div\n style={{\n fontSize: \"14px\",\n textAlign: \"center\",\n maxWidth: \"90vw\"\n }}\n >\n {prompt_text}\n </div>\n\n {/* Text input and submit button side by side */}\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"12px\"\n }}\n >\n <input\n type=\"text\"\n value={response}\n onChange={(e) => setResponse(e.target.value)}\n style={{\n width: \"min(400px, 50vw)\",\n padding: \"6px 10px\",\n fontSize: \"14px\",\n borderRadius: \"6px\",\n border: \"2px solid #ccc\",\n fontFamily: \"inherit\",\n boxSizing: \"border-box\"\n }}\n placeholder=\"Type your response here...\"\n />\n\n <button\n className=\"jspsych-btn\"\n onClick={handleSubmit}\n disabled={isSubmitDisabled}\n style={{\n padding: \"6px 16px\",\n fontSize: \"13px\",\n cursor: isSubmitDisabled ? \"not-allowed\" : \"pointer\",\n opacity: isSubmitDisabled ? 0.5 : 1,\n flexShrink: 0\n }}\n >\n {button_text}\n </button>\n </div>\n </div>\n </div>\n );\n}\n","import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\nimport { startGridTrial, StartGridTrialParams } from \"./GridApp\";\n\nconst info = {\n name: \"tangram-grid\",\n version: \"1.0.0\",\n parameters: {\n /** Array of tangram specifications to display in the grid */\n tangrams: {\n type: ParameterType.COMPLEX,\n default: undefined,\n description: \"Array of TangramSpec objects to display in the grid\"\n },\n /** Number of rows in the grid */\n n_rows: {\n type: ParameterType.INT,\n default: 1,\n description: \"Number of rows in the tangram grid\"\n },\n /** Number of columns in the grid */\n n_cols: {\n type: ParameterType.INT,\n default: 1,\n description: \"Number of columns in the tangram grid\"\n },\n /** Prompt text displayed above the text input */\n prompt_text: {\n type: ParameterType.STRING,\n default: \"\",\n description: \"Text displayed above the text input field\"\n },\n /** Label for the submit button */\n button_text: {\n type: ParameterType.STRING,\n default: \"Submit\",\n description: \"Text displayed on the submit button\"\n },\n /** Whether to show tangrams decomposed into primitives */\n show_tangram_decomposition: {\n type: ParameterType.BOOL,\n default: false,\n description:\n \"Whether to show tangrams decomposed into individual primitives\"\n },\n /** Whether to use distinct colors for each primitive type */\n use_primitive_colors: {\n type: ParameterType.BOOL,\n default: false,\n description:\n \"Whether each primitive shape type should have its own distinct color\"\n },\n /** Indices mapping primitives to colors */\n primitive_color_indices: {\n type: ParameterType.OBJECT,\n default: [0, 1, 2, 3, 4],\n description:\n \"Array of 5 integers indexing into primitiveColors array, mapping \" +\n \"[square, smalltriangle, parallelogram, medtriangle, largetriangle] \" +\n \"to colors\"\n },\n /** Callback fired when trial ends */\n onTrialEnd: {\n type: ParameterType.FUNCTION,\n default: undefined,\n description: \"Callback when trial completes with full data\"\n }\n },\n data: {\n /** The text response entered by the participant */\n response: {\n type: ParameterType.STRING,\n description: \"The text response entered by the participant\"\n },\n /** Reaction time from trial start to submit button click */\n rt: {\n type: ParameterType.INT,\n description: \"Time in milliseconds from trial start to submit\"\n }\n },\n citations: \"\"\n};\n\ntype Info = typeof info;\n\n/**\n * **tangram-grid**\n *\n * A jsPsych plugin that displays a grid of tangrams with a text input field\n * and submit button for collecting free-text responses.\n *\n * @author Sean Paul Anderson & Justin Yang\n * @see {@link https://github.com/cogtoolslab/tangram_construction.git}\n */\nclass TangramGridPlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n /**\n * Launches the trial by invoking startGridTrial with the display element,\n * parameters, and jsPsych instance.\n *\n * REQUIRES: display_element is a valid HTMLElement, trial contains valid\n * parameters\n * MODIFIES: display_element (renders React component)\n * EFFECTS: Starts the grid trial and handles cleanup on completion\n */\n trial(display_element: HTMLElement, trial: TrialType<Info>) {\n // Wrap onTrialEnd to handle React cleanup and jsPsych trial completion\n const wrappedOnTrialEnd = (data: any) => {\n // Call user-provided callback if exists\n if (trial.onTrialEnd) {\n trial.onTrialEnd(data);\n }\n\n // Clean up React first (before clearing DOM)\n const reactContext = (display_element as any).__reactContext;\n if (reactContext?.root) {\n reactContext.root.unmount();\n }\n\n // Clear display after React cleanup\n display_element.innerHTML = \"\";\n\n // Finish jsPsych trial with data\n this.jsPsych.finishTrial(data);\n };\n\n // Create parameter object for wrapper\n const params: StartGridTrialParams = {\n tangrams: trial.tangrams,\n n_rows: trial.n_rows,\n n_cols: trial.n_cols,\n prompt_text: trial.prompt_text,\n button_text: trial.button_text,\n show_tangram_decomposition: trial.show_tangram_decomposition,\n usePrimitiveColors: trial.use_primitive_colors,\n primitiveColorIndices: trial.primitive_color_indices,\n onTrialEnd: wrappedOnTrialEnd\n };\n\n // Use React wrapper to start the trial\n const { root, display_element: element, jsPsych } = startGridTrial(\n display_element,\n params,\n this.jsPsych\n );\n\n // Store React context for cleanup\n (element as any).__reactContext = { root, jsPsych };\n }\n}\n\nexport default TangramGridPlugin;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/core/config/config.ts","../../src/plugins/tangram-grid/GridApp.tsx","../../src/plugins/tangram-grid/index.ts"],"sourcesContent":["// src/core/config/config.ts\nexport type Config = {\n color: {\n background: string;\n bands: {\n silhouette: { fillEven: string; fillOdd: string; stroke: string };\n workspace: { fillEven: string; fillOdd: string; stroke: string };\n };\n completion: { fill: string; stroke: string };\n silhouetteMask: string;\n anchors: { invalid: string; valid: string };\n piece: { draggingFill: string; validFill: string; invalidFill: string; invalidStroke: string; selectedStroke: string; allGreenStroke: string; borderStroke: string };\n ui: { light: string; dark: string };\n blueprint: { fill: string; selectedStroke: string; badgeFill: string; labelFill: string };\n tangramDecomposition: { stroke: string };\n primitiveColors: string[];\n };\n opacity: {\n blueprint: number;\n silhouetteMask: number;\n anchors: { invalid: number; valid: number };\n piece: { invalid: number; dragging: number; locked: number; normal: number };\n };\n size: {\n stroke: { bandPx: number; pieceSelectedPx: number; allGreenStrokePx: number; pieceBorderPx: number; tangramDecompositionPx: number };\n anchorRadiusPx: { valid: number; invalid: number };\n badgeFontPx: number;\n centerBadge: { fractionOfOuterR: number; minPx: number; marginPx: number };\n invalidMarker: { sizePx: number; strokePx: number };\n };\n layout: {\n grid: { stepPx: number; unitPx: number };\n paddingPx: number;\n viewportScale: number;\n /** renamed from capacity → constraints */\n constraints: {\n workspaceDiamAnchors: number;\n quickstashDiamAnchors: number;\n primitiveDiamAnchors: number;\n };\n defaults: { maxQuickstashSlots: number };\n };\n game: {\n snapRadiusPx: number;\n showBorders: boolean;\n hideTouchingBorders: boolean;\n silhouettesBelowPieces: boolean;\n };\n};\n\nexport const CONFIG: Config = {\n color: {\n background: \"#fff7e0ff\",\n bands: {\n silhouette: { fillEven: \"#ffffff\", fillOdd: \"#ffffff\", stroke: \"#b1b1b1\" },\n workspace: { fillEven: \"#ffffff\", fillOdd: \"#ffffff\", stroke: \"#b1b1b1\" }\n },\n completion: { fill: \"#ccffcc\", stroke: \"#13da57\" },\n silhouetteMask: \"#374151\",\n anchors: { invalid: \"#7dd3fc\", valid: \"#475569\" },\n // validFill used here for placed composites\n piece: { draggingFill: \"#8e7cc3\", validFill: \"#8e7cc3\", invalidFill: \"#d55c00\", invalidStroke: \"#dc2626\", selectedStroke: \"#674ea7\", allGreenStroke: \"#86efac\", borderStroke: \"#674ea7\" },\n ui: { light: \"#60a5fa\", dark: \"#1d4ed8\" },\n blueprint: { fill: \"#374151\", selectedStroke: \"#111827\", badgeFill: \"#000000\", labelFill: \"#ffffff\" },\n tangramDecomposition: { stroke: \"#fef2cc\" },\n primitiveColors: [ // from seaborn \"colorblind\" palette, 6 colors, with red omitted\n '#0173b2',\n '#de8f05',\n '#029e73',\n '#cc78bc',\n '#ca9161'\n ]\n },\n opacity: {\n blueprint: 0.6,\n silhouetteMask: 0.25,\n //anchors: { valid: 0.80, invalid: 0.50 },\n anchors: { invalid: 0.0, valid: 0.0 },\n piece: { invalid: 1, dragging: 1, locked: 1, normal: 1 },\n },\n size: {\n stroke: { bandPx: 5, pieceSelectedPx: 5, allGreenStrokePx: 10, pieceBorderPx: 2, tangramDecompositionPx: 1 },\n anchorRadiusPx: { valid: 1.0, invalid: 1.0 },\n badgeFontPx: 16,\n centerBadge: { fractionOfOuterR: 0.15, minPx: 20, marginPx: 4 },\n invalidMarker: { sizePx: 10, strokePx: 4 }\n },\n layout: {\n grid: { stepPx: 20, unitPx: 40 },\n paddingPx: 1,\n viewportScale: 0.8,\n constraints: {\n workspaceDiamAnchors: 10, // num anchors req'd to be on diagonal\n quickstashDiamAnchors: 7, // num anchors req'd to be in single quickstash slot\n primitiveDiamAnchors: 5,\n },\n defaults: { maxQuickstashSlots: 1 }\n },\n game: {\n snapRadiusPx: 15,\n showBorders: false,\n hideTouchingBorders: true,\n silhouettesBelowPieces: true\n }\n};","/**\n * GridApp.tsx - React wrapper for tangram grid display with text input\n *\n * This component displays a grid of tangrams with a text input field\n * and submit button for collecting free-text responses.\n */\n\nimport React, { useRef, useState, useMemo, useEffect } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { JsPsych } from \"jspsych\";\nimport type { Poly, TanKind } from \"../../core/domain/types\";\nimport { CONFIG } from \"../../core/config/config\";\n\nexport interface StartGridTrialParams {\n tangrams: any[];\n n_rows: number;\n n_cols: number;\n prompt_text: string;\n button_text: string;\n show_tangram_decomposition?: boolean;\n usePrimitiveColors?: boolean;\n primitiveColorIndices?: number[];\n onTrialEnd?: (data: any) => void;\n}\n\n/**\n * Start a grid trial by rendering the GridView component\n *\n * REQUIRES: display_element is a valid HTMLElement\n * MODIFIES: display_element (renders React into it)\n * EFFECTS: Creates a React root and renders GridView with the given params\n */\nexport function startGridTrial(\n display_element: HTMLElement,\n params: StartGridTrialParams,\n _jsPsych: JsPsych\n) {\n const root = createRoot(display_element);\n root.render(React.createElement(GridView, { params }));\n return { root, display_element, jsPsych: _jsPsych };\n}\n\ninterface GridViewProps {\n params: StartGridTrialParams;\n}\n\n/**\n * Compute bounding box for an array of polygons\n *\n * REQUIRES: polys is an array of polygons with {x, y} points\n * MODIFIES: nothing\n * EFFECTS: Returns {minX, minY, maxX, maxY, width, height} of bounding box\n */\nfunction computeBounds(polys: Poly[]) {\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n for (const poly of polys) {\n for (const p of poly) {\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n }\n }\n return { minX, minY, maxX, maxY, width: maxX - minX, height: maxY - minY };\n}\n\n/**\n * GridView - Main React component for the grid trial\n *\n * REQUIRES: params contains valid tangram specs and grid dimensions\n * MODIFIES: nothing\n * EFFECTS: Renders a grid of tangrams, text input, and submit button;\n * calls onTrialEnd with response data when submitted\n */\nfunction GridView({ params }: GridViewProps) {\n const {\n tangrams,\n n_rows,\n n_cols,\n prompt_text,\n button_text,\n show_tangram_decomposition = false,\n usePrimitiveColors = false,\n primitiveColorIndices = [0, 1, 2, 3, 4],\n onTrialEnd\n } = params;\n\n // Extract non-callback params for trial data\n const { onTrialEnd: _, ...trialParams } = params;\n\n const trialStartTime = useRef<number>(Date.now());\n const [response, setResponse] = useState<string>(\"\");\n const [cellSize, setCellSize] = useState<number>(100);\n const controlsRef = useRef<HTMLDivElement>(null);\n\n // Layout constants\n const GRID_GAP = 6;\n const CONTAINER_PADDING = 8;\n const CELL_MARGIN = 0.05; // 5% margin inside cell for tangram\n // jsPsych progress bar: 20px height + 8px top/bottom padding + 18px margin\n const PROGRESS_BAR_HEIGHT = 58;\n // Border width on each cell (from CONFIG.size.stroke.bandPx)\n const CELL_BORDER = CONFIG.size.stroke.bandPx ?? 1;\n // Extra buffer to prevent scrollbars from appearing\n const SAFETY_BUFFER = 8;\n\n // Inject style to override jspsych-content max-width constraint\n useEffect(() => {\n const styleId = \"tangram-grid-jspsych-override\";\n if (!document.getElementById(styleId)) {\n const style = document.createElement(\"style\");\n style.id = styleId;\n style.textContent = `\n .jspsych-content {\n max-width: 100% !important;\n width: 100% !important;\n }\n `;\n document.head.appendChild(style);\n }\n return () => {\n const style = document.getElementById(styleId);\n if (style) style.remove();\n };\n }, []);\n\n // Canonical piece names\n const CANON = new Set([\n \"square\",\n \"smalltriangle\",\n \"parallelogram\",\n \"medtriangle\",\n \"largetriangle\"\n ]);\n\n // Convert TangramSpec to internal format with mask and decomposition\n const processedTangrams = useMemo(() => {\n return tangrams.map((tangramSpec) => {\n const filteredTans = tangramSpec.solutionTans.filter((tan: any) => {\n const tanName = tan.name ?? tan.kind;\n return CANON.has(tanName);\n });\n\n const mask: Poly[] = filteredTans.map((tan: any) => {\n return tan.vertices.map(([x, y]: number[]) => ({\n x: x ?? 0,\n y: -(y ?? 0)\n }));\n });\n\n const primitiveDecomposition = filteredTans.map((tan: any) => ({\n kind: (tan.name ?? tan.kind) as TanKind,\n polygon: tan.vertices.map(([x, y]: number[]) => ({\n x: x ?? 0,\n y: -(y ?? 0)\n }))\n }));\n\n return {\n tangramId: tangramSpec.tangramID,\n mask,\n primitiveDecomposition\n };\n });\n }, [tangrams]);\n\n // Find the largest tangram dimensions to determine scaling\n const maxTangramExtent = useMemo(() => {\n let maxExtent = 0;\n for (const t of processedTangrams) {\n const bounds = computeBounds(t.mask);\n maxExtent = Math.max(maxExtent, bounds.width, bounds.height);\n }\n return maxExtent || 1;\n }, [processedTangrams]);\n\n // Calculate cell size based on available space\n useEffect(() => {\n const calculateCellSize = () => {\n // Use document.documentElement for more accurate viewport size\n const viewportWidth = document.documentElement.clientWidth;\n const viewportHeight = document.documentElement.clientHeight;\n\n // Reserve space for controls (prompt + input row) and progress bar\n const controlsHeight = controlsRef.current?.offsetHeight ?? 70;\n\n // Available space for the grid (subtract progress bar, controls, padding, buffer)\n const availableWidth =\n viewportWidth - CONTAINER_PADDING * 2 - SAFETY_BUFFER;\n const availableHeight =\n viewportHeight - PROGRESS_BAR_HEIGHT - controlsHeight -\n CONTAINER_PADDING * 2 - SAFETY_BUFFER;\n\n // Account for gaps between cells AND borders on each cell\n // Each cell has border on all sides, so total border per cell = 2 * CELL_BORDER\n const totalHorizontalGaps = GRID_GAP * (n_cols - 1);\n const totalVerticalGaps = GRID_GAP * (n_rows - 1);\n const totalHorizontalBorders = CELL_BORDER * 2 * n_cols;\n const totalVerticalBorders = CELL_BORDER * 2 * n_rows;\n\n // Calculate max cell size that fits in available space\n const maxCellWidth =\n (availableWidth - totalHorizontalGaps - totalHorizontalBorders) / n_cols;\n const maxCellHeight =\n (availableHeight - totalVerticalGaps - totalVerticalBorders) / n_rows;\n\n // Use the smaller dimension to keep cells square\n const newCellSize = Math.floor(Math.min(maxCellWidth, maxCellHeight));\n setCellSize(Math.max(newCellSize, 50)); // Minimum 50px\n };\n\n calculateCellSize();\n window.addEventListener(\"resize\", calculateCellSize);\n return () => window.removeEventListener(\"resize\", calculateCellSize);\n }, [n_rows, n_cols]);\n\n // Scale factor to fit largest tangram in cell with margin\n const tangramScale = useMemo(() => {\n const usableSize = cellSize * (1 - CELL_MARGIN * 2);\n return usableSize / maxTangramExtent;\n }, [cellSize, maxTangramExtent]);\n\n // Mapping from TanKind to color index\n const kindToIndex: Record<TanKind, number> = {\n square: 0,\n smalltriangle: 1,\n parallelogram: 2,\n medtriangle: 3,\n largetriangle: 4\n };\n\n // Helper to convert polygon to SVG path\n const pathD = (poly: Poly): string => {\n if (!poly || poly.length === 0) return \"\";\n const moves = poly.map(\n (p, i) => `${i === 0 ? \"M\" : \"L\"} ${p.x} ${p.y}`\n );\n return moves.join(\" \") + \" Z\";\n };\n\n // Handle submit\n const handleSubmit = () => {\n const rt = Date.now() - trialStartTime.current;\n const trialData = {\n response,\n rt,\n trialParams: {\n ...trialParams,\n tangramIDs: processedTangrams.map((t) => t.tangramId)\n }\n };\n if (onTrialEnd) {\n onTrialEnd(trialData);\n }\n };\n\n // Render a single tangram SVG\n const renderTangram = (\n tangramData: (typeof processedTangrams)[0],\n index: number\n ) => {\n const { mask, primitiveDecomposition } = tangramData;\n\n if (show_tangram_decomposition) {\n // Show individual primitives with optional coloring\n // Scale each primitive and fit to viewport while preserving relative positions\n const scaledPrimitives = primitiveDecomposition.map(\n (prim: { kind: TanKind; polygon: Poly }) => {\n const scaledPoly = prim.polygon.map((p: { x: number; y: number }) => ({\n x: p.x * tangramScale,\n y: p.y * tangramScale\n }));\n return { kind: prim.kind, polygon: scaledPoly };\n }\n );\n\n // Find bounds of all primitives together\n let minX = Infinity,\n minY = Infinity,\n maxX = -Infinity,\n maxY = -Infinity;\n for (const prim of scaledPrimitives) {\n for (const p of prim.polygon) {\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n }\n }\n\n // Compute translation to center all primitives together in viewport\n const width = maxX - minX;\n const height = maxY - minY;\n const tx = cellSize / 2 - (minX + width / 2);\n const ty = cellSize / 2 - (minY + height / 2);\n\n const translatedPrimitives = scaledPrimitives.map(\n (prim: { kind: TanKind; polygon: Poly }) => ({\n kind: prim.kind,\n polygon: prim.polygon.map((p: { x: number; y: number }) => ({\n x: p.x + tx,\n y: p.y + ty\n }))\n })\n );\n\n return (\n <svg\n key={index}\n width={cellSize}\n height={cellSize}\n viewBox={`0 0 ${cellSize} ${cellSize}`}\n style={{\n display: \"block\",\n background: CONFIG.color.bands.silhouette.fillEven,\n border: `${CONFIG.size.stroke.bandPx}px solid ${CONFIG.color.bands.silhouette.stroke}`,\n borderRadius: \"8px\"\n }}\n >\n {translatedPrimitives.map(\n (prim: { kind: TanKind; polygon: Poly }, i: number) => {\n let fillColor: string;\n\n if (usePrimitiveColors) {\n // Use primitive colors: map piece type to color via primitiveColorIndices\n const primitiveIndex = kindToIndex[prim.kind];\n if (\n primitiveIndex !== undefined &&\n primitiveColorIndices[primitiveIndex] !== undefined\n ) {\n const colorIndex = primitiveColorIndices[primitiveIndex];\n const color = CONFIG.color.primitiveColors[colorIndex];\n fillColor = color || CONFIG.color.piece.validFill;\n } else {\n fillColor = CONFIG.color.piece.validFill;\n }\n } else {\n // Use default piece color when not using primitive colors\n fillColor = CONFIG.color.piece.validFill;\n }\n\n return (\n <path\n key={`prim-${i}`}\n d={pathD(prim.polygon)}\n fill={fillColor}\n opacity={CONFIG.opacity.piece.normal}\n stroke={CONFIG.color.tangramDecomposition.stroke}\n strokeWidth={CONFIG.size.stroke.tangramDecompositionPx}\n />\n );\n }\n )}\n </svg>\n );\n } else {\n // Show as silhouette (merged shape) - scale and center\n const scaledMask = mask.map((poly) =>\n poly.map((p) => ({\n x: p.x * tangramScale,\n y: p.y * tangramScale\n }))\n );\n\n // Find bounds\n let minX = Infinity,\n minY = Infinity,\n maxX = -Infinity,\n maxY = -Infinity;\n for (const poly of scaledMask) {\n for (const p of poly) {\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n }\n }\n\n // Center in viewport\n const width = maxX - minX;\n const height = maxY - minY;\n const tx = cellSize / 2 - (minX + width / 2);\n const ty = cellSize / 2 - (minY + height / 2);\n\n const placedMask = scaledMask.map((poly) =>\n poly.map((p) => ({ x: p.x + tx, y: p.y + ty }))\n );\n\n return (\n <svg\n key={index}\n width={cellSize}\n height={cellSize}\n viewBox={`0 0 ${cellSize} ${cellSize}`}\n style={{\n display: \"block\",\n background: CONFIG.color.bands.silhouette.fillEven,\n border: `${CONFIG.size.stroke.bandPx}px solid ${CONFIG.color.bands.silhouette.stroke}`,\n borderRadius: \"8px\"\n }}\n >\n {placedMask.map((poly, i) => (\n <path\n key={`sil-${i}`}\n d={pathD(poly)}\n fill={CONFIG.color.piece.validFill}\n opacity={CONFIG.opacity.piece.normal}\n stroke=\"none\"\n />\n ))}\n </svg>\n );\n }\n };\n\n const isSubmitDisabled = response.trim().length === 0;\n\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n background: CONFIG.color.background,\n width: \"100%\",\n height: `calc(100vh - ${PROGRESS_BAR_HEIGHT}px)`,\n overflow: \"hidden\",\n fontFamily: \"Roboto, sans-serif\",\n boxSizing: \"border-box\",\n padding: `${CONTAINER_PADDING}px`\n }}\n >\n {/* Grid of tangrams - takes up available space */}\n <div\n style={{\n flex: \"1 1 auto\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n minHeight: 0\n }}\n >\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: `repeat(${n_cols}, ${cellSize}px)`,\n gridTemplateRows: `repeat(${n_rows}, ${cellSize}px)`,\n gap: `${GRID_GAP}px`\n }}\n >\n {processedTangrams.slice(0, n_rows * n_cols).map((t, i) =>\n renderTangram(t, i)\n )}\n </div>\n </div>\n\n {/* Controls section - fixed height at bottom */}\n <div\n ref={controlsRef}\n style={{\n flex: \"0 0 auto\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: \"4px\",\n paddingTop: \"4px\"\n }}\n >\n {/* Prompt text */}\n <div\n style={{\n fontSize: \"14px\",\n textAlign: \"center\",\n maxWidth: \"90vw\"\n }}\n >\n {prompt_text}\n </div>\n\n {/* Text input and submit button side by side */}\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"12px\"\n }}\n >\n <input\n type=\"text\"\n value={response}\n onChange={(e) => setResponse(e.target.value)}\n style={{\n width: \"min(400px, 50vw)\",\n padding: \"6px 10px\",\n fontSize: \"14px\",\n borderRadius: \"6px\",\n border: \"2px solid #ccc\",\n fontFamily: \"inherit\",\n boxSizing: \"border-box\"\n }}\n placeholder=\"Type your response here...\"\n />\n\n <button\n className=\"jspsych-btn\"\n onClick={handleSubmit}\n disabled={isSubmitDisabled}\n style={{\n padding: \"6px 16px\",\n fontSize: \"13px\",\n cursor: isSubmitDisabled ? \"not-allowed\" : \"pointer\",\n opacity: isSubmitDisabled ? 0.5 : 1,\n flexShrink: 0\n }}\n >\n {button_text}\n </button>\n </div>\n </div>\n </div>\n );\n}\n","import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from \"jspsych\";\nimport { startGridTrial, StartGridTrialParams } from \"./GridApp\";\n\nconst info = {\n name: \"tangram-grid\",\n version: \"1.0.0\",\n parameters: {\n /** Array of tangram specifications to display in the grid */\n tangrams: {\n type: ParameterType.COMPLEX,\n default: undefined,\n description: \"Array of TangramSpec objects to display in the grid\"\n },\n /** Number of rows in the grid */\n n_rows: {\n type: ParameterType.INT,\n default: 1,\n description: \"Number of rows in the tangram grid\"\n },\n /** Number of columns in the grid */\n n_cols: {\n type: ParameterType.INT,\n default: 1,\n description: \"Number of columns in the tangram grid\"\n },\n /** Prompt text displayed above the text input */\n prompt_text: {\n type: ParameterType.STRING,\n default: \"\",\n description: \"Text displayed above the text input field\"\n },\n /** Label for the submit button */\n button_text: {\n type: ParameterType.STRING,\n default: \"Submit\",\n description: \"Text displayed on the submit button\"\n },\n /** Whether to show tangrams decomposed into primitives */\n show_tangram_decomposition: {\n type: ParameterType.BOOL,\n default: false,\n description:\n \"Whether to show tangrams decomposed into individual primitives\"\n },\n /** Whether to use distinct colors for each primitive type */\n use_primitive_colors: {\n type: ParameterType.BOOL,\n default: false,\n description:\n \"Whether each primitive shape type should have its own distinct color\"\n },\n /** Indices mapping primitives to colors */\n primitive_color_indices: {\n type: ParameterType.OBJECT,\n default: [0, 1, 2, 3, 4],\n description:\n \"Array of 5 integers indexing into primitiveColors array, mapping \" +\n \"[square, smalltriangle, parallelogram, medtriangle, largetriangle] \" +\n \"to colors\"\n },\n /** Callback fired when trial ends */\n onTrialEnd: {\n type: ParameterType.FUNCTION,\n default: undefined,\n description: \"Callback when trial completes with full data\"\n }\n },\n data: {\n /** The text response entered by the participant */\n response: {\n type: ParameterType.STRING,\n description: \"The text response entered by the participant\"\n },\n /** Reaction time from trial start to submit button click */\n rt: {\n type: ParameterType.INT,\n description: \"Time in milliseconds from trial start to submit\"\n }\n },\n citations: \"\"\n};\n\ntype Info = typeof info;\n\n/**\n * **tangram-grid**\n *\n * A jsPsych plugin that displays a grid of tangrams with a text input field\n * and submit button for collecting free-text responses.\n *\n * @author Sean Paul Anderson & Justin Yang\n * @see {@link https://github.com/cogtoolslab/tangram_construction.git}\n */\nclass TangramGridPlugin implements JsPsychPlugin<Info> {\n static info = info;\n\n constructor(private jsPsych: JsPsych) {}\n\n /**\n * Launches the trial by invoking startGridTrial with the display element,\n * parameters, and jsPsych instance.\n *\n * REQUIRES: display_element is a valid HTMLElement, trial contains valid\n * parameters\n * MODIFIES: display_element (renders React component)\n * EFFECTS: Starts the grid trial and handles cleanup on completion\n */\n trial(display_element: HTMLElement, trial: TrialType<Info>) {\n // Wrap onTrialEnd to handle React cleanup and jsPsych trial completion\n const wrappedOnTrialEnd = (data: any) => {\n // Call user-provided callback if exists\n if (trial.onTrialEnd) {\n trial.onTrialEnd(data);\n }\n\n // Clean up React first (before clearing DOM)\n const reactContext = (display_element as any).__reactContext;\n if (reactContext?.root) {\n reactContext.root.unmount();\n }\n\n // Clear display after React cleanup\n display_element.innerHTML = \"\";\n\n // Finish jsPsych trial with data\n this.jsPsych.finishTrial(data);\n };\n\n // Create parameter object for wrapper\n const params: StartGridTrialParams = {\n tangrams: trial.tangrams,\n n_rows: trial.n_rows,\n n_cols: trial.n_cols,\n prompt_text: trial.prompt_text,\n button_text: trial.button_text,\n show_tangram_decomposition: trial.show_tangram_decomposition,\n usePrimitiveColors: trial.use_primitive_colors,\n primitiveColorIndices: trial.primitive_color_indices,\n onTrialEnd: wrappedOnTrialEnd\n };\n\n // Use React wrapper to start the trial\n const { root, display_element: element, jsPsych } = startGridTrial(\n display_element,\n params,\n this.jsPsych\n );\n\n // Store React context for cleanup\n (element as any).__reactContext = { root, jsPsych };\n }\n}\n\nexport default TangramGridPlugin;\n"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -4968,6 +4968,7 @@ function GridView({ params }) {
4968
4968
  primitiveColorIndices = [0, 1, 2, 3, 4],
4969
4969
  onTrialEnd
4970
4970
  } = params;
4971
+ const { onTrialEnd: _, ...trialParams } = params;
4971
4972
  const trialStartTime = React.useRef(Date.now());
4972
4973
  const [response, setResponse] = React.useState("");
4973
4974
  const [cellSize, setCellSize] = React.useState(100);
@@ -5080,12 +5081,10 @@ function GridView({ params }) {
5080
5081
  const trialData = {
5081
5082
  response,
5082
5083
  rt,
5083
- n_rows,
5084
- n_cols,
5085
- tangram_ids: processedTangrams.map((t) => t.tangramId),
5086
- show_tangram_decomposition,
5087
- use_primitive_colors: usePrimitiveColors,
5088
- primitive_color_indices: primitiveColorIndices
5084
+ trialParams: {
5085
+ ...trialParams,
5086
+ tangramIDs: processedTangrams.map((t) => t.tangramId)
5087
+ }
5089
5088
  };
5090
5089
  if (onTrialEnd) {
5091
5090
  onTrialEnd(trialData);