Skip to content

Commit 82645f7

Browse files
erun1012claude
andcommitted
refactor(extension-webview): address Export tab review comments
- extract clipboard copy logic into useCopy hook - use Text for text nodes instead of Box - replace odd px values with even ones Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 494b9e1 commit 82645f7

2 files changed

Lines changed: 58 additions & 42 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { useState } from 'react';
2+
3+
const COPIED_RESET_MS = 1500;
4+
5+
/** 클립보드 복사 + 잠시 동안 "복사됨" 상태(어떤 id가 복사됐는지)를 유지한다. */
6+
export function useCopy() {
7+
const [copiedId, setCopiedId] = useState<string | null>(null);
8+
9+
function copy(content: string, id: string) {
10+
navigator.clipboard
11+
.writeText(content)
12+
.then(() => {
13+
setCopiedId(id);
14+
setTimeout(() => setCopiedId(null), COPIED_RESET_MS);
15+
})
16+
.catch(console.error);
17+
}
18+
19+
return { copiedId, copy };
20+
}

‎apps/extension-webview/src/tabs/Export.tsx‎

Lines changed: 38 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { postMessage } from '../vscode';
44
import type { AppState } from '../App';
55
import { DEFAULT_SCHEMAS } from '../App';
66
import type { OrmType, WebviewMessage } from '../vscode';
7+
import { useCopy } from '../hooks/useCopy';
78

89
// ── Types ─────────────────────────────────────────────────────────────────────
910

