Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9d4d6c2
use storageKey
jooha-yoo Jun 16, 2025
b94d29f
creating taskIDs and storage keys for each
jooha-yoo Jun 16, 2025
4538814
merging Alina's refactored codes for simplifying duplicates
jooha-yoo Jun 16, 2025
9cb9cd9
initial content for task 2&3 appear, each task gets stored separately…
jooha-yoo Jun 16, 2025
b6a8704
logging taskID to see which entry it's generated from
jooha-yoo Jun 17, 2025
3c80ae3
unnecessary comment
jooha-yoo Jun 17, 2025
8208118
imports that are not being used
jooha-yoo Jun 17, 2025
d2bb2b3
Merging main into my branch
jooha-yoo Jun 17, 2025
16b7bd8
Lexical uses a tree structure so this createInitialState function is …
jooha-yoo Jun 17, 2025
69691f5
cleanup
jooha-yoo Jun 17, 2025
0b55330
using storageKey
jooha-yoo Jun 17, 2025
2357f61
don't actually need
jooha-yoo Jun 17, 2025
dc0e98e
task prompt + inital content = taskPrompt
jooha-yoo Jun 17, 2025
2029e97
Merge remote-tracking branch 'origin/main' into feat/pre-loading
jooha-yoo Jun 18, 2025
e534025
prepended to docContext?
jooha-yoo Jun 23, 2025
323d533
preprending to docContext
jooha-yoo Jun 24, 2025
7b79b5a
no lines needed
jooha-yoo Jun 24, 2025
ba44e63
Thread through the task prompt, and tweak API.
kcarnold Jun 24, 2025
554480b
remove task prompt from the overall containter, push it down into edi…
kcarnold Jun 24, 2025
ad2bf6c
We do need newline separators between the prompt and the document.
kcarnold Jun 24, 2025
482d036
Merge branch 'main' into feat/pre-loading
kcarnold Jun 24, 2025
cfdf8a9
Recover changes lost by bad merge at 2029e971c16d307c891c9983c7f2b73b…
kcarnold Jun 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions frontend/src/editor/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,14 @@ function getCursorText(aNode: any, aOffset: any, mode: string): string {

function LexicalEditor({
updateDocContext,
initialState
initialState,
storageKey = 'doc',
taskPrompt
}: {
updateDocContext: (docContext: DocContext) => void;
initialState: InitialEditorStateType | null;
storageKey?: string;
taskPrompt?: string;
}) {
return (
<>
Expand All @@ -166,9 +170,11 @@ function LexicalEditor({
} }
>
<div className={ classes.editorContainer }>
<div className={ classes.editor }>
<div className="whitespace-pre-line border-b-2">{taskPrompt}</div>
<RichTextPlugin
contentEditable={
<ContentEditable className={ classes.editor } />
<ContentEditable className="" />
}
placeholder={ <div className={ classes.placeholder } /> }
ErrorBoundary={ LexicalErrorBoundary }
Expand All @@ -179,17 +185,19 @@ function LexicalEditor({
editorState.read(() => {
const docContext = $getDocContext();

// Prepend if necessary
if (taskPrompt) {
docContext.beforeCursor = taskPrompt + '\n\n' + docContext.beforeCursor;
}

updateDocContext(docContext);

localStorage.setItem(
'doc',
storageKey,
JSON.stringify(editorState)
Comment thread
jooha-yoo marked this conversation as resolved.
);
const currentDate = new Date().toISOString();
localStorage.setItem(
'doc-date',
currentDate
);
localStorage.setItem(`${storageKey}-date`, currentDate);
});
} }
/>
Expand All @@ -198,6 +206,7 @@ function LexicalEditor({

<HistoryPlugin />
</div>
</div>
</LexicalComposer>
</>
);
Expand Down
133 changes: 91 additions & 42 deletions frontend/src/editor/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { OverallMode, overallModeAtom, PageName, pageNameAtom } from '@/contexts/pageContext';
import { studyConditionAtom, taskDescriptionAtom } from '@/contexts/studyContext';
import * as SidebarInner from '@/pages/app';
import { Auth0ContextInterface } from '@auth0/auth0-react';
import { Auth0ContextInterface} from '@auth0/auth0-react';
import { getDefaultStore, useAtomValue } from 'jotai';
import { useRef, useState } from 'react';
import ReactDOM from 'react-dom';
Expand All @@ -16,7 +16,7 @@ function Sidebar({ editorAPI }: { editorAPI: EditorAPI}) {
);
}

function EditorScreen() {
function EditorScreen( {taskID, taskPrompt}: {taskID?: string; taskPrompt?: string}) {
const mode = useAtomValue(overallModeAtom);
const isDemo = mode === OverallMode.demo;

Expand All @@ -37,7 +37,6 @@ function EditorScreen() {
selectionChangeHandlers.current.forEach(handler => handler());
};


const editorAPI: EditorAPI = {
doLogin: async (auth0Client: Auth0ContextInterface) => {
try {
Expand Down Expand Up @@ -90,14 +89,32 @@ function EditorScreen() {
handleSelectionChange();
};

//Determine storage keys based on the task
const getStorageKey = () => {
return taskID ? `doc-${taskID}` : 'doc';
};

const getInitialState = () => {
const storageKey = getStorageKey();

// if (taskPrompt) {
// localStorage.removeItem(storageKey);
// localStorage.removeItem(`${storageKey}-date`);
// return createInitialState(taskPrompt);
// }
return localStorage.getItem(storageKey) || undefined;
};

return (
<div className={ isDemo ? classes.democontainer : classes.container }>

<div className={ isDemo ? classes.demoeditor : classes.editor }>
<LexicalEditor
//@ts-ignore, see https://github.com/facebook/lexical/issues/5079
initialState={ localStorage.getItem('doc') || undefined }
//@ts-ignore
initialState={ getInitialState()}
updateDocContext={ docUpdated }
storageKey={ getStorageKey()}
taskPrompt={ taskPrompt }
/>
{ isDemo && (
<div className={ `${classes.wordCount}` }>
Expand Down Expand Up @@ -141,31 +158,62 @@ const SURVEY_URLS = {


const taskConfigs = {
'1': {
taskDescription: 'Task 1: Should companies adopt a four-day work week (working Monday through Thursday) instead of the traditional five-day schedule? Consider impacts on productivity, employee well-being, and business operations.'
},
'2': {
taskDescription: 'Task 2: Write a cover letter for the position described. The applicant is a recent college graduate with a major in Environmental Sustainability and a minor in Marketing, with relevant internship experience. Demonstrate how their background aligns with the company’s mission and requirements.'
},
'3': {
taskDescription: 'Task 3: After reading these paragraphs, write a summary that explains CRISPR gene editing to your 11th grade biology classmates. Your goal is to help them understand what CRISPR is, how it works, and why it matters, using language and examples they would find clear and engaging.'
}
}
'1': {
condition: 'Completion',
taskPrompt: 'Task 1: Should companies adopt a four-day work week (working Monday through Thursday) instead of the traditional five-day schedule? Consider impacts on productivity, employee well-being, and business operations.',
},
'2': {
condition: 'Question',
taskPrompt: `Task 2: Write a cover letter for the position described. The applicant is a recent college graduate with a major in Environmental Sustainability and a minor in Marketing, with relevant internship experience. Demonstrate how their background aligns with the company’s mission and requirements. [Details are given below in the editor document]
GreenTech Solutions - Sustainability Coordinator Position

Company Overview:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String has a bunch of embedded tab characters. This may or may not be a problem in practice.

Maybe declare this string outside of the render function.

GreenTech Solutions is a fast-growing environmental consulting firm that helps businesses reduce their carbon footprint and implement sustainable practices. We work with companies across various industries to develop eco-friendly strategies that benefit both the environment and their bottom line.

Position Requirements:
- Bachelor's degree in Environmental Science, Sustainability, or related field
- Strong communication and project management skills
- Experience with sustainability reporting and environmental assessments
- Knowledge of marketing principles for promoting green initiatives
- Ability to work with diverse teams and clients
- Internship or work experience in environmental or sustainability roles preferred

Job Responsibilities:
- Assist clients in developing and implementing sustainability plans
- Conduct environmental impact assessments
- Create marketing materials to promote sustainable practices
- Collaborate with cross-functional teams on green initiatives
- Prepare sustainability reports and presentations for clients
- Stay current with environmental regulations and industry trends`
},
'3': {
condition: 'RMove',
taskPrompt: `Task 3: After reading these paragraphs, write a summary that explains CRISPR gene editing to your 11th grade biology classmates. Your goal is to help them understand what CRISPR is, how it works, and why it matters, using language and examples they would find clear and engaging.

CRISPR-Cas9 is a revolutionary gene-editing technology that allows scientists to make precise changes to DNA. Originally discovered as part of bacteria's immune system, CRISPR works like molecular scissors that can cut DNA at specific locations and either remove, add, or replace genetic material.

const letterToCondition = {
e: 'Completion',
q: 'Question',
r: 'RMove'
};
The CRISPR system consists of two main components: a guide RNA that identifies the target DNA sequence, and the Cas9 protein that acts as the cutting tool. When these components are introduced into a cell, they seek out the matching DNA sequence and make a precise cut. The cell's natural repair mechanisms then fix the break, allowing scientists to insert new genetic material or correct defective genes.

// This is the mapping of condition order letter abbreviation received from the URL parameter (eg. eqr, req, ...) to conditions.
function mapInputToDict(input: string) {
const result: Record<string, { condition: string }> = {};
input.split('').forEach((letter, idx) => {
result[(idx + 1).toString()] = { condition: letterToCondition[letter as keyof typeof letterToCondition] };
});
return result;
}
This technology has enormous potential for treating genetic diseases, improving crops, and advancing medical research. Scientists have already begun clinical trials using CRISPR to treat conditions like sickle cell disease and certain types of cancer. In agriculture, researchers are developing crops that are more resistant to diseases and climate change.

However, CRISPR also raises important ethical questions, particularly regarding its use in human embryos, which could create permanent changes that would be passed down to future generations. The scientific community continues to debate the appropriate boundaries for this powerful technology while working to ensure its safe and beneficial application.`
}
}

const letterToCondition = {
e: 'Completion',
q: 'Question',
r: 'RMove'
};

// This is the mapping of condition order letter abbreviation received from the URL parameter (eg. eqr, req, ...) to conditions.
function mapInputToDict(input: string) {
const result: Record<string, { condition: string }> = {};
input.split('').forEach((letter, idx) => {
result[(idx + 1).toString()] = { condition: letterToCondition[letter as keyof typeof letterToCondition] };
});
return result;
}

function Router({
page
Expand Down Expand Up @@ -258,8 +306,8 @@ function Router({
interaction: 'User clicked Start Study button'
});
urlParams.set('page', nextPage)
window.location.search = urlParams.toString()
; }}
window.location.search = urlParams.toString();
}}
className={classes.startButton}
>
Start Study
Expand All @@ -269,8 +317,8 @@ function Router({
}
else if (page === 'study-introSurvey') {
const nextUrlParams = new URLSearchParams(window.location.search);
nextUrlParams.set('page', nextPage);
const redirectURL = encodeURIComponent(window.location.origin + `/editor.html?${nextUrlParams.toString()}`);
nextUrlParams.set('page', nextPage);
const redirectURL = encodeURIComponent(window.location.origin + `/editor.html?${nextUrlParams.toString()}`);
const introSurveyURL = SURVEY_URLS.preStudy;

return (
Expand Down Expand Up @@ -333,20 +381,23 @@ function Router({
if (!taskConfig) {
return <div>Invalid task number</div>;
}
const taskID = `task${taskNumber}`;
getDefaultStore().set(studyConditionAtom, conditionConfig.condition);
getDefaultStore().set(taskDescriptionAtom, taskConfig.taskDescription);
getDefaultStore().set(taskDescriptionAtom, taskConfig.taskPrompt);

return (
<div>
<div className={classes.studytaskcontainer}>{taskConfig.taskDescription}</div>

<EditorScreen />
<EditorScreen
taskID={taskID}
taskPrompt={taskConfig.taskPrompt}
/>

<button
onClick={() => {
log({
username: username,
event: `FinishTask${taskNumber}`,
taskID: taskID,
interaction: `User finished Task ${taskNumber}`
});
urlParams.set('page', nextPage);
Expand Down Expand Up @@ -401,7 +452,7 @@ function Router({
event: 'PostStudySurvey',
interaction: 'User clicked final Post Study Survey button'
});
; }}
; }}
href={`${postStudySurveyURL}?redirect_url=${redirectURL}`}
className={classes.startButton}
>
Expand All @@ -423,18 +474,16 @@ function Router({
interaction: 'User finished the study'
});
urlParams.set('page', 'study-intro')
window.location.search = urlParams.toString()
; }}
window.location.search = urlParams.toString();
}}
className={classes.startButton}
>
Return to Start
</button>
</div>;
}
else {
return <div>Unknown study page</div>;
}
}
return <div>Unknown study page</div>; }}
else {
return <div>Page not found</div>;
}
Expand Down
Loading