JSON Reference

Writing a Custom JSON File

Pre-populate a subject with topics, questions, procedures, definitions, and more by writing a .json file and importing it into the app.

Back to User Manual

How to Import

How to import: Click the gear icon in the top-right of the subject header bar, then select "Import (.json)". Pick your .json file. The app creates a new subject immediately. If a subject with the same name already exists, it appends "(2)", "(3)", and so on to avoid overwriting anything.

The file must be valid JSON. The _type field must be exactly "dacosta-thinklab-backup". The app rejects any file that does not match. Use 4 for _version. All internal IDs are remapped on import so you can use any values you like.

Minimum Valid File

Save the text below as anything.json and it imports as a blank subject. Add topics, questions, procedures, and more around it.

{
  "_type":     "dacosta-thinklab-backup",
  "_version":  4,
  "subject": {
    "id":    1,
    "name":  "My Subject",
    "color": "#2455a4"
  },
  "topics":       [],
  "questions":    [],
  "arrows":       [],
  "groups":       [],
  "procedures":   [],
  "procSteps":    [],
  "definitions":  [],
  "confusingTerms": []
}

The subject id can be any number; the app replaces it with a new one on import. The ThinkNote field inside each question is called myAnswer. Question text goes in the text field. Topics are a top-level array (not nested inside the subject). Valid stage values are: "unknown", "heard", "think", "understood".

Topics

Each object in the topics array becomes one card on the Topic Board. You invent the id strings yourself; they just need to be unique within your file, because questions and arrows reference them. The app remaps everything to new IDs on import.

"topics": [
  {
    "id":        "t1",          // unique string, you choose
    "name":      "Derivatives",
    "col":       "core",        // "uncat" | "core" | "detail" | "secondary"
    "slot":      0,             // vertical position, 0 is top of column
    "orderNum":  1,             // number shown on card corner (optional)
    "topicType": "concept"      // type badge (optional)
  }
]
FieldTypeDetails
id required
string
Any unique string, e.g. "t1", "topic-limits". Referenced in questions and arrows.
name required
string
The topic name shown on the card.
col required
string
Column the card starts in. One of: "uncat", "core", "detail", "secondary".
slot optional
number
Vertical position within the column. 0 is the top card.
orderNum optional
number
Number shown on the card's left edge.
topicType optional
string
Badge below the card name. Values: "concept", "mechanism", "method", "problem", "constraint", "tool".

Questions

Each object in the questions array becomes one row in the Questions section. Link a question to topics using the id strings you defined above. The ThinkNote content goes in myAnswer.

"questions": [
  {
    "id":         101,
    "text":       "What is a derivative?",
    "stage":      "unknown",        // "unknown" | "heard" | "think" | "understood"
    "topicIds":   ["t1"],
    "stuck":      false,
    "myAnswer":   "A derivative measures the rate of change.",
    "dependsOn":  null,             // id of prerequisite question, or null
    "depthChecks": [true,false,false,false,false,false],
    "depthNotes":  ["It measures how fast something changes at one moment.","","","","",""],
    "sources":    [
      { "tag": "Book", "text": "Stewart Calculus Ch.3", "helped": true, "notClear": false }
    ],
    "mistakeItems": [
      {
        "id": "m1",
        "text": "Confusing average rate with instantaneous rate",
        "type": "Understanding",
        "severity": "Would cause a problem",
        "resolved": false
      }
    ]
  }
]
FieldTypeDetails
id required
number
Any unique number. The app reassigns IDs on import.
text required
string
The question text.
stage required
string
Starting stage: "unknown", "heard", "think", "understood".
topicIds required
array
Array of topic id strings. Use [] for an unlinked question.
stuck optional
string/false
false or one of: "missing", "dontget", "notconfident".
myAnswer optional
string
Content for the ThinkNote editor. Plain text or HTML.
depthChecks optional
array
6 booleans: Teach, Mistakes, Scenario, Limits, Contrast, Apply.
depthNotes optional
array
6 strings, one per depth check in the same order.
dependsOn optional
number
The id of a prerequisite question. Creates a dependency chain.
sources optional
array
Array of source objects with tag, text, helped, notClear.
mistakeItems optional
array
Array of mistake objects with id, text, type, severity, resolved.

