Skip to content

Commit eb69d85

Browse files
sumi-0011claude
andauthored
Claude Code 연동 페이지 추가 (web, webview) (#384)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b5f86b7 commit eb69d85

4 files changed

Lines changed: 172 additions & 3 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,7 @@ apps/admin-main/.env.local
7878
apps/admin-main/docs/superpowers
7979
.omc
8080

81+
.superpowers
82+
docs/superpowers
83+
84+
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
'use client';
2+
3+
import { signIn } from 'next-auth/react';
4+
import { css } from '_panda/css';
5+
import { TextField } from '@gitanimals/ui-panda';
6+
7+
import { useClientSession, useClientUser } from '@/utils/clientAuth';
8+
9+
export default function ClaudeCodePage() {
10+
const { status } = useClientSession();
11+
const user = useClientUser();
12+
13+
if (status === 'loading') {
14+
return (
15+
<div className={pageRootCss}>
16+
<p className={loadingTextCss}>로딩 중…</p>
17+
</div>
18+
);
19+
}
20+
21+
if (status === 'unauthenticated') {
22+
signIn(undefined, { callbackUrl: '/auth/claude-code' });
23+
return null;
24+
}
25+
26+
const username = user.name ?? '';
27+
const command = `/gitanimals-buddy:login ${username}`;
28+
29+
return (
30+
<div className={pageRootCss}>
31+
<div className={cardCss}>
32+
<div className={headerBlockCss}>
33+
<p className={emojiCss}>
34+
<svg
35+
xmlns="http://www.w3.org/2000/svg"
36+
width="52"
37+
height="52"
38+
viewBox="0 0 24 24"
39+
fill="none"
40+
stroke="#ffffff"
41+
stroke-width="2"
42+
stroke-linecap="round"
43+
stroke-linejoin="round"
44+
className="lucide lucide-paw-print-icon lucide-paw-print"
45+
>
46+
<circle cx="11" cy="4" r="2" />
47+
<circle cx="18" cy="8" r="2" />
48+
<circle cx="20" cy="16" r="2" />
49+
<path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z" />
50+
</svg>
51+
</p>
52+
<h1 className={titleCss}>Claude Code 연동</h1>
53+
</div>
54+
55+
<div className={usernameBlockCss}>
56+
<p className={mutedLabelCss}>당신의 username은</p>
57+
<p className={usernameValueCss}>{username}</p>
58+
</div>
59+
60+
<div className={commandBlockCss}>
61+
<p className={commandHintCss}>아래 명령어를 Claude Code 터미널에 입력하세요</p>
62+
<TextField readOnly value={command} />
63+
</div>
64+
</div>
65+
</div>
66+
);
67+
}
68+
69+
/** 상점 Auction 섹션과 동일 그라데이션 */
70+
const pageRootCss = css({
71+
display: 'flex',
72+
flexDirection: 'column',
73+
alignItems: 'center',
74+
justifyContent: 'center',
75+
minHeight: '100vh',
76+
padding: '24px',
77+
bg: 'linear-gradient(180deg, #000 0%, #004875 38.51%, #005B93 52.46%, #006FB3 73.8%, #0187DB 100%)',
78+
color: 'white',
79+
_mobile: {
80+
padding: '16px',
81+
},
82+
});
83+
84+
const loadingTextCss = css({
85+
textStyle: 'glyph20.regular',
86+
color: 'white.white_70',
87+
});
88+
89+
const cardCss = css({
90+
borderRadius: '16px',
91+
background: 'rgba(255, 255, 255, 0.1)',
92+
backdropFilter: 'blur(7px)',
93+
padding: '40px',
94+
width: 'fit-content',
95+
minWidth: '520px',
96+
maxWidth: '100%',
97+
display: 'flex',
98+
flexDirection: 'column',
99+
alignItems: 'center',
100+
gap: '24px',
101+
102+
_mobile: {
103+
minWidth: '100%',
104+
padding: '24px 16px',
105+
background: 'rgba(255, 255, 255, 0.08)',
106+
},
107+
});
108+
109+
const headerBlockCss = css({
110+
textAlign: 'center',
111+
display: 'flex',
112+
flexDirection: 'column',
113+
alignItems: 'center',
114+
gap: '24px',
115+
});
116+
117+
const emojiCss = css({
118+
textStyle: 'glyph82.bold',
119+
lineHeight: '1',
120+
color: 'white,',
121+
});
122+
123+
const titleCss = css({
124+
textStyle: 'glyph28.bold',
125+
color: 'white',
126+
127+
_mobile: {
128+
textStyle: 'glyph24.bold',
129+
},
130+
});
131+
132+
const usernameBlockCss = css({
133+
textAlign: 'center',
134+
});
135+
136+
const mutedLabelCss = css({
137+
textStyle: 'glyph20.regular',
138+
color: 'white.white_80',
139+
});
140+
141+
const usernameValueCss = css({
142+
textStyle: 'glyph28.bold',
143+
color: 'white',
144+
marginTop: '4px',
145+
146+
_mobile: {
147+
textStyle: 'glyph24.bold',
148+
},
149+
});
150+
151+
const commandBlockCss = css({
152+
width: '100%',
153+
textAlign: 'center',
154+
});
155+
156+
const commandHintCss = css({
157+
textStyle: 'glyph14.regular',
158+
color: 'white.white_70',
159+
marginBottom: '12px',
160+
});

apps/webview/src/router/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,16 @@ export const router = createBrowserRouter([
7777
// 공개 라우트들 (인증 불필요)
7878
{
7979
path: NESTED_PATHS.AUTH(),
80-
element: <LoginPage />,
8180
children: [
81+
{
82+
index: true,
83+
element: <LoginPage />,
84+
},
8285
{
8386
path: 'login',
8487
element: <LoginPage />,
8588
},
89+
8690
],
8791
},
8892
{

packages/ui/panda/src/components/Textfield/TextField.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import { ComponentProps } from 'react';
33

44
interface TextFieldProps extends ComponentProps<'input'> {
55
error?: string;
6+
containerClassName?: string;
67
}
78

8-
export const TextField = ({ error, ...props }: TextFieldProps) => {
9+
export const TextField = ({ error, containerClassName, ...props }: TextFieldProps) => {
910
return (
10-
<div>
11+
<div className={cx(containerClassName)}>
1112
<input type="text" {...props} className={cx(textFieldStyle, props.className)} />
1213
{error && <p className={errorStyle}>{error}</p>}
1314
</div>

0 commit comments

Comments
 (0)