@@ -50,7 +51,7 @@ interface Props { state: AppState; setState: React.Dispatch<React.SetStateAction
5051

5152
export default function Export({ state }: Props) {
5253
const [panelId, setPanelId] = useState('sql-pg');
53-
const [copied, setCopied] = useState<string | null>(null);
54+
const { copiedId, copy } = useCopy();
5455

5556
const hasSql = !!(state.postgres || state.mysql || state.sqlite);
5657
const hasSvg = !!state.svg;
@@ -69,13 +70,6 @@ export default function Export({ state }: Props) {
6970

7071
const selectedFile = files.find((f) => f.id === panelId);
7172

72-
function copyContent(content: string, id: string) {
73-
navigator.clipboard.writeText(content).then(() => {
74-
setCopied(id);
75-
setTimeout(() => setCopied(null), 1500);
76-
});
77-
}
78-
7973
return (
8074
<Flex h="100%" overflow="hidden">
8175

@@ -116,8 +110,8 @@ export default function Export({ state }: Props) {
116110
<>
117111
<FileHeader
118112
file={selectedFile}
119-
copied={copied === selectedFile.id}
120-
onCopy={() => copyContent(selectedFile.content, selectedFile.id)}
113+
copied={copiedId === selectedFile.id}
114+
onCopy={() => copy(selectedFile.content, selectedFile.id)}
121115
onSave={() => saveFile(selectedFile, state.ormType)}
122116
/>
123117
<FilePreviewBody file={selectedFile} />
@@ -140,7 +134,7 @@ function SectionHeader({ label }: { label: string }) {
140134

141135
function GroupLabel({ label }: { label: string }) {
142136
return (
143-
<Text as="div" py="6px" px="10px" pb="2px" fontSize="9px" fontWeight={600} color="$nodeTextDim" letterSpacing="0.06em">
137+
<Text as="div" py="6px" px="10px" pb="2px" fontSize="10px" fontWeight={600} color="$nodeTextDim" letterSpacing="0.06em">
144138
{label}
145139
</Text>
146140
);
@@ -158,13 +152,13 @@ function FileRow({ f, active, onClick }: { f: ExportFile; active: boolean; onCli
158152
bg={active ? 'rgba(99,102,241,0.15)' : 'transparent'}
159153
borderLeft={active ? '2px solid $focusBorder' : '2px solid transparent'}
160154
>
161-
<Box as="span" fontSize="10px" color="$nodeTextDim" flexShrink={0}>
155+
<Text as="span" fontSize="10px" color="$nodeTextDim" flexShrink={0}>
162156
{f.ext === '.sql' ? '≡' : f.ext === '.svg' || f.ext === '.pdf' ? '◫' : '{ }'}
163-
</Box>
164-
<Box as="span" flex={1} fontSize="12px" overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap" color="$nodeText">
165-
{f.label}<Box as="span" color="$nodeTextDim">{f.ext}</Box>
166-
</Box>
167-
{f.isDummy && <Box as="span" fontSize="9px" color="$nodeTextDim">~</Box>}
157+
</Text>
158+
<Text as="span" flex={1} fontSize="12px" overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap" color="$nodeText">
159+
{f.label}<Text as="span" color="$nodeTextDim">{f.ext}</Text>
160+
</Text>
161+
{f.isDummy && <Text as="span" fontSize="10px" color="$nodeTextDim">~</Text>}
168162
</Flex>
169163
);
170164
}
@@ -185,28 +179,28 @@ function FileHeader({ file, copied, onCopy, onSave }: {
185179
>
186180
<Text as="span" fontWeight={600}>{file.label}</Text>
187181
<Text as="span" color="$nodeTextDim">{file.ext}</Text>
188-
<Box
182+
<Text
189183
as="span"
190-
fontSize="9px"
184+
fontSize="10px"
191185
py="2px"
192186
px="6px"
193-
borderRadius="3px"
187+
borderRadius="4px"
194188
bg="rgba(99,102,241,0.15)"
195189
color="#a5b4fc"
196190
border="1px solid rgba(99,102,241,0.25)"
197191
fontWeight={700}
198-
>{file.lang}</Box>
192+
>{file.lang}</Text>
199193
{file.isDummy && (
200-
<Box
194+
<Text
201195
as="span"
202-
fontSize="9px"
196+
fontSize="10px"
203197
py="2px"
204198
px="6px"
205-
borderRadius="3px"
199+
borderRadius="4px"
206200
bg="rgba(251,191,36,0.12)"
207201
color="#fbbf24"
208202
border="1px solid rgba(251,191,36,0.25)"
209-
>PREVIEW</Box>
203+
>PREVIEW</Text>
210204
)}
211205
<Box flex={1} />
212206
<Box as="button" onClick={onCopy} {...btnStyle(copied ? 'green' : 'default')}>
@@ -221,38 +215,40 @@ function FilePreviewBody({ file }: { file: ExportFile }) {
221215
if (file.id === 'erd-pdf') {
222216
return (
223217
<Box flex={1} overflow="auto" p="24px" bg="$editorBg">
224-
<Box
218+
<Text
219+
as="div"
225220
py="16px"
226221
px="20px"
227222
borderRadius="8px"
228223
bg="rgba(99,102,241,0.08)"
229224
border="1px solid rgba(99,102,241,0.2)"
230-
fontSize="13px"
225+
fontSize="14px"
231226
lineHeight={1.8}
232227
color="$editorFg"
233228
>
234229
PDF export converts the ERD diagram SVG to a portable document.{'\n\n'}
235230
Click &quot;저장&quot; to generate the PDF file.{'\n'}
236231
{file.isDummy ? '⚠ ORM Editor에서 스키마를 먼저 입력하세요.' : '✓ ERD 준비 완료.'}
237-
</Box>
232+
</Text>
238233
</Box>
239234
);
240235
}
241236

242237
if (file.id === 'erd-svg' && file.isDummy) {
243238
return (
244239
<Box flex={1} overflow="auto" p="24px" bg="$editorBg">
245-
<Box
240+
<Text
241+
as="div"
246242
py="16px"
247243
px="20px"
248244
borderRadius="8px"
249245
bg="rgba(99,102,241,0.08)"
250246
border="1px solid rgba(99,102,241,0.2)"
251-
fontSize="13px"
247+
fontSize="14px"
252248
color="$editorFg"
253249
>
254250
⚠ ORM Editor에서 스키마를 먼저 입력하세요.
255-
</Box>
251+
</Text>
256252
</Box>
257253
);
258254
}
@@ -261,20 +257,20 @@ function FilePreviewBody({ file }: { file: ExportFile }) {
261257
return (
262258
<Box flex={1} overflow="auto" p="24px" bg="$editorBg">
263259
<Box dangerouslySetInnerHTML={{ __html: file.content }} maxWidth="100%" />
264-
<Box mt="16px" fontSize="11px" opacity={0.4}>SVG 소스:</Box>
265-
<Box
260+
<Text as="div" mt="16px" fontSize="12px" opacity={0.4}>SVG 소스:</Text>
261+
<Text
266262
as="pre"
267263
mt="8px"
268264
py="10px"
269265
px="14px"
270266
bg="rgba(0,0,0,0.2)"
271267
borderRadius="6px"
272-
fontSize="11px"
268+
fontSize="12px"
273269
lineHeight={1.6}
274270
whiteSpace="pre"
275271
overflowX="auto"
276272
color="$editorFg"
277-
>{file.content}</Box>
273+
>{file.content}</Text>
278274
</Box>
279275
);
280276
}
@@ -290,18 +286,18 @@ function FilePreviewBody({ file }: { file: ExportFile }) {
290286
>
291287
{file.content.split('\n').map((line, i) => (
292288
<Flex key={i} minH="20px">
293-
<Box
289+
<Text
294290
as="span"
295291
minW="44px"
296292
pr="10px"
297293
textAlign="right"
298294
flexShrink={0}
299-
fontSize="11px"
295+
fontSize="12px"
300296
lineHeight="20px"
301297
userSelect="none"
302298
color="$diffLinenum"
303-
>{i + 1}</Box>
304-
<Box as="span" flex={1} pr="16px" lineHeight="20px" whiteSpace="pre" color="$editorFg">{line}</Box>
299+
>{i + 1}</Text>
300+
<Text as="span" flex={1} pr="16px" lineHeight="20px" whiteSpace="pre" color="$editorFg">{line}</Text>
305301
</Flex>
306302
))}
307303
</Box>
@@ -313,10 +309,10 @@ function FilePreviewBody({ file }: { file: ExportFile }) {
313309
function btnStyle(variant: 'primary' | 'default' | 'green') {
314310
const base = {
315311
border: 'none' as const,
316-
borderRadius: '3px',
317-
py: '3px',
312+
borderRadius: '4px',
313+
py: '4px',
318314
px: '12px',
319-
fontSize: '11px',
315+
fontSize: '12px',
320316
cursor: 'pointer' as const,
321317
fontFamily: 'inherit',
322318
flexShrink: 0,

0 commit comments

Comments
 (0)