Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
57ac28b
chore: upgrade to daisyUI v5.5 & tailwindcss v4.3
yeongsheng-tan May 16, 2026
74a94ce
upgrade to Tailwind v4 and daisyUI v5
yeongsheng-tan May 17, 2026
fb3d2c5
fix e2e interact with daisyUI v5 dialogs in Cypress
yeongsheng-tan May 17, 2026
59a0158
fix frontend impl & UT for folder merge conflict and navigation on Fo…
yeongsheng-tan May 17, 2026
dddae18
fix storybook: mock note APIs and drop test-helper imports from stories
yeongsheng-tan May 17, 2026
d79a238
fixs storybook to satisfy vue-tsc for showNote SDK mock
yeongsheng-tan May 17, 2026
4ed4163
@vitejs/plugin-vue@6.0.7 unimport@6.3.0
yeongsheng-tan May 17, 2026
1ad8c73
@cucumber/cucumber@12.9.0 hono@4.12.19 @types/node@25.7.0
yeongsheng-tan May 17, 2026
5fe5d22
tsx@4.22.0
yeongsheng-tan May 17, 2026
3f3b179
hono@4.12.19
yeongsheng-tan May 17, 2026
e5b1d3d
Merge branch 'main' into chore/upgrade_daisyUI_v5.5
yeongsheng-tan May 17, 2026
d7e7699
replace deprecated lucide-vue-next with @lucide/vue; version bump cli…
yeongsheng-tan May 18, 2026
3c73dc4
fix: FolderPage submitMove after incomplete SDK result refactor
yeongsheng-tan May 18, 2026
07d9bb7
ignore frontend/storybook-static in Biome and gitignore
yeongsheng-tan May 18, 2026
714e434
fix: restore daisyUI v5 modal visibility and stabilize E2E flows
yeongsheng-tan May 18, 2026
a68b5fe
lint formatting
yeongsheng-tan May 18, 2026
20747b0
pnpm@11.1.3
yeongsheng-tan May 19, 2026
35a83d2
syncpack@15.2.0
yeongsheng-tan May 19, 2026
1c297a6
@types/node@25.8.0
yeongsheng-tan May 19, 2026
3854cac
daisyUI@5.5.20
yeongsheng-tan May 19, 2026
df59980
refactor: update daisyUI prefix and component button classes
terryyin May 19, 2026
02b78d6
npm-run-all2@9.0.0
yeongsheng-tan May 19, 2026
e8bbecf
refactor: update table classes and error alert in recent notes compon…
terryyin May 19, 2026
2da47c8
refactor: enhance NoteMoreOptionsForm and related components for impr…
terryyin May 19, 2026
899eab0
add daisyui.com/llms.txt file daisyUI docs to help AI generate accura…
yeongsheng-tan May 19, 2026
c4bb405
remove extraneous daisyUI.mdc cursor rules
yeongsheng-tan May 19, 2026
65713f6
refactor: enhance dropdown functionality and styling across components
terryyin May 19, 2026
7fa2a9b
refactor: update note handling and improve component structure
terryyin May 19, 2026
bdf186a
refactor: improve NoteMoreOptionsForm and note page interactions
terryyin May 19, 2026
f90947c
Merge branch 'main' into chore/upgrade_daisyUI_v5.5
yeongsheng-tan May 20, 2026
82940d8
@hey-api/openapi-ts@0.97.2 @redocly/cli@2.31.1
yeongsheng-tan May 20, 2026
8ab3f7c
tsx@4.22.2 vue-tsc@3.3.0 @types/node@25.9.0 syncpack@15.3.0
yeongsheng-tan May 20, 2026
6ee9ece
pnpm recursive i to ensure node_modules updates are hoisted
yeongsheng-tan May 20, 2026
6febb76
restore Quiz.vue build under Tailwind v4 after merge from main
yeongsheng-tan May 20, 2026
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
6 changes: 3 additions & 3 deletions .cursor/rules/frontend-component.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ Use this rule when writing or modifying Vue components, component SCSS, or Story
## Core Technologies

- Vue 3 with TypeScript.
- DaisyUI + Tailwind for styling, with the `daisy-` prefix.
- DaisyUI + Tailwind for styling: unprefixed Tailwind utilities (`flex`, `text-primary`, …) and `daisy-`-prefixed DaisyUI component classes (`daisy-btn`, `daisy-card`, …).
- Vitest for testing with Playwright browser mode.
- Biome for linting and formatting.

## Icons

