Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,8 @@ venv.bak/
# Javascript stuff
node_modules/

# Build outputs
dist-gdocs/

# Data
*.db
2 changes: 1 addition & 1 deletion frontend/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import officeAddinsPlugin from 'eslint-plugin-office-addins';
export default [
// Global ignores
{
ignores: ['dist/**', 'build/**', 'node_modules/**', 'coverage/**']
ignores: ['dist/**', 'dist-gdocs/**', 'build/**', 'node_modules/**', 'coverage/**']
},

// Base JavaScript configuration for all files
Expand Down
49 changes: 25 additions & 24 deletions frontend/src/editor/editor.html
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>Editor</title>
<style>
body, html {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
#container {
height: 100%;
}
</style>
</head>

<body>
<div id="container"></div>
</body>
</html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Editor</title>
<style>
body,
html {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
min-height: 100vh;
overflow: auto;
}

#container {
min-height: 100vh;
}
</style>
</head>

<body>
<div id="container"></div>
</body>

</html>
3 changes: 2 additions & 1 deletion frontend/src/editor/editor.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
text-align: left;
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.3);
padding: 10px;
height: 80vh;
flex: 1;
min-height: 0;
}

.placeholder {
Expand Down
21 changes: 15 additions & 6 deletions frontend/src/editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ export function EditorScreen({
editorPreamble,
contextData,
falseContextData,
doneButton,
}: {
taskID?: string;
editorPreamble?: JSX.Element;
contextData?: ContextSection[];
falseContextData?: ContextSection[];
doneButton?: JSX.Element;
}) {
const mode = useAtomValue(overallModeAtom);
const username = useAtomValue(usernameAtom);
Expand Down Expand Up @@ -158,12 +160,19 @@ export function EditorScreen({
) : null}
</div>

<div
className={isDemo ? classes.demosidebar : classes.sidebar}
>
<EditorContext.Provider value={editorAPI}>
<Sidebar />
</EditorContext.Provider>
<div className={`flex flex-col overflow-hidden ${isDemo ? 'w-[28rem] min-w-[28rem] flex-shrink-0 mx-5' : 'flex-[0_1_28rem] min-w-[14rem]'}`}>

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

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

The inline className string mixes multiple layout responsibilities with a long conditional tailwind expression, making future tweaks error-prone. Consider extracting these computed class names into a small helper (e.g., sidebarWrapperClass) or using a classnames utility so layout decisions are easier to read and maintain.

Copilot uses AI. Check for mistakes.
<div
className={isDemo ? classes.demosidebar : classes.sidebar}

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

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

The wrapper uses overflow-hidden, but the immediate sidebar container (classes.sidebar/classes.demosidebar) doesn’t obviously enable scrolling. That combination can clip the sidebar contents and/or the new doneButton block when vertical space is tight. Recommendation: keep the wrapper overflow-hidden only if the inner sidebar container enforces scrolling (e.g., add flex-1 overflow-y-auto min-h-0 to the sidebar container), or move the overflow control to the inner sidebar area so the done button remains visible.

Suggested change
className={isDemo ? classes.demosidebar : classes.sidebar}
className={`flex-1 overflow-y-auto min-h-0 ${isDemo ? classes.demosidebar : classes.sidebar}`}

Copilot uses AI. Check for mistakes.
>
<EditorContext.Provider value={editorAPI}>
<Sidebar />
</EditorContext.Provider>
</div>
{doneButton ? (
<div className="flex justify-center mt-4 mb-8">
{doneButton}
</div>
) : null}
</div>
</div>
);
Expand Down
83 changes: 43 additions & 40 deletions frontend/src/editor/studyRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ function SurveyPage({ title, basename, questions, username, redirectURL, childre
questions={questions}
onAdvance={(surveyData: Record<string, any>) => {
logThenRedirect({
username: username,
event: `surveyComplete:${basename}`,
surveyData: surveyData,
}, redirectURL);
}}
/>
</ScrollablePage>
);
username: username,
event: `surveyComplete:${basename}`,
surveyData: surveyData,
}, redirectURL);
}}
/>
</ScrollablePage>
);

}

Expand Down Expand Up @@ -133,10 +133,10 @@ export function StudyRouter({ page }: { page: string }) {
}

const nextPage = studyPageNames[studyPageIndex + 1] || 'study-intro';
const nextUrlParams = new URLSearchParams(window.location.search);
nextUrlParams.set('page', nextPage);
const nextUrlParams = new URLSearchParams(window.location.search);
nextUrlParams.set('page', nextPage);
const nextPageURL = `${window.location.origin}/editor.html?${nextUrlParams.toString()}`;
const isProlific = urlParams.get('isProlific') === 'true';
const isProlific = urlParams.get('isProlific') === 'true';

if (page === 'study-consentForm') {
return (
Expand Down Expand Up @@ -250,37 +250,40 @@ export function StudyRouter({ page }: { page: string }) {
}));

const editorPreamble = (
<>
{curTaskContexts.map((section, index) => (
// biome-ignore lint/suspicious/noArrayIndexKey: it will actually be mostly stable.
<div key={index}>
<h3 className="font-bold">{section.title}</h3>
<p className="whitespace-pre-line">{section.content}</p>
</div>
))}
<h3 className="mt-4 pt-3 pb-3 font-bold border-t-2">Write Here</h3>
</>
);
<>
{curTaskContexts.map((section, index) => (
// biome-ignore lint/suspicious/noArrayIndexKey: it will actually be mostly stable.
<div key={index}>
<h3 className="font-bold">{section.title}</h3>
<p className="whitespace-pre-line">{section.content}</p>
</div>
))}
<h3 className="mt-4 pt-3 pb-3 font-bold border-t-2">Write Here</h3>
</>
);

