Mayo commited on
fix: ui issue
Browse files
ui/components/canvas/CanvasToolbar.tsx
CHANGED
|
@@ -1,32 +1,38 @@
|
|
| 1 |
'use client'
|
| 2 |
|
| 3 |
-
import { Toolbar,
|
| 4 |
-
import {
|
| 5 |
|
| 6 |
export function CanvasToolbar() {
|
| 7 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
return (
|
| 10 |
-
<Toolbar.Root className='flex items-center gap-
|
| 11 |
-
|
| 12 |
-
<
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
sideOffset={6}
|
| 24 |
-
className='rounded bg-black px-2 py-1 text-xs text-white'
|
| 25 |
>
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
<span
|
| 31 |
className={`ml-auto rounded-full px-2 py-1 text-xs ${
|
| 32 |
llmReady ? 'bg-rose-100 text-rose-700' : 'bg-rose-50 text-rose-400'
|
|
|
|
| 1 |
'use client'
|
| 2 |
|
| 3 |
+
import { Toolbar, Slider } from 'radix-ui'
|
| 4 |
+
import { useAppStore, useConfigStore } from '@/lib/store'
|
| 5 |
|
| 6 |
export function CanvasToolbar() {
|
| 7 |
+
const llmReady = useAppStore((state) => state.llmReady)
|
| 8 |
+
const {
|
| 9 |
+
maskConfig: { brushSize },
|
| 10 |
+
setMaskConfig,
|
| 11 |
+
} = useConfigStore()
|
| 12 |
|
| 13 |
return (
|
| 14 |
+
<Toolbar.Root className='flex items-center gap-4 border-b border-neutral-200 bg-white px-3 py-1.5 text-xs text-neutral-900'>
|
| 15 |
+
<div className='flex items-center gap-2'>
|
| 16 |
+
<span className='text-neutral-600'>Brush</span>
|
| 17 |
+
<div className='w-40'>
|
| 18 |
+
<Slider.Root
|
| 19 |
+
className='relative flex h-4 w-full touch-none items-center select-none'
|
| 20 |
+
min={8}
|
| 21 |
+
max={128}
|
| 22 |
+
step={4}
|
| 23 |
+
value={[brushSize]}
|
| 24 |
+
onValueChange={(vals) =>
|
| 25 |
+
setMaskConfig({ brushSize: vals[0] ?? brushSize })
|
| 26 |
+
}
|
|
|
|
|
|
|
| 27 |
>
|
| 28 |
+
<Slider.Track className='relative h-1 flex-1 rounded bg-rose-100'>
|
| 29 |
+
<Slider.Range className='absolute h-full rounded bg-rose-400' />
|
| 30 |
+
</Slider.Track>
|
| 31 |
+
<Slider.Thumb className='block h-2.5 w-2.5 rounded-full bg-rose-500' />
|
| 32 |
+
</Slider.Root>
|
| 33 |
+
</div>
|
| 34 |
+
<span className='w-14 text-right tabular-nums'>{brushSize}px</span>
|
| 35 |
+
</div>
|
| 36 |
<span
|
| 37 |
className={`ml-auto rounded-full px-2 py-1 text-xs ${
|
| 38 |
llmReady ? 'bg-rose-100 text-rose-700' : 'bg-rose-50 text-rose-400'
|
ui/components/canvas/Workspace.tsx
CHANGED
|
@@ -17,7 +17,8 @@ const MASK_CURSOR =
|
|
| 17 |
'url(\'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16"%3E%3Ccircle cx="8" cy="8" r="4" stroke="black" stroke-width="1.5" fill="white"/%3E%3C/svg%3E\') 8 8, crosshair'
|
| 18 |
|
| 19 |
export function Workspace() {
|
| 20 |
-
const { scale, showSegmentationMask, showInpaintedImage, mode } =
|
|
|
|
| 21 |
const {
|
| 22 |
document: currentDocument,
|
| 23 |
selectedBlockIndex,
|
|
@@ -99,16 +100,19 @@ export function Workspace() {
|
|
| 99 |
>
|
| 100 |
<Layer>
|
| 101 |
<Image data={currentDocument.image} />
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
|
|
|
|
|
|
|
|
|
| 112 |
</Layer>
|
| 113 |
<Layer>
|
| 114 |
<TextBlockAnnotations
|
|
|
|
| 17 |
'url(\'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="16" height="16"%3E%3Ccircle cx="8" cy="8" r="4" stroke="black" stroke-width="1.5" fill="white"/%3E%3C/svg%3E\') 8 8, crosshair'
|
| 18 |
|
| 19 |
export function Workspace() {
|
| 20 |
+
const { scale, showSegmentationMask, showInpaintedImage, mode } =
|
| 21 |
+
useAppStore()
|
| 22 |
const {
|
| 23 |
document: currentDocument,
|
| 24 |
selectedBlockIndex,
|
|
|
|
| 100 |
>
|
| 101 |
<Layer>
|
| 102 |
<Image data={currentDocument.image} />
|
| 103 |
+
{currentDocument?.segment && (
|
| 104 |
+
<Image
|
| 105 |
+
data={currentDocument.segment}
|
| 106 |
+
visible={showSegmentationMask}
|
| 107 |
+
opacity={0.8}
|
| 108 |
+
/>
|
| 109 |
+
)}
|
| 110 |
+
{currentDocument.inpainted && (
|
| 111 |
+
<Image
|
| 112 |
+
data={currentDocument.inpainted}
|
| 113 |
+
visible={showInpaintedImage}
|
| 114 |
+
/>
|
| 115 |
+
)}
|
| 116 |
</Layer>
|
| 117 |
<Layer>
|
| 118 |
<TextBlockAnnotations
|
ui/components/panels/MaskControls.tsx
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
'use client'
|
| 2 |
|
| 3 |
-
import { useState } from 'react'
|
| 4 |
import { Separator } from 'radix-ui'
|
| 5 |
import { useAppStore, useConfigStore } from '@/lib/store'
|
| 6 |
import {
|
|
@@ -18,7 +17,6 @@ export function MaskControls() {
|
|
| 18 |
inpaint,
|
| 19 |
} = useAppStore()
|
| 20 |
const { inpaintConfig, setInpaintConfig } = useConfigStore()
|
| 21 |
-
const [brushSize, setBrushSize] = useState(36)
|
| 22 |
|
| 23 |
return (
|
| 24 |
<div className='space-y-2 text-xs text-neutral-600'>
|
|
@@ -33,14 +31,6 @@ export function MaskControls() {
|
|
| 33 |
onChange={setShowInpaintedImage}
|
| 34 |
/>
|
| 35 |
<Separator.Root className='my-1 h-px bg-neutral-200' />
|
| 36 |
-
<SliderField
|
| 37 |
-
label='Brush size'
|
| 38 |
-
min={8}
|
| 39 |
-
max={128}
|
| 40 |
-
step={4}
|
| 41 |
-
value={brushSize}
|
| 42 |
-
onChange={setBrushSize}
|
| 43 |
-
/>
|
| 44 |
<div className='grid grid-cols-2 gap-1.5'>
|
| 45 |
<SliderField
|
| 46 |
label='Dilate'
|
|
|
|
| 1 |
'use client'
|
| 2 |
|
|
|
|
| 3 |
import { Separator } from 'radix-ui'
|
| 4 |
import { useAppStore, useConfigStore } from '@/lib/store'
|
| 5 |
import {
|
|
|
|
| 17 |
inpaint,
|
| 18 |
} = useAppStore()
|
| 19 |
const { inpaintConfig, setInpaintConfig } = useConfigStore()
|
|
|
|
| 20 |
|
| 21 |
return (
|
| 22 |
<div className='space-y-2 text-xs text-neutral-600'>
|
|
|
|
| 31 |
onChange={setShowInpaintedImage}
|
| 32 |
/>
|
| 33 |
<Separator.Root className='my-1 h-px bg-neutral-200' />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
<div className='grid grid-cols-2 gap-1.5'>
|
| 35 |
<SliderField
|
| 36 |
label='Dilate'
|
ui/components/panels/TextBlocksPanel.tsx
CHANGED
|
@@ -88,13 +88,12 @@ function BlockCard({ block, index, selected, onChange }: BlockCardProps) {
|
|
| 88 |
className='overflow-hidden rounded border border-neutral-200 bg-white/90 text-sm transition data-[selected=true]:border-rose-400 data-[state=open]:shadow-sm'
|
| 89 |
>
|
| 90 |
<Accordion.Header>
|
| 91 |
-
<Accordion.Trigger className='flex w-full flex-col gap-1 px-3 py-2 text-left transition outline-none data-[state=open]:bg-rose-50'>
|
| 92 |
<div className='flex items-center justify-between text-xs text-neutral-500'>
|
| 93 |
-
<span className='
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
Click to edit
|
| 98 |
</span>
|
| 99 |
</div>
|
| 100 |
{!selected && (
|
|
|
|
| 88 |
className='overflow-hidden rounded border border-neutral-200 bg-white/90 text-sm transition data-[selected=true]:border-rose-400 data-[state=open]:shadow-sm'
|
| 89 |
>
|
| 90 |
<Accordion.Header>
|
| 91 |
+
<Accordion.Trigger className='flex w-full cursor-pointer flex-col gap-1 px-3 py-2 text-left transition outline-none data-[state=open]:bg-rose-50'>
|
| 92 |
<div className='flex items-center justify-between text-xs text-neutral-500'>
|
| 93 |
+
<span className='inline-flex items-center gap-2'>
|
| 94 |
+
<span className='rounded-full bg-rose-100 px-2 py-0.5 text-[11px] font-semibold text-rose-700'>
|
| 95 |
+
#{index + 1}
|
| 96 |
+
</span>
|
|
|
|
| 97 |
</span>
|
| 98 |
</div>
|
| 99 |
{!selected && (
|
ui/hooks/useCanvasCommands.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
| 1 |
-
'use client'
|
| 2 |
-
|
| 3 |
-
import { useCallback, useMemo } from 'react'
|
| 4 |
-
import { useAppStore, useConfigStore } from '@/lib/store'
|
| 5 |
-
|
| 6 |
-
export type CanvasCommand = {
|
| 7 |
-
label: string
|
| 8 |
-
action: () => void
|
| 9 |
-
disabled: boolean
|
| 10 |
-
}
|
| 11 |
-
|
| 12 |
-
export function useCanvasCommands() {
|
| 13 |
-
const { detect, ocr, inpaint, llmGenerate, documents, llmReady } =
|
| 14 |
-
useAppStore()
|
| 15 |
-
const { detectConfig, inpaintConfig } = useConfigStore()
|
| 16 |
-
|
| 17 |
-
const hasDocument = documents.length > 0
|
| 18 |
-
|
| 19 |
-
const runDetect = useCallback(() => {
|
| 20 |
-
if (!hasDocument) return
|
| 21 |
-
detect(detectConfig.confThreshold, detectConfig.nmsThreshold)
|
| 22 |
-
}, [
|
| 23 |
-
detect,
|
| 24 |
-
detectConfig.confThreshold,
|
| 25 |
-
detectConfig.nmsThreshold,
|
| 26 |
-
hasDocument,
|
| 27 |
-
])
|
| 28 |
-
|
| 29 |
-
const runOcr = useCallback(() => {
|
| 30 |
-
if (!hasDocument) return
|
| 31 |
-
ocr()
|
| 32 |
-
}, [hasDocument, ocr])
|
| 33 |
-
|
| 34 |
-
const runInpaint = useCallback(() => {
|
| 35 |
-
if (!hasDocument) return
|
| 36 |
-
inpaint(inpaintConfig.dilateKernelSize, inpaintConfig.erodeDistance)
|
| 37 |
-
}, [
|
| 38 |
-
hasDocument,
|
| 39 |
-
inpaint,
|
| 40 |
-
inpaintConfig.dilateKernelSize,
|
| 41 |
-
inpaintConfig.erodeDistance,
|
| 42 |
-
])
|
| 43 |
-
|
| 44 |
-
const runTranslate = useCallback(() => {
|
| 45 |
-
if (!hasDocument) return
|
| 46 |
-
llmGenerate()
|
| 47 |
-
}, [hasDocument, llmGenerate])
|
| 48 |
-
|
| 49 |
-
const commands = useMemo<CanvasCommand[]>(
|
| 50 |
-
() => [
|
| 51 |
-
{ label: 'Detect', action: runDetect, disabled: !hasDocument },
|
| 52 |
-
{ label: 'OCR', action: runOcr, disabled: !hasDocument },
|
| 53 |
-
{ label: 'Inpaint', action: runInpaint, disabled: !hasDocument },
|
| 54 |
-
{ label: 'Translate', action: runTranslate, disabled: !hasDocument },
|
| 55 |
-
],
|
| 56 |
-
[hasDocument, runDetect, runOcr, runInpaint, runTranslate],
|
| 57 |
-
)
|
| 58 |
-
|
| 59 |
-
return { commands, llmReady }
|
| 60 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ui/lib/store.ts
CHANGED
|
@@ -177,8 +177,12 @@ type ConfigState = {
|
|
| 177 |
dilateKernelSize: number
|
| 178 |
erodeDistance: number
|
| 179 |
}
|
|
|
|
|
|
|
|
|
|
| 180 |
setDetectConfig: (config: Partial<ConfigState['detectConfig']>) => void
|
| 181 |
setInpaintConfig: (config: Partial<ConfigState['inpaintConfig']>) => void
|
|
|
|
| 182 |
}
|
| 183 |
|
| 184 |
export const useConfigStore = create<ConfigState>((set) => ({
|
|
@@ -190,6 +194,9 @@ export const useConfigStore = create<ConfigState>((set) => ({
|
|
| 190 |
dilateKernelSize: 9,
|
| 191 |
erodeDistance: 3,
|
| 192 |
},
|
|
|
|
|
|
|
|
|
|
| 193 |
setDetectConfig: (config) =>
|
| 194 |
set((state) => ({
|
| 195 |
detectConfig: { ...state.detectConfig, ...config },
|
|
@@ -198,4 +205,8 @@ export const useConfigStore = create<ConfigState>((set) => ({
|
|
| 198 |
set((state) => ({
|
| 199 |
inpaintConfig: { ...state.inpaintConfig, ...config },
|
| 200 |
})),
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
}))
|
|
|
|
| 177 |
dilateKernelSize: number
|
| 178 |
erodeDistance: number
|
| 179 |
}
|
| 180 |
+
maskConfig: {
|
| 181 |
+
brushSize: number
|
| 182 |
+
}
|
| 183 |
setDetectConfig: (config: Partial<ConfigState['detectConfig']>) => void
|
| 184 |
setInpaintConfig: (config: Partial<ConfigState['inpaintConfig']>) => void
|
| 185 |
+
setMaskConfig: (config: Partial<ConfigState['maskConfig']>) => void
|
| 186 |
}
|
| 187 |
|
| 188 |
export const useConfigStore = create<ConfigState>((set) => ({
|
|
|
|
| 194 |
dilateKernelSize: 9,
|
| 195 |
erodeDistance: 3,
|
| 196 |
},
|
| 197 |
+
maskConfig: {
|
| 198 |
+
brushSize: 36,
|
| 199 |
+
},
|
| 200 |
setDetectConfig: (config) =>
|
| 201 |
set((state) => ({
|
| 202 |
detectConfig: { ...state.detectConfig, ...config },
|
|
|
|
| 205 |
set((state) => ({
|
| 206 |
inpaintConfig: { ...state.inpaintConfig, ...config },
|
| 207 |
})),
|
| 208 |
+
setMaskConfig: (config) =>
|
| 209 |
+
set((state) => ({
|
| 210 |
+
maskConfig: { ...state.maskConfig, ...config },
|
| 211 |
+
})),
|
| 212 |
}))
|