feat: configure all notes file visibility

This commit is contained in:
lucaronin
2026-04-30 02:02:59 +02:00
parent 0ad207e318
commit fd3afc2493
31 changed files with 742 additions and 84 deletions

View File

@@ -19,6 +19,9 @@ const emptySettings: Settings = {
ui_language: null,
default_ai_agent: null,
hide_gitignored_files: null,
all_notes_show_pdfs: null,
all_notes_show_images: null,
all_notes_show_unsupported: null,
}
function installPointerCapturePolyfill() {
@@ -107,6 +110,9 @@ describe('SettingsPanel', () => {
release_channel: null,
theme_mode: 'light',
hide_gitignored_files: true,
all_notes_show_pdfs: false,
all_notes_show_images: false,
all_notes_show_unsupported: false,
}))
expect(onClose).toHaveBeenCalled()
})
@@ -125,6 +131,56 @@ describe('SettingsPanel', () => {
expect(onClose).toHaveBeenCalled()
})
it('renders All Notes file visibility checkboxes off by default', () => {
render(
<SettingsPanel open={true} settings={emptySettings} onSave={onSave} onClose={onClose} />
)
expect(screen.getByText('All Notes visibility')).toBeInTheDocument()
expect(within(screen.getByTestId('settings-all-notes-show-pdfs')).getByRole('checkbox')).toHaveAttribute('aria-checked', 'false')
expect(within(screen.getByTestId('settings-all-notes-show-images')).getByRole('checkbox')).toHaveAttribute('aria-checked', 'false')
expect(within(screen.getByTestId('settings-all-notes-show-unsupported')).getByRole('checkbox')).toHaveAttribute('aria-checked', 'false')
})
it('preserves saved All Notes file visibility checkboxes', () => {
render(
<SettingsPanel
open={true}
settings={{
...emptySettings,
all_notes_show_pdfs: true,
all_notes_show_images: true,
all_notes_show_unsupported: false,
}}
onSave={onSave}
onClose={onClose}
/>
)
expect(within(screen.getByTestId('settings-all-notes-show-pdfs')).getByRole('checkbox')).toHaveAttribute('aria-checked', 'true')
expect(within(screen.getByTestId('settings-all-notes-show-images')).getByRole('checkbox')).toHaveAttribute('aria-checked', 'true')
expect(within(screen.getByTestId('settings-all-notes-show-unsupported')).getByRole('checkbox')).toHaveAttribute('aria-checked', 'false')
})
it('saves All Notes file visibility from keyboard toggles before Escape close', () => {
render(
<SettingsPanel open={true} settings={emptySettings} onSave={onSave} onClose={onClose} />
)
const pdfCheckbox = within(screen.getByTestId('settings-all-notes-show-pdfs')).getByRole('checkbox')
pdfCheckbox.focus()
fireEvent.keyDown(pdfCheckbox, { key: ' ', code: 'Space' })
fireEvent.keyUp(pdfCheckbox, { key: ' ', code: 'Space' })
fireEvent.keyDown(screen.getByTestId('settings-panel'), { key: 'Escape' })
expect(onSave).toHaveBeenCalledWith(expect.objectContaining({
all_notes_show_pdfs: true,
all_notes_show_images: false,
all_notes_show_unsupported: false,
}))
expect(onClose).toHaveBeenCalled()
})
it('defaults the color mode control to light', () => {
render(
<SettingsPanel open={true} settings={emptySettings} onSave={onSave} onClose={onClose} />

View File

@@ -14,6 +14,7 @@ import {
type KeyboardEvent as ReactKeyboardEvent,
type MouseEvent as ReactMouseEvent,
type ReactNode,
type RefObject,
} from 'react'
import { Moon, Sun, X } from '@phosphor-icons/react'
import { Copy } from 'lucide-react'
@@ -38,6 +39,11 @@ import {
import { normalizeReleaseChannel, serializeReleaseChannel, type ReleaseChannel } from '../lib/releaseChannel'
import { shouldHideGitignoredFiles } from '../lib/gitignoredVisibility'
import { trackEvent } from '../lib/telemetry'
import {
resolveAllNotesFileVisibility,
settingsWithAllNotesFileVisibility,
type AllNotesFileVisibility,
} from '../utils/allNotesFileVisibility'
import { Button } from './ui/button'
import { Checkbox, type CheckedState } from './ui/checkbox'
import { Input } from './ui/input'
@@ -76,6 +82,7 @@ interface SettingsDraft {
uiLanguage: UiLanguagePreference
initialH1AutoRename: boolean
hideGitignoredFiles: boolean
allNotesFileVisibility: AllNotesFileVisibility
crashReporting: boolean
analytics: boolean
explicitOrganization: boolean
@@ -110,6 +117,8 @@ interface SettingsBodyProps {
setInitialH1AutoRename: (value: boolean) => void
hideGitignoredFiles: boolean
setHideGitignoredFiles: (value: boolean) => void
allNotesFileVisibility: AllNotesFileVisibility
setAllNotesFileVisibility: (value: AllNotesFileVisibility) => void
explicitOrganization: boolean
setExplicitOrganization: (value: boolean) => void
crashReporting: boolean
@@ -149,6 +158,7 @@ function createSettingsDraft(
uiLanguage: settings.ui_language ?? SYSTEM_UI_LANGUAGE,
initialH1AutoRename: settings.initial_h1_auto_rename_enabled ?? true,
hideGitignoredFiles: shouldHideGitignoredFiles(settings),
allNotesFileVisibility: resolveAllNotesFileVisibility(settings),
crashReporting: settings.crash_reporting_enabled ?? false,
analytics: settings.analytics_enabled ?? false,
explicitOrganization: explicitOrganizationEnabled,
@@ -175,7 +185,7 @@ function resolveAnonymousId(settings: Settings, draft: SettingsDraft): string |
}
function buildSettingsFromDraft(settings: Settings, draft: SettingsDraft): Settings {
return {
const nextSettings = {
auto_pull_interval_minutes: draft.pullInterval,
autogit_enabled: draft.autoGitEnabled,
autogit_idle_threshold_seconds: draft.autoGitIdleThresholdSeconds,
@@ -192,6 +202,7 @@ function buildSettingsFromDraft(settings: Settings, draft: SettingsDraft): Setti
default_ai_agent: draft.defaultAiAgent,
hide_gitignored_files: draft.hideGitignoredFiles,
}
return settingsWithAllNotesFileVisibility(nextSettings, draft.allNotesFileVisibility)
}
function trackTelemetryConsentChange(previousAnalytics: boolean, nextAnalytics: boolean): void {
@@ -213,6 +224,16 @@ function applyThemeModeSelection(value: ThemeMode): void {
if (typeof window !== 'undefined') writeStoredThemeMode(window.localStorage, value)
}
function useSettingsPanelAutofocus(panelRef: RefObject<HTMLDivElement | null>): void {
useEffect(() => {
const timer = setTimeout(() => {
const focusTarget = panelRef.current?.querySelector<HTMLElement>('[data-settings-autofocus="true"]')
focusTarget?.focus()
}, 50)
return () => clearTimeout(timer)
}, [panelRef])
}
export function SettingsPanel({
open,
settings,
@@ -272,13 +293,7 @@ function SettingsPanelInner({
setDraft(createSettingsDraft(settings, explicitOrganizationEnabled))
}, [explicitOrganizationEnabled, settings])
useEffect(() => {
const timer = setTimeout(() => {
const focusTarget = panelRef.current?.querySelector<HTMLElement>('[data-settings-autofocus="true"]')
focusTarget?.focus()
}, 50)
return () => clearTimeout(timer)
}, [])
useSettingsPanelAutofocus(panelRef)
const updateDraft = useCallback(
<Key extends keyof SettingsDraft>(key: Key, value: SettingsDraft[Key]) => {
@@ -292,6 +307,11 @@ function SettingsPanelInner({
onSave({ ...settings, hide_gitignored_files: value })
}, [onSave, settings, updateDraft])
const handleAllNotesFileVisibilityChange = useCallback((value: AllNotesFileVisibility) => {
updateDraft('allNotesFileVisibility', value)
onSave(settingsWithAllNotesFileVisibility(settings, value))
}, [onSave, settings, updateDraft])
const handleThemeModeChange = useCallback((value: ThemeMode) => {
updateDraft('themeMode', value)
applyThemeModeSelection(value)
@@ -368,6 +388,8 @@ function SettingsPanelInner({
setInitialH1AutoRename={(value) => updateDraft('initialH1AutoRename', value)}
hideGitignoredFiles={draft.hideGitignoredFiles}
setHideGitignoredFiles={handleGitignoredVisibilityChange}
allNotesFileVisibility={draft.allNotesFileVisibility}
setAllNotesFileVisibility={handleAllNotesFileVisibilityChange}
explicitOrganization={draft.explicitOrganization}
setExplicitOrganization={(value) => updateDraft('explicitOrganization', value)}
crashReporting={draft.crashReporting}
@@ -401,7 +423,17 @@ function SettingsHeader({ onClose, t }: { onClose: () => void; t: Translate }) {
)
}
function SettingsBody({
function SettingsBody(props: SettingsBodyProps) {
return (
<div style={{ padding: 24, display: 'flex', flexDirection: 'column', gap: 0, overflow: 'auto' }}>
<SettingsSyncAndAppearanceSections {...props} />
<SettingsContentSections {...props} />
<SettingsAgentWorkflowSections {...props} />
</div>
)
}
function SettingsSyncAndAppearanceSections({
t,
locale,
systemLocale,
@@ -414,31 +446,15 @@ function SettingsBody({
setAutoGitIdleThresholdSeconds,
autoGitInactiveThresholdSeconds,
setAutoGitInactiveThresholdSeconds,
autoAdvanceInboxAfterOrganize,
setAutoAdvanceInboxAfterOrganize,
aiAgentsStatus,
defaultAiAgent,
setDefaultAiAgent,
onCopyMcpConfig,
releaseChannel,
setReleaseChannel,
themeMode,
setThemeMode,
uiLanguage,
setUiLanguage,
initialH1AutoRename,
setInitialH1AutoRename,
hideGitignoredFiles,
setHideGitignoredFiles,
explicitOrganization,
setExplicitOrganization,
crashReporting,
setCrashReporting,
analytics,
setAnalytics,
}: SettingsBodyProps) {
return (
<div style={{ padding: 24, display: 'flex', flexDirection: 'column', gap: 0, overflow: 'auto' }}>
<>
<SettingsSection showDivider={false}>
<SyncAndUpdatesSection
t={t}
@@ -448,7 +464,6 @@ function SettingsBody({
setReleaseChannel={setReleaseChannel}
/>
</SettingsSection>
<SettingsSection>
<AutoGitSettingsSection
t={t}
@@ -479,7 +494,21 @@ function SettingsBody({
setUiLanguage={setUiLanguage}
/>
</SettingsSection>
</>
)
}
function SettingsContentSections({
t,
initialH1AutoRename,
setInitialH1AutoRename,
hideGitignoredFiles,
setHideGitignoredFiles,
allNotesFileVisibility,
setAllNotesFileVisibility,
}: SettingsBodyProps) {
return (
<>
<SettingsSection>
<TitleSettingsSection
t={t}
@@ -493,9 +522,31 @@ function SettingsBody({
t={t}
hideGitignoredFiles={hideGitignoredFiles}
setHideGitignoredFiles={setHideGitignoredFiles}
allNotesFileVisibility={allNotesFileVisibility}
setAllNotesFileVisibility={setAllNotesFileVisibility}
/>
</SettingsSection>
</>
)
}
function SettingsAgentWorkflowSections({
t,
autoAdvanceInboxAfterOrganize,
setAutoAdvanceInboxAfterOrganize,
aiAgentsStatus,
defaultAiAgent,
setDefaultAiAgent,
onCopyMcpConfig,
explicitOrganization,
setExplicitOrganization,
crashReporting,
setCrashReporting,
analytics,
setAnalytics,
}: SettingsBodyProps) {
return (
<>
<SettingsSection>
<AiAgentSettingsSection
t={t}
@@ -525,7 +576,7 @@ function SettingsBody({
setAnalytics={setAnalytics}
/>
</SettingsSection>
</div>
</>
)
}
@@ -779,7 +830,16 @@ function VaultContentSettingsSection({
t,
hideGitignoredFiles,
setHideGitignoredFiles,
}: Pick<SettingsBodyProps, 't' | 'hideGitignoredFiles' | 'setHideGitignoredFiles'>) {
allNotesFileVisibility,
setAllNotesFileVisibility,
}: Pick<
SettingsBodyProps,
't' | 'hideGitignoredFiles' | 'setHideGitignoredFiles' | 'allNotesFileVisibility' | 'setAllNotesFileVisibility'
>) {
const updateAllNotesFileVisibility = (patch: Partial<AllNotesFileVisibility>) => {
setAllNotesFileVisibility({ ...allNotesFileVisibility, ...patch })
}
return (
<>
<SectionHeading
@@ -794,6 +854,35 @@ function VaultContentSettingsSection({
onChange={setHideGitignoredFiles}
testId="settings-hide-gitignored-files"
/>
<div className="space-y-3 pt-1">
<SectionHeading
title={t('settings.allNotesVisibility.title')}
description={t('settings.allNotesVisibility.description')}
/>
<SettingsCheckboxRow
label={t('settings.allNotesVisibility.pdfs')}
description={t('settings.allNotesVisibility.pdfsDescription')}
checked={allNotesFileVisibility.pdfs}
onChange={(checked) => updateAllNotesFileVisibility({ pdfs: checked })}
testId="settings-all-notes-show-pdfs"
/>
<SettingsCheckboxRow
label={t('settings.allNotesVisibility.images')}
description={t('settings.allNotesVisibility.imagesDescription')}
checked={allNotesFileVisibility.images}
onChange={(checked) => updateAllNotesFileVisibility({ images: checked })}
testId="settings-all-notes-show-images"
/>
<SettingsCheckboxRow
label={t('settings.allNotesVisibility.unsupported')}
description={t('settings.allNotesVisibility.unsupportedDescription')}
checked={allNotesFileVisibility.unsupported}
onChange={(checked) => updateAllNotesFileVisibility({ unsupported: checked })}
testId="settings-all-notes-show-unsupported"
/>
</div>
</>
)
}
@@ -1084,6 +1173,40 @@ function SettingsSwitchRow({
)
}
function SettingsCheckboxRow({
label,
description,
checked,
onChange,
testId,
}: {
label: string
description: string
checked: boolean
onChange: (value: boolean) => void
testId: string
}) {
return (
<label className="flex items-start gap-3" style={{ cursor: 'pointer' }} data-testid={testId}>
<Checkbox
checked={checked}
onCheckedChange={(value) => onChange(isChecked(value))}
onKeyDown={(event) => {
if (event.key !== ' ' && event.key !== 'Spacebar') return
event.preventDefault()
onChange(!checked)
}}
aria-label={label}
className="mt-0.5"
/>
<span className="space-y-1">
<span className="block" style={{ fontSize: 13, fontWeight: 500, color: 'var(--foreground)' }}>{label}</span>
<span className="block" style={{ fontSize: 11, color: 'var(--muted-foreground)' }}>{description}</span>
</span>
</label>
)
}
function TelemetryToggle({
label,
description,

View File

@@ -29,6 +29,7 @@ import {
import { useSidebarTypeInteractions } from './sidebar/useSidebarTypeInteractions'
import type { AppLocale } from '../lib/i18n'
import type { FolderFileActions } from '../hooks/useFileActions'
import type { AllNotesFileVisibility } from '../utils/allNotesFileVisibility'
interface SidebarProps {
entries: VaultEntry[]
@@ -60,6 +61,7 @@ interface SidebarProps {
vaultRootPath?: string
showInbox?: boolean
inboxCount?: number
allNotesFileVisibility?: AllNotesFileVisibility
locale?: AppLocale
onCollapse?: () => void
loading?: boolean
@@ -489,10 +491,11 @@ function useSidebarRuntime({
onReorderSections,
onRenameSection,
onToggleTypeVisibility,
allNotesFileVisibility,
locale = 'en',
}: SidebarProps) {
const { typeEntryMap, allSectionGroups, visibleSections, sectionIds } = useSidebarSections(entries)
const { activeCount, archivedCount } = useEntryCounts(entries)
const { activeCount, archivedCount } = useEntryCounts(entries, allNotesFileVisibility)
const { collapsed: groupCollapsed, toggle: toggleGroup } = useSidebarCollapsed()
const typeInteractions = useSidebarTypeInteractions({
allSectionGroups,

View File

@@ -18,6 +18,7 @@ import { useMultiSelect, type MultiSelectState } from '../../hooks/useMultiSelec
import { useNoteListKeyboard } from '../../hooks/useNoteListKeyboard'
import { prefetchNoteContent } from '../../hooks/useTabManagement'
import type { NoteListPropertiesScope } from './noteListPropertiesEvents'
import type { AllNotesFileVisibility } from '../../utils/allNotesFileVisibility'
// --- useTypeEntryMap ---
@@ -36,6 +37,7 @@ interface FilteredEntriesParams {
subFilter?: NoteListFilter
inboxPeriod?: InboxPeriod
views?: ViewFile[]
allNotesFileVisibility?: AllNotesFileVisibility
}
function buildFilteredEntries({
@@ -50,6 +52,7 @@ function buildFilteredEntries({
subFilter,
inboxPeriod,
views,
allNotesFileVisibility,
}: FilteredEntriesParams & {
isEntityView: boolean
isChangesView: boolean
@@ -61,7 +64,11 @@ function buildFilteredEntries({
return entries.filter((entry) => isModifiedEntry(entry.path, modifiedPathSet, modifiedSuffixes))
}
if (isInboxView) return filterInboxEntries(entries, inboxPeriod ?? 'month')
return filterEntries(entries, selection, subFilter, views)
return filterEntries(entries, selection, {
subFilter,
views,
allNotesFileVisibility,
})
}
export function useFilteredEntries({
@@ -73,6 +80,7 @@ export function useFilteredEntries({
subFilter,
inboxPeriod,
views,
allNotesFileVisibility,
}: FilteredEntriesParams) {
const isEntityView = selection.kind === 'entity'
const isChangesView = selection.kind === 'filter' && selection.filter === 'changes'
@@ -90,8 +98,9 @@ export function useFilteredEntries({
subFilter,
inboxPeriod,
views,
allNotesFileVisibility,
})
}, [entries, inboxPeriod, isChangesView, isEntityView, isInboxView, modifiedFiles, modifiedPathSet, modifiedSuffixes, selection, subFilter, views])
}, [allNotesFileVisibility, entries, inboxPeriod, isChangesView, isEntityView, isInboxView, modifiedFiles, modifiedPathSet, modifiedSuffixes, selection, subFilter, views])
}
// --- useNoteListData ---
@@ -104,9 +113,23 @@ interface NoteListDataParams {
subFilter?: NoteListFilter
inboxPeriod?: InboxPeriod
views?: ViewFile[]
allNotesFileVisibility?: AllNotesFileVisibility
}
export function useNoteListData({ entries, selection, query, listSort, listDirection, modifiedPathSet, modifiedSuffixes, modifiedFiles, subFilter, inboxPeriod, views }: NoteListDataParams) {
export function useNoteListData({
entries,
selection,
query,
listSort,
listDirection,
modifiedPathSet,
modifiedSuffixes,
modifiedFiles,
subFilter,
inboxPeriod,
views,
allNotesFileVisibility,
}: NoteListDataParams) {
const isEntityView = selection.kind === 'entity'
const isArchivedView = (selection.kind === 'filter' && selection.filter === 'archived') || subFilter === 'archived'
const entityEntry = useMemo(() => {
@@ -123,6 +146,7 @@ export function useNoteListData({ entries, selection, query, listSort, listDirec
subFilter,
inboxPeriod,
views,
allNotesFileVisibility,
})
const searched = useMemo(() => {
@@ -530,8 +554,8 @@ function useAllNotesPropertyPickerState(
const allNotesEntries = useMemo(
() => isAllNotesView
? [
...filterEntries(entries, selection, 'open'),
...filterEntries(entries, selection, 'archived'),
...filterEntries(entries, selection, { subFilter: 'open' }),
...filterEntries(entries, selection, { subFilter: 'archived' }),
]
: [],
[entries, isAllNotesView, selection],
@@ -588,7 +612,7 @@ function useViewPropertyPickerState(
[selection, views],
)
const viewEntries = useMemo(
() => selectedView ? filterEntries(entries, selection, undefined, views) : [],
() => selectedView ? filterEntries(entries, selection, { views }) : [],
[entries, selection, selectedView, views],
)

View File

@@ -11,6 +11,7 @@ import type {
import type { AppLocale } from '../../lib/i18n'
import type { NoteListFilter } from '../../utils/noteListHelpers'
import { countByFilter, countAllByFilter, countAllNotesByFilter } from '../../utils/noteListHelpers'
import type { AllNotesFileVisibility } from '../../utils/allNotesFileVisibility'
import { NoteItem } from '../NoteItem'
import { prefetchNoteContent } from '../../hooks/useTabManagement'
import type { MultiSelectState } from '../../hooks/useMultiSelect'
@@ -108,13 +109,19 @@ function useBulkActions(
}
}
function useFilterCounts(entries: VaultEntry[], selection: SidebarSelection) {
function useFilterCounts(
entries: VaultEntry[],
selection: SidebarSelection,
allNotesFileVisibility?: AllNotesFileVisibility,
) {
return useMemo(() => {
if (selection.kind === 'sectionGroup') return countByFilter(entries, selection.type)
if (selection.kind === 'folder') return countAllByFilter(entries)
if (selection.kind === 'filter' && selection.filter === 'all') return countAllNotesByFilter(entries)
if (selection.kind === 'filter' && selection.filter === 'all') {
return countAllNotesByFilter(entries, allNotesFileVisibility)
}
return { open: 0, archived: 0 }
}, [entries, selection])
}, [allNotesFileVisibility, entries, selection])
}
interface UseNoteListContentParams {
@@ -136,6 +143,7 @@ interface UseNoteListContentParams {
updateEntry?: (path: string, patch: Partial<VaultEntry>) => void
views?: ViewFile[]
visibleNotesRef?: React.MutableRefObject<VaultEntry[]>
allNotesFileVisibility?: AllNotesFileVisibility
}
function useNoteListContent({
@@ -157,6 +165,7 @@ function useNoteListContent({
updateEntry,
views,
visibleNotesRef,
allNotesFileVisibility,
}: UseNoteListContentParams) {
const subFilter = (selection.kind === 'sectionGroup' || selection.kind === 'folder')
? noteListFilter
@@ -217,6 +226,7 @@ function useNoteListContent({
subFilter,
inboxPeriod: effectiveInboxPeriod,
views,
allNotesFileVisibility,
})
const searched = useMemo(() => filterEntriesByNoteListQuery(sortedEntries, query, {
allEntries: entries,
@@ -465,6 +475,7 @@ export interface NoteListProps {
onUpdateViewDefinition?: (filename: string, patch: Partial<ViewDefinition>) => void
views?: ViewFile[]
visibleNotesRef?: React.MutableRefObject<VaultEntry[]>
allNotesFileVisibility?: AllNotesFileVisibility
locale?: AppLocale
}
@@ -574,12 +585,13 @@ export function useNoteListModel({
onUpdateViewDefinition,
views,
visibleNotesRef,
allNotesFileVisibility,
locale = 'en',
}: NoteListProps) {
const selectedNotePath = selectedNote?.path ?? null
const { modifiedPathSet, modifiedSuffixes, resolvedGetNoteStatus } = useModifiedFilesState(modifiedFiles, getNoteStatus)
const { isInboxView } = useViewFlags(selection)
const filterCounts = useFilterCounts(entries, selection)
const filterCounts = useFilterCounts(entries, selection, allNotesFileVisibility)
const content = useNoteListContent({
entries,
selection,
@@ -599,6 +611,7 @@ export function useNoteListModel({
updateEntry,
views,
visibleNotesRef,
allNotesFileVisibility,
})
const interaction = useNoteListInteractionState({
searched: content.searched,

View File

@@ -4,6 +4,7 @@ import { APP_STORAGE_KEYS, LEGACY_APP_STORAGE_KEYS, getAppStorageItem } from '..
import { buildTypeEntryMap } from '../../utils/typeColors'
import { countAllNotesByFilter } from '../../utils/noteListHelpers'
import { buildDynamicSections, sortSections } from '../../utils/sidebarSections'
import type { AllNotesFileVisibility } from '../../utils/allNotesFileVisibility'
export type SidebarGroupKey = 'favorites' | 'views' | 'sections' | 'folders'
@@ -57,11 +58,14 @@ export function useSidebarCollapsed() {
return { collapsed, toggle }
}
export function useEntryCounts(entries: VaultEntry[]) {
export function useEntryCounts(
entries: VaultEntry[],
allNotesFileVisibility?: AllNotesFileVisibility,
) {
return useMemo(() => {
const counts = countAllNotesByFilter(entries)
const counts = countAllNotesByFilter(entries, allNotesFileVisibility)
return { activeCount: counts.open, archivedCount: counts.archived }
}, [entries])
}, [allNotesFileVisibility, entries])
}
export function computeReorder(sectionIds: string[], activeId: string, overId: string): string[] | null {