feat(V2-1a): Monorepo 구조 전환 — apps/desktop으로 V1 이동

- npm workspaces 루트 (apps/*, packages/*) 세팅
- V1 전체를 apps/desktop/으로 git mv (src, resources, tests, sidecar,
  scripts, electron.vite.config.ts, electron-builder.yml, vitest.config.ts,
  tsconfig.node.json, tsconfig.web.json)
- apps/desktop/package.json 신규 (name=@d3ro/desktop)
- productName: 'd3ro-voice' 명시 — app.getName()을 고정하여 userData 경로
  %APPDATA%\d3ro-voice\ 그대로 유지 (기존 DB/설정 연속성 보장)
- 루트 package.json을 workspace 루트로 재구성, 공통 devDep만 유지
  (typescript, eslint, prettier)
- turbo.json, tsconfig.base.json 추가 (Turborepo 자체 설치는 별도 sub-phase)
- memory/project_status.md 생성 (규칙 13)

검증:
- npm run typecheck 통과
- npm run build 통과 (electron-vite main+preload+renderer)
- npm run dev 실제 실행 → DB/핫키/Ollama 자동 실행 모두 정상
This commit is contained in:
yunchan8804 2026-04-08 14:04:41 +09:00
parent 3a160b9032
commit 45a580878a
178 changed files with 214 additions and 0 deletions

View file

@ -0,0 +1,163 @@
// src/renderer/components/OllamaGuideModal.tsx
// Ollama 설치/설정 안내 모달
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Box,
Typography,
Button,
Divider,
IconButton,
} from '@mui/material'
import CloseIcon from '@mui/icons-material/Close'
import OpenInNewIcon from '@mui/icons-material/OpenInNew'
import ContentCopyIcon from '@mui/icons-material/ContentCopy'
import { d3roPalette, d3roFontMono, d3roShadow } from '../theme'
import { Led } from './ds'
import { useI18n } from '../i18n'
interface OllamaGuideModalProps {
open: boolean
onClose: () => void
}
function CodeBlock({ children }: { children: string }): React.ReactElement {
return (
<Box
sx={{
bgcolor: d3roPalette.bg.inset,
borderRadius: '8px',
p: 1.5,
fontFamily: d3roFontMono,
fontSize: '12px',
color: d3roPalette.accent.amber,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
boxShadow: d3roShadow.inset,
}}
>
<span>{children}</span>
<IconButton
size="small"
onClick={() => navigator.clipboard.writeText(children)}
sx={{ color: d3roPalette.text.inactive, '&:hover': { color: d3roPalette.accent.amber } }}
>
<ContentCopyIcon sx={{ fontSize: 14 }} />
</IconButton>
</Box>
)
}
export function OllamaGuideModal({ open, onClose }: OllamaGuideModalProps): React.ReactElement {
const { t } = useI18n()
const handleOpenLink = (url: string) => {
window.electronAPI.system.openExternal({ url })
}
return (
<Dialog
open={open}
onClose={onClose}
maxWidth="sm"
fullWidth
PaperProps={{
sx: {
bgcolor: d3roPalette.bg.chassis,
backgroundImage: 'none',
border: `1px solid ${d3roPalette.border.subtle}`,
},
}}
>
<DialogTitle
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
fontFamily: d3roFontMono,
fontWeight: 700,
fontSize: '14px',
letterSpacing: '1px',
color: d3roPalette.accent.amber,
py: 1.5,
}}
>
{t('ollama.title')}
<IconButton onClick={onClose} size="small" sx={{ color: d3roPalette.text.inactive }}>
<CloseIcon sx={{ fontSize: 18 }} />
</IconButton>
</DialogTitle>
<Divider sx={{ borderColor: d3roPalette.border.subtle }} />
<DialogContent sx={{ bgcolor: d3roPalette.bg.app }}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3, py: 1 }}>
{/* Step 1 */}
<Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1 }}>
<Led color="amber" size={8} />
<Typography sx={{ fontFamily: d3roFontMono, fontSize: '12px', fontWeight: 700 }}>
{t('ollama.step1.title')}
</Typography>
</Box>
<Typography variant="body2" sx={{ color: d3roPalette.text.secondary, mb: 1.5 }}>
{t('ollama.step1.desc')}
</Typography>
<Button
variant="outlined"
size="small"
endIcon={<OpenInNewIcon sx={{ fontSize: 14 }} />}
onClick={() => handleOpenLink('https://ollama.com/download')}
sx={{ fontFamily: d3roFontMono, fontSize: '11px' }}
>
ollama.com/download
</Button>
</Box>
<Divider sx={{ borderColor: d3roPalette.border.subtle }} />
{/* Step 2 */}
<Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1 }}>
<Led color="amber" size={8} />
<Typography sx={{ fontFamily: d3roFontMono, fontSize: '12px', fontWeight: 700 }}>
{t('ollama.step2.title')}
</Typography>
</Box>
<Typography variant="body2" sx={{ color: d3roPalette.text.secondary, mb: 1.5 }}>
{t('ollama.step2.desc')}
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
<CodeBlock>ollama pull qwen3:4b</CodeBlock>
<Typography variant="caption" sx={{ color: d3roPalette.text.inactive }}>
{t('ollama.step2.alt')}
</Typography>
</Box>
</Box>
<Divider sx={{ borderColor: d3roPalette.border.subtle }} />
{/* Step 3 */}
<Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1 }}>
<Led color="green" size={8} />
<Typography sx={{ fontFamily: d3roFontMono, fontSize: '12px', fontWeight: 700 }}>
{t('ollama.step3.title')}
</Typography>
</Box>
<Typography variant="body2" sx={{ color: d3roPalette.text.secondary }}>
{t('ollama.step3.desc')}
</Typography>
</Box>
</Box>
</DialogContent>
<DialogActions sx={{ px: 3, pb: 2, bgcolor: d3roPalette.bg.app }}>
<Button onClick={onClose} variant="contained">
{t('common.confirm')}
</Button>
</DialogActions>
</Dialog>
)
}