feat: UX improvements — copy buttons, shortcuts modal, breadcrumb rewind, create tree CTA
- Add copy-to-clipboard buttons on command blocks (action + custom step nodes) - Add keyboard shortcuts modal (?) with option loading spinners and [Esc] hint - Restyle session timer as a pill badge for better visibility - Add prominent "Create Tree" CTA to MyTreesPage header and empty state - Make breadcrumb items clickable to rewind navigation to any previous step Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,7 @@ export interface TreeNavigationShortcutsConfig {
|
|||||||
onSelectOption: (index: number) => void
|
onSelectOption: (index: number) => void
|
||||||
onGoBack: () => void
|
onGoBack: () => void
|
||||||
onContinue: () => void
|
onContinue: () => void
|
||||||
|
onShowShortcuts?: () => void
|
||||||
optionCount: number
|
optionCount: number
|
||||||
canGoBack: boolean
|
canGoBack: boolean
|
||||||
canContinue: boolean
|
canContinue: boolean
|
||||||
@@ -60,6 +61,7 @@ export function useTreeNavigationShortcuts({
|
|||||||
onSelectOption,
|
onSelectOption,
|
||||||
onGoBack,
|
onGoBack,
|
||||||
onContinue,
|
onContinue,
|
||||||
|
onShowShortcuts,
|
||||||
optionCount,
|
optionCount,
|
||||||
canGoBack,
|
canGoBack,
|
||||||
canContinue,
|
canContinue,
|
||||||
@@ -89,6 +91,13 @@ export function useTreeNavigationShortcuts({
|
|||||||
document.getElementById('session-notes')?.focus()
|
document.getElementById('session-notes')?.focus()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// ? to show shortcuts modal
|
||||||
|
{
|
||||||
|
key: '?',
|
||||||
|
shift: true,
|
||||||
|
handler: () => onShowShortcuts?.(),
|
||||||
|
enabled: !!onShowShortcuts,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
useKeyboardShortcuts(shortcuts)
|
useKeyboardShortcuts(shortcuts)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useNavigate, Link } from 'react-router-dom'
|
import { useNavigate, Link } from 'react-router-dom'
|
||||||
import { Play, Pencil, Share2, Trash2, GitBranch, Clock, TrendingUp, FolderTree } from 'lucide-react'
|
import { Play, Pencil, Share2, Trash2, GitBranch, Clock, TrendingUp, FolderTree, Plus } from 'lucide-react'
|
||||||
import { treesApi } from '@/api/trees'
|
import { treesApi } from '@/api/trees'
|
||||||
import { sessionsApi } from '@/api/sessions'
|
import { sessionsApi } from '@/api/sessions'
|
||||||
import type { TreeListItem } from '@/types'
|
import type { TreeListItem } from '@/types'
|
||||||
@@ -22,7 +22,7 @@ interface TreeWithStats extends TreeListItem {
|
|||||||
export function MyTreesPage() {
|
export function MyTreesPage() {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { user } = useAuthStore()
|
const { user } = useAuthStore()
|
||||||
const { canEditTree } = usePermissions()
|
const { canEditTree, canCreateTrees } = usePermissions()
|
||||||
const [trees, setTrees] = useState<TreeWithStats[]>([])
|
const [trees, setTrees] = useState<TreeWithStats[]>([])
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
const [treeToDelete, setTreeToDelete] = useState<TreeWithStats | null>(null)
|
const [treeToDelete, setTreeToDelete] = useState<TreeWithStats | null>(null)
|
||||||
@@ -101,11 +101,22 @@ export function MyTreesPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto px-4 py-6 sm:px-6 sm:py-8">
|
<div className="container mx-auto px-4 py-6 sm:px-6 sm:py-8">
|
||||||
<div className="mb-6 sm:mb-8">
|
<div className="mb-6 flex items-center justify-between sm:mb-8">
|
||||||
<h1 className="text-2xl font-bold text-white sm:text-3xl">My Trees</h1>
|
<div>
|
||||||
<p className="mt-2 text-white/40">
|
<h1 className="text-2xl font-bold text-white sm:text-3xl">My Trees</h1>
|
||||||
Your forked and custom decision trees
|
<p className="mt-2 text-white/40">
|
||||||
</p>
|
Your forked and custom decision trees
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{canCreateTrees && (
|
||||||
|
<Link
|
||||||
|
to="/trees/new"
|
||||||
|
className="flex items-center gap-2 rounded-md bg-white px-4 py-2 text-sm font-medium text-black hover:bg-white/90"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
Create Tree
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Loading State */}
|
{/* Loading State */}
|
||||||
@@ -120,15 +131,29 @@ export function MyTreesPage() {
|
|||||||
<p className="mb-4 text-sm text-white/40">
|
<p className="mb-4 text-sm text-white/40">
|
||||||
Fork a tree from the library to customize it for your workflow
|
Fork a tree from the library to customize it for your workflow
|
||||||
</p>
|
</p>
|
||||||
<Link
|
<div className="flex items-center justify-center gap-3">
|
||||||
to="/trees"
|
<Link
|
||||||
className={cn(
|
to="/trees"
|
||||||
'inline-flex items-center gap-2 rounded-md bg-white px-4 py-2 text-sm font-medium text-black',
|
className={cn(
|
||||||
'hover:bg-white/90'
|
'inline-flex items-center gap-2 rounded-md bg-white px-4 py-2 text-sm font-medium text-black',
|
||||||
|
'hover:bg-white/90'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Browse Library
|
||||||
|
</Link>
|
||||||
|
{canCreateTrees && (
|
||||||
|
<Link
|
||||||
|
to="/trees/new"
|
||||||
|
className={cn(
|
||||||
|
'inline-flex items-center gap-2 rounded-md border border-white/10 px-4 py-2 text-sm font-medium text-white/60',
|
||||||
|
'hover:bg-white/10 hover:text-white'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
Create from Scratch
|
||||||
|
</Link>
|
||||||
)}
|
)}
|
||||||
>
|
</div>
|
||||||
Browse Trees
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import { cn, safeGetItem, safeSetItem } from '@/lib/utils'
|
|||||||
import { MarkdownContent } from '@/components/ui/MarkdownContent'
|
import { MarkdownContent } from '@/components/ui/MarkdownContent'
|
||||||
import { CustomStepModal } from '@/components/step-library/CustomStepModal'
|
import { CustomStepModal } from '@/components/step-library/CustomStepModal'
|
||||||
import { PostStepActionModal, ContinuationModal, ForkTreeModal, ScratchpadSidebar, SessionOutcomeModal } from '@/components/session'
|
import { PostStepActionModal, ContinuationModal, ForkTreeModal, ScratchpadSidebar, SessionOutcomeModal } from '@/components/session'
|
||||||
import { Plus, CheckCircle, ArrowRight, Clock, Terminal } from 'lucide-react'
|
import { Plus, CheckCircle, ArrowRight, Clock, Terminal, Clipboard, Check, HelpCircle } from 'lucide-react'
|
||||||
|
import { Modal } from '@/components/common/Modal'
|
||||||
|
|
||||||
interface LocationState {
|
interface LocationState {
|
||||||
sessionId?: string
|
sessionId?: string
|
||||||
@@ -41,6 +42,15 @@ export function TreeNavigationPage() {
|
|||||||
const [showOutcomeModal, setShowOutcomeModal] = useState(false)
|
const [showOutcomeModal, setShowOutcomeModal] = useState(false)
|
||||||
const [pendingCompletionDecision, setPendingCompletionDecision] = useState<DecisionRecord | null>(null)
|
const [pendingCompletionDecision, setPendingCompletionDecision] = useState<DecisionRecord | null>(null)
|
||||||
const [completionSource, setCompletionSource] = useState<CompletionSource>('standard')
|
const [completionSource, setCompletionSource] = useState<CompletionSource>('standard')
|
||||||
|
const [copiedCommand, setCopiedCommand] = useState<string | null>(null)
|
||||||
|
const [shortcutsModalOpen, setShortcutsModalOpen] = useState(false)
|
||||||
|
const [selectingOption, setSelectingOption] = useState<string | null>(null)
|
||||||
|
|
||||||
|
const handleCopyCommand = (text: string) => {
|
||||||
|
navigator.clipboard.writeText(text)
|
||||||
|
setCopiedCommand(text)
|
||||||
|
setTimeout(() => setCopiedCommand(null), 2000)
|
||||||
|
}
|
||||||
|
|
||||||
// Session metadata (prefill from Repeat Last Session)
|
// Session metadata (prefill from Repeat Last Session)
|
||||||
const [ticketNumber, setTicketNumber] = useState<string>(locationState?.prefillTicketNumber || '')
|
const [ticketNumber, setTicketNumber] = useState<string>(locationState?.prefillTicketNumber || '')
|
||||||
@@ -220,10 +230,12 @@ export function TreeNavigationPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleSelectOption = async (_optionId: string, optionLabel: string, nextNodeId: string) => {
|
const handleSelectOption = async (_optionId: string, optionLabel: string, nextNodeId: string) => {
|
||||||
if (!session || !tree) return
|
if (!session || !tree || selectingOption) return
|
||||||
|
|
||||||
|
setSelectingOption(_optionId)
|
||||||
|
|
||||||
const node = findNode(currentNodeId, tree.tree_structure)
|
const node = findNode(currentNodeId, tree.tree_structure)
|
||||||
if (!node) return
|
if (!node) { setSelectingOption(null); return }
|
||||||
|
|
||||||
const exitedAt = new Date().toISOString()
|
const exitedAt = new Date().toISOString()
|
||||||
const enteredAt = currentStepEnteredAt || session.started_at || exitedAt
|
const enteredAt = currentStepEnteredAt || session.started_at || exitedAt
|
||||||
@@ -259,6 +271,8 @@ export function TreeNavigationPage() {
|
|||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to update session:', err)
|
console.error('Failed to update session:', err)
|
||||||
|
} finally {
|
||||||
|
setSelectingOption(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,6 +384,16 @@ export function TreeNavigationPage() {
|
|||||||
setCommandOutputOpen(!!prevOutput)
|
setCommandOutputOpen(!!prevOutput)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleBreadcrumbJump = (nodeId: string, index: number) => {
|
||||||
|
setPathTaken(prev => prev.slice(0, index + 1))
|
||||||
|
setDecisions(prev => prev.slice(0, index))
|
||||||
|
setCurrentNodeId(nodeId)
|
||||||
|
setCurrentStepEnteredAt(new Date().toISOString())
|
||||||
|
setNotes('')
|
||||||
|
setCommandOutput('')
|
||||||
|
setCommandOutputOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
// Compute current node for keyboard shortcuts (must be before any returns for hooks rules)
|
// Compute current node for keyboard shortcuts (must be before any returns for hooks rules)
|
||||||
const currentNode = tree ? findNode(currentNodeId, tree.tree_structure) : null
|
const currentNode = tree ? findNode(currentNodeId, tree.tree_structure) : null
|
||||||
const currentCustomStep = customStepFlow.findCustomStep(currentNodeId)
|
const currentCustomStep = customStepFlow.findCustomStep(currentNodeId)
|
||||||
@@ -379,11 +403,12 @@ export function TreeNavigationPage() {
|
|||||||
useTreeNavigationShortcuts({
|
useTreeNavigationShortcuts({
|
||||||
onSelectOption: (index) => {
|
onSelectOption: (index) => {
|
||||||
const option = currentOptions[index]
|
const option = currentOptions[index]
|
||||||
if (option && session && tree) {
|
if (option && session && tree && !selectingOption) {
|
||||||
handleSelectOption(option.id, option.label, option.next_node_id)
|
handleSelectOption(option.id, option.label, option.next_node_id)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onGoBack: handleGoBack,
|
onGoBack: handleGoBack,
|
||||||
|
onShowShortcuts: () => setShortcutsModalOpen(true),
|
||||||
onContinue: () => {
|
onContinue: () => {
|
||||||
if (currentNode?.type === 'action' && currentNode.next_node_id) {
|
if (currentNode?.type === 'action' && currentNode.next_node_id) {
|
||||||
handleContinue()
|
handleContinue()
|
||||||
@@ -392,8 +417,8 @@ export function TreeNavigationPage() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
optionCount: currentOptions.length,
|
optionCount: currentOptions.length,
|
||||||
canGoBack: pathTaken.length > 1 && !showMetadataForm && !isLoading,
|
canGoBack: pathTaken.length > 1 && !showMetadataForm && !isLoading && !selectingOption,
|
||||||
canContinue: !showMetadataForm && !isLoading && (currentNode?.type === 'action' || currentNode?.type === 'solution'),
|
canContinue: !showMetadataForm && !isLoading && !selectingOption && (currentNode?.type === 'action' || currentNode?.type === 'solution'),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
@@ -502,11 +527,19 @@ export function TreeNavigationPage() {
|
|||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<h1 className="text-xl font-bold text-white">{tree.name}</h1>
|
<h1 className="text-xl font-bold text-white">{tree.name}</h1>
|
||||||
{timerDisplay && (
|
{timerDisplay && (
|
||||||
<span className="flex items-center gap-1 text-sm text-white/40">
|
<span className="flex items-center gap-1.5 rounded-full bg-white/10 px-2.5 py-0.5 text-sm text-white/60">
|
||||||
<Clock className="h-3.5 w-3.5" />
|
<Clock className="h-4 w-4" />
|
||||||
{timerDisplay}
|
{timerDisplay}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShortcutsModalOpen(true)}
|
||||||
|
className="flex items-center justify-center rounded-full p-1 text-white/30 hover:bg-white/10 hover:text-white/60"
|
||||||
|
title="Keyboard shortcuts"
|
||||||
|
>
|
||||||
|
<HelpCircle className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{(ticketNumber || clientName) && (
|
{(ticketNumber || clientName) && (
|
||||||
<p className="text-sm text-white/40">
|
<p className="text-sm text-white/40">
|
||||||
@@ -530,18 +563,23 @@ export function TreeNavigationPage() {
|
|||||||
const node = findNode(nodeId, tree?.tree_structure)
|
const node = findNode(nodeId, tree?.tree_structure)
|
||||||
const customStep = customStepFlow.findCustomStep(nodeId)
|
const customStep = customStepFlow.findCustomStep(nodeId)
|
||||||
const label = node?.question || node?.title || customStep?.step_data.title || nodeId
|
const label = node?.question || node?.title || customStep?.step_data.title || nodeId
|
||||||
|
const truncatedLabel = label.length > 30 ? `${label.slice(0, 30)}...` : label
|
||||||
return (
|
return (
|
||||||
<span key={nodeId} className="flex items-center gap-2 whitespace-nowrap">
|
<span key={nodeId} className="flex items-center gap-2 whitespace-nowrap">
|
||||||
{index > 0 && <span className="text-white/40">→</span>}
|
{index > 0 && <span className="text-white/40">→</span>}
|
||||||
<span
|
{index < pathTaken.length - 1 ? (
|
||||||
className={cn(
|
<button
|
||||||
index === pathTaken.length - 1
|
type="button"
|
||||||
? 'font-medium text-white'
|
onClick={() => handleBreadcrumbJump(nodeId, index)}
|
||||||
: 'text-white/40'
|
className="text-white/40 hover:text-white/70 hover:underline"
|
||||||
)}
|
>
|
||||||
>
|
{truncatedLabel}
|
||||||
{label.length > 30 ? `${label.slice(0, 30)}...` : label}
|
</button>
|
||||||
</span>
|
) : (
|
||||||
|
<span className="font-medium text-white">
|
||||||
|
{truncatedLabel}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
@@ -565,16 +603,24 @@ export function TreeNavigationPage() {
|
|||||||
<button
|
<button
|
||||||
key={option.id}
|
key={option.id}
|
||||||
onClick={() => handleSelectOption(option.id, option.label, option.next_node_id)}
|
onClick={() => handleSelectOption(option.id, option.label, option.next_node_id)}
|
||||||
|
disabled={!!selectingOption}
|
||||||
className={cn(
|
className={cn(
|
||||||
'w-full rounded-md border border-white/10 p-3 text-left text-white transition-colors',
|
'w-full rounded-md border border-white/10 p-3 text-left text-white transition-colors',
|
||||||
'hover:border-white/30 hover:bg-white/[0.06]',
|
'hover:border-white/30 hover:bg-white/[0.06]',
|
||||||
'flex items-center gap-3'
|
'flex items-center gap-3',
|
||||||
|
selectingOption && selectingOption !== option.id && 'opacity-50 pointer-events-none'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{index < 9 && (
|
{index < 9 && (
|
||||||
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded bg-white/10 text-xs font-medium text-white/50">
|
selectingOption === option.id ? (
|
||||||
{index + 1}
|
<span className="flex h-6 w-6 shrink-0 items-center justify-center">
|
||||||
</span>
|
<span className="h-4 w-4 animate-spin rounded-full border-2 border-white/20 border-t-white" />
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded bg-white/10 text-xs font-medium text-white/50">
|
||||||
|
{index + 1}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
<span>{option.label}</span>
|
<span>{option.label}</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -650,9 +696,23 @@ export function TreeNavigationPage() {
|
|||||||
{currentCustomStep.step_data.content.commands.map((cmd, index) => (
|
{currentCustomStep.step_data.content.commands.map((cmd, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<p className="mb-1 text-xs text-white/40">{cmd.label}</p>
|
<p className="mb-1 text-xs text-white/40">{cmd.label}</p>
|
||||||
<code className="block rounded bg-white/10 p-2 text-sm font-mono">
|
<div className="group relative">
|
||||||
{cmd.command}
|
<code className="block rounded bg-white/10 p-2 pr-8 text-sm font-mono">
|
||||||
</code>
|
{cmd.command}
|
||||||
|
</code>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => handleCopyCommand(cmd.command)}
|
||||||
|
className="absolute right-1.5 top-1.5 opacity-0 transition-opacity group-hover:opacity-100"
|
||||||
|
title="Copy command"
|
||||||
|
>
|
||||||
|
{copiedCommand === cmd.command ? (
|
||||||
|
<Check className="h-3.5 w-3.5 text-green-400" />
|
||||||
|
) : (
|
||||||
|
<Clipboard className="h-3.5 w-3.5 text-white/40 hover:text-white" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -761,12 +821,23 @@ export function TreeNavigationPage() {
|
|||||||
<p className="mb-2 text-sm font-medium text-white">Commands:</p>
|
<p className="mb-2 text-sm font-medium text-white">Commands:</p>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{currentNode.commands.map((cmd, index) => (
|
{currentNode.commands.map((cmd, index) => (
|
||||||
<code
|
<div key={index} className="group relative">
|
||||||
key={index}
|
<code className="block rounded bg-white/10 p-2 pr-8 text-sm font-mono">
|
||||||
className="block rounded bg-white/10 p-2 text-sm font-mono"
|
{cmd}
|
||||||
>
|
</code>
|
||||||
{cmd}
|
<button
|
||||||
</code>
|
type="button"
|
||||||
|
onClick={() => handleCopyCommand(cmd)}
|
||||||
|
className="absolute right-1.5 top-1.5 opacity-0 transition-opacity group-hover:opacity-100"
|
||||||
|
title="Copy command"
|
||||||
|
>
|
||||||
|
{copiedCommand === cmd ? (
|
||||||
|
<Check className="h-3.5 w-3.5 text-green-400" />
|
||||||
|
) : (
|
||||||
|
<Clipboard className="h-3.5 w-3.5 text-white/40 hover:text-white" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{/* Command Output Capture */}
|
{/* Command Output Capture */}
|
||||||
@@ -885,7 +956,7 @@ export function TreeNavigationPage() {
|
|||||||
onClick={handleGoBack}
|
onClick={handleGoBack}
|
||||||
className="mt-4 text-sm text-white/50 hover:text-white"
|
className="mt-4 text-sm text-white/50 hover:text-white"
|
||||||
>
|
>
|
||||||
← Go back
|
← Go back <span className="text-xs text-white/30">[Esc]</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -950,6 +1021,37 @@ export function TreeNavigationPage() {
|
|||||||
onSubmit={handleSubmitOutcome}
|
onSubmit={handleSubmitOutcome}
|
||||||
isSubmitting={isCompleting}
|
isSubmitting={isCompleting}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Keyboard Shortcuts Modal */}
|
||||||
|
<Modal
|
||||||
|
isOpen={shortcutsModalOpen}
|
||||||
|
onClose={() => setShortcutsModalOpen(false)}
|
||||||
|
title="Keyboard Shortcuts"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
<div className="space-y-3 text-sm">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-white/60">Select option</span>
|
||||||
|
<span className="rounded bg-white/10 px-2 py-0.5 font-mono text-xs text-white/80">1-9</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-white/60">Go back</span>
|
||||||
|
<span className="rounded bg-white/10 px-2 py-0.5 font-mono text-xs text-white/80">Esc</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-white/60">Continue / Complete</span>
|
||||||
|
<span className="rounded bg-white/10 px-2 py-0.5 font-mono text-xs text-white/80">Enter</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-white/60">Focus notes</span>
|
||||||
|
<span className="rounded bg-white/10 px-2 py-0.5 font-mono text-xs text-white/80">Tab</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-white/60">Show shortcuts</span>
|
||||||
|
<span className="rounded bg-white/10 px-2 py-0.5 font-mono text-xs text-white/80">?</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user