[next]随手记,页面顶部不够沉浸,重新设计下,还有左侧第一栏和第二栏中间不应有边距

This commit is contained in:
RememBerBer
2026-08-26 14:07:53 +08:00
parent fe58c75c23
commit 8fdbe1f4d2
3 changed files with 87 additions and 37 deletions
+27 -13
View File
@@ -2,8 +2,11 @@ import DOMPurify from 'dompurify'
import {
Check,
ChevronDown,
Columns2,
CopyPlus,
Download,
Eye,
FilePenLine,
FilePlus2,
FolderOpen,
FolderPlus,
@@ -928,19 +931,9 @@ export function QuickNoteTool() {
: state.quickReplaceOpen ? [320, 170] : [320]
return (
<section className="tool-page quick-note-tool">
<div className="tool-page__header tool-page__header--actions quick-note-page-header">
<h1 className="visually-hidden">{t('quickNote.title')}</h1>
<WorkspaceDragZone />
<div className="quick-note-view-switch segmented" role="tablist">
{(['editor', 'split', 'preview'] as const).map((mode) => (
<button className={state.viewMode === mode ? 'segmented__item segmented__item--active' : 'segmented__item'} type="button" role="tab" aria-selected={state.viewMode === mode} key={mode} onClick={() => update({ viewMode: mode })}>
{t(`quickNote.view.${mode}` as MessageKey)}
</button>
))}
</div>
</div>
<h1 className="visually-hidden">{t('quickNote.title')}</h1>
<div className={[state.treeOpen ? '' : 'quick-note-layout--tree-closed', state.quickReplaceOpen ? 'quick-note-layout--replace-open' : ''].filter(Boolean).join(' ') || undefined}>
<div className={['quick-note-stage', state.treeOpen ? '' : 'quick-note-layout--tree-closed', state.quickReplaceOpen ? 'quick-note-layout--replace-open' : ''].filter(Boolean).join(' ')}>
<ResizableColumns
className="quick-note-layout"
columns={quickNoteColumns}
@@ -983,6 +976,27 @@ export function QuickNoteTool() {
<section className="quick-note-editor-shell">
<div className="quick-note-toolbar">
<IconButton label={state.treeOpen ? t('quickNote.openVault') : t('quickNote.newNote')} icon={state.treeOpen ? PanelLeftClose : PanelLeftOpen} onClick={() => update({ treeOpen: !state.treeOpen })} />
<div className="quick-note-view-switch segmented" role="tablist" aria-label={t('quickNote.title')}>
{([
{ id: 'editor', icon: FilePenLine },
{ id: 'split', icon: Columns2 },
{ id: 'preview', icon: Eye }
] as const).map(({ id, icon: ViewIcon }) => (
<button
className={state.viewMode === id ? 'segmented__item segmented__item--active' : 'segmented__item'}
type="button"
role="tab"
aria-label={t(`quickNote.view.${id}` as MessageKey)}
aria-selected={state.viewMode === id}
title={t(`quickNote.view.${id}` as MessageKey)}
key={id}
onClick={() => update({ viewMode: id })}
>
<ViewIcon size={15} />
</button>
))}
</div>
<span className="quick-note-toolbar__divider" aria-hidden="true" />
<NoteColorPicker key={state.note?.relativePath ?? 'empty'} value={state.note?.metadata.color} disabled={!state.note} label={t('quickNote.color')} onChange={(color) => patchMetadata({ color })} />
<select className="quick-note-syntax" aria-label={t('quickNote.syntax')} disabled={!state.note} value={state.note?.metadata.syntax ?? 'text/plain'} onChange={(event) => patchMetadata({ syntax: event.target.value })}>
{syntaxOptions.map(([value, label]) => <option value={value} key={value}>{label}</option>)}
@@ -1004,7 +1018,7 @@ export function QuickNoteTool() {
<IconButton label={t('common.action.format')} icon={WandSparkles} disabled={!state.note || !state.content.trim()} onClick={() => { void formatCurrent() }} />
<IconButton label={t('quickNote.bulletList')} icon={List} disabled={!state.note} onClick={() => prefixSelectedLines('bullet')} />
<IconButton label={t('quickNote.numberedList')} icon={ListOrdered} disabled={!state.note} onClick={() => prefixSelectedLines('numbered')} />
<span className="quick-note-toolbar__spacer" />
<WorkspaceDragZone className="quick-note-toolbar__drag-zone" />
<IconButton label={t('quickNote.find')} icon={Search} disabled={!state.note} active={state.findOpen} onClick={() => { if (state.findOpen) update({ findOpen: false }); else openFindReplace() }} />
<IconButton label={t('quickNote.save')} icon={Save} disabled={!state.note || state.busy} active={dirty} onClick={() => { void saveCurrent() }} />
<IconButton label={t('quickNote.duplicate')} icon={CopyPlus} disabled={!state.note} onClick={() => { void duplicateNote() }} />
+39 -11
View File
@@ -645,16 +645,23 @@
/* Phase 6 Quick Note */
.quick-note-page-header {
min-height: 36px;
margin-bottom: 14px;
}
.quick-note-view-switch {
gap: 1px;
padding: 2px;
flex: 0 0 auto;
}
.quick-note-tool > :nth-child(2) {
.quick-note-view-switch .segmented__item {
display: inline-flex;
width: 28px;
min-width: 28px;
min-height: 28px;
align-items: center;
justify-content: center;
padding: 0;
}
.quick-note-stage {
flex: 1;
min-height: 0;
}
@@ -662,7 +669,7 @@
.quick-note-layout {
display: grid;
grid-template-columns: 220px minmax(320px, 1fr);
gap: 9px;
gap: 0;
width: 100%;
height: 100%;
min-height: 0;
@@ -692,6 +699,14 @@
overflow: hidden;
}
.quick-note-sidebar {
border-width: 0 1px 0 0;
}
.quick-replace-panel {
border-width: 0 0 0 1px;
}
.quick-note-search {
display: flex;
align-items: center;
@@ -865,7 +880,7 @@
min-height: 0;
flex-direction: column;
background: var(--surface);
border: 1px solid var(--border);
border: 0;
border-radius: 8px;
box-shadow: 0 8px 24px var(--shadow-soft);
overflow: hidden;
@@ -910,9 +925,22 @@
width: 64px;
}
.quick-note-toolbar__spacer {
flex: 1 1 auto;
min-width: 8px;
.quick-note-toolbar__divider {
width: 1px;
height: 20px;
margin: 0 1px;
background: var(--border-soft);
flex: 0 0 1px;
}
.quick-note-toolbar__drag-zone {
min-width: 32px;
min-height: 32px;
flex: 1 1 56px;
}
.tool-view-shell--detached.tool-view-shell--macos .quick-note-search {
margin-left: 82px;
}
.quick-note-icon-button {
+21 -13
View File
@@ -206,35 +206,41 @@ test('keeps a custom-header tool immersive while preserving its view controls',
await expect.poll(async () => (await evaluateTool('quickNote', `(() => {
const shell = document.querySelector('.tool-view-shell')
const page = document.querySelector('.quick-note-tool')
const header = document.querySelector('.quick-note-page-header')?.getBoundingClientRect()
const title = document.querySelector('.quick-note-page-header h1')?.getBoundingClientRect()
const toolbar = document.querySelector('.quick-note-toolbar')?.getBoundingClientRect()
const title = document.querySelector('.quick-note-tool > h1')?.getBoundingClientRect()
const switcher = document.querySelector('.quick-note-view-switch')?.getBoundingClientRect()
const workspaceDrag = document.querySelector('[data-window-drag-zone]')?.getBoundingClientRect()
const sidebar = document.querySelector('.quick-note-sidebar')?.getBoundingClientRect()
const editor = document.querySelector('.quick-note-editor-shell')?.getBoundingClientRect()
const slot = document.querySelector('.tool-window-toggle-slot')?.getBoundingClientRect()
if (!shell || !page || !header || !title || !switcher || !workspaceDrag || !slot) return null
if (!shell || !page || !toolbar || !title || !switcher || !workspaceDrag || !sidebar || !editor || !slot) return null
return {
immersive: shell.classList.contains('tool-view-shell--immersive'),
brandCount: shell.querySelectorAll('.tool-window-brand-zone').length,
standaloneHeaderCount: page.querySelectorAll(':scope > .tool-page__header').length,
pageTop: Math.round(page.getBoundingClientRect().top),
headerTop: Math.round(header.top),
headerCompact: header.height >= 42 && header.height <= 48,
toolbarTop: Math.round(toolbar.top),
toolbarCompact: toolbar.height >= 48 && toolbar.height <= 54,
titleWidth: Math.round(title.width),
titleHeight: Math.round(title.height),
switchVisible: switcher.width > 0 && switcher.height > 0,
switchInsideToolbar: switcher.top >= toolbar.top && switcher.bottom <= toolbar.bottom,
firstColumnsGap: Math.round(editor.left - sidebar.right),
dragRailCount: shell.querySelectorAll('[data-window-drag-rail]').length,
workspaceDragUsable: workspaceDrag.width >= 48 && workspaceDrag.height >= 24,
workspaceDragUsable: workspaceDrag.width >= 32 && workspaceDrag.height >= 24,
workspaceDragRegion: getComputedStyle(document.querySelector('[data-window-drag-zone]')).getPropertyValue('-webkit-app-region'),
edgeSlotWidth: Math.round(slot.width)
}
})()`)).value).toMatchObject({
immersive: true,
brandCount: 0,
standaloneHeaderCount: 0,
pageTop: 0,
headerTop: 0,
headerCompact: true,
toolbarTop: 0,
toolbarCompact: true,
titleWidth: 1,
titleHeight: 1,
switchVisible: true,
switchInsideToolbar: true,
firstColumnsGap: 0,
dragRailCount: 0,
workspaceDragUsable: true,
workspaceDragRegion: 'drag',
@@ -246,12 +252,14 @@ test('keeps a custom-header tool immersive while preserving its view controls',
return {
controlsClass: document.querySelector('.tool-view-shell').classList.contains('tool-view-shell--window-controls-visible'),
brandCount: document.querySelectorAll('.tool-window-brand-zone').length,
headerTop: Math.round(document.querySelector('.quick-note-page-header').getBoundingClientRect().top)
toolbarTop: Math.round(document.querySelector('.quick-note-toolbar').getBoundingClientRect().top),
searchInset: Math.round(document.querySelector('.quick-note-search').getBoundingClientRect().left - document.querySelector('.quick-note-sidebar').getBoundingClientRect().left)
}
})()`)).value).toEqual({
controlsClass: true,
brandCount: 0,
headerTop: 0
toolbarTop: 0,
searchInset: process.platform === 'darwin' ? 82 : 8
})
await sendToolWindowControlsVisibility('quickNote', false)
@@ -265,7 +273,7 @@ test('keeps double-clicked Quick Note split-preview text selected after auto-sav
await mainPage.locator('.tool-button').filter({ hasText: '随手记' }).click()
await waitForToolSelector('quickNote', '.quick-note-tool')
await evaluateTool('quickNote', `(() => {
const split = [...document.querySelectorAll('[role="tab"]')].find((tab) => tab.textContent === '分栏')
const split = [...document.querySelectorAll('[role="tab"]')].find((tab) => tab.getAttribute('aria-label') === '分栏')
if (!split) throw new Error('Quick Note split tab not found')
split.click()
})()`)