- Prefer Lucide for normal UI icons via `lucide-vue-next` (`import { IconName } from "lucide-vue-next"`). Import only the icons each file needs; rely on `currentColor` so icons follow text/theme color.
- `lucide-vue-next` defaults to 24x24 via the `size` prop. Use `:size="..."` and/or `daisy-w-*`, `daisy-h-*`, or `daisy-size-*`. Never use bare `w-*` / `h-*`; Tailwind uses the `daisy-` prefix here.
- Prefer Lucide for normal UI icons via `@lucide/vue` (`import { IconName } from "@lucide/vue"`). Import only the icons each file needs; rely on `currentColor` so icons follow text/theme color.
- `@lucide/vue` defaults to 24x24 via the `size` prop. Use `:size="..."` and/or Tailwind `w-*`, `h-*`, or `size-*` on icons.

## Naming Conventions

Expand Down
1 change: 1 addition & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"!**/.idea",
"!**/.vscode",
"!**/dist",
"!**/storybook-static",
"!**/gradle",
"!**/node_modules",
"!**/*.md",
Expand Down
6 changes: 3 additions & 3 deletions cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
},
"engines": {
"node": ">=24.14",
"pnpm": ">=11.1.2"
"pnpm": ">=11.1.3"
},
"scripts": {
"preinstall": "npx only-allow pnpm",
Expand All @@ -38,13 +38,13 @@
"devDependencies": {
"@stryker-mutator/core": "^9.6.1",
"@stryker-mutator/vitest-runner": "^9.6.1",
"@types/node": "25.7.0",
"@types/node": "25.9.0",
"@types/react": "^19.2.14",
"doughnut-test-fixtures": "workspace:*",
"esbuild": "0.28.0",
"ink": "^7.0.3",
"ink-testing-library": "^4.0.0",
"tsx": "4.21.0",
"tsx": "4.22.2",
"typescript": "6.0.3",
"vitest": "4.1.6"
}
Expand Down
4 changes: 2 additions & 2 deletions e2e_test/start/formField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ const formField = (label: string) => {
return self
},
expectError(message: string) {
formControl(label).find('.daisy-text-error').findByText(message)
formControl(label).find('.text-error').findByText(message)
return self
},
expectNoError() {
formControl(label).find('.daisy-text-error').should('not.exist')
formControl(label).find('.text-error').should('not.exist')
return self
},
type(text: string) {
Expand Down
7 changes: 1 addition & 6 deletions e2e_test/start/pageBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,5 @@

/** Waits until no `.loading-bar` nodes remain (thin bar + spinners). */
export const pageIsNotLoading = () => {
cy.get('body').should(
($body) => {
expect($body.find('.loading-bar').length).to.eq(0)
},
{ timeout: 30000 }
)
cy.get('.loading-bar', { timeout: 30000 }).should('not.exist')
}
2 changes: 1 addition & 1 deletion e2e_test/start/pageObjects/QuizQuestionPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const assumeQuestionPage = (stem?: string) => {
skipQuestion() {
pageIsNotLoading()
getQuestionSection().should('exist')
cy.get('.daisy-progress-bar').first().click()
cy.get('.progress-bar').first().click()
cy.findByRole('button', { name: 'Move to end of list' }).click()
},
answerFirstOption() {
Expand Down
5 changes: 2 additions & 3 deletions e2e_test/start/pageObjects/adminPages/adminDashboardPage.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { clickDaisyDialogButton } from '../../../support/daisyModalHelpers'
import { pageIsNotLoading } from '../../pageBase'
import { submittableForm } from '../../forms'

Expand All @@ -16,9 +17,7 @@ export function assumeAdminDashboardPage() {
},
deleteSelected() {
cy.get('button').contains('Delete Selected').click()
cy.get('.daisy-modal-action')
.findByRole('button', { name: 'Delete' })
.click()
clickDaisyDialogButton('dialog.daisy-modal', 'Delete')
return this
},
shouldBeEmpty() {
Expand Down
42 changes: 32 additions & 10 deletions e2e_test/start/pageObjects/assimilationPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,33 @@ const understandingChecklist = () =>
cy
.get('[data-test="refine-note-modal"]')
.contains('Understanding Checklist:')
.closest('.daisy-bg-accent')
.closest('.bg-accent')

const mainNoteTitle = () =>
cy.get('#main-note-content [data-test="note-title"]', { timeout: 15000 })
const mainNoteHeadingTitleSelector =
'#main-note-content h2.path-name-heading [role=title], #main-note-content [data-test="note-title"]'

function waitForAssimilationNoteTitle(expectedTitle?: string) {
pageIsNotLoading()
cy.get('#main-note-content', { timeout: 15000 }).should('be.visible')
const title = cy.get(mainNoteHeadingTitleSelector, { timeout: 15000 })
if (expectedTitle !== undefined && expectedTitle.trim() !== '') {
title.should('contain', expectedTitle.trim())
} else {
title.should('exist')
}
}

export const assumeAssimilationPage = () => ({
expectToAssimilateAndTotal(toAssimilateAndTotal: string) {
const [assimilatedTodayCount, toAssimilateCountForToday, totalCount] =
toAssimilateAndTotal.split('/')

cy.get('.daisy-progress-bar').should(
cy.get('.progress-bar').should(
'contain',
`Assimilating: ${assimilatedTodayCount}/${toAssimilateCountForToday}`
)
// Click progress bar to show tooltip
cy.get('.daisy-progress-bar').first().click()
cy.get('.progress-bar').first().click()

// Check tooltip content
cy.get('.tooltip-content').within(() => {
Expand Down Expand Up @@ -61,7 +72,8 @@ export const assumeAssimilationPage = () => ({
return this
},
assimilateWithSpellingOption() {
cy.get('[data-test="note-title"]')
waitForAssimilationNoteTitle()
cy.get(mainNoteHeadingTitleSelector)
.first()
.invoke('text')
.then((noteTitle: string) => {
Expand All @@ -85,14 +97,17 @@ export const assumeAssimilationPage = () => ({
} else {
switch (assimilationType) {
case 'single note': {
if (title) mainNoteTitle().should('contain', title)
waitForAssimilationNoteTitle(title)
this.waitForAssimilationReady()
if (additionalInfo) {
cy.get('.note-content').should('contain', additionalInfo)
}
break
}

case 'image note': {
waitForAssimilationNoteTitle()
this.waitForAssimilationReady()
if (additionalInfo) {
const [expectedBodyText, expectedImage] = commonSenseSplit(
additionalInfo,
Expand All @@ -113,9 +128,15 @@ export const assumeAssimilationPage = () => ({
additionalInfo,
'; '
)
if (title) mainNoteTitle().should('contain', title)
if (targetNote) mainNoteTitle().should('contain', targetNote)
if (relationType) mainNoteTitle().should('contain', relationType)
if (title) waitForAssimilationNoteTitle(title)
if (targetNote) waitForAssimilationNoteTitle(targetNote)
if (relationType) {
cy.get(mainNoteHeadingTitleSelector).should(
'contain',
relationType
)
}
this.waitForAssimilationReady()
}
break
}
Expand Down Expand Up @@ -274,6 +295,7 @@ export const assimilation = () => {
getAssimilateListItemInSidebar(($el) => {
$el.click()
})
pageIsNotLoading()
return assumeAssimilationPage()
},
}
Expand Down
52 changes: 27 additions & 25 deletions e2e_test/start/pageObjects/bookReadingPage.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import {
expectDaisyDialogBoxVisible,
openDaisyDialog,
} from '../../support/daisyModalHelpers'
import { pageIsNotLoading } from '../pageBase'

export type BookLayoutRow = { depth: number; title: string }
Expand Down Expand Up @@ -646,10 +650,8 @@ const bookReadingPage = () => {
},
expectReorganizationPreviewDialog() {
pageIsNotLoading()
cy.get('[data-testid="book-layout-reorganize-preview-dialog"]').should(
'have.class',
'daisy-modal-open'
)
const dialog = '[data-testid="book-layout-reorganize-preview-dialog"]'
expectDaisyDialogBoxVisible(dialog)
cy.get('#book-layout-reorganize-preview-title').should(
'contain',
'Reorganize layout (preview)'
Expand All @@ -661,23 +663,24 @@ const bookReadingPage = () => {
suggestedDepth: number
) {
pageIsNotLoading()
cy.get('[data-testid="book-layout-reorganize-preview-dialog"]')
.should('have.class', 'daisy-modal-open')
.within(() => {
cy.contains(
'[data-testid="book-layout-reorganize-preview-row"]',
blockTitle
)
.should('be.visible')
.and('have.attr', 'data-suggested-depth', String(suggestedDepth))
})
const dialog = '[data-testid="book-layout-reorganize-preview-dialog"]'
expectDaisyDialogBoxVisible(dialog)
cy.get(`${dialog}.daisy-modal-open .daisy-modal-box`).within(() => {
cy.contains(
'[data-testid="book-layout-reorganize-preview-row"]',
blockTitle
)
.should('be.visible')
.and('have.attr', 'data-suggested-depth', String(suggestedDepth))
})
return this
},
confirmAiReorganizeSuggestion() {
pageIsNotLoading()
cy.get('[data-testid="book-layout-reorganize-preview-confirm"]')
.should('be.visible')
.click()
openDaisyDialog('[data-testid="book-layout-reorganize-preview-dialog"]')
cy.get('[data-testid="book-layout-reorganize-preview-confirm"]').click({
force: true,
})
pageIsNotLoading()
return this
},
Expand Down Expand Up @@ -730,18 +733,17 @@ const bookReadingPage = () => {
},
expectTitlePromptWithDefaultTitle() {
pageIsNotLoading()
cy.get('[data-testid="new-block-title-dialog"]').should('be.visible')
cy.get('[data-testid="new-block-title-input"]').should(
'not.have.value',
''
)
const dialog = '[data-testid="new-block-title-dialog"]'
expectDaisyDialogBoxVisible(dialog)
cy.get('[data-testid="new-block-title-input"]')
.should('exist')
.should('not.have.value', '')
return this
},
confirmTitlePrompt() {
pageIsNotLoading()
cy.get('[data-testid="new-block-title-confirm"]')
.should('be.visible')
.click()
openDaisyDialog('[data-testid="new-block-title-dialog"]')
cy.get('[data-testid="new-block-title-confirm"]').click({ force: true })
return this
},
}
Expand Down
2 changes: 2 additions & 0 deletions e2e_test/start/pageObjects/messageCenterIndicator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { assumeMessageCenterPage } from './messageCenterPage'
import { pageIsNotLoading } from '../pageBase'

export function messageCenterIndicator() {
const getMessageInSidebar = (
Expand All @@ -21,6 +22,7 @@ export function messageCenterIndicator() {
getMessageInSidebar(($el) => {
$el.click()
})
pageIsNotLoading()
return assumeMessageCenterPage()
},
}
Expand Down
28 changes: 23 additions & 5 deletions e2e_test/start/pageObjects/messageCenterPage.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,46 @@
import { pageIsNotLoading } from '../pageBase'
import { mainMenu } from './mainMenu'

function withinConversationList(fn: () => void) {
cy.findByText('Message Center').should('be.visible')
pageIsNotLoading()
cy.get('[data-testid="message-center-conversation-item"]').should(
'have.length.at.least',
1
)
cy.get('.message-center-container').within(fn)
}

export const assumeMessageCenterPage = () => {
cy.findByText('Message Center').should('be.visible')

return {
expectConversation(subject: string, partner: string) {
cy.findByText(subject).should('be.visible')
cy.findByText(partner).should('be.visible')
withinConversationList(() => {
cy.findByText(subject).should('be.visible')
cy.findByText(partner).should('be.visible')
})
return this
},
expectMessageDisplayAtUserSide(message: string) {
cy.findByText(message).parents('.daisy-justify-end').should('be.visible')
cy.findByText(message).parents('.justify-end').should('be.visible')
return this
},
expectMessageDisplayAtOtherSide(message: string) {
cy.findByText(message)
.parent()
.should('be.visible')
.and('not.have.class', 'daisy-justify-end')
.and('not.have.class', 'justify-end')
return this
},
conversation(conversationSubject: string) {
cy.findByText(conversationSubject).parent().should('be.visible').click()
withinConversationList(() => {
cy.get(
`[data-testid="message-center-conversation-item"][data-conversation-subject="${conversationSubject}"]`
)
.should('be.visible')
.click()
})
pageIsNotLoading()
return {
expectMessage(message: string) {
Expand Down
24 changes: 20 additions & 4 deletions e2e_test/start/pageObjects/noteMoreOptionsForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,26 @@ import { assumeAssimilationPage } from './assimilationPage'
import { toolbarButton } from './toolbarButton'
import { questionListPage } from './questionListPage'

const moreOptionsDetails = () =>
cy
.get('details[data-auto-collapse-dropdown]')
.filter(':has(summary[title="more options"])')

const isMoreOptionsDropdownOpen = ($details: JQuery<HTMLElement>) =>
$details.prop('open') === true || $details.hasClass('daisy-dropdown-open')

export const makeSureNoteMoreOptionsFormIsOpen = () => {
cy.findByRole('button', { name: 'more options' }).then(($button) => {
if (!$button.hasClass('daisy-btn-active')) {
cy.wrap($button).click()
moreOptionsDetails().then(($details) => {
if (!isMoreOptionsDropdownOpen($details)) {
cy.wrap($details).find('summary[title="more options"]').click()
}
})
moreOptionsDetails().should(($details) => {
expect(isMoreOptionsDropdownOpen($details)).to.eq(true)
})
cy.findByRole('button', { name: 'Assimilation settings' }).should(
'be.visible'
)

return noteMoreOptionsPage()
}
Expand Down Expand Up @@ -40,7 +54,9 @@ const noteMoreOptionsPage = () => {
return questionListPage()
},
openAssimilationPage() {
toolbarButton('Assimilation settings').click()
cy.findByRole('button', { name: 'Assimilation settings' })
.scrollIntoView()
.click()
cy.url().should('include', '/assimilate/')
pageIsNotLoading()
return assumeAssimilationPage().waitForAssimilationReady()
Expand Down
Loading