return (
<div>
<EditorScreen contextData={curTaskContexts} falseContextData={falseContext} editorPreamble={editorPreamble} />

<button
type="button"
onClick={() => {
logThenRedirect({
username: username,
event: 'taskComplete',
condition: conditionName,
// TODO: add the document text here
}, nextPageURL);
}}
className={classes.doneButton}
>
I'm Done (I've written about 200 words)
</button>
</div>
<EditorScreen
contextData={curTaskContexts}
falseContextData={falseContext}
editorPreamble={editorPreamble}
doneButton={
<button
type="button"
onClick={() => {
logThenRedirect({
username: username,
event: 'taskComplete',
condition: conditionName,
// TODO: add the document text here
}, nextPageURL);
}}
className={classes.doneButton}
>
I'm Done (I've written about 200 words)
</button>
}
/>
);
} else if (page.startsWith('study-postTask')) {
const postTaskSurveyQuestions: QuestionType[] = [];
Expand Down
28 changes: 15 additions & 13 deletions frontend/src/editor/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,24 @@
font-family: 'Segoe UI', sans-serif, Arial, Helvetica;
display: flex;
flex-direction: row;
height: 100vh;
overflow: hidden;
Comment on lines +7 to +8

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

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

With the outer container forcing overflow: hidden, the sidebar area must provide its own scrolling when content exceeds the viewport. In this diff the sidebar switches to flex: 1 / min-height: 0 but does not set overflow-y: auto, which can cause sidebar content to be clipped/unreachable in smaller viewports. Fix: add overflow-y: auto (and typically display: flex; flex-direction: column; if needed) to .sidebar/.demosidebar (or remove the parent overflow: hidden and rely on page scrolling).

Copilot uses AI. Check for mistakes.
}

.editor {
flex: 1;
min-width: 15rem;
max-width: 60rem;
overflow-y: auto;
display: flex;
flex-direction: column;
}

/* Sidebar for editor and study page */
.sidebar {
flex: 0 1 28rem;
min-width: 14rem;
flex: 1;
margin: 20px 0px 20px 0px;
height: 80vh;
min-height: 0;
Comment on lines +22 to +24

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

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

With the outer container forcing overflow: hidden, the sidebar area must provide its own scrolling when content exceeds the viewport. In this diff the sidebar switches to flex: 1 / min-height: 0 but does not set overflow-y: auto, which can cause sidebar content to be clipped/unreachable in smaller viewports. Fix: add overflow-y: auto (and typically display: flex; flex-direction: column; if needed) to .sidebar/.demosidebar (or remove the parent overflow: hidden and rely on page scrolling).

Copilot uses AI. Check for mistakes.
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.3);
font-family: 'Segoe UI', sans-serif, Arial, Helvetica;
right: 0;
Expand All @@ -39,21 +43,24 @@
display: flex;
justify-content: center;
font-family: 'Segoe UI', sans-serif, Arial, Helvetica;
height: 100vh;
overflow: hidden;
}

.demoeditor {
min-width: 40rem;
max-width: 40rem;
width: 100%;
position: relative;
display: flex;
flex-direction: column;
}

/* Sidebar for demo page */
.demosidebar {
margin: 20px;
height: 80vh;
width: 28rem;
min-width: 28rem;
margin: 20px 0;
flex: 1;
min-height: 0;
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1);
font-family: 'Segoe UI', sans-serif, Arial, Helvetica;
background-color: white;
Expand Down Expand Up @@ -132,14 +139,9 @@
background-color: #0078d4;
color: white;
border: none;
padding: 0.5rem 1rem;
padding: 0.75rem 2rem;
border-radius: 0.25rem;
cursor: pointer;
font-size: 1rem;
position: fixed;
bottom: 1rem;
right: 1rem;
z-index: 1000;
transform: translateX(-2rem);
/* border: #d32f2f 1px solid; */
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions frontend/tests/draft-flows.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ test.describe('Draft component - Main flows', () => {
await expect(frame.getByText('First example suggestion')).toBeVisible({ timeout: 5000 });

// Delete example suggestion
const deleteButton1 = frame.locator('button[aria-label="Delete saved item"]').first();
const deleteButton1 = frame.locator('button[aria-label="Delete suggestion"]').first();
await deleteButton1.click();
await expect(frame.getByText('First example suggestion')).not.toBeVisible({ timeout: 2000 });

Expand All @@ -86,7 +86,7 @@ test.describe('Draft component - Main flows', () => {
await expect(frame.getByText('First reader perspective')).toBeVisible({ timeout: 5000 });

// Delete reader perspective suggestion
const deleteButton2 = frame.locator('button[aria-label="Delete saved item"]').first();
const deleteButton2 = frame.locator('button[aria-label="Delete suggestion"]').first();
await deleteButton2.click();
await expect(frame.getByText('First reader perspective')).not.toBeVisible({ timeout: 2000 });

Expand All @@ -95,7 +95,7 @@ test.describe('Draft component - Main flows', () => {
await expect(frame.getByText('First piece of advice')).toBeVisible({ timeout: 5000 });

// Delete advice suggestion
const deleteButton3 = frame.locator('button[aria-label="Delete saved item"]').first();
const deleteButton3 = frame.locator('button[aria-label="Delete suggestion"]').first();
await deleteButton3.click();
await expect(frame.getByText('First piece of advice')).not.toBeVisible({ timeout: 2000 });
});
Expand Down