fix: keep the notes list accessible in narrow workspaces
Default the notes list to 200px with a 160px minimum and hide it automatically only below 600px. Keep the detail toolbar toggle available to open a drawer, with dismissal and note-switching support. Restore reading positions after rich text content effects populate the record. Include the existing document-table and file-tree scroll anchoring styles in this file-level commit. Validation: 143 related tests, Electron layout regression, typecheck and changed-file lint passed; repository lint is blocked by unrelated App.tsx changes. Release note: The notes list takes less space and remains accessible through a drawer in narrow windows. Existing saved list widths are preserved.
This commit is contained in:
@@ -1,5 +1,20 @@
|
||||
# Magic Notes Progress
|
||||
|
||||
## 2026-10-03: Compact Notes List And Narrow Drawer
|
||||
|
||||
The notes list now defaults to 200px with a 160px minimum. At workspace widths
|
||||
of 600px or more it can remain beside detail; below 600px the toolbar toggle
|
||||
opens the mounted list as an overlay drawer. Escape, outside click, the close
|
||||
button and note selection dismiss it. Existing desktop width preferences remain
|
||||
intact. Scroll restoration now runs after read-only content effects populate the
|
||||
record, fixing a failure exposed by the Electron layout regression.
|
||||
|
||||
Validation: 143 workspace and list-window tests passed, along with the real
|
||||
Electron layout test covering native resizing, drawer geometry, Escape focus
|
||||
return and note switching. Typecheck and lint on changed code passed. Repository
|
||||
lint was blocked by concurrent App.tsx changes (max-lines error and a missing
|
||||
conversationStore hook dependency warning). No provider calls were made.
|
||||
|
||||
## 2026-10-01: Header New Note Action
|
||||
|
||||
New note is now one primary page-header button that always follows the view
|
||||
|
||||
@@ -21,13 +21,17 @@ stream events, error causes and nested release/dispose cleanup.
|
||||
## Continuous Record Workspace
|
||||
|
||||
The outer workspace separates the persistent notes list from the detail container.
|
||||
The list defaults to 280px, resizes within 240-420px and stores `notesPaneWidth`
|
||||
and `notesPaneOpen` in `goodbuddy.magic-notes-layout.v1`. At workspace widths up to
|
||||
900px, selection replaces the visible list with detail while retaining the mounted
|
||||
list. The right-hand `magic-notes-detail` query container and its ResizeObserver
|
||||
The list defaults to 200px, resizes within 160-420px and stores `notesPaneWidth`
|
||||
and `notesPaneOpen` in `goodbuddy.magic-notes-layout.v1`. Existing saved widths are
|
||||
preserved. Below 600px, selection replaces the visible list with detail while
|
||||
retaining the mounted list. The detail toolbar keeps its list toggle, opening an
|
||||
overlay drawer without shrinking detail. Selecting a note, clicking outside,
|
||||
using the drawer close button or pressing Escape closes the drawer; dismissal
|
||||
returns focus to the toggle. Drawer state is transient and separate from the
|
||||
desktop open preference. The right-hand `magic-notes-detail` query container and its ResizeObserver
|
||||
measure the detail width for two breakpoints: at 800px or less the record index
|
||||
becomes a drawer while AI stays beside the editor; at 600px or less AI stacks
|
||||
below the stream. The 900px list/detail switch is driven by the same measured
|
||||
below the stream. The 600px list/detail switch is driven by the same measured
|
||||
workspace width in both CSS class and React state, so the two cannot disagree.
|
||||
Per-note stream scroll positions live in a workspace-local map; explicit entry
|
||||
navigation takes precedence over reading-position restoration.
|
||||
@@ -405,7 +409,9 @@ The overview's `libraryView` and the optional note-only `detailView` are indepen
|
||||
opening a todo's source note does not lose the originating task view.
|
||||
The notes list remains visible beside detail on desktop unless collapsed. On
|
||||
narrow screens, or when opening a source note from To-dos, the originating list
|
||||
remains mounted but hidden, retaining scroll position and filters. Returning restores the initiating control's focus and
|
||||
remains mounted but hidden, retaining scroll position and filters. The narrow
|
||||
notes view can reopen that list through the detail toolbar drawer toggle.
|
||||
Returning restores the initiating control's focus and
|
||||
invalidates outstanding detail requests. Initial loading fetches summaries and
|
||||
todos without selecting the first note or task. Navigation away from unsaved content or
|
||||
an unsaved title uses the existing draft confirmation; active writes finish
|
||||
|
||||
@@ -610,13 +610,13 @@ describe('MagicNotesWorkspace overview navigation', () => {
|
||||
const view = render(<MagicNotesWorkspace onNotify={onNotify} />)
|
||||
await openNote()
|
||||
const separator = screen.getByRole('separator', { name: '调整笔记列表宽度' })
|
||||
expect(separator).toHaveAttribute('aria-valuenow', '280')
|
||||
expect(separator).toHaveAttribute('aria-valuenow', '200')
|
||||
fireEvent.keyDown(separator, { key: 'ArrowRight' })
|
||||
expect(separator).toHaveAttribute('aria-valuenow', '296')
|
||||
expect(separator).toHaveAttribute('aria-valuenow', '216')
|
||||
fireEvent.keyDown(separator, { key: 'End' })
|
||||
expect(separator).toHaveAttribute('aria-valuenow', '420')
|
||||
fireEvent.keyDown(separator, { key: 'Home' })
|
||||
expect(separator).toHaveAttribute('aria-valuenow', '240')
|
||||
expect(separator).toHaveAttribute('aria-valuenow', '160')
|
||||
fireEvent.click(screen.getByRole('button', { name: '收起笔记列表' }))
|
||||
expect(screen.queryByRole('searchbox')).not.toBeInTheDocument()
|
||||
expect(screen.getByRole('region', { name: '笔记记录' })).toBeVisible()
|
||||
@@ -626,7 +626,41 @@ describe('MagicNotesWorkspace overview navigation', () => {
|
||||
await openNote()
|
||||
expect(screen.getByRole('button', { name: '展开笔记列表' })).toHaveAttribute('aria-expanded', 'false')
|
||||
fireEvent.click(screen.getByRole('button', { name: '展开笔记列表' }))
|
||||
expect(screen.getByRole('separator', { name: '调整笔记列表宽度' })).toHaveAttribute('aria-valuenow', '240')
|
||||
expect(screen.getByRole('separator', { name: '调整笔记列表宽度' })).toHaveAttribute('aria-valuenow', '160')
|
||||
})
|
||||
|
||||
it.each([600, 700])('keeps the notes list beside detail at %ipx', async (width) => {
|
||||
vi.spyOn(window, 'innerWidth', 'get').mockReturnValue(width)
|
||||
render(<MagicNotesWorkspace onNotify={onNotify} />)
|
||||
await openNote()
|
||||
expect(screen.getByRole('region', { name: '笔记列表' })).toBeVisible()
|
||||
expect(screen.getByRole('separator', { name: '调整笔记列表宽度' })).toBeVisible()
|
||||
})
|
||||
|
||||
it('opens the notes drawer below 600px and dismisses or switches notes', async () => {
|
||||
vi.spyOn(window, 'innerWidth', 'get').mockReturnValue(599)
|
||||
render(<MagicNotesWorkspace onNotify={onNotify} />)
|
||||
await openNote()
|
||||
expect(screen.queryByRole('region', { name: '笔记列表' })).not.toBeInTheDocument()
|
||||
const toggle = screen.getByRole('button', { name: '展开笔记列表' })
|
||||
fireEvent.click(toggle)
|
||||
const sidebar = screen.getByRole('region', { name: '笔记列表' })
|
||||
expect(sidebar).toBeVisible()
|
||||
expect(screen.getByRole('searchbox')).toHaveFocus()
|
||||
expect(screen.queryByRole('separator', { name: '调整笔记列表宽度' })).not.toBeInTheDocument()
|
||||
fireEvent.keyDown(screen.getByRole('searchbox'), { key: 'Escape' })
|
||||
expect(toggle).toHaveFocus()
|
||||
expect(toggle).toHaveAttribute('aria-expanded', 'false')
|
||||
fireEvent.click(toggle)
|
||||
fireEvent.click(document.querySelector('.magic-notes-list-backdrop')!)
|
||||
expect(toggle).toHaveAttribute('aria-expanded', 'false')
|
||||
fireEvent.click(toggle)
|
||||
fireEvent.click(within(sidebar).getByRole('button', { name: '收起笔记列表' }))
|
||||
expect(toggle).toHaveAttribute('aria-expanded', 'false')
|
||||
fireEvent.click(toggle)
|
||||
await openNote(second.title)
|
||||
expect(toggle).toHaveAttribute('aria-expanded', 'false')
|
||||
expect(screen.getByLabelText('笔记标题')).toHaveValue(second.title)
|
||||
})
|
||||
|
||||
it('pins and deletes through the persistent sidebar while detail is open', async () => {
|
||||
|
||||
@@ -134,7 +134,7 @@ type MagicNotesLayoutPreferences = {
|
||||
function loadMagicNotesLayoutPreferences(): MagicNotesLayoutPreferences {
|
||||
const defaults = {
|
||||
notesPaneOpen: true,
|
||||
notesPaneWidth: 280,
|
||||
notesPaneWidth: 200,
|
||||
recordIndexOpen: false,
|
||||
indexPaneWidth: defaultIndexPaneWidth,
|
||||
aiPanePinned: false,
|
||||
@@ -152,7 +152,7 @@ function loadMagicNotesLayoutPreferences(): MagicNotesLayoutPreferences {
|
||||
return {
|
||||
notesPaneOpen: parsed.notesPaneOpen !== false,
|
||||
notesPaneWidth: typeof parsed.notesPaneWidth === 'number' && Number.isFinite(parsed.notesPaneWidth)
|
||||
? Math.min(420, Math.max(240, parsed.notesPaneWidth)) : defaults.notesPaneWidth,
|
||||
? Math.min(420, Math.max(160, parsed.notesPaneWidth)) : defaults.notesPaneWidth,
|
||||
todoPaneWidth: typeof parsed.todoPaneWidth === 'number' && Number.isFinite(parsed.todoPaneWidth)
|
||||
? Math.max(240, parsed.todoPaneWidth) : defaults.todoPaneWidth,
|
||||
// Older layouts stored always-open panes; the compact defaults ignore those flags.
|
||||
@@ -521,6 +521,7 @@ export function MagicNotesWorkspace({
|
||||
const [indexPaneWidth, setIndexPaneWidth] = useState(initialLayoutPreferences.indexPaneWidth)
|
||||
const [todoPaneWidth, setTodoPaneWidth] = useState(initialLayoutPreferences.todoPaneWidth)
|
||||
const [notesPaneOpen, setNotesPaneOpen] = useState(initialLayoutPreferences.notesPaneOpen)
|
||||
const [narrowNotesOpen, setNarrowNotesOpen] = useState(false)
|
||||
const [notesPaneWidth, setNotesPaneWidth] = useState(initialLayoutPreferences.notesPaneWidth)
|
||||
const [workspaceWidth, setWorkspaceWidth] = useState(window.innerWidth)
|
||||
const [todoLayoutWidth, setTodoLayoutWidth] = useState(window.innerWidth)
|
||||
@@ -696,19 +697,23 @@ export function MagicNotesWorkspace({
|
||||
useEffect(() => {
|
||||
const workspace = workspaceRef.current
|
||||
if (!workspace) return
|
||||
const update = (): void => setWorkspaceWidth(workspace.getBoundingClientRect().width || window.innerWidth)
|
||||
const update = (): void => {
|
||||
const width = workspace.getBoundingClientRect().width || window.innerWidth
|
||||
setWorkspaceWidth(width)
|
||||
if (width >= 600) setNarrowNotesOpen(false)
|
||||
}
|
||||
update()
|
||||
const observer = new ResizeObserver(update)
|
||||
observer.observe(workspace)
|
||||
return () => observer.disconnect()
|
||||
}, [loadStatus])
|
||||
|
||||
useLayoutEffect(() => {
|
||||
useEffect(() => {
|
||||
const stream = streamRef.current
|
||||
if (!stream || !detail?.id) return
|
||||
const position = noteScrollPositions.current.get(detail.id) ?? 0
|
||||
if (requestedEntryIdRef.current) return
|
||||
// Read-only rich text mounts in an effect; restore after it has laid out.
|
||||
// Run after the read-only rich text effects have populated their content.
|
||||
const frame = requestAnimationFrame(() => { stream.scrollTop = position })
|
||||
return () => cancelAnimationFrame(frame)
|
||||
}, [detail?.id, detailView])
|
||||
@@ -1168,6 +1173,7 @@ export function MagicNotesWorkspace({
|
||||
return
|
||||
}
|
||||
if (target.kind === 'overview') {
|
||||
setNarrowNotesOpen(false)
|
||||
discardComposerDraft()
|
||||
discardEditingDraft()
|
||||
detailRequestRef.current += 1
|
||||
@@ -1183,6 +1189,7 @@ export function MagicNotesWorkspace({
|
||||
return
|
||||
}
|
||||
if (target.kind === 'library-view') {
|
||||
setNarrowNotesOpen(false)
|
||||
if (target.value === 'todos') {
|
||||
discardComposerDraft()
|
||||
discardEditingDraft()
|
||||
@@ -1218,6 +1225,7 @@ export function MagicNotesWorkspace({
|
||||
setOverviewReturnNoteId(target.noteId)
|
||||
}
|
||||
setDetailView('notes')
|
||||
setNarrowNotesOpen(false)
|
||||
focusSwitchTarget(target)
|
||||
void loadDetail(target.noteId, target.entryId)
|
||||
},
|
||||
@@ -1754,14 +1762,22 @@ export function MagicNotesWorkspace({
|
||||
// Detail tiers: >800 three columns; 601-800 editor + AI with a record drawer; <=600 AI stacks below.
|
||||
const isNarrowLayout = magicNotesLayoutWidth <= 800
|
||||
const isStackedLayout = magicNotesLayoutWidth <= 600
|
||||
const isNarrowWorkspace = workspaceWidth <= 900
|
||||
const isNarrowWorkspace = workspaceWidth < 600
|
||||
const paneResizeDisabled = (pane: 'ai' | 'index' | 'todo' | 'notes'): boolean =>
|
||||
pane === 'notes' ? isNarrowWorkspace : pane === 'todo' ? todoLayoutWidth <= 700 : pane === 'ai' ? isStackedLayout : isNarrowLayout
|
||||
const notesPaneWidthLimits = { minimum: 240, maximum: Math.min(420, Math.max(240, workspaceWidth - minimumMagicNotesEditorWidth - magicNotesResizeHandleWidth)) }
|
||||
const notesPaneWidthLimits = { minimum: 160, maximum: Math.min(420, Math.max(160, workspaceWidth - minimumMagicNotesEditorWidth - magicNotesResizeHandleWidth)) }
|
||||
const displayedNotesWidth = clampMagicNotesPaneWidth(notesPaneWidth, notesPaneWidthLimits)
|
||||
// Overview shows every note full width; the split list appears only beside an open note.
|
||||
const notesExpanded = isNarrowWorkspace ? !detailView : notesPaneOpen || !detailView
|
||||
const notesDrawerOpen = libraryView === 'notes' && Boolean(detailView) && isNarrowWorkspace && narrowNotesOpen
|
||||
const notesExpanded = isNarrowWorkspace ? !detailView || notesDrawerOpen : notesPaneOpen || !detailView
|
||||
const notesSplit = libraryView === 'notes' && Boolean(detailView) && !isNarrowWorkspace && notesPaneOpen
|
||||
const closeNotesDrawer = (): void => {
|
||||
setNarrowNotesOpen(false)
|
||||
document.getElementById('magic-notes-list-toggle')?.focus({ preventScroll: true })
|
||||
}
|
||||
useEffect(() => {
|
||||
if (notesDrawerOpen) document.querySelector<HTMLInputElement>('#magic-library-panel-notes input[type="search"]')?.focus({ preventScroll: true })
|
||||
}, [notesDrawerOpen])
|
||||
const visibleNoteIdsKey = visibleNotes.map((note) => note.id).join('\n')
|
||||
const visibleNoteIds = useMemo(
|
||||
() => (visibleNoteIdsKey ? visibleNoteIdsKey.split('\n') : []),
|
||||
@@ -2216,12 +2232,16 @@ export function MagicNotesWorkspace({
|
||||
className="magic-note-list-item"
|
||||
aria-current={selectedNoteId === note.id ? 'true' : undefined}
|
||||
type="button"
|
||||
onClick={() =>
|
||||
selectedNoteId !== note.id && requestDraftSwitch({
|
||||
onClick={() => {
|
||||
if (selectedNoteId === note.id) {
|
||||
if (notesDrawerOpen) closeNotesDrawer()
|
||||
return
|
||||
}
|
||||
void requestDraftSwitch({
|
||||
kind: 'note',
|
||||
noteId: note.id
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span className="magic-note-list-item__title">
|
||||
{note.pinned && (
|
||||
@@ -2359,9 +2379,10 @@ export function MagicNotesWorkspace({
|
||||
<div
|
||||
ref={workspaceRef}
|
||||
aria-busy={Boolean(busy)}
|
||||
className={`magic-notes-workspace${notesSplit ? ' magic-notes-workspace--notes' : ''}${libraryView === 'notes' && !detailView ? ' magic-notes-workspace--overview' : ''}${notesExpanded ? '' : ' magic-notes-workspace--list-hidden'}${isNarrowWorkspace ? ' magic-notes-workspace--narrow' : ''}${resizingPane === 'notes' ? ' magic-notes-layout--resizing' : ''}`}
|
||||
className={`magic-notes-workspace${notesSplit || notesDrawerOpen ? ' magic-notes-workspace--notes' : ''}${notesDrawerOpen ? ' magic-notes-workspace--drawer' : ''}${libraryView === 'notes' && !detailView ? ' magic-notes-workspace--overview' : ''}${notesExpanded ? '' : ' magic-notes-workspace--list-hidden'}${isNarrowWorkspace ? ' magic-notes-workspace--narrow' : ''}${resizingPane === 'notes' ? ' magic-notes-layout--resizing' : ''}`}
|
||||
style={{ '--magic-notes-notes-width': `${displayedNotesWidth}px` } as React.CSSProperties}
|
||||
>
|
||||
{notesDrawerOpen && <div className="magic-notes-list-backdrop" aria-hidden="true" onClick={closeNotesDrawer} />}
|
||||
<section
|
||||
aria-label={t(
|
||||
libraryView === 'notes'
|
||||
@@ -2370,6 +2391,12 @@ export function MagicNotesWorkspace({
|
||||
)}
|
||||
className={`magic-notes-overview${libraryView === 'todos' ? ' magic-notes-overview--todos' : ''}`}
|
||||
hidden={libraryView === 'notes' ? !notesExpanded : Boolean(detailView)}
|
||||
onKeyDown={(event) => {
|
||||
if (notesDrawerOpen && event.key === 'Escape') {
|
||||
event.stopPropagation()
|
||||
closeNotesDrawer()
|
||||
}
|
||||
}}
|
||||
>
|
||||
{libraryView === 'notes' ? (
|
||||
<div
|
||||
@@ -2379,6 +2406,7 @@ export function MagicNotesWorkspace({
|
||||
<div className="magic-notes-pane-heading">
|
||||
<strong>{t('notes.heading')}</strong>
|
||||
<span>{notes.length}</span>
|
||||
{notesDrawerOpen && <button type="button" className="icon-button" aria-label={t('actions.hideNotes')} onClick={closeNotesDrawer}><PanelLeftClose aria-hidden="true" size={16} /></button>}
|
||||
<MagicNoteTagManagerButton
|
||||
compact={Boolean(detailView)}
|
||||
disabled={Boolean(busy)}
|
||||
@@ -2744,11 +2772,11 @@ export function MagicNotesWorkspace({
|
||||
}}
|
||||
>
|
||||
<div className="magic-note-detail-toolbar" role="toolbar" aria-label={t('notes.toolbarLabel')}>
|
||||
{libraryView === 'notes' && !isNarrowWorkspace && (
|
||||
<button type="button" className="icon-button" aria-controls="magic-library-panel-notes"
|
||||
{libraryView === 'notes' && (
|
||||
<button id="magic-notes-list-toggle" type="button" className="icon-button" aria-controls="magic-library-panel-notes"
|
||||
aria-expanded={notesExpanded} aria-label={t(notesExpanded ? 'actions.hideNotes' : 'actions.showNotes')}
|
||||
title={t(notesExpanded ? 'actions.hideNotes' : 'actions.showNotes')}
|
||||
onClick={() => setNotesPaneOpen((current) => !current)}>
|
||||
onClick={() => isNarrowWorkspace ? setNarrowNotesOpen((current) => !current) : setNotesPaneOpen((current) => !current)}>
|
||||
{notesExpanded ? <PanelLeftClose aria-hidden="true" size={16} /> : <PanelLeftOpen aria-hidden="true" size={16} />}
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -1283,6 +1283,7 @@
|
||||
}
|
||||
|
||||
.magic-notes-workspace {
|
||||
position: relative;
|
||||
display: grid;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
@@ -1291,7 +1292,7 @@
|
||||
}
|
||||
|
||||
.magic-notes-workspace--notes {
|
||||
grid-template-columns: var(--magic-notes-notes-width, 280px) 9px minmax(0, 1fr);
|
||||
grid-template-columns: var(--magic-notes-notes-width, 200px) 9px minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.magic-notes-workspace--list-hidden,
|
||||
@@ -1327,6 +1328,24 @@
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.magic-notes-list-backdrop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 4;
|
||||
background: var(--overlay-backdrop);
|
||||
}
|
||||
|
||||
.magic-notes-workspace--drawer > .magic-notes-overview {
|
||||
position: absolute;
|
||||
inset: 0 auto 0 0;
|
||||
z-index: 5;
|
||||
width: min(var(--magic-notes-notes-width, 200px), calc(100% - 32px));
|
||||
padding: var(--space-2);
|
||||
background: var(--surface-raised);
|
||||
border-right: 1px solid var(--border-default);
|
||||
box-shadow: var(--shadow-dialog);
|
||||
}
|
||||
|
||||
.magic-notes-workspace--notes .magic-notes-pane-heading {
|
||||
min-height: var(--magic-notes-heading-height);
|
||||
}
|
||||
@@ -15302,6 +15321,14 @@ textarea.field-control {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
/* Windowed document table (P5): the table keeps the reader's row in place by
|
||||
correcting the page's scrollTop; native scroll anchoring on the page could
|
||||
pick a spacer and fight the correction. */
|
||||
.workspace-panel-scroll:has(.knowledge-documents__table-scroll--windowed),
|
||||
.knowledge-documents__table-scroll--windowed {
|
||||
overflow-anchor: none;
|
||||
}
|
||||
|
||||
/* Windowed document table (P5): spacer rows stand in for unmounted rows. */
|
||||
.knowledge-documents__table-scroll tbody tr.knowledge-documents__spacer {
|
||||
border: 0;
|
||||
@@ -19431,6 +19458,11 @@ textarea.field-control {
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
/* A windowed tree keeps the reader's row in place itself; native scroll
|
||||
anchoring could pick a spacer and fight the correction. */
|
||||
.workspace-files__files-view--windowed {
|
||||
overflow-anchor: none;
|
||||
}
|
||||
.workspace-git__changes {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
|
||||
@@ -94,7 +94,7 @@ it('resizes record columns with native Electron input and preserves desktop widt
|
||||
const toolbarBox = await rect('.magic-note-detail-toolbar'), streamBox = await rect('.magic-notes-stream-pane');
|
||||
const streamClient = await js('document.querySelector(".magic-notes-stream-pane").clientWidth');
|
||||
assert(Math.abs(toolbarBox.x - streamBox.x) <= 1 && Math.abs(toolbarBox.width - streamClient) <= 1, 'Toolbar spans the stream: ' + JSON.stringify({ toolbarBox, streamBox, streamClient }));
|
||||
assert.equal((await rect('.magic-notes-overview')).width, 280);
|
||||
assert.equal((await rect('.magic-notes-overview')).width, 200);
|
||||
assert(await js('document.querySelector(".magic-notes-overview").checkVisibility()'));
|
||||
await js('document.querySelector(".magic-notes-card-grid").scrollTop = 300; document.querySelector(".magic-notes-stream-pane").scrollTop = 420');
|
||||
await wait('document.querySelector(".magic-notes-stream-pane").scrollTop === 420');
|
||||
@@ -108,7 +108,7 @@ it('resizes record columns with native Electron input and preserves desktop widt
|
||||
assert.equal(await js('document.querySelector("#magic-note-select-layout-note").getAttribute("aria-current")'), 'true');
|
||||
await drag('.magic-notes-notes-resize-handle', 40);
|
||||
const notesWidth = (await rect('.magic-notes-overview')).width;
|
||||
assert(notesWidth >= 320 && notesWidth <= 325);
|
||||
assert(notesWidth >= 240 && notesWidth <= 245);
|
||||
await key('.magic-notes-notes-resize-handle', 'Left');
|
||||
assert.equal((await rect('.magic-notes-overview')).width, notesWidth - 16);
|
||||
await click('button[aria-controls="magic-library-panel-notes"]');
|
||||
@@ -117,7 +117,7 @@ it('resizes record columns with native Electron input and preserves desktop widt
|
||||
assert.equal((await rect('.magic-notes-overview')).width, notesWidth - 16);
|
||||
assert.equal(await js('document.querySelector(".magic-notes-card-grid").scrollTop'), 300);
|
||||
await key('.magic-notes-notes-resize-handle', 'Home');
|
||||
assert.equal((await rect('.magic-notes-overview')).width, 240);
|
||||
assert.equal((await rect('.magic-notes-overview')).width, 160);
|
||||
await js('document.querySelector(".magic-notes-stream-pane").scrollTop = 0');
|
||||
const editor = '.magic-note-composer .magic-note-editor__content .ql-editor';
|
||||
const saveButton = '.magic-note-composer > footer > .primary-button';
|
||||
@@ -184,10 +184,10 @@ it('resizes record columns with native Electron input and preserves desktop widt
|
||||
const withoutAi = (await rect(index)).width; assert(withoutAi < 250 && withoutAi > 230);
|
||||
await click('button[aria-controls="magic-notes-ai-pane"]'); assert.equal((await rect(index)).width, withoutAi);
|
||||
await win.loadURL(${JSON.stringify(server.resolvedUrls!.local[0] + 'layout.html')}); await open(); assert.equal((await rect(index)).width, withoutAi);
|
||||
assert.equal((await rect('.magic-notes-overview')).width, 240);
|
||||
assert.equal((await rect('.magic-notes-overview')).width, 160);
|
||||
await js('document.querySelector(".magic-notes-card-grid").scrollTop = 300');
|
||||
win.setContentSize(1000, 720); await settle(); await settle();
|
||||
assert.equal((await rect('.magic-notes-overview')).width, 240);
|
||||
win.setContentSize(960, 720); await settle(); await settle();
|
||||
assert.equal((await rect('.magic-notes-overview')).width, 160);
|
||||
assert.equal((await rect(index)).width, 0, 'Record drawer follows detail width, not the outer page');
|
||||
const assertSideBySide = async () => {
|
||||
const s = await rect(stream), ai = await rect('.magic-notes-ai-pane'), list = await rect('.magic-notes-overview');
|
||||
@@ -198,12 +198,14 @@ it('resizes record columns with native Electron input and preserves desktop widt
|
||||
await assertSideBySide();
|
||||
win.setContentSize(930, 720); await settle(); await settle();
|
||||
await assertSideBySide();
|
||||
win.setContentSize(1000, 720); await settle(); await settle();
|
||||
win.setContentSize(960, 720); await settle(); await settle();
|
||||
assert(await js('document.documentElement.scrollWidth <= innerWidth'));
|
||||
win.setContentSize(820, 720); await settle(); await settle();
|
||||
assert((await rect(stream)).width >= 299);
|
||||
const constrained = { index: (await rect(index)).width, editor: (await rect(stream)).width, ai: (await rect('.magic-notes-ai-pane')).width };
|
||||
win.setContentSize(720, 640); await settle(); await settle();
|
||||
assert.equal((await rect('.magic-notes-overview')).width, 160);
|
||||
await click('#magic-notes-list-toggle');
|
||||
assert.equal((await rect('.magic-notes-overview')).width, 0);
|
||||
await click('#magic-notes-back');
|
||||
assert(await js('document.querySelector(".magic-notes-overview").checkVisibility()'));
|
||||
@@ -224,10 +226,28 @@ it('resizes record columns with native Electron input and preserves desktop widt
|
||||
assert(Math.abs(narrowFooter.x + narrowFooter.width - footerPaddingRight - narrowSave.x - narrowSave.width) <= 1, 'Narrow Save button stays right-aligned within footer padding');
|
||||
await screenshot('magic-notes-narrow');
|
||||
win.setContentSize(560, 640); await settle(); await settle();
|
||||
assert(await js('document.querySelector("#magic-notes-list-toggle").checkVisibility()'));
|
||||
const detailBeforeDrawer = await rect('.magic-notes-detail-container');
|
||||
await click('#magic-notes-list-toggle');
|
||||
assert(await js('document.querySelector(".magic-notes-workspace--drawer") !== null'));
|
||||
assert.equal((await rect('.magic-notes-overview')).width, 160);
|
||||
assert.deepEqual(await rect('.magic-notes-detail-container'), detailBeforeDrawer);
|
||||
assert(await js('document.querySelector(".magic-notes-overview").scrollWidth <= document.querySelector(".magic-notes-overview").clientWidth'));
|
||||
await key('#magic-library-panel-notes input', 'Escape');
|
||||
assert.equal((await rect('.magic-notes-overview')).width, 0);
|
||||
assert(await js('document.activeElement.id === "magic-notes-list-toggle"'));
|
||||
await click('#magic-notes-list-toggle');
|
||||
await click('#magic-note-select-other-note-3');
|
||||
await wait('document.querySelector(".magic-note-detail-title").value === "Other note 3"');
|
||||
assert.equal((await rect('.magic-notes-overview')).width, 0);
|
||||
await click('#magic-notes-list-toggle');
|
||||
await click('#magic-note-select-layout-note');
|
||||
await wait('document.querySelector(".magic-note-detail-title").value === "Layout note"');
|
||||
assert((await rect('.magic-notes-ai-pane')).y >= (await rect(stream)).y + (await rect(stream)).height, 'AI pane stacks below the stream on very narrow widths');
|
||||
assert.equal(await js('getComputedStyle(document.querySelector(".magic-notes-ai-resize-handle")).display'), 'none');
|
||||
win.setContentSize(1280, 800); await settle(); await settle();
|
||||
assert.equal((await rect('.magic-notes-overview')).width, 240);
|
||||
await click('#magic-notes-list-toggle');
|
||||
assert.equal((await rect('.magic-notes-overview')).width, 160);
|
||||
assert.equal((await rect(index)).width, withoutAi);
|
||||
await key('.magic-note-record', 'Tab');
|
||||
await js('document.querySelector(".magic-note-record").focus()');
|
||||
|
||||
Reference in New Issue
Block a user