Arrows (Connections)

Each object in the arrows array draws a curved line between two topic cards.

"arrows": [
  {
    "id":        "a1",
    "from":      "t2",
    "to":        "t1",
    "type":      "requires",
    "color":     "#2455a4",
    "label":     "Requires",
    "sentence":  "Chain Rule requires Derivatives first.",
    "direction": "forward",
    "lineStyle": "dashed"
  }
]

Built-in type values: "requires", "leads-to", "extends", "causes", "supports", "opposes", "results". Any custom string also works.

Groups

Groups visually cluster adjacent cards in the same column under a shared colour and label.

"groups": [
  {
    "id":       "g1",
    "name":     "Differentiation Rules",
    "color":    "#1a7a60",
    "col":      "detail",
    "topicIds": ["t2", "t3"],
    "icon":     ""
  }
]

The topicIds must all share the same col value and have consecutive slot numbers.

Procedures and Steps

Procedures are visual flowcharts. Each procedure has a list of steps placed on a column/row grid. Steps can have ThinkNote modules attached (text, code, YouTube, images, etc.).

"procedures": [
  {
    "id":   "proc1",
    "name": "How to take a derivative"
  }
],

"procSteps": [
  {
    "id":          "st1",
    "procedureId": "proc1",
    "col":         0,
    "row":         0,
    "title":       "Identify the function",
    "description": "Write down f(x) clearly",
    "type":        "Start",          // "Start"|"Task"|"Decision"|"Input"|"Sub-process"|"End"
    "color":       "#1a7a60",
    "icon":        "",
    "connections": [
      {
        "id": "c1",
        "toStepId": "st2",
        "label": "next",
        "type": "Next",
        "color": "#1a7a60",
        "sentence": "Once you have identified the function, apply the power rule."
      }
    ],
    "linkTo": null,
    "modules": [
      {
        "id": "row1",
        "slots": [
          {
            "id": "s1",
            "type": "text",
            "content": "Always write the original function first."
          }
        ]
      }
    ]
  },
  {
    "id":          "st2",
    "procedureId": "proc1",
    "col":         1,
    "row":         0,
    "title":       "Apply the power rule",
    "description": "Bring down the exponent and reduce by 1",
    "type":        "Task",
    "color":       "#2455a4",
    "icon":        "",
    "connections": [],
    "modules": [
      {
        "id": "row2",
        "slots": [
          {
            "id": "s2",
            "type": "code",
            "content": "f(x) = x^3\nf'(x) = 3x^2",
            "language": "plaintext"
          },
          {
            "id": "s3",
            "type": "youtube",
            "youtubeUrl": "https://www.youtube.com/watch?v=example",
            "startTime": 120,
            "chapters": [
              { "time": 0, "label": "Intro" },
              { "time": 120, "label": "Power rule explained" }
            ]
          }
        ]
      }
    ]
  }
]

Procedure fields

FieldTypeDetails
id required
string
Unique procedure ID. Referenced by steps via procedureId.
name required
string
Procedure name shown in the sidebar.
attachedQIds optional
array
Array of question IDs linked to this procedure.
attachedTopicIds optional
array
Array of topic IDs linked to this procedure.

Step fields

FieldTypeDetails
id required
string
Unique step ID.
procedureId required
string
Must match a procedure id.
col required
number
Column position on the canvas grid (0-based).
row required
number
Row position within the column (0-based).
title required
string
Step title shown on the card.
type optional
string
"Start", "Task", "Decision", "Input", "Sub-process", "End". Defaults to "Task".
description optional
string
Short description shown below the title.
color optional
string
Hex colour for the step card.
icon optional
string
Icon identifier for the step card.
linkTo optional
string|null
ID of another step this step loops back to. Draws a loop arrow on the canvas.
connections optional
array
Array of connection objects. Each draws an arrow to another step. See connection fields below.
modules optional
array
Array of module row objects. Each row has id and slots array.

