diff --git a/frontend/src/editor/EmailTemplateFormContent/index.tsx b/frontend/src/editor/EmailTemplateFormContent/index.tsx
index 00b9f9e4d877add6e6431efb6b523b989a28c283..aed6d96a839d6b1cf4cd55e8d1dbce06754ce32f 100644
--- a/frontend/src/editor/EmailTemplateFormContent/index.tsx
+++ b/frontend/src/editor/EmailTemplateFormContent/index.tsx
@@ -1,6 +1,7 @@
 import { InputGroup, Label, TextArea } from '@blueprintjs/core'
 import { memo, type FC } from 'react'
 import EmailAddressSelector from '../EmailAddressSelector'
+import FileSelector from '../FileSelector'
 
 interface EmailTemplateFormProps {
   context: string
@@ -9,6 +10,8 @@ interface EmailTemplateFormProps {
   onContentChange: (value: string) => void
   emailAddressId: number
   onEmailAddressIdChange: (value: number) => void
+  fileId: number
+  onFileIdChange: (value: number) => void
 }
 
 const EmailTemplateForm: FC<EmailTemplateFormProps> = ({
@@ -18,6 +21,8 @@ const EmailTemplateForm: FC<EmailTemplateFormProps> = ({
   onContentChange,
   emailAddressId,
   onEmailAddressIdChange,
+  fileId,
+  onFileIdChange,
 }) => (
   <div>
     <EmailAddressSelector
@@ -46,6 +51,7 @@ const EmailTemplateForm: FC<EmailTemplateFormProps> = ({
         onChange={e => onContentChange(e.target.value)}
       />
     </Label>
+    <FileSelector fileId={fileId} onChange={id => onFileIdChange(id)} />
   </div>
 )
 
diff --git a/frontend/src/editor/EmailTemplateFormDialog/index.tsx b/frontend/src/editor/EmailTemplateFormDialog/index.tsx
index 604aa52541796344b2ccf3438324cecf04108940..6ee86c7274295357e8efe1b609990d73b63e7dd9 100644
--- a/frontend/src/editor/EmailTemplateFormDialog/index.tsx
+++ b/frontend/src/editor/EmailTemplateFormDialog/index.tsx
@@ -23,16 +23,19 @@ const EmailTemplateFormDialog: FC<EmailTemplateFormDialogProps> = ({
   const [context, setContext] = useState<string>('')
   const [content, setContent] = useState<string>('')
   const [selectedAddressId, setSelectedAddressId] = useState<number>(0)
+  const [fileId, setFileId] = useState<number>(0)
 
   useEffect(() => {
     setContext(template?.context || '')
     setContent(template?.content || '')
     setSelectedAddressId(template?.emailAddressId || emailAddressId)
+    setFileId(template?.fileId || 0)
   }, [template, isOpen])
 
   const clearInput = useCallback(() => {
     setContext('')
     setContent('')
+    setFileId(0)
   }, [])
 
   const handleAddButton = useCallback(
@@ -78,11 +81,13 @@ const EmailTemplateFormDialog: FC<EmailTemplateFormDialogProps> = ({
             context={context}
             content={content}
             emailAddressId={selectedAddressId}
+            fileId={fileId}
             onContextChange={(value: string) => setContext(value)}
             onContentChange={(value: string) => setContent(value)}
             onEmailAddressIdChange={(value: number) =>
               setSelectedAddressId(value)
             }
+            onFileIdChange={(value: number) => setFileId(value)}
           />
         </DialogBody>
         <DialogFooter
@@ -96,6 +101,7 @@ const EmailTemplateFormDialog: FC<EmailTemplateFormDialogProps> = ({
                     context,
                     content,
                     emailAddressId: selectedAddressId,
+                    fileId,
                   })
                 }
                 intent='primary'
@@ -110,6 +116,7 @@ const EmailTemplateFormDialog: FC<EmailTemplateFormDialogProps> = ({
                     context,
                     content,
                     emailAddressId: selectedAddressId,
+                    fileId,
                   })
                 }
                 intent='primary'
diff --git a/frontend/src/editor/FileSelector/index.tsx b/frontend/src/editor/FileSelector/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..67e6195ea73459edacb1ab956cc19c7c8b8343f4
--- /dev/null
+++ b/frontend/src/editor/FileSelector/index.tsx
@@ -0,0 +1,66 @@
+import type { OptionProps } from '@blueprintjs/core'
+import { HTMLSelect, Label } from '@blueprintjs/core'
+import { useLiveQuery } from 'dexie-react-hooks'
+import type { FC } from 'react'
+import { useEffect, useMemo } from 'react'
+import FileUploader from '../FileUploader'
+import { db } from '../indexeddb/db'
+import type { ContentFile } from '../indexeddb/types'
+
+interface FileSelectorProps {
+  fileId: number
+  onChange: (id: number) => void
+}
+
+const FileSelector: FC<FileSelectorProps> = ({ fileId, onChange }) => {
+  const files = useLiveQuery(() => db.files.toArray(), [], [])
+
+  const fileOptions: OptionProps[] = useMemo(() => {
+    if (files === undefined || files.length === 0) {
+      return [
+        {
+          label: 'No files',
+          value: 0,
+          disabled: true,
+        },
+      ]
+    }
+
+    return [
+      { label: 'No file', value: 0 },
+      ...(files ?? []).map((file: ContentFile) => ({
+        value: file.id,
+        label: file.name,
+      })),
+    ]
+  }, [files])
+
+  useEffect(() => {
+    if (!fileId) {
+      onChange(0)
+    }
+  }, [files, fileId])
+
+  return (
+    <div style={{ display: 'flex', width: '100%' }}>
+      <Label style={{ flexGrow: '1' }}>
+        File
+        <HTMLSelect
+          options={fileOptions}
+          value={fileId}
+          onChange={event => onChange(Number(event.currentTarget.value))}
+        />
+      </Label>
+      <FileUploader
+        buttonProps={{
+          minimal: true,
+          icon: 'plus',
+          style: { marginRight: '1rem' },
+        }}
+        onAdd={fileId => onChange(fileId)}
+      />
+    </div>
+  )
+}
+
+export default FileSelector
diff --git a/frontend/src/editor/FileUploader/index.tsx b/frontend/src/editor/FileUploader/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..86645bc5bac2048772c266c91468880394469705
--- /dev/null
+++ b/frontend/src/editor/FileUploader/index.tsx
@@ -0,0 +1,160 @@
+import type { ButtonProps } from '@blueprintjs/core'
+import {
+  Button,
+  Classes,
+  Dialog,
+  DialogBody,
+  DialogFooter,
+  FileInput,
+  InputGroup,
+  Label,
+} from '@blueprintjs/core'
+import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyContext'
+import type { ChangeEvent, FC } from 'react'
+import { useCallback, useEffect, useMemo, useState } from 'react'
+import { addFile, updateFile } from '../indexeddb/operations'
+import type { ContentFile } from '../indexeddb/types'
+
+interface FileUploaderProps {
+  contentFile?: ContentFile
+  buttonProps: ButtonProps
+  onAdd?: (id: number) => void
+}
+
+const FileUploader: FC<FileUploaderProps> = ({
+  contentFile,
+  buttonProps,
+  onAdd,
+}) => {
+  const [isOpen, setIsOpen] = useState<boolean>(false)
+  const [name, setName] = useState<string>('')
+  const [file, setFile] = useState<File | undefined>()
+  const { notify } = useNotifyContext()
+
+  const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
+    if (e.target.files) {
+      setFile(e.target.files[0])
+    }
+  }
+
+  const placeholder = useMemo(() => file?.name || 'Input text', [file?.name])
+
+  const clearInput = useCallback(() => {
+    setName('')
+    setFile(undefined)
+  }, [])
+
+  const handleAddButton = useCallback(
+    async (file: Omit<ContentFile, 'id'>) => {
+      try {
+        const id = await addFile(file)
+        if (onAdd) onAdd(Number(id))
+        clearInput()
+        setIsOpen(false)
+      } catch (err) {
+        notify(`Failed to add file '${file.name}': ${err}`, {
+          intent: 'danger',
+        })
+      }
+    },
+    [notify]
+  )
+
+  const handleUpdateButton = useCallback(
+    async (file: ContentFile) => {
+      try {
+        await updateFile(file)
+        setIsOpen(false)
+      } catch (err) {
+        notify(`Failed to update file '${file.name}': ${err}`, {
+          intent: 'danger',
+        })
+      }
+    },
+    [notify]
+  )
+
+  useEffect(() => {
+    setName(contentFile?.name || '')
+    setFile(
+      contentFile?.blob
+        ? new File([contentFile?.blob], contentFile?.name)
+        : undefined
+    )
+  }, [contentFile])
+
+  return (
+    <>
+      <Button {...buttonProps} onClick={() => setIsOpen(true)} />
+      <Dialog
+        isOpen={isOpen}
+        onClose={() => setIsOpen(false)}
+        icon={contentFile ? 'edit' : 'plus'}
+        title={contentFile ? 'Edit file' : 'New file'}
+      >
+        <DialogBody>
+          <Label>
+            Name
+            <InputGroup
+              placeholder={placeholder}
+              value={name}
+              onChange={e => setName(e.target.value)}
+            />
+          </Label>
+          <Label>
+            File
+            <div
+              style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-end' }}
+            >
+              <FileInput
+                className={Classes.INPUT}
+                fill
+                hasSelection={file !== undefined}
+                text={file ? file.name : 'Choose file...'}
+                onInputChange={handleFileChange}
+              />
+            </div>
+          </Label>
+        </DialogBody>
+        <DialogFooter
+          actions={
+            contentFile ? (
+              <Button
+                disabled={!file}
+                onClick={() =>
+                  handleUpdateButton({
+                    id: contentFile.id,
+                    name: file && name === '' ? file?.name : name,
+                    blob: file
+                      ? new Blob([file], { type: file.type })
+                      : new Blob(),
+                  })
+                }
+                intent='primary'
+                icon='edit'
+                text='Save changes'
+              />
+            ) : (
+              <Button
+                disabled={!file}
+                onClick={() =>
+                  handleAddButton({
+                    name: file && name === '' ? file?.name : name,
+                    blob: file
+                      ? new Blob([file], { type: file.type })
+                      : new Blob(),
+                  })
+                }
+                intent='primary'
+                icon='plus'
+                text='Add'
+              />
+            )
+          }
+        />
+      </Dialog>
+    </>
+  )
+}
+
+export default FileUploader
diff --git a/frontend/src/editor/InjectSpecification/ConnectionsForm.tsx b/frontend/src/editor/InjectSpecification/ConnectionsForm.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..aacaaa3aa72debd511d136b528229b6af4e2813e
--- /dev/null
+++ b/frontend/src/editor/InjectSpecification/ConnectionsForm.tsx
@@ -0,0 +1,98 @@
+import { Button, Label, NumericInput } from '@blueprintjs/core'
+import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyContext'
+import { useLiveQuery } from 'dexie-react-hooks'
+import { memo, useCallback, useEffect, useState, type FC } from 'react'
+import {
+  addInjectControl,
+  getInjectControlByInjectInfoId,
+  updateInjectControl,
+} from '../indexeddb/operations'
+import type { InjectControl } from '../indexeddb/types'
+import { InjectType } from '../indexeddb/types'
+
+interface ConnectionsFormProps {
+  injectInfoId: number
+  injectType: InjectType
+}
+
+const ConnectionsForm: FC<ConnectionsFormProps> = ({
+  injectInfoId,
+  injectType,
+}) => {
+  const injectControl = useLiveQuery(
+    () => getInjectControlByInjectInfoId(injectInfoId),
+    [injectInfoId],
+    null
+  ) as InjectControl
+
+  const { notify } = useNotifyContext()
+
+  const [start, setStart] = useState<number>(0)
+  const [delay, setDelay] = useState<number>(0)
+
+  useEffect(() => {
+    setStart(injectControl?.start || 0)
+    injectType === InjectType.QUESTIONNAIRE
+      ? setDelay(0)
+      : setDelay(injectControl?.delay || 0)
+  }, [injectControl, injectType])
+
+  const handleUpdateButton = useCallback(
+    async (newInjectControl: InjectControl | Omit<InjectControl, 'id'>) => {
+      try {
+        if (injectControl) {
+          await updateInjectControl({
+            id: injectControl.id,
+            ...newInjectControl,
+          })
+        } else {
+          await addInjectControl(newInjectControl)
+        }
+      } catch (err) {
+        notify(`Failed to update inject control: ${err}`, {
+          intent: 'danger',
+        })
+      }
+    },
+    [notify, injectControl]
+  )
+
+  return (
+    <div>
+      <Label>
+        Start
+        <NumericInput
+          placeholder='Input number'
+          min={0}
+          value={start}
+          onValueChange={(value: number) => setStart(value)}
+        />
+      </Label>
+      {injectType !== InjectType.QUESTIONNAIRE && (
+        <Label>
+          Delay
+          <NumericInput
+            placeholder='Input number'
+            min={0}
+            value={delay}
+            onValueChange={(value: number) => setDelay(value)}
+          />
+        </Label>
+      )}
+      <Button
+        onClick={() =>
+          handleUpdateButton({
+            injectInfoId,
+            start,
+            delay,
+          })
+        }
+        intent='primary'
+        icon='edit'
+        text='Save changes'
+      />
+    </div>
+  )
+}
+
+export default memo(ConnectionsForm)
diff --git a/frontend/src/editor/InjectSpecification/EmailInjectForm.tsx b/frontend/src/editor/InjectSpecification/EmailInjectForm.tsx
index 0163856bf502eceeabd7a5baf89c25f7e9bbd360..47bf4da2afc149ebfe250a8566b65ea7ae088c26 100644
--- a/frontend/src/editor/InjectSpecification/EmailInjectForm.tsx
+++ b/frontend/src/editor/InjectSpecification/EmailInjectForm.tsx
@@ -9,6 +9,7 @@ import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyCon
 import { useLiveQuery } from 'dexie-react-hooks'
 import { memo, useCallback, useEffect, useState, type FC } from 'react'
 import EmailAddressSelector from '../EmailAddressSelector'
+import FileSelector from '../FileSelector'
 import {
   addEmailInject,
   getEmailInjectByInjectInfoId,
@@ -33,12 +34,14 @@ const EmailInjectForm: FC<EmailInjectFormProps> = ({ injectInfoId }) => {
   const [content, setContent] = useState<string>('')
   const [selectedAddressId, setSelectedAddressId] = useState<number>(0)
   const [extraCopies, setExtraCopies] = useState<number>(0)
+  const [fileId, setFileId] = useState<number>(0)
 
   useEffect(() => {
     setSubject(emailInject?.subject || '')
     setContent(emailInject?.content || '')
     setSelectedAddressId(emailInject?.emailAddressId || 0)
     setExtraCopies(emailInject?.extraCopies || 0)
+    setFileId(emailInject?.fileId || 0)
   }, [emailInject])
 
   const handleUpdateButton = useCallback(
@@ -95,6 +98,7 @@ const EmailInjectForm: FC<EmailInjectFormProps> = ({ injectInfoId }) => {
           onValueChange={(value: number) => setExtraCopies(value)}
         />
       </Label>
+      <FileSelector fileId={fileId} onChange={id => setFileId(id)} />
       <Button
         onClick={() =>
           handleUpdateButton({
@@ -103,6 +107,7 @@ const EmailInjectForm: FC<EmailInjectFormProps> = ({ injectInfoId }) => {
             content,
             emailAddressId: selectedAddressId,
             extraCopies,
+            fileId,
           })
         }
         intent='primary'
diff --git a/frontend/src/editor/InjectSpecification/InformationInjectForm.tsx b/frontend/src/editor/InjectSpecification/InformationInjectForm.tsx
index c9d37f26dfbfc5faab63702cabd90a1fb76efa89..3c0efd073381fc41c9fb48d9574ba83de6fc7056 100644
--- a/frontend/src/editor/InjectSpecification/InformationInjectForm.tsx
+++ b/frontend/src/editor/InjectSpecification/InformationInjectForm.tsx
@@ -2,6 +2,7 @@ import { Button, Label, TextArea } from '@blueprintjs/core'
 import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyContext'
 import { useLiveQuery } from 'dexie-react-hooks'
 import { memo, useCallback, useEffect, useState, type FC } from 'react'
+import FileSelector from '../FileSelector'
 import {
   addInformationInject,
   getInformationInjectByInjectInfoId,
@@ -25,9 +26,11 @@ const InformationInjectForm: FC<InformationInjectFormProps> = ({
   const { notify } = useNotifyContext()
 
   const [content, setContent] = useState<string>('')
+  const [fileId, setFileId] = useState<number>(0)
 
   useEffect(() => {
     setContent(informationInject?.content || '')
+    setFileId(informationInject?.fileId || 0)
   }, [informationInject])
 
   const handleUpdateButton = useCallback(
@@ -68,11 +71,13 @@ const InformationInjectForm: FC<InformationInjectFormProps> = ({
           onChange={e => setContent(e.target.value)}
         />
       </Label>
+      <FileSelector fileId={fileId} onChange={id => setFileId(id)} />
       <Button
         onClick={() =>
           handleUpdateButton({
             injectInfoId,
             content,
+            fileId,
           })
         }
         intent='primary'
diff --git a/frontend/src/editor/InjectSpecification/OverlayForm.tsx b/frontend/src/editor/InjectSpecification/OverlayForm.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..a39e83f71b9eed95bb2c71c028fc37aa53cfa155
--- /dev/null
+++ b/frontend/src/editor/InjectSpecification/OverlayForm.tsx
@@ -0,0 +1,92 @@
+import { Button, Checkbox, Label, NumericInput } from '@blueprintjs/core'
+import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyContext'
+import { useLiveQuery } from 'dexie-react-hooks'
+import { memo, useCallback, useEffect, useState, type FC } from 'react'
+import {
+  addOverlay,
+  deleteOverlay,
+  getOverlayByInjectInfoId,
+  updateOverlay,
+} from '../indexeddb/operations'
+import type { Overlay } from '../indexeddb/types'
+
+interface OverlayFormProps {
+  injectInfoId: number
+}
+
+const OverlayForm: FC<OverlayFormProps> = ({ injectInfoId }) => {
+  const overlay = useLiveQuery(
+    () => getOverlayByInjectInfoId(injectInfoId),
+    [injectInfoId],
+    null
+  ) as Overlay
+
+  const { notify } = useNotifyContext()
+
+  const [enableOverlay, setEnableOverlay] = useState<boolean>(false)
+  const [duration, setDuration] = useState<number>(1)
+
+  useEffect(() => {
+    setEnableOverlay(overlay !== undefined)
+    setDuration(overlay?.duration || 1)
+  }, [overlay])
+
+  const handleUpdateButton = useCallback(
+    async (newOverlay: Overlay | Omit<Overlay, 'id'>) => {
+      try {
+        if (overlay) {
+          if (!enableOverlay) {
+            await deleteOverlay(overlay.id)
+            return
+          }
+
+          await updateOverlay({
+            id: overlay.id,
+            ...newOverlay,
+          })
+        } else {
+          await addOverlay(newOverlay)
+        }
+      } catch (err) {
+        notify(`Failed to update overlay: ${err}`, {
+          intent: 'danger',
+        })
+      }
+    },
+    [notify, overlay]
+  )
+
+  return (
+    <div>
+      <Checkbox
+        checked={enableOverlay}
+        onChange={() => setEnableOverlay(prev => !prev)}
+        label={'Use overlay'}
+      />
+      {enableOverlay && (
+        <Label>
+          Duration in minutes
+          <NumericInput
+            placeholder='Input number'
+            min={1}
+            value={duration}
+            onValueChange={(value: number) => setDuration(value)}
+          />
+        </Label>
+      )}
+      <Button
+        onClick={() =>
+          handleUpdateButton({
+            injectInfoId,
+            duration,
+          })
+        }
+        intent='primary'
+        icon='edit'
+        text='Save changes'
+      />
+    </div>
+  )
+}
+
+export default memo(OverlayForm)
diff --git a/frontend/src/editor/InjectSpecification/index.tsx b/frontend/src/editor/InjectSpecification/index.tsx
index 883b3ec5201e37455366f7b8768a366fe32386f3..cc1681a52ad7d3e2b0f00a6a22068a2a7c27b549 100644
--- a/frontend/src/editor/InjectSpecification/index.tsx
+++ b/frontend/src/editor/InjectSpecification/index.tsx
@@ -1,12 +1,14 @@
-import { Divider, NonIdealState } from '@blueprintjs/core'
+import { NonIdealState, Tab, Tabs, TabsExpander } from '@blueprintjs/core'
 import { useLiveQuery } from 'dexie-react-hooks'
 import { memo, type FC } from 'react'
 import InjectForm from '../InjectForm'
 import { getInjectInfoById } from '../indexeddb/operations'
 import type { InjectInfo } from '../indexeddb/types'
 import { InjectType } from '../indexeddb/types'
+import ConnectionsForm from './ConnectionsForm'
 import EmailInjectForm from './EmailInjectForm'
 import InformationInjectForm from './InformationInjectForm'
+import OverlayForm from './OverlayForm'
 import QuestionnaireForm from './QuestionnaireForm'
 
 interface InjectSpecificationProps {
@@ -34,7 +36,7 @@ const InjectSpecification: FC<InjectSpecificationProps> = ({
 
   return (
     <div>
-      <div>
+      <div style={{ marginBottom: '1rem' }}>
         <p>Name: {injectInfo.name}</p>
         <p>Description: {injectInfo.description}</p>
         <p>Type: {injectInfo.type}</p>
@@ -47,16 +49,41 @@ const InjectSpecification: FC<InjectSpecificationProps> = ({
           }}
         />
       </div>
-      <Divider style={{ margin: '1rem 0' }} />
-      {injectInfo.type === InjectType.EMAIL && (
-        <EmailInjectForm injectInfoId={injectInfoId} />
-      )}
-      {injectInfo.type === InjectType.INFORMATION && (
-        <InformationInjectForm injectInfoId={injectInfoId} />
-      )}
-      {injectInfo.type === InjectType.QUESTIONNAIRE && (
-        <QuestionnaireForm injectInfoId={injectInfoId} />
-      )}
+      <Tabs>
+        <Tab
+          id='parameters'
+          title='Parameters'
+          panel={
+            <>
+              {injectInfo.type === InjectType.EMAIL && (
+                <EmailInjectForm injectInfoId={injectInfoId} />
+              )}
+              {injectInfo.type === InjectType.INFORMATION && (
+                <InformationInjectForm injectInfoId={injectInfoId} />
+              )}
+              {injectInfo.type === InjectType.QUESTIONNAIRE && (
+                <QuestionnaireForm injectInfoId={injectInfoId} />
+              )}
+            </>
+          }
+        />
+        <Tab
+          id='connections'
+          title='Connections'
+          panel={
+            <ConnectionsForm
+              injectInfoId={injectInfoId}
+              injectType={injectInfo.type}
+            />
+          }
+        />
+        <Tab
+          id='overlay'
+          title='Overlay'
+          panel={<OverlayForm injectInfoId={injectInfoId} />}
+        />
+        <TabsExpander />
+      </Tabs>
     </div>
   )
 }
diff --git a/frontend/src/editor/LearningActivitySpecification/EmailTemplateForm.tsx b/frontend/src/editor/LearningActivitySpecification/EmailTemplateForm.tsx
index a9ba887d60daf3508e2fc9879c8cad0d65cb144f..3d8ac06985c6dc108fc3280ed7723559175cf170 100644
--- a/frontend/src/editor/LearningActivitySpecification/EmailTemplateForm.tsx
+++ b/frontend/src/editor/LearningActivitySpecification/EmailTemplateForm.tsx
@@ -28,11 +28,13 @@ const EmailTemplateForm: FC<EmailTemplateFormProps> = ({
   const [context, setContext] = useState<string>('')
   const [content, setContent] = useState<string>('')
   const [selectedAddressId, setSelectedAddressId] = useState<number>(0)
+  const [fileId, setFileId] = useState<number>(0)
 
   useEffect(() => {
     setContext(template?.context || '')
     setContent(template?.content || '')
     setSelectedAddressId(template?.emailAddressId || 0)
+    setFileId(template?.fileId || 0)
   }, [template])
 
   const handleUpdateButton = useCallback(
@@ -58,9 +60,11 @@ const EmailTemplateForm: FC<EmailTemplateFormProps> = ({
         context={context}
         content={content}
         emailAddressId={selectedAddressId}
+        fileId={fileId}
         onContextChange={(value: string) => setContext(value)}
         onContentChange={(value: string) => setContent(value)}
         onEmailAddressIdChange={(value: number) => setSelectedAddressId(value)}
+        onFileIdChange={(value: number) => setFileId(value)}
       />
       <Button
         disabled={!context || !selectedAddressId}
@@ -70,6 +74,7 @@ const EmailTemplateForm: FC<EmailTemplateFormProps> = ({
             context,
             content,
             emailAddressId: selectedAddressId,
+            fileId,
           })
         }
         intent='primary'
diff --git a/frontend/src/editor/LearningActivitySpecification/ToolResponseForm.tsx b/frontend/src/editor/LearningActivitySpecification/ToolResponseForm.tsx
index 4387df86f20de1759fc8b1decbed9b7b5fe41d85..5c4abae05077e989a60c4e7b4398bf1a1c8ace46 100644
--- a/frontend/src/editor/LearningActivitySpecification/ToolResponseForm.tsx
+++ b/frontend/src/editor/LearningActivitySpecification/ToolResponseForm.tsx
@@ -29,12 +29,16 @@ const ToolResponseForm: FC<ToolResponseFormProps> = ({
   const [content, setContent] = useState<string>('')
   const [isRegex, setIsRegex] = useState<boolean>(false)
   const [selectedToolId, setSelectedToolId] = useState<number>(0)
+  const [fileId, setFileId] = useState<number>(0)
+  const [time, setTime] = useState<number>(0)
 
   useEffect(() => {
     setParameter(response?.parameter || '')
     setContent(response?.content || '')
     setIsRegex(response?.isRegex || false)
     setSelectedToolId(response?.toolId || 0)
+    setTime(response?.time || 0)
+    setFileId(response?.fileId || 0)
   }, [response])
 
   const handleUpdateButton = useCallback(
@@ -61,10 +65,14 @@ const ToolResponseForm: FC<ToolResponseFormProps> = ({
         content={content}
         isRegex={isRegex}
         toolId={selectedToolId}
+        fileId={fileId}
+        time={time}
         onParameterChange={(value: string) => setParameter(value)}
         onContentChange={(value: string) => setContent(value)}
         onIsRegexChange={(value: boolean) => setIsRegex(value)}
         onToolIdChange={(value: number) => setSelectedToolId(value)}
+        onFileIdChange={(value: number) => setFileId(value)}
+        onTimeChange={(value: number) => setTime(value)}
       />
       <Button
         disabled={!parameter || !selectedToolId}
@@ -75,6 +83,8 @@ const ToolResponseForm: FC<ToolResponseFormProps> = ({
             content,
             isRegex,
             toolId: selectedToolId,
+            time,
+            fileId,
           })
         }
         intent='primary'
diff --git a/frontend/src/editor/ToolResponseFormContent/index.tsx b/frontend/src/editor/ToolResponseFormContent/index.tsx
index 167139611f816c2295bb72caa9d3cadb6c8cba4e..0e6b17e89c504de9e8b10414375d2faabac3d0a8 100644
--- a/frontend/src/editor/ToolResponseFormContent/index.tsx
+++ b/frontend/src/editor/ToolResponseFormContent/index.tsx
@@ -1,5 +1,15 @@
-import { Checkbox, InputGroup, Label, TextArea } from '@blueprintjs/core'
+import {
+  Checkbox,
+  InputGroup,
+  Label,
+  NumericInput,
+  Tab,
+  Tabs,
+  TabsExpander,
+  TextArea,
+} from '@blueprintjs/core'
 import { memo, type FC } from 'react'
+import FileSelector from '../FileSelector'
 import ToolSelector from '../ToolSelector'
 
 interface ToolResponseFormProps {
@@ -11,6 +21,10 @@ interface ToolResponseFormProps {
   onIsRegexChange: (value: boolean) => void
   toolId: number
   onToolIdChange: (value: number) => void
+  fileId: number
+  onFileIdChange: (value: number) => void
+  time: number
+  onTimeChange: (value: number) => void
 }
 
 const ToolResponseForm: FC<ToolResponseFormProps> = ({
@@ -22,37 +36,68 @@ const ToolResponseForm: FC<ToolResponseFormProps> = ({
   onIsRegexChange,
   toolId,
   onToolIdChange,
+  fileId,
+  onFileIdChange,
+  time,
+  onTimeChange,
 }) => (
-  <div>
-    <ToolSelector toolId={toolId} onChange={id => onToolIdChange(id)} />
-    <Label>
-      Parameter
-      <InputGroup
-        placeholder='Input text'
-        value={parameter}
-        onChange={e => onParameterChange(e.target.value)}
-      />
-    </Label>
-    <Checkbox
-      label='Is parameter regex?'
-      checked={isRegex}
-      onChange={e => onIsRegexChange(e.target.checked)}
+  <Tabs>
+    <Tab
+      id='parameters'
+      title='Parameters'
+      panel={
+        <div>
+          <ToolSelector toolId={toolId} onChange={id => onToolIdChange(id)} />
+          <Label>
+            Parameter
+            <InputGroup
+              placeholder='Input text'
+              value={parameter}
+              onChange={e => onParameterChange(e.target.value)}
+            />
+          </Label>
+          <Checkbox
+            label='Is parameter regex?'
+            checked={isRegex}
+            onChange={e => onIsRegexChange(e.target.checked)}
+          />
+          <Label>
+            Response
+            <TextArea
+              value={content}
+              style={{
+                width: '100%',
+                height: '10rem',
+                resize: 'none',
+                overflowY: 'auto',
+              }}
+              placeholder='Input text'
+              onChange={e => onContentChange(e.target.value)}
+            />
+          </Label>
+          <FileSelector fileId={fileId} onChange={id => onFileIdChange(id)} />
+        </div>
+      }
     />
-    <Label>
-      Response
-      <TextArea
-        value={content}
-        style={{
-          width: '100%',
-          height: '10rem',
-          resize: 'none',
-          overflowY: 'auto',
-        }}
-        placeholder='Input text'
-        onChange={e => onContentChange(e.target.value)}
-      />
-    </Label>
-  </div>
+    <Tab
+      id='connections'
+      title='Connections'
+      panel={
+        <div>
+          <Label>
+            Time
+            <NumericInput
+              placeholder='Input number'
+              min={0}
+              value={time}
+              onValueChange={(value: number) => onTimeChange(value)}
+            />
+          </Label>
+        </div>
+      }
+    />
+    <TabsExpander />
+  </Tabs>
 )
 
 export default memo(ToolResponseForm)
diff --git a/frontend/src/editor/ToolResponseFormDialog/index.tsx b/frontend/src/editor/ToolResponseFormDialog/index.tsx
index be96fc4c7ba12cc2bbb77c368ccb0c7ef958d381..b4adbfe74999ad9d9458b065ff861ae676cbcb89 100644
--- a/frontend/src/editor/ToolResponseFormDialog/index.tsx
+++ b/frontend/src/editor/ToolResponseFormDialog/index.tsx
@@ -24,11 +24,15 @@ const ToolResponseFormDialog: FC<ToolResponseFormDialogProps> = ({
   const [content, setContent] = useState<string>('')
   const [isRegex, setIsRegex] = useState<boolean>(false)
   const [selectedToolId, setSelectedToolId] = useState<number>(0)
+  const [fileId, setFileId] = useState<number>(0)
+  const [time, setTime] = useState<number>(0)
 
   const clearInput = useCallback(() => {
     setParameter('')
     setContent('')
     setIsRegex(false)
+    setTime(0)
+    setFileId(0)
   }, [])
 
   useEffect(() => {
@@ -36,6 +40,8 @@ const ToolResponseFormDialog: FC<ToolResponseFormDialogProps> = ({
     setContent(response?.content || '')
     setIsRegex(response?.isRegex || false)
     setSelectedToolId(response?.toolId || toolId)
+    setFileId(response?.fileId || 0)
+    setTime(response?.time || 0)
   }, [response, isOpen])
 
   const handleAddButton = useCallback(
@@ -88,10 +94,14 @@ const ToolResponseFormDialog: FC<ToolResponseFormDialogProps> = ({
             content={content}
             isRegex={isRegex}
             toolId={selectedToolId}
+            fileId={fileId}
+            time={time}
             onParameterChange={(value: string) => setParameter(value)}
             onContentChange={(value: string) => setContent(value)}
             onIsRegexChange={(value: boolean) => setIsRegex(value)}
             onToolIdChange={(value: number) => setSelectedToolId(value)}
+            onFileIdChange={(value: number) => setFileId(value)}
+            onTimeChange={(value: number) => setTime(value)}
           />
         </DialogBody>
         <DialogFooter
@@ -106,6 +116,8 @@ const ToolResponseFormDialog: FC<ToolResponseFormDialogProps> = ({
                     content,
                     isRegex,
                     toolId: selectedToolId,
+                    fileId,
+                    time,
                   })
                 }
                 intent='primary'
@@ -121,6 +133,8 @@ const ToolResponseFormDialog: FC<ToolResponseFormDialogProps> = ({
                     content,
                     isRegex,
                     toolId: selectedToolId,
+                    fileId,
+                    time,
                   })
                 }
                 intent='primary'
diff --git a/frontend/src/editor/indexeddb/db.tsx b/frontend/src/editor/indexeddb/db.tsx
index 9d519808270488762b180fd848034c66b7e035bb..ef7f0b82616fedc2a61e7cdd8061f8586f491635 100644
--- a/frontend/src/editor/indexeddb/db.tsx
+++ b/frontend/src/editor/indexeddb/db.tsx
@@ -1,12 +1,15 @@
 import Dexie, { type EntityTable } from 'dexie'
 import type {
+  ContentFile,
   EmailAddressInfo,
   EmailInject,
   EmailTemplate,
   InformationInject,
+  InjectControl,
   InjectInfo,
   LearningActivityInfo,
   LearningObjectiveInfo,
+  Overlay,
   Questionnaire,
   QuestionnaireQuestion,
   ToolInfo,
@@ -28,6 +31,9 @@ const db = new Dexie(dbName) as Dexie & {
   informationInjects: EntityTable<InformationInject, 'id'>
   questionnaires: EntityTable<Questionnaire, 'id'>
   questionnaireQuestions: EntityTable<QuestionnaireQuestion, 'id'>
+  overlays: EntityTable<Overlay, 'id'>
+  injectControls: EntityTable<InjectControl, 'id'>
+  files: EntityTable<ContentFile, 'id'>
 }
 
 db.version(dbVersion).stores({
@@ -36,14 +42,18 @@ db.version(dbVersion).stores({
   injectInfos: '++id, &name, description, type',
   tools: '++id, &name, tooltipDescription, hint, defaultResponse, category',
   toolResponses:
-    '++id, &learningActivityId, toolId, parameter, isRegex, content', // TODO file
+    '++id, &learningActivityId, toolId, parameter, isRegex, content, fileId',
   emailAddresses: '++id, address, organization, description, teamVisible',
-  emailTemplates: '++id, &learningActivityId, emailAddressId, context, content', // TODO file
+  emailTemplates:
+    '++id, &learningActivityId, emailAddressId, context, content, fileId',
   emailInjects:
-    '++id, &injectInfoId, emailAddressId, subject, content, extraCopies', // TODO file
-  informationInjects: '++id, &injectInfoId, content', // TODO file
+    '++id, &injectInfoId, emailAddressId, subject, content, extraCopies, fileId',
+  informationInjects: '++id, &injectInfoId, content, fileId',
   questionnaires: '++id, &injectInfoId, title',
   questionnaireQuestions: '++id, questionnaireId, text, max, correct, labels',
+  overlays: '++id, &injectInfoId, duration',
+  injectControls: '++id, &injectInfoId, start, delay, milestoneCondition',
+  files: '++id, &name, blob',
 })
 
 export { db }
diff --git a/frontend/src/editor/indexeddb/operations.tsx b/frontend/src/editor/indexeddb/operations.tsx
index c51e8e58c44b3b992b057c07f1f5a8f61861af37..edde591f676116a107352294642d811b7e1cd547 100644
--- a/frontend/src/editor/indexeddb/operations.tsx
+++ b/frontend/src/editor/indexeddb/operations.tsx
@@ -1,12 +1,15 @@
 import { db } from './db'
 import type {
+  ContentFile,
   EmailAddressInfo,
   EmailInject,
   EmailTemplate,
   InformationInject,
+  InjectControl,
   InjectInfo,
   LearningActivityInfo,
   LearningObjectiveInfo,
+  Overlay,
   Questionnaire,
   QuestionnaireQuestion,
   ToolInfo,
@@ -66,7 +69,17 @@ export const updateInjectInfo = async (injectInfo: InjectInfo) =>
   await db.injectInfos.put(injectInfo)
 
 export const deleteInjectInfo = async (id: number) =>
-  await db.injectInfos.delete(id)
+  await db.transaction(
+    'rw',
+    db.injectInfos,
+    db.overlays,
+    db.injectControls,
+    async () => {
+      await db.injectInfos.delete(id)
+      await db.overlays.where({ injectInfoId: id }).delete()
+      await db.injectControls.where({ injectInfoId: id }).delete()
+    }
+  )
 
 // tool operations
 export const addTool = async (tool: Omit<ToolInfo, 'id'>) =>
@@ -207,3 +220,47 @@ export const updateQuestionnaireQuestion = async (
 
 export const deleteQuestionnaireQuestion = async (id: number) =>
   await db.questionnaireQuestions.delete(id)
+
+// overlay operations
+export const getOverlayByInjectInfoId = async (injectInfoId: number) =>
+  await db.overlays.get({ injectInfoId })
+
+export const addOverlay = async (overlay: Omit<Overlay, 'id'>) =>
+  await db.transaction('rw', db.overlays, async () => {
+    await db.overlays.add(overlay)
+  })
+
+export const updateOverlay = async (overlay: Overlay) =>
+  await db.overlays.put(overlay)
+
+export const deleteOverlay = async (id: number) => await db.overlays.delete(id)
+
+// inject control operations
+export const getInjectControlByInjectInfoId = async (injectInfoId: number) =>
+  await db.injectControls.get({ injectInfoId })
+
+export const addInjectControl = async (
+  injectControl: Omit<InjectControl, 'id'>
+) =>
+  await db.transaction('rw', db.injectControls, async () => {
+    await db.injectControls.add(injectControl)
+  })
+
+export const updateInjectControl = async (injectControl: InjectControl) =>
+  await db.injectControls.put(injectControl)
+
+export const deleteInjectControl = async (id: number) =>
+  await db.injectControls.delete(id)
+
+// file operations
+export const getFileById = async (id: number) => await db.files.get(id)
+
+export const addFile = async (file: Omit<ContentFile, 'id'>) =>
+  await db.transaction('rw', db.files, async () => {
+    const id = await db.files.add(file)
+    return id
+  })
+
+export const updateFile = async (file: ContentFile) => await db.files.put(file)
+
+export const deleteFile = async (id: number) => await db.files.delete(id)
diff --git a/frontend/src/editor/indexeddb/types.tsx b/frontend/src/editor/indexeddb/types.tsx
index d308ae7f2a3526e4f3e42cd2eeb3039b2a65e71c..8201baf290eb782d276c308081eb8a19f477ae8a 100644
--- a/frontend/src/editor/indexeddb/types.tsx
+++ b/frontend/src/editor/indexeddb/types.tsx
@@ -2,7 +2,6 @@ import type { EmailAddress } from '@inject/graphql/fragments/EmailAddress.genera
 import type { LearningObjective } from '@inject/graphql/fragments/LearningObjective.generated'
 
 export enum LearningActivityType {
-  CONFIRMATION = 'Confirmation',
   TOOL = 'Tool',
   EMAIL = 'Email',
 }
@@ -13,6 +12,9 @@ export enum InjectType {
   QUESTIONNAIRE = 'Questionnaire',
 }
 
+const eventTypes = { ...LearningActivityType, ...InjectType }
+export type EventTypes = typeof eventTypes
+
 export type LearningObjectiveInfo = Pick<LearningObjective, 'id' | 'name'>
 
 export type LearningActivityInfo = {
@@ -46,6 +48,9 @@ export type ToolResponse = {
   parameter: string
   isRegex: boolean
   content: string
+  fileId?: number
+  time: number
+  milestoneCondition?: string[]
 }
 
 export type EmailAddressInfo = Omit<EmailAddress, 'control'>
@@ -56,6 +61,7 @@ export type EmailTemplate = {
   emailAddressId: number
   context: string
   content: string
+  fileId?: number
 }
 
 export type EmailInject = {
@@ -65,12 +71,14 @@ export type EmailInject = {
   subject: string
   content: string
   extraCopies: number
+  fileId?: number
 }
 
 export type InformationInject = {
   id: number
   injectInfoId: number
   content: string
+  fileId?: number
 }
 
 export type Questionnaire = {
@@ -87,3 +95,23 @@ export type QuestionnaireQuestion = {
   correct: number
   labels: string
 }
+
+export type InjectControl = {
+  id: number
+  injectInfoId: number
+  start: number
+  delay: number
+  milestoneCondition?: string[]
+}
+
+export type Overlay = {
+  id: number
+  injectInfoId: number
+  duration: number
+}
+
+export type ContentFile = {
+  id: number
+  name: string
+  blob: Blob
+}