feat: add sidebar navigation arrows
This commit is contained in:
@@ -1632,7 +1632,7 @@ function App() {
|
||||
{sidebarVisible && (
|
||||
<>
|
||||
<div className="app__sidebar" style={{ width: layout.sidebarWidth }}>
|
||||
<Sidebar entries={vault.entries} folders={vault.folders} views={vault.views} selection={effectiveSelection} onSelect={handleSetSelection} onSelectNote={notes.handleSelectNote} onSelectFavorite={handleOpenFavorite} onReorderFavorites={entryActions.handleReorderFavorites} onCreateType={notes.handleCreateNoteImmediate} onCreateNewType={dialogs.openCreateType} onCustomizeType={entryActions.handleCustomizeType} onUpdateTypeTemplate={entryActions.handleUpdateTypeTemplate} onReorderSections={entryActions.handleReorderSections} onRenameSection={entryActions.handleRenameSection} onToggleTypeVisibility={entryActions.handleToggleTypeVisibility} onCreateFolder={handleCreateFolder} onRenameFolder={folderActions.renameFolder} onDeleteFolder={folderActions.requestDeleteFolder} folderFileActions={fileActions.folderActions} renamingFolderPath={folderActions.renamingFolderPath} onStartRenameFolder={folderActions.startFolderRename} onCancelRenameFolder={folderActions.cancelFolderRename} onCreateView={dialogs.openCreateView} onEditView={handleEditView} onDeleteView={handleDeleteView} onReorderViews={viewOrdering.onReorderViews} showInbox={explicitOrganizationEnabled} inboxCount={inboxCount} allNotesFileVisibility={allNotesFileVisibility} locale={appLocale} loading={isVaultContentLoading} vaultRootPath={resolvedPath} />
|
||||
<Sidebar entries={vault.entries} folders={vault.folders} views={vault.views} selection={effectiveSelection} onSelect={handleSetSelection} onSelectNote={notes.handleSelectNote} onSelectFavorite={handleOpenFavorite} onReorderFavorites={entryActions.handleReorderFavorites} onCreateType={notes.handleCreateNoteImmediate} onCreateNewType={dialogs.openCreateType} onCustomizeType={entryActions.handleCustomizeType} onUpdateTypeTemplate={entryActions.handleUpdateTypeTemplate} onReorderSections={entryActions.handleReorderSections} onRenameSection={entryActions.handleRenameSection} onToggleTypeVisibility={entryActions.handleToggleTypeVisibility} onCreateFolder={handleCreateFolder} onRenameFolder={folderActions.renameFolder} onDeleteFolder={folderActions.requestDeleteFolder} folderFileActions={fileActions.folderActions} renamingFolderPath={folderActions.renamingFolderPath} onStartRenameFolder={folderActions.startFolderRename} onCancelRenameFolder={folderActions.cancelFolderRename} onCreateView={dialogs.openCreateView} onEditView={handleEditView} onDeleteView={handleDeleteView} onReorderViews={viewOrdering.onReorderViews} showInbox={explicitOrganizationEnabled} inboxCount={inboxCount} allNotesFileVisibility={allNotesFileVisibility} locale={appLocale} loading={isVaultContentLoading} vaultRootPath={resolvedPath} canGoBack={canGoBack} canGoForward={canGoForward} onGoBack={handleGoBack} onGoForward={handleGoForward} />
|
||||
</div>
|
||||
<ResizeHandle onResize={layout.handleSidebarResize} />
|
||||
</>
|
||||
|
||||
@@ -65,6 +65,10 @@ interface SidebarProps {
|
||||
locale?: AppLocale
|
||||
onCollapse?: () => void
|
||||
loading?: boolean
|
||||
canGoBack?: boolean
|
||||
canGoForward?: boolean
|
||||
onGoBack?: () => void
|
||||
onGoForward?: () => void
|
||||
}
|
||||
|
||||
interface SidebarNavigationProps extends Pick<
|
||||
@@ -633,7 +637,14 @@ export const Sidebar = memo(function Sidebar(props: SidebarProps) {
|
||||
|
||||
return (
|
||||
<aside className="flex h-full flex-col overflow-hidden border-r border-[var(--sidebar-border)] bg-sidebar text-sidebar-foreground">
|
||||
<SidebarTitleBar locale={locale} onCollapse={props.onCollapse} />
|
||||
<SidebarTitleBar
|
||||
locale={locale}
|
||||
onCollapse={props.onCollapse}
|
||||
canGoBack={props.canGoBack}
|
||||
canGoForward={props.canGoForward}
|
||||
onGoBack={props.onGoBack}
|
||||
onGoForward={props.onGoForward}
|
||||
/>
|
||||
<SidebarRuntimeNavigation props={props} runtime={runtime} />
|
||||
<SidebarInteractionOverlays locale={locale} runtime={runtime} />
|
||||
</aside>
|
||||
|
||||
87
src/components/sidebar/SidebarNavArrows.test.tsx
Normal file
87
src/components/sidebar/SidebarNavArrows.test.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import type { ComponentProps } from 'react'
|
||||
import { render, screen, fireEvent } from '@testing-library/react'
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { TooltipProvider } from '@/components/ui/tooltip'
|
||||
import { SidebarNavArrows } from './SidebarNavArrows'
|
||||
|
||||
const { trackNavigationHistoryButtonClickedMock } = vi.hoisted(() => ({
|
||||
trackNavigationHistoryButtonClickedMock: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('../../lib/productAnalytics', () => ({
|
||||
trackNavigationHistoryButtonClicked: trackNavigationHistoryButtonClickedMock,
|
||||
}))
|
||||
|
||||
function renderArrows(props: Partial<ComponentProps<typeof SidebarNavArrows>> = {}) {
|
||||
const defaults = {
|
||||
canGoBack: true,
|
||||
canGoForward: true,
|
||||
onGoBack: vi.fn(),
|
||||
onGoForward: vi.fn(),
|
||||
}
|
||||
const merged = { ...defaults, ...props }
|
||||
return {
|
||||
...render(
|
||||
<TooltipProvider>
|
||||
<SidebarNavArrows {...merged} />
|
||||
</TooltipProvider>,
|
||||
),
|
||||
props: merged,
|
||||
}
|
||||
}
|
||||
|
||||
describe('SidebarNavArrows', () => {
|
||||
beforeEach(() => {
|
||||
trackNavigationHistoryButtonClickedMock.mockClear()
|
||||
})
|
||||
|
||||
it('renders back and forward buttons with default English labels', () => {
|
||||
renderArrows()
|
||||
expect(screen.getByRole('button', { name: 'Back' })).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'Forward' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('localizes button labels', () => {
|
||||
renderArrows({ locale: 'zh-CN' })
|
||||
expect(screen.getByRole('button', { name: '返回' })).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: '前进' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('calls and tracks back navigation when back is clicked', () => {
|
||||
const { props } = renderArrows()
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Back' }))
|
||||
expect(props.onGoBack).toHaveBeenCalledOnce()
|
||||
expect(trackNavigationHistoryButtonClickedMock).toHaveBeenCalledWith('back')
|
||||
})
|
||||
|
||||
it('calls and tracks forward navigation when forward is clicked', () => {
|
||||
const { props } = renderArrows()
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Forward' }))
|
||||
expect(props.onGoForward).toHaveBeenCalledOnce()
|
||||
expect(trackNavigationHistoryButtonClickedMock).toHaveBeenCalledWith('forward')
|
||||
})
|
||||
|
||||
it('disables back navigation when history is unavailable', () => {
|
||||
const { props } = renderArrows({ canGoBack: false })
|
||||
const back = screen.getByRole('button', { name: 'Back' })
|
||||
expect(back).toBeDisabled()
|
||||
fireEvent.click(back)
|
||||
expect(props.onGoBack).not.toHaveBeenCalled()
|
||||
expect(trackNavigationHistoryButtonClickedMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('disables forward navigation when future history is unavailable', () => {
|
||||
const { props } = renderArrows({ canGoForward: false })
|
||||
const forward = screen.getByRole('button', { name: 'Forward' })
|
||||
expect(forward).toBeDisabled()
|
||||
fireEvent.click(forward)
|
||||
expect(props.onGoForward).not.toHaveBeenCalled()
|
||||
expect(trackNavigationHistoryButtonClickedMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('marks both buttons as non-drag controls', () => {
|
||||
renderArrows()
|
||||
expect(screen.getByRole('button', { name: 'Back' })).toHaveAttribute('data-no-drag')
|
||||
expect(screen.getByRole('button', { name: 'Forward' })).toHaveAttribute('data-no-drag')
|
||||
})
|
||||
})
|
||||
73
src/components/sidebar/SidebarNavArrows.tsx
Normal file
73
src/components/sidebar/SidebarNavArrows.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import { CaretLeft, CaretRight } from '@phosphor-icons/react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { ActionTooltip } from '@/components/ui/action-tooltip'
|
||||
import { APP_COMMAND_IDS, getAppCommandShortcutDisplay } from '../../hooks/appCommandCatalog'
|
||||
import { translate, type AppLocale } from '../../lib/i18n'
|
||||
import { trackNavigationHistoryButtonClicked } from '../../lib/productAnalytics'
|
||||
|
||||
export interface SidebarNavArrowsProps {
|
||||
canGoBack: boolean
|
||||
canGoForward: boolean
|
||||
onGoBack: () => void
|
||||
onGoForward: () => void
|
||||
locale?: AppLocale
|
||||
}
|
||||
|
||||
const navButtonClass = 'h-6 w-6 rounded text-muted-foreground hover:text-foreground disabled:opacity-40'
|
||||
|
||||
export function SidebarNavArrows({
|
||||
canGoBack,
|
||||
canGoForward,
|
||||
onGoBack,
|
||||
onGoForward,
|
||||
locale = 'en',
|
||||
}: SidebarNavArrowsProps) {
|
||||
const backLabel = translate(locale, 'sidebar.nav.back')
|
||||
const forwardLabel = translate(locale, 'sidebar.nav.forward')
|
||||
const backShortcut = getAppCommandShortcutDisplay(APP_COMMAND_IDS.viewGoBack)
|
||||
const forwardShortcut = getAppCommandShortcutDisplay(APP_COMMAND_IDS.viewGoForward)
|
||||
|
||||
const handleBackClick = () => {
|
||||
trackNavigationHistoryButtonClicked('back')
|
||||
onGoBack()
|
||||
}
|
||||
const handleForwardClick = () => {
|
||||
trackNavigationHistoryButtonClicked('forward')
|
||||
onGoForward()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1" data-testid="sidebar-nav-arrows">
|
||||
<ActionTooltip copy={{ label: backLabel, shortcut: backShortcut }} side="bottom">
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
className={navButtonClass}
|
||||
disabled={!canGoBack}
|
||||
onClick={handleBackClick}
|
||||
aria-label={backLabel}
|
||||
data-no-drag
|
||||
data-testid="sidebar-nav-back"
|
||||
>
|
||||
<CaretLeft size={14} weight="bold" />
|
||||
</Button>
|
||||
</ActionTooltip>
|
||||
<ActionTooltip copy={{ label: forwardLabel, shortcut: forwardShortcut }} side="bottom">
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
className={navButtonClass}
|
||||
disabled={!canGoForward}
|
||||
onClick={handleForwardClick}
|
||||
aria-label={forwardLabel}
|
||||
data-no-drag
|
||||
data-testid="sidebar-nav-forward"
|
||||
>
|
||||
<CaretRight size={14} weight="bold" />
|
||||
</Button>
|
||||
</ActionTooltip>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
CaretLeft, Plus,
|
||||
} from '@phosphor-icons/react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { TooltipProvider } from '@/components/ui/tooltip'
|
||||
import {
|
||||
type SectionGroup, isSelectionActive, SectionContent, VisibilityPopover,
|
||||
} from '../SidebarParts'
|
||||
@@ -21,6 +22,7 @@ import { TypeCustomizePopover } from '../TypeCustomizePopover'
|
||||
import { useDragRegion } from '../../hooks/useDragRegion'
|
||||
import { SidebarGroupHeader } from './SidebarGroupHeader'
|
||||
import { SidebarViewItem } from './SidebarViewItem'
|
||||
import { SidebarNavArrows } from './SidebarNavArrows'
|
||||
import { computeReorder } from './sidebarHooks'
|
||||
import { countByFilter } from '../../utils/noteListHelpers'
|
||||
import { translate, type AppLocale } from '../../lib/i18n'
|
||||
@@ -302,27 +304,55 @@ export function TypesSection({
|
||||
)
|
||||
}
|
||||
|
||||
export function SidebarTitleBar({ locale = 'en', onCollapse }: { locale?: AppLocale; onCollapse?: () => void }) {
|
||||
const noopNavigation = () => {}
|
||||
|
||||
export function SidebarTitleBar({
|
||||
locale = 'en',
|
||||
onCollapse,
|
||||
canGoBack = false,
|
||||
canGoForward = false,
|
||||
onGoBack = noopNavigation,
|
||||
onGoForward = noopNavigation,
|
||||
}: {
|
||||
locale?: AppLocale
|
||||
onCollapse?: () => void
|
||||
canGoBack?: boolean
|
||||
canGoForward?: boolean
|
||||
onGoBack?: () => void
|
||||
onGoForward?: () => void
|
||||
}) {
|
||||
const { onMouseDown } = useDragRegion()
|
||||
|
||||
return (
|
||||
<div
|
||||
className="shrink-0 flex items-center justify-end border-b border-border"
|
||||
style={{ height: 52, padding: '0 8px', paddingLeft: 80, cursor: 'default' }}
|
||||
onMouseDown={onMouseDown}
|
||||
>
|
||||
{onCollapse && (
|
||||
<button
|
||||
className="flex shrink-0 cursor-pointer items-center justify-center rounded border-none bg-transparent p-0 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
||||
style={{ width: 24, height: 24 }}
|
||||
onClick={onCollapse}
|
||||
aria-label={translate(locale, 'sidebar.action.collapse')}
|
||||
title={translate(locale, 'sidebar.action.collapse')}
|
||||
>
|
||||
<CaretLeft size={14} weight="bold" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<TooltipProvider>
|
||||
<div
|
||||
className="flex shrink-0 items-center justify-between gap-2 border-b border-border"
|
||||
style={{ height: 52, padding: '0 8px', paddingLeft: 80, cursor: 'default' }}
|
||||
onMouseDown={onMouseDown}
|
||||
>
|
||||
<SidebarNavArrows
|
||||
canGoBack={canGoBack}
|
||||
canGoForward={canGoForward}
|
||||
onGoBack={onGoBack}
|
||||
onGoForward={onGoForward}
|
||||
locale={locale}
|
||||
/>
|
||||
{onCollapse && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
className="h-6 w-6 rounded text-muted-foreground hover:text-foreground"
|
||||
onClick={onCollapse}
|
||||
aria-label={translate(locale, 'sidebar.action.collapse')}
|
||||
title={translate(locale, 'sidebar.action.collapse')}
|
||||
data-no-drag
|
||||
>
|
||||
<CaretLeft size={14} weight="bold" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
51
src/components/sidebar/SidebarTitleBar.test.tsx
Normal file
51
src/components/sidebar/SidebarTitleBar.test.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { ComponentProps } from 'react'
|
||||
import { render, screen, fireEvent } from '@testing-library/react'
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { SidebarTitleBar } from './SidebarSections'
|
||||
|
||||
function renderTitleBar(overrides: Partial<ComponentProps<typeof SidebarTitleBar>> = {}) {
|
||||
const props = {
|
||||
canGoBack: true,
|
||||
canGoForward: true,
|
||||
onGoBack: vi.fn(),
|
||||
onGoForward: vi.fn(),
|
||||
onCollapse: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
return {
|
||||
...render(<SidebarTitleBar {...props} />),
|
||||
props,
|
||||
}
|
||||
}
|
||||
|
||||
describe('SidebarTitleBar', () => {
|
||||
it('renders navigation arrows and the collapse button together', () => {
|
||||
renderTitleBar()
|
||||
expect(screen.getByTestId('sidebar-nav-arrows')).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'Back' })).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'Forward' })).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'Collapse sidebar' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('forwards back and forward clicks', () => {
|
||||
const { props } = renderTitleBar()
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Back' }))
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Forward' }))
|
||||
expect(props.onGoBack).toHaveBeenCalledOnce()
|
||||
expect(props.onGoForward).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('keeps the collapse button clickable and outside the drag region', () => {
|
||||
const { props } = renderTitleBar()
|
||||
const collapse = screen.getByRole('button', { name: 'Collapse sidebar' })
|
||||
fireEvent.click(collapse)
|
||||
expect(props.onCollapse).toHaveBeenCalledOnce()
|
||||
expect(collapse).toHaveAttribute('data-no-drag')
|
||||
})
|
||||
|
||||
it('hides collapse when unavailable but keeps history controls visible', () => {
|
||||
renderTitleBar({ onCollapse: undefined })
|
||||
expect(screen.getByTestId('sidebar-nav-arrows')).toBeInTheDocument()
|
||||
expect(screen.queryByRole('button', { name: 'Collapse sidebar' })).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -245,6 +245,8 @@
|
||||
"sidebar.nav.inbox": "Posteingang",
|
||||
"sidebar.nav.allNotes": "Alle Notizen",
|
||||
"sidebar.nav.archive": "Archivieren",
|
||||
"sidebar.nav.back": "Zurück",
|
||||
"sidebar.nav.forward": "Weiter",
|
||||
"sidebar.group.favorites": "FAVORITEN",
|
||||
"sidebar.group.views": "AUFRUFE",
|
||||
"sidebar.group.types": "TYPEN",
|
||||
|
||||
@@ -245,6 +245,8 @@
|
||||
"sidebar.nav.inbox": "Inbox",
|
||||
"sidebar.nav.allNotes": "All Notes",
|
||||
"sidebar.nav.archive": "Archive",
|
||||
"sidebar.nav.back": "Back",
|
||||
"sidebar.nav.forward": "Forward",
|
||||
"sidebar.group.favorites": "FAVORITES",
|
||||
"sidebar.group.views": "VIEWS",
|
||||
"sidebar.group.types": "TYPES",
|
||||
|
||||
@@ -245,6 +245,8 @@
|
||||
"sidebar.nav.inbox": "Bandeja de entrada",
|
||||
"sidebar.nav.allNotes": "Todas las notas",
|
||||
"sidebar.nav.archive": "Archivar",
|
||||
"sidebar.nav.back": "Atrás",
|
||||
"sidebar.nav.forward": "Siguiente",
|
||||
"sidebar.group.favorites": "FAVORITOS",
|
||||
"sidebar.group.views": "VISTAS",
|
||||
"sidebar.group.types": "TIPOS",
|
||||
|
||||
@@ -245,6 +245,8 @@
|
||||
"sidebar.nav.inbox": "Bandeja de entrada",
|
||||
"sidebar.nav.allNotes": "Todas las notas",
|
||||
"sidebar.nav.archive": "Archivar",
|
||||
"sidebar.nav.back": "Atrás",
|
||||
"sidebar.nav.forward": "Siguiente",
|
||||
"sidebar.group.favorites": "FAVORITOS",
|
||||
"sidebar.group.views": "VISUALIZACIONES",
|
||||
"sidebar.group.types": "TIPOS",
|
||||
|
||||
@@ -245,6 +245,8 @@
|
||||
"sidebar.nav.inbox": "Boîte de réception",
|
||||
"sidebar.nav.allNotes": "Toutes les notes",
|
||||
"sidebar.nav.archive": "Archiver",
|
||||
"sidebar.nav.back": "Retour",
|
||||
"sidebar.nav.forward": "Suivant",
|
||||
"sidebar.group.favorites": "FAVORIS",
|
||||
"sidebar.group.views": "VUES",
|
||||
"sidebar.group.types": "TYPES",
|
||||
|
||||
@@ -245,6 +245,8 @@
|
||||
"sidebar.nav.inbox": "Inbox",
|
||||
"sidebar.nav.allNotes": "Tutte le note",
|
||||
"sidebar.nav.archive": "Archiviate",
|
||||
"sidebar.nav.back": "Indietro",
|
||||
"sidebar.nav.forward": "Avanti",
|
||||
"sidebar.group.favorites": "PREFERITI",
|
||||
"sidebar.group.views": "VISTE",
|
||||
"sidebar.group.types": "TIPI",
|
||||
|
||||
@@ -245,6 +245,8 @@
|
||||
"sidebar.nav.inbox": "受信トレイ",
|
||||
"sidebar.nav.allNotes": "すべてのノート",
|
||||
"sidebar.nav.archive": "アーカイブ",
|
||||
"sidebar.nav.back": "戻る",
|
||||
"sidebar.nav.forward": "進む",
|
||||
"sidebar.group.favorites": "お気に入り",
|
||||
"sidebar.group.views": "閲覧数",
|
||||
"sidebar.group.types": "タイプ",
|
||||
|
||||
@@ -245,6 +245,8 @@
|
||||
"sidebar.nav.inbox": "받은 편지함",
|
||||
"sidebar.nav.allNotes": "모든 노트",
|
||||
"sidebar.nav.archive": "보관",
|
||||
"sidebar.nav.back": "뒤로",
|
||||
"sidebar.nav.forward": "다음",
|
||||
"sidebar.group.favorites": "즐겨찾기",
|
||||
"sidebar.group.views": "조회수",
|
||||
"sidebar.group.types": "유형",
|
||||
|
||||
@@ -245,6 +245,8 @@
|
||||
"sidebar.nav.inbox": "Caixa de entrada",
|
||||
"sidebar.nav.allNotes": "Todas as anotações",
|
||||
"sidebar.nav.archive": "Arquivar",
|
||||
"sidebar.nav.back": "Voltar",
|
||||
"sidebar.nav.forward": "Avançar",
|
||||
"sidebar.group.favorites": "FAVORITOS",
|
||||
"sidebar.group.views": "VISUALIZAÇÕES",
|
||||
"sidebar.group.types": "TIPOS",
|
||||
|
||||
@@ -245,6 +245,8 @@
|
||||
"sidebar.nav.inbox": "Caixa de entrada",
|
||||
"sidebar.nav.allNotes": "Todas as notas",
|
||||
"sidebar.nav.archive": "Arquivar",
|
||||
"sidebar.nav.back": "Voltar",
|
||||
"sidebar.nav.forward": "Avançar",
|
||||
"sidebar.group.favorites": "FAVORITOS",
|
||||
"sidebar.group.views": "VISUALIZAÇÕES",
|
||||
"sidebar.group.types": "TIPOS",
|
||||
|
||||
@@ -245,6 +245,8 @@
|
||||
"sidebar.nav.inbox": "Входящие",
|
||||
"sidebar.nav.allNotes": "Все заметки",
|
||||
"sidebar.nav.archive": "Архив",
|
||||
"sidebar.nav.back": "Назад",
|
||||
"sidebar.nav.forward": "Вперед",
|
||||
"sidebar.group.favorites": "ИЗБРАННОЕ",
|
||||
"sidebar.group.views": "ПРОСМОТРЫ",
|
||||
"sidebar.group.types": "ТИПЫ",
|
||||
|
||||
@@ -245,6 +245,8 @@
|
||||
"sidebar.nav.inbox": "收件箱",
|
||||
"sidebar.nav.allNotes": "全部笔记",
|
||||
"sidebar.nav.archive": "归档",
|
||||
"sidebar.nav.back": "返回",
|
||||
"sidebar.nav.forward": "前进",
|
||||
"sidebar.group.favorites": "收藏",
|
||||
"sidebar.group.views": "视图",
|
||||
"sidebar.group.types": "类型",
|
||||
|
||||
@@ -245,6 +245,8 @@
|
||||
"sidebar.nav.inbox": "收件箱",
|
||||
"sidebar.nav.allNotes": "全部筆記",
|
||||
"sidebar.nav.archive": "歸檔",
|
||||
"sidebar.nav.back": "返回",
|
||||
"sidebar.nav.forward": "下一頁",
|
||||
"sidebar.group.favorites": "收藏",
|
||||
"sidebar.group.views": "檢視",
|
||||
"sidebar.group.types": "型別",
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { FilePreviewKind } from '../utils/filePreview'
|
||||
type TrackedPreviewKind = FilePreviewKind | 'unsupported'
|
||||
type FilePreviewAction = 'copy_path' | 'open_external' | 'reveal'
|
||||
type AgentBlockedReason = 'agent_unavailable' | 'missing_vault'
|
||||
type NavigationHistoryDirection = 'back' | 'forward'
|
||||
|
||||
const ALL_NOTES_VISIBILITY_CATEGORIES: ReadonlyArray<keyof AllNotesFileVisibility> = [
|
||||
'pdfs',
|
||||
@@ -52,6 +53,10 @@ export function trackAllNotesVisibilityChanged(
|
||||
}
|
||||
}
|
||||
|
||||
export function trackNavigationHistoryButtonClicked(direction: NavigationHistoryDirection): void {
|
||||
trackEvent('navigation_history_button_clicked', { direction })
|
||||
}
|
||||
|
||||
export function trackAiAgentMessageBlocked(agent: AiAgentId, reason: AgentBlockedReason): void {
|
||||
trackEvent('ai_agent_message_blocked', { agent, reason })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user