Module slot types

Each slot in a module row must have an id and a type. Additional fields depend on the type:

TypeKey fieldsDetails
text
content
Rich-text or plain text content.
code
content, language
Code string + language ID (e.g. "javascript", "python").
youtube
youtubeUrl, startTime, chapters
YouTube URL or video ID, start time in seconds, array of chapter objects.
image
imageUrl
Image URL string.
doodle
doodleKey, doodleId
References a saved doodle sketch.
webpage
pageUrl
URL to embed in an iframe.
pdf
pdfUrl
PDF document URL.
split
pages, rightContent
Code tabs on left, notes on right.

Connection fields

Each object in a step's connections array draws an arrow from that step to another step on the procedure canvas. The arrow label appears on hover.

FieldTypeDetails
id required
string
Unique connection ID (within this step).
toStepId required
string
ID of the target step this arrow points to.
label optional
string
Short label shown on the arrow (e.g. "next", "if yes").
type optional
string
Connection type, e.g. "Next", "If yes", "Requires". Defaults to the label value.
color optional
string
Hex colour for the arrow line (e.g. "#2455a4"). Defaults to the source step's colour.
sentence optional
string
Longer description shown when hovering the arrow. Use this to explain the relationship between steps.

Definitions

The definitions array creates a per-subject glossary. Each definition can be linked inline in ThinkNotes.

"definitions": [
  {
    "id":         "def1",
    "term":       "Derivative",
    "definition": "A measure of how a function changes as its input changes."
  }
]

Confusing Terms

The confusingTerms array pre-populates the confusing terms list for the subject.

"confusingTerms": [
  {
    "id":    1,
    "term":  "differential",
    "count": 1
  }
]

Complete Working Example

This file demonstrates every data type: topics, questions with ThinkNotes and sources and mistakes, connections, groups, a procedure with steps and modules, definitions, and confusing terms. Copy it, save as calculus.json, and import it.

{
  "_type": "dacosta-thinklab-backup",
  "_version": 4,
  "subject": { "id": 1, "name": "Calculus", "color": "#2455a4" },

  "topics": [
    { "id": "t1", "name": "Derivatives",   "col": "core",   "slot": 0, "topicType": "concept" },
    { "id": "t2", "name": "Chain Rule",   "col": "detail", "slot": 0, "topicType": "method"  },
    { "id": "t3", "name": "Product Rule", "col": "detail", "slot": 1, "topicType": "method"  },
    { "id": "t4", "name": "Limits",        "col": "core",   "slot": 1, "topicType": "concept" }
  ],

  "questions": [
    {
      "id": 101,
      "text": "What is a derivative?",
      "stage": "think",
      "topicIds": ["t1"],
      "stuck": false,
      "myAnswer": "A derivative measures the instantaneous rate of change of a function.",
      "depthChecks": [true, true, false, false, false, false],
      "depthNotes": [
        "It measures how fast something changes at one specific moment.",
        "Students often confuse average rate of change with instantaneous rate.",
        "", "", "", ""
      ],
      "sources": [
        { "tag": "Book", "text": "Stewart Calculus Ch.3", "helped": true, "notClear": false }
      ],
      "mistakeItems": [
        {
          "id": "m1",
          "text": "Confusing average rate with instantaneous rate",
          "type": "Understanding",
          "severity": "Would cause a problem",
          "resolved": false
        }
      ]
    },
    {
      "id": 102,
      "text": "When do I use Chain Rule vs Product Rule?",
      "stage": "unknown",
      "topicIds": ["t2", "t3"],
      "stuck": false,
      "dependsOn": 101
    },
    {
      "id": 103,
      "text": "What is a limit and why does it matter for derivatives?",
      "stage": "heard",
      "topicIds": ["t4", "t1"],
      "stuck": "dontget"
    }
  ],

  "arrows": [
    {
      "id": "a1", "from": "t2", "to": "t1",
      "type": "requires", "color": "#2455a4", "label": "Requires",
      "direction": "forward", "lineStyle": "dashed",
      "sentence": "Chain Rule requires understanding Derivatives first."
    },
    {
      "id": "a2", "from": "t4", "to": "t1",
      "type": "supports", "color": "#1a7a60", "label": "Supports",
      "direction": "forward", "lineStyle": "solid",
      "sentence": "A derivative is defined as a limit."
    }
  ],

  "groups": [
    {
      "id": "g1", "name": "Differentiation Rules",
      "color": "#1a7a60", "col": "detail",
      "topicIds": ["t2", "t3"], "icon": ""
    }
  ],

  "procedures": [
    {
      "id": "proc1", "name": "How to differentiate a function",
      "attachedTopicIds": ["t1"],
      "attachedQIds": []
    }
  ],

  "procSteps": [
    {
      "id": "st1", "procedureId": "proc1",
      "col": 0, "row": 0,
      "title": "Identify the function",
      "description": "Write down f(x) clearly",
      "type": "Start", "color": "#1a7a60",
      "linkTo": null,
      "connections": [{
        "id": "c1", "toStepId": "st2", "label": "next",
        "type": "Next", "color": "#1a7a60",
        "sentence": "Once you have identified the function, decide which rule to apply."
      }],
      "modules": [
        {
          "id": "row1",
          "slots": [{ "id": "s1", "type": "text", "content": "Always write the original function first." }]
        }
      ]
    },
    {
      "id": "st2", "procedureId": "proc1",
      "col": 1, "row": 0,
      "title": "Choose the differentiation rule",
      "description": "Is it power rule, chain rule, or product rule?",
      "type": "Decision", "color": "#c06a10",
      "linkTo": null,
      "connections": [{
        "id": "c2", "toStepId": "st3", "label": "power rule",
        "type": "If yes", "color": "#c06a10",
        "sentence": "If the function is a simple polynomial, use the power rule."
      }],
      "modules": []
    },
    {
      "id": "st3", "procedureId": "proc1",
      "col": 2, "row": 0,
      "title": "Apply and simplify",
      "description": "Compute f'(x) and simplify",
      "type": "Task", "color": "#2455a4",
      "linkTo": "st1",
      "connections": [],
      "modules": [
        {
          "id": "row2",
          "slots": [
            { "id": "s2", "type": "code", "content": "f(x) = x^3\nf'(x) = 3x^2", "language": "plaintext" },
            { "id": "s3", "type": "text", "content": "Bring down the exponent, subtract 1 from it." }
          ]
        }
      ]
    }
  ],

  "definitions": [
    {
      "id": "def1",
      "term": "Derivative",
      "definition": "A measure of how a function changes as its input changes."
    },
    {
      "id": "def2",
      "term": "Limit",
      "definition": "The value a function approaches as the input approaches some value."
    }
  ],

  "confusingTerms": [
    { "id": 1, "term": "differential", "count": 2 },
    { "id": 2, "term": "antiderivative", "count": 1 }
  ],

  "connTypes": [
    { "id": "requires", "label": "Requires", "color": "#2455a4" },
    { "id": "supports", "label": "Supports", "color": "#1a7a60" }
  ],

  "srcTags": ["Book", "Video", "Course"]
}

Before importing, check: Valid JSON with no trailing commas. Every topic id referenced in questions, arrows, or groups exists in the topics array. Every procedureId in procSteps matches a procedure id. Every toStepId in connections matches a step id. The _type field is exactly "dacosta-thinklab-backup".

If the import silently fails, paste your file into jsonlint.com to find syntax errors first.