@akanjs/cli 3.0.0-beta.31 → 3.0.0-beta.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.build-stamp +1 -1
- package/{CodeAgent-tbdpdjat.js → CodeAgent-2vmp9qe0.js} +2 -2
- package/{CodeTui-my2x5fah.js → CodeTui-5ed6bg2a.js} +1 -1
- package/{agent.command-vz60x05c.js → agent.command-gn73rhe7.js} +5 -5
- package/akanCode.js +2 -2
- package/{application.command-pxqfjrqm.js → application.command-e968t0hw.js} +14 -9
- package/{applicationBuildRunner-shnfyfdy.js → applicationBuildRunner-64taxseg.js} +3 -3
- package/buildBatch.proc.js +3 -3
- package/{cloud.command-9v9tpgps.js → cloud.command-0jm301rr.js} +9 -9
- package/cloud.script-nemnzjpv.js +20 -0
- package/{code.command-13h14j25.js → code.command-18kcm7hq.js} +6 -6
- package/{context.command-x1hbasm5.js → context.command-trd9bn18.js} +13 -13
- package/context.runner-y5hwh673.js +21 -0
- package/{guideline.command-vfk11hpj.js → guideline.command-bdmdn2de.js} +2 -2
- package/guidelines/conventions/conventions.instruction.md +5 -3
- package/guidelines/runtimeRule/runtimeRule.instruction.md +26 -6
- package/incrementalBuilder.proc.js +2 -2
- package/{index-ptshj7y1.js → index-0wpybxtr.js} +3 -3
- package/{index-e52z7psd.js → index-3w94e7n3.js} +18 -2
- package/{index-nhhrjvk1.js → index-4npz05nm.js} +4 -4
- package/{index-9s5zga3q.js → index-6he6gvfg.js} +1 -1
- package/{index-q9q7x4zj.js → index-93j60em0.js} +3 -3
- package/{index-vcdxpmsx.js → index-9z771ptz.js} +2 -2
- package/{index-rqxa9r98.js → index-abxpehf8.js} +7 -7
- package/{index-arnkqb6f.js → index-by9pxkr0.js} +2 -2
- package/{index-947t6zm4.js → index-drehwpdt.js} +2 -2
- package/{index-f50dy7yq.js → index-f3c78nd6.js} +7 -7
- package/{index-f8q701fb.js → index-gbggbqcp.js} +77 -22
- package/{index-7yf7rnjg.js → index-hsmtysz4.js} +2 -2
- package/{index-ksq4t16z.js → index-jmrn54tt.js} +1 -1
- package/{index-ew8dna9s.js → index-kcdj1z5v.js} +2 -2
- package/{index-cxafpw23.js → index-kqabytj7.js} +4 -4
- package/{index-qk9vv9hm.js → index-mbxbhay5.js} +1 -1
- package/{index-zvr7wfyg.js → index-mgxpx3f2.js} +2 -2
- package/{index-zc90rh75.js → index-p993efkq.js} +33 -18
- package/{index-8psse8dw.js → index-pmhqttv3.js} +5 -5
- package/{index-d5yn19ef.js → index-tqm7jjcn.js} +1 -1
- package/{index-1skctzbq.js → index-vpj42xwt.js} +1 -1
- package/{index-smbecayj.js → index-wprnsjms.js} +3 -3
- package/{index-84r80zdx.js → index-y3mphr1m.js} +1 -1
- package/{index-ymnqrsxm.js → index-yg7jhz8x.js} +4 -4
- package/index.js +21 -21
- package/{library.command-m52jqc0s.js → library.command-9h749a1x.js} +3 -3
- package/{localRegistry.command-x5vs1z43.js → localRegistry.command-2bqqhxyz.js} +8 -8
- package/{module.command-ty3k6rwd.js → module.command-nv8nzgft.js} +5 -5
- package/{package.command-hphdh4g4.js → package.command-mq4bggp6.js} +3 -3
- package/package.json +2 -2
- package/{page.command-5aq8mzvq.js → page.command-j639n707.js} +3 -3
- package/{primitive.command-adwj84kx.js → primitive.command-kz6849th.js} +6 -6
- package/{quality.command-y10kea2f.js → quality.command-4hyabwkb.js} +2 -2
- package/{repair.command-v7mwqvjh.js → repair.command-a9awpbqr.js} +5 -5
- package/{scalar.command-pg3gneer.js → scalar.command-ma4fpm9m.js} +4 -4
- package/{subspace.command-ejdmvbq7.js → subspace.command-3k2pmrs4.js} +4 -4
- package/templates/app/page/_index.tsx +257 -258
- package/templates/app/page/_layout.tsx +5 -1
- package/templates/app/page/_overrides.tsx +12 -0
- package/templates/app/page/styles.css.template +316 -14
- package/templates/app/public/apple-touch-icon.png +0 -0
- package/templates/app/public/favicon.ico +0 -0
- package/templates/app/public/jelly/cloud.webp +0 -0
- package/templates/app/public/jelly/comet.webp +0 -0
- package/templates/app/public/jelly/moon.webp +0 -0
- package/templates/app/public/jelly/planet.webp +0 -0
- package/templates/app/public/jelly/rocket.webp +0 -0
- package/templates/app/public/jelly/star.webp +0 -0
- package/templates/app/public/jelly/sugar.webp +0 -0
- package/templates/app/public/logo.png +0 -0
- package/templates/app/ui/Recipe/index.ts +9 -0
- package/templates/app/ui/Recipe/jellyButton.ts +63 -0
- package/templates/appSample/lib/task/Task.Unit.tsx +1 -1
- package/templates/appSample/lib/task/Task.Util.tsx +10 -13
- package/templates/appSample/lib/task/Task.View.tsx +2 -2
- package/templates/appSample/lib/task/Task.Zone.tsx +5 -2
- package/templates/appSample/page/task/[taskId]/_index.tsx +6 -6
- package/templates/appSample/page/task/[taskId]/edit.tsx +5 -4
- package/templates/appSample/page/task/_index.tsx +4 -3
- package/templates/appSample/page/task/_layout.tsx +1 -1
- package/templates/appSample/page/task/new.tsx +5 -4
- package/templates/appSample/ui/GlobalLoading.tsx +2 -1
- package/templates/appSample/ui/QuantityControl.tsx +3 -3
- package/{tunnel.command-rz9qf230.js → tunnel.command-xwr46b66.js} +2 -2
- package/{workflow.command-6r809r2z.js → workflow.command-t7wqe4h7.js} +10 -10
- package/{workspace.command-8g3cw7jd.js → workspace.command-4derhzrh.js} +24 -21
- package/cloud.script-baqdthnz.js +0 -20
- package/context.runner-kqbjjea1.js +0 -21
|
@@ -3,295 +3,294 @@ import type { AppInfo, LibInfo } from "akanjs";
|
|
|
3
3
|
interface Dict {
|
|
4
4
|
appName: string;
|
|
5
5
|
}
|
|
6
|
-
|
|
6
|
+
interface Options {
|
|
7
|
+
libs?: string[];
|
|
8
|
+
sample?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dict, options: Options = {}) {
|
|
11
|
+
const sampleButton = options.sample
|
|
12
|
+
? `
|
|
13
|
+
<Link className={jellyButtonRecipe({ variant: "default", size: "lg" })} href="/task">
|
|
14
|
+
{l.trans({ en: "Open the Sample", ko: "샘플 열어 보기" })}
|
|
15
|
+
</Link>`
|
|
16
|
+
: `
|
|
17
|
+
<Link
|
|
18
|
+
className={jellyButtonRecipe({ variant: "default", size: "lg" })}
|
|
19
|
+
href="https://akanjs.com/docs/intro/practice"
|
|
20
|
+
target="_blank"
|
|
21
|
+
>
|
|
22
|
+
{l.trans({ en: "Learn By Building", ko: "만들면서 배우기" })}
|
|
23
|
+
</Link>`;
|
|
24
|
+
const sampleStep = options.sample
|
|
25
|
+
? `
|
|
26
|
+
{
|
|
27
|
+
title: l.trans({ en: "Read the sample module", ko: "샘플 모듈을 읽어 보세요" }),
|
|
28
|
+
body: l.trans({
|
|
29
|
+
en: "The task module walks one feature end to end — the model, its guarded API, the store and the pages.",
|
|
30
|
+
ko: "task 모듈이 모델부터 가드가 걸린 API, 스토어, 페이지까지 기능 하나를 처음부터 끝까지 보여 줍니다.",
|
|
31
|
+
}),
|
|
32
|
+
code: "lib/task",
|
|
33
|
+
href: "/task",
|
|
34
|
+
},`
|
|
35
|
+
: "";
|
|
7
36
|
return {
|
|
8
37
|
filename: "_index.tsx",
|
|
9
38
|
content: `
|
|
39
|
+
import { jellyButtonRecipe } from "@apps/${dict.appName}/ui";
|
|
10
40
|
import { getEnv } from "akanjs/base";
|
|
11
|
-
import { page, usePage } from "akanjs/client";
|
|
12
|
-
import { Link, System } from "akanjs/ui";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
import { cn, page, usePage } from "akanjs/client";
|
|
42
|
+
import { Image, Link, System } from "akanjs/ui";
|
|
43
|
+
|
|
44
|
+
const friendFloat = {
|
|
45
|
+
planet: "[--float-delay:-0.4s] [--float-duration:6.2s]",
|
|
46
|
+
rocket: "[--float-delay:-2.2s] [--float-duration:5.2s] [--float-tilt:7deg]",
|
|
47
|
+
moon: "[--float-delay:-3.6s] [--float-duration:7s] [--float-tilt:-5deg]",
|
|
48
|
+
cloud: "[--float-delay:-1.4s] [--float-duration:8s] [--float-rise:0.6rem]",
|
|
49
|
+
comet: "[--float-delay:-4.8s] [--float-duration:4.6s] [--float-tilt:9deg]",
|
|
50
|
+
} as const;
|
|
51
|
+
|
|
52
|
+
const numeralTiles = [
|
|
53
|
+
"tint-planet text-white",
|
|
54
|
+
"tint-rocket text-black/70",
|
|
55
|
+
"tint-moon text-black/70",
|
|
56
|
+
"tint-comet text-white",
|
|
57
|
+
] as const;
|
|
24
58
|
|
|
25
59
|
export default page().render(() => {
|
|
26
60
|
const appName = getEnv().appName;
|
|
27
61
|
const { l } = usePage();
|
|
62
|
+
const friends = [
|
|
63
|
+
{ name: "planet", role: l.trans({ en: "Web", ko: "웹" }) },
|
|
64
|
+
{ name: "rocket", role: l.trans({ en: "App", ko: "앱" }) },
|
|
65
|
+
{ name: "moon", role: l.trans({ en: "Server · DB", ko: "서버 · DB" }) },
|
|
66
|
+
{ name: "cloud", role: l.trans({ en: "Infra", ko: "인프라" }) },
|
|
67
|
+
{ name: "comet", role: l.trans({ en: "Agent", ko: "에이전트" }) },
|
|
68
|
+
] as const;
|
|
69
|
+
const chapters = [
|
|
70
|
+
{
|
|
71
|
+
numeral: "1",
|
|
72
|
+
title: l.trans({ en: "The line you write.", ko: "당신이 쓰는 한 줄." }),
|
|
73
|
+
body: l.trans({
|
|
74
|
+
en: "A field is one declaration — a name and a type. The database, API, screens and agent tools all come from it.",
|
|
75
|
+
ko: "필드는 이름과 타입, 선언 한 줄입니다. DB, API, 화면, 에이전트 도구가 모두 이 한 줄에서 나옵니다.",
|
|
76
|
+
}),
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
numeral: "×8",
|
|
80
|
+
title: l.trans({ en: "Through every layer.", ko: "모든 레이어를 관통하고," }),
|
|
81
|
+
body: l.trans({
|
|
82
|
+
en: "Schema, query, service, API, fetch, client type, state and UI change together. Nothing to chase, nothing to miss.",
|
|
83
|
+
ko: "스키마, 쿼리, 서비스, API, fetch, 클라이언트 타입, 상태, UI가 함께 바뀝니다. 따라 고칠 곳도 빠뜨릴 곳도 없습니다.",
|
|
84
|
+
}),
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
numeral: "×6",
|
|
88
|
+
title: l.trans({ en: "Onto every platform.", ko: "모든 플랫폼에 닿고," }),
|
|
89
|
+
body: l.trans({
|
|
90
|
+
en: "The same code ships as SEO-ready web, iOS and Android apps, and macOS, Windows and Linux desktop apps.",
|
|
91
|
+
ko: "같은 코드가 SEO 웹, iOS·Android 앱, macOS·Windows·Linux 데스크톱 앱으로 배포됩니다.",
|
|
92
|
+
}),
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
numeral: "×2",
|
|
96
|
+
title: l.trans({ en: "For people and agents alike.", ko: "사람과 에이전트 모두에게." }),
|
|
97
|
+
body: l.trans({
|
|
98
|
+
en: "Guarded endpoints become MCP tools and on-screen controls become in-page agent tools, behind the same guards people pass.",
|
|
99
|
+
ko: "가드를 거친 엔드포인트는 MCP 도구가, 화면의 컨트롤은 인페이지 에이전트 도구가 됩니다. 사람과 같은 가드를 거쳐서요.",
|
|
100
|
+
}),
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
const steps = [
|
|
104
|
+
{
|
|
105
|
+
title: l.trans({ en: "Make this page yours", ko: "이 페이지를 바꿔 보세요" }),
|
|
106
|
+
body: l.trans({
|
|
107
|
+
en: "This welcome screen is an ordinary page. Replace it with the first screen of your product.",
|
|
108
|
+
ko: "이 환영 화면은 평범한 페이지입니다. 제품의 첫 화면으로 바꿔 보세요.",
|
|
109
|
+
}),
|
|
110
|
+
code: "page/_index.tsx",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
title: l.trans({ en: "Restyle the theme", ko: "테마를 바꿔 보세요" }),
|
|
114
|
+
body: l.trans({
|
|
115
|
+
en: "Swap the palette tokens, and every surface and button follows.",
|
|
116
|
+
ko: "팔레트 토큰을 바꾸면 모든 표면과 버튼이 따라옵니다.",
|
|
117
|
+
}),
|
|
118
|
+
code: "page/styles.css",
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
title: l.trans({ en: "Add a feature", ko: "기능을 추가해 보세요" }),
|
|
122
|
+
body: l.trans({
|
|
123
|
+
en: "One command scaffolds a domain module — model, service, API, store and UI.",
|
|
124
|
+
ko: "명령 하나로 도메인 모듈(모델, 서비스, API, 스토어, UI)을 만듭니다.",
|
|
125
|
+
}),
|
|
126
|
+
code: "akan create-module <name>",
|
|
127
|
+
},${sampleStep}
|
|
128
|
+
{
|
|
129
|
+
title: l.trans({ en: "Connect an agent", ko: "에이전트를 연결해 보세요" }),
|
|
130
|
+
body: l.trans({
|
|
131
|
+
en: "Every guarded endpoint is already published as an MCP tool.",
|
|
132
|
+
ko: "가드가 걸린 엔드포인트는 이미 MCP 도구로 공개되어 있습니다.",
|
|
133
|
+
}),
|
|
134
|
+
code: "POST /mcp",
|
|
135
|
+
href: "https://akanjs.com/docs/arch/agentic",
|
|
136
|
+
},
|
|
137
|
+
];
|
|
28
138
|
return (
|
|
29
|
-
<main className="relative min-h-screen overflow-
|
|
30
|
-
<div className="
|
|
31
|
-
|
|
32
|
-
<div className="absolute inset-x-0 top-0 h-px bg-linear-to-r from-transparent via-primary/60 to-transparent" />
|
|
33
|
-
|
|
34
|
-
<section className="relative mx-auto flex min-h-screen w-full max-w-7xl flex-col px-6 py-8 lg:px-8">
|
|
35
|
-
<nav className="flex items-center justify-between">
|
|
139
|
+
<main className="relative min-h-screen overflow-x-clip break-keep text-foreground">
|
|
140
|
+
<div className="mx-auto flex min-h-screen w-full max-w-6xl flex-col px-6 py-6 lg:px-8">
|
|
141
|
+
<nav className="flex items-center justify-between gap-4">
|
|
36
142
|
<div className="flex items-center gap-3">
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<p className="
|
|
47
|
-
|
|
143
|
+
<Image
|
|
144
|
+
alt={l.trans({ en: "Logo", ko: "로고" })}
|
|
145
|
+
className="jelly-breathe size-10"
|
|
146
|
+
height={80}
|
|
147
|
+
priority
|
|
148
|
+
src="/logo.png"
|
|
149
|
+
width={80}
|
|
150
|
+
/>
|
|
151
|
+
<div className="leading-tight">
|
|
152
|
+
<p className="font-black text-lg tracking-tight">{appName}</p>
|
|
153
|
+
<p className="text-foreground/50 text-xs">
|
|
154
|
+
{l.trans({ en: "Built with Akan.js", ko: "Akan.js로 만든 앱" })}
|
|
48
155
|
</p>
|
|
49
156
|
</div>
|
|
50
157
|
</div>
|
|
51
158
|
<div className="flex items-center gap-2">
|
|
52
159
|
<System.ThemeToggle themes={["light", "dark"]} />
|
|
53
160
|
<System.SelectLanguage languages={["en", "ko"]} />
|
|
54
|
-
<Link
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
161
|
+
<Link
|
|
162
|
+
className={jellyButtonRecipe({ variant: "default", size: "sm" }, "max-sm:hidden")}
|
|
163
|
+
href="https://akanjs.com"
|
|
164
|
+
target="_blank"
|
|
165
|
+
>
|
|
166
|
+
{l.trans({ en: "Official Site", ko: "공식 사이트" })} ↗
|
|
59
167
|
</Link>
|
|
60
168
|
</div>
|
|
61
169
|
</nav>
|
|
62
170
|
|
|
63
|
-
<
|
|
64
|
-
<div>
|
|
65
|
-
<div className="
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
{l.trans({
|
|
77
|
-
en: "Agents Write. Keep It Minimal. Always Readable. Nice To Review. Build with less code, fewer repeated decisions, and a clearer path from idea to production.",
|
|
78
|
-
ko: "Agents Write. Keep It Minimal. Always Readable. Nice To Review. 더 적은 코드, 더 적은 반복 결정, 더 선명한 출시 경로로 만드세요.",
|
|
79
|
-
})}
|
|
80
|
-
</p>
|
|
81
|
-
|
|
82
|
-
<div className="mt-8 flex flex-wrap gap-3">
|
|
83
|
-
<span className="inline-flex items-center gap-1 rounded-full border border-foreground/10 bg-foreground/10 px-4 py-1.5 text-foreground text-sm">
|
|
84
|
-
Agent-ready
|
|
85
|
-
</span>
|
|
86
|
-
<span className="inline-flex items-center gap-1 rounded-full border border-foreground/10 bg-foreground/10 px-4 py-1.5 text-foreground text-sm">
|
|
87
|
-
Minimal code
|
|
88
|
-
</span>
|
|
89
|
-
<span className="inline-flex items-center gap-1 rounded-full border border-foreground/10 bg-foreground/10 px-4 py-1.5 text-foreground text-sm">
|
|
90
|
-
Readable by default
|
|
91
|
-
</span>
|
|
92
|
-
<span className="inline-flex items-center gap-1 rounded-full border border-foreground/10 bg-foreground/10 px-4 py-1.5 text-foreground text-sm">
|
|
93
|
-
Review-friendly
|
|
94
|
-
</span>
|
|
95
|
-
</div>
|
|
96
|
-
|
|
97
|
-
<div className="mt-10 flex flex-col gap-3 sm:flex-row">
|
|
98
|
-
<Link href="https://akanjs.com/docs/intro/quickstart" target="_blank">
|
|
99
|
-
<button className="inline-flex items-center justify-center gap-2 rounded-field bg-primary px-4 py-2 text-background text-sm transition-colors hover:bg-primary/80">
|
|
100
|
-
{l.trans({ en: "Read Quick Start", ko: "빠른 시작 읽기" })}
|
|
101
|
-
<FaBookOpen />
|
|
102
|
-
</button>
|
|
103
|
-
</Link>
|
|
104
|
-
<Link href="https://akanjs.com/docs/intro/practice" target="_blank">
|
|
105
|
-
<button className="inline-flex items-center justify-center gap-2 rounded-field border border-foreground/10 bg-foreground/10 px-4 py-2 text-foreground text-sm transition-colors hover:border-foreground/20 hover:bg-foreground/15">
|
|
106
|
-
{l.trans({ en: "Learn By Building", ko: "만들면서 배우기" })}
|
|
107
|
-
<FaCodeBranch />
|
|
108
|
-
</button>
|
|
109
|
-
</Link>
|
|
110
|
-
</div>
|
|
111
|
-
</div>
|
|
112
|
-
|
|
113
|
-
<div className="relative">
|
|
114
|
-
<div className="absolute inset-0 rotate-3 rounded-4xl bg-primary/20 blur-2xl" />
|
|
115
|
-
<div className="relative overflow-hidden rounded-4xl border border-foreground/10 bg-foreground/6 p-5 shadow-2xl backdrop-blur">
|
|
116
|
-
<div className="mb-5 flex items-center justify-between rounded-2xl border border-foreground/10 bg-background/70 px-4 py-3">
|
|
117
|
-
<div>
|
|
118
|
-
<p className="text-foreground/40 text-xs uppercase tracking-[0.24em]">
|
|
119
|
-
{l.trans({ en: "Akan Acrostic", ko: "Akan 사행시" })}
|
|
120
|
-
</p>
|
|
121
|
-
<p className="font-semibold text-foreground text-lg">
|
|
122
|
-
{l.trans({ en: "A framework for focused builders", ko: "집중하는 빌더를 위한 프레임워크" })}
|
|
123
|
-
</p>
|
|
124
|
-
</div>
|
|
125
|
-
<div className="rounded-xl bg-primary/10 px-3 py-2 font-medium text-primary text-sm">{appName}</div>
|
|
126
|
-
</div>
|
|
127
|
-
|
|
128
|
-
<div className="grid gap-3">
|
|
129
|
-
<div className="rounded-2xl border border-foreground/10 bg-background/80 p-4">
|
|
130
|
-
<div className="flex gap-4">
|
|
131
|
-
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl bg-background font-black text-primary text-xl">
|
|
132
|
-
A
|
|
133
|
-
</div>
|
|
134
|
-
<div>
|
|
135
|
-
<p className="font-bold text-foreground">Agents Write</p>
|
|
136
|
-
<p className="mt-1 text-foreground/60 text-sm leading-6">
|
|
137
|
-
{l.trans({
|
|
138
|
-
en: "Business definitions become the source code, so teams and agents can focus on what to build.",
|
|
139
|
-
ko: "비즈니스 정의가 소스 코드가 되므로, 팀과 에이전트는 무엇을 만들지에 집중할 수 있습니다.",
|
|
140
|
-
})}
|
|
141
|
-
</p>
|
|
142
|
-
</div>
|
|
143
|
-
</div>
|
|
144
|
-
</div>
|
|
145
|
-
<div className="rounded-2xl border border-foreground/10 bg-background/80 p-4">
|
|
146
|
-
<div className="flex gap-4">
|
|
147
|
-
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl bg-background font-black text-primary text-xl">
|
|
148
|
-
K
|
|
149
|
-
</div>
|
|
150
|
-
<div>
|
|
151
|
-
<p className="font-bold text-foreground">Keep It Minimal</p>
|
|
152
|
-
<p className="mt-1 text-foreground/60 text-sm leading-6">
|
|
153
|
-
{l.trans({
|
|
154
|
-
en: "One definition flows into web, app, server, database, and infrastructure without repeated logic.",
|
|
155
|
-
ko: "하나의 정의가 반복 로직 없이 웹, 앱, 서버, 데이터베이스, 인프라로 이어집니다.",
|
|
156
|
-
})}
|
|
157
|
-
</p>
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
</div>
|
|
161
|
-
<div className="rounded-2xl border border-foreground/10 bg-background/80 p-4">
|
|
162
|
-
<div className="flex gap-4">
|
|
163
|
-
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl bg-background font-black text-primary text-xl">
|
|
164
|
-
A
|
|
165
|
-
</div>
|
|
166
|
-
<div>
|
|
167
|
-
<p className="font-bold text-foreground">Always Readable</p>
|
|
168
|
-
<p className="mt-1 text-foreground/60 text-sm leading-6">
|
|
169
|
-
{l.trans({
|
|
170
|
-
en: "Strict conventions keep the app easy to understand long after the first version ships.",
|
|
171
|
-
ko: "엄격한 컨벤션은 첫 버전 출시 후에도 앱을 쉽게 이해할 수 있게 지켜줍니다.",
|
|
172
|
-
})}
|
|
173
|
-
</p>
|
|
174
|
-
</div>
|
|
175
|
-
</div>
|
|
176
|
-
</div>
|
|
177
|
-
<div className="rounded-2xl border border-foreground/10 bg-background/80 p-4">
|
|
178
|
-
<div className="flex gap-4">
|
|
179
|
-
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl bg-background font-black text-primary text-xl">
|
|
180
|
-
N
|
|
181
|
-
</div>
|
|
182
|
-
<div>
|
|
183
|
-
<p className="font-bold text-foreground">Nice To Review</p>
|
|
184
|
-
<p className="mt-1 text-foreground/60 text-sm leading-6">
|
|
185
|
-
{l.trans({
|
|
186
|
-
en: "Focused changes make business intent clear, so reviews stay fast and releases stay calm.",
|
|
187
|
-
ko: "집중된 변경은 비즈니스 의도를 선명하게 만들어 리뷰를 빠르게 하고 배포를 안정적으로 유지합니다.",
|
|
188
|
-
})}
|
|
189
|
-
</p>
|
|
190
|
-
</div>
|
|
191
|
-
</div>
|
|
192
|
-
</div>
|
|
193
|
-
</div>
|
|
194
|
-
|
|
195
|
-
<div className="mt-5 overflow-x-auto rounded-box border border-foreground/10 bg-background py-4 font-mono text-sm">
|
|
196
|
-
<pre className="px-6">
|
|
197
|
-
<span className="select-none text-foreground/40">$ </span>
|
|
198
|
-
<code className="text-primary">bun run akan start {appName}</code>
|
|
199
|
-
</pre>
|
|
200
|
-
<pre className="px-6">
|
|
201
|
-
<span className="select-none text-foreground/40">✓ </span>
|
|
202
|
-
<code className="text-accent">
|
|
203
|
-
{l.trans({ en: "web, app, server, db, and infra ready", ko: "웹, 앱, 서버, DB, 인프라 준비 완료" })}
|
|
204
|
-
</code>
|
|
205
|
-
</pre>
|
|
206
|
-
<pre className="px-6">
|
|
207
|
-
<span className="select-none text-foreground/40">→ </span>
|
|
208
|
-
<code className="text-foreground/70">
|
|
209
|
-
{l.trans({
|
|
210
|
-
en: "edit page/_index.tsx around your business",
|
|
211
|
-
ko: "비즈니스에 맞게 page/_index.tsx를 수정하세요",
|
|
212
|
-
})}
|
|
213
|
-
</code>
|
|
214
|
-
</pre>
|
|
215
|
-
</div>
|
|
171
|
+
<section className="grid min-h-[calc(100svh-5rem)] items-center gap-6 py-12 lg:grid-cols-[0.8fr_1.2fr] lg:gap-10">
|
|
172
|
+
<div className="intro-rise group relative mx-auto aspect-square w-48 sm:w-64 lg:w-full lg:max-w-md">
|
|
173
|
+
<div className="absolute inset-x-[16%] bottom-[3%] h-[7%] rounded-full bg-black/25 blur-xl dark:bg-black/60" />
|
|
174
|
+
<div className="jelly-breathe group-hover:jelly-wobble size-full">
|
|
175
|
+
<Image
|
|
176
|
+
alt=""
|
|
177
|
+
className="pointer-events-none size-full select-none object-contain"
|
|
178
|
+
draggable={false}
|
|
179
|
+
height={640}
|
|
180
|
+
priority
|
|
181
|
+
src="/jelly/star.webp"
|
|
182
|
+
width={640}
|
|
183
|
+
/>
|
|
216
184
|
</div>
|
|
217
185
|
</div>
|
|
218
|
-
</div>
|
|
219
186
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
</div>
|
|
225
|
-
<h2 className="font-bold text-foreground text-lg">
|
|
226
|
-
{l.trans({ en: "Agentic By Design", ko: "에이전틱 설계" })}
|
|
227
|
-
</h2>
|
|
228
|
-
<p className="mt-2 text-foreground/60 text-sm leading-6">
|
|
229
|
-
{l.trans({
|
|
230
|
-
en: "Describe the business once and let Akan shape the application surfaces around it.",
|
|
231
|
-
ko: "비즈니스를 한 번 설명하면 Akan이 그 주변의 애플리케이션 표면을 구성합니다.",
|
|
232
|
-
})}
|
|
233
|
-
</p>
|
|
234
|
-
</div>
|
|
235
|
-
<div className="rounded-3xl border border-foreground/10 bg-foreground/4 p-6 backdrop-blur">
|
|
236
|
-
<div className="mb-5 flex h-12 w-12 items-center justify-center rounded-2xl bg-primary/10 text-primary text-xl">
|
|
237
|
-
<FaLayerGroup />
|
|
238
|
-
</div>
|
|
239
|
-
<h2 className="font-bold text-foreground text-lg">
|
|
240
|
-
{l.trans({ en: "One Definition", ko: "하나의 정의" })}
|
|
241
|
-
</h2>
|
|
242
|
-
<p className="mt-2 text-foreground/60 text-sm leading-6">
|
|
243
|
-
{l.trans({
|
|
244
|
-
en: "Pages, services, database models, and deployment artifacts stay connected in one workspace.",
|
|
245
|
-
ko: "페이지, 서비스, 데이터베이스 모델, 배포 산출물이 하나의 워크스페이스에서 연결됩니다.",
|
|
246
|
-
})}
|
|
187
|
+
<div className="flex flex-col items-center text-center lg:items-start lg:text-left">
|
|
188
|
+
<p className="intro-rise jelly-glass inline-flex items-center gap-2 rounded-full px-4 py-1.5 font-bold text-foreground/75 text-sm [--intro-delay:40ms]">
|
|
189
|
+
<span className="size-2 rounded-full bg-success ring-4 ring-success/20" />
|
|
190
|
+
{l.trans({ en: "Your app is running", ko: "앱이 실행 중입니다" })}
|
|
247
191
|
</p>
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
</div>
|
|
253
|
-
<h2 className="font-bold text-foreground text-lg">
|
|
254
|
-
{l.trans({ en: "Less To Review", ko: "리뷰할 것이 적습니다" })}
|
|
255
|
-
</h2>
|
|
256
|
-
<p className="mt-2 text-foreground/60 text-sm leading-6">
|
|
192
|
+
<h1 className="intro-rise wrap-anywhere mt-6 max-w-full font-black text-[clamp(3.5rem,12vw,8.5rem)] leading-[0.85] tracking-[-0.06em] [--intro-delay:80ms]">
|
|
193
|
+
{appName}
|
|
194
|
+
</h1>
|
|
195
|
+
<p className="intro-rise mt-6 max-w-xl font-bold text-foreground/60 text-lg leading-8 [--intro-delay:160ms]">
|
|
257
196
|
{l.trans({
|
|
258
|
-
en: "
|
|
259
|
-
ko: "
|
|
197
|
+
en: "Built on Akan.js — the TypeScript framework, agents included.",
|
|
198
|
+
ko: "에이전트까지 들어 있는 TypeScript 프레임워크, Akan.js로 만들었습니다.",
|
|
260
199
|
})}
|
|
261
200
|
</p>
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
201
|
+
<ul className="intro-rise mt-8 flex items-end gap-2 [--intro-delay:240ms] sm:gap-4">
|
|
202
|
+
{friends.map((friend) => (
|
|
203
|
+
<li className="group flex flex-col items-center gap-2" key={friend.name}>
|
|
204
|
+
<span className={cn("jelly-float block", friendFloat[friend.name])}>
|
|
205
|
+
<span className="group-hover:jelly-wobble block">
|
|
206
|
+
<Image
|
|
207
|
+
alt=""
|
|
208
|
+
className="pointer-events-none size-14 select-none sm:size-18"
|
|
209
|
+
draggable={false}
|
|
210
|
+
height={200}
|
|
211
|
+
src={\`/jelly/\${friend.name}.webp\`}
|
|
212
|
+
width={200}
|
|
213
|
+
/>
|
|
214
|
+
</span>
|
|
215
|
+
</span>
|
|
216
|
+
<span className="whitespace-nowrap rounded-full bg-foreground/6 px-2.5 py-0.5 font-bold text-[11px] text-foreground/60">
|
|
217
|
+
{friend.role}
|
|
218
|
+
</span>
|
|
219
|
+
</li>
|
|
220
|
+
))}
|
|
221
|
+
</ul>
|
|
222
|
+
<div className="intro-rise mt-10 flex flex-wrap justify-center gap-3 [--intro-delay:320ms] lg:justify-start">
|
|
223
|
+
<Link
|
|
224
|
+
className={jellyButtonRecipe({ size: "lg" })}
|
|
225
|
+
href="https://akanjs.com/docs/intro/quickstart"
|
|
226
|
+
target="_blank"
|
|
227
|
+
>
|
|
228
|
+
{l.trans({ en: "Read Quick Start", ko: "빠른 시작 읽기" })}
|
|
229
|
+
</Link>${sampleButton}
|
|
266
230
|
</div>
|
|
267
|
-
<h2 className="font-bold text-foreground text-lg">
|
|
268
|
-
{l.trans({ en: "Type-Safe Growth", ko: "타입 안전한 성장" })}
|
|
269
|
-
</h2>
|
|
270
|
-
<p className="mt-2 text-foreground/60 text-sm leading-6">
|
|
271
|
-
{l.trans({
|
|
272
|
-
en: "Conventions and contracts keep the stack readable as the product expands.",
|
|
273
|
-
ko: "컨벤션과 계약은 제품이 확장되어도 스택을 읽기 쉽게 유지합니다.",
|
|
274
|
-
})}
|
|
275
|
-
</p>
|
|
276
231
|
</div>
|
|
277
|
-
</
|
|
232
|
+
</section>
|
|
278
233
|
|
|
279
|
-
<
|
|
280
|
-
<
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
234
|
+
<section className="py-12">
|
|
235
|
+
<p className="font-bold text-primary text-sm">{l.trans({ en: "How it works", ko: "작동 방식" })}</p>
|
|
236
|
+
<h2 className="mt-2 font-black text-3xl sm:text-4xl">
|
|
237
|
+
{l.trans({ en: "One line reaches the whole product.", ko: "한 줄이 제품 전체에 닿습니다." })}
|
|
238
|
+
</h2>
|
|
239
|
+
<div className="mt-8 grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
|
240
|
+
{chapters.map((chapter, idx) => (
|
|
241
|
+
<article className="jelly-glass rounded-box p-6" key={chapter.numeral}>
|
|
242
|
+
<span
|
|
243
|
+
className={cn(
|
|
244
|
+
"jelly flex size-12 items-center justify-center rounded-2xl font-black text-xl",
|
|
245
|
+
numeralTiles[idx],
|
|
246
|
+
)}
|
|
247
|
+
>
|
|
248
|
+
{chapter.numeral}
|
|
249
|
+
</span>
|
|
250
|
+
<h3 className="mt-5 font-bold text-lg">{chapter.title}</h3>
|
|
251
|
+
<p className="mt-2 text-foreground/60 text-sm leading-6">{chapter.body}</p>
|
|
252
|
+
</article>
|
|
253
|
+
))}
|
|
292
254
|
</div>
|
|
293
|
-
</
|
|
294
|
-
|
|
255
|
+
</section>
|
|
256
|
+
|
|
257
|
+
<section className="py-12">
|
|
258
|
+
<p className="font-bold text-primary text-sm">{l.trans({ en: "Next steps", ko: "다음 단계" })}</p>
|
|
259
|
+
<h2 className="mt-2 font-black text-3xl sm:text-4xl">
|
|
260
|
+
{l.trans({ en: "Start here.", ko: "여기서 시작하세요." })}
|
|
261
|
+
</h2>
|
|
262
|
+
<ol className="jelly-glass mt-8 divide-y divide-foreground/8 rounded-box">
|
|
263
|
+
{steps.map((step, idx) => (
|
|
264
|
+
<li className="flex items-start gap-4 p-5 sm:p-6" key={step.code}>
|
|
265
|
+
<span className="jelly tint-primary flex size-9 shrink-0 items-center justify-center rounded-full font-black text-primary-foreground text-sm">
|
|
266
|
+
{idx + 1}
|
|
267
|
+
</span>
|
|
268
|
+
<div className="min-w-0 flex-1">
|
|
269
|
+
<h3 className="font-bold">{step.title}</h3>
|
|
270
|
+
<p className="mt-1 text-foreground/60 text-sm leading-6">{step.body}</p>
|
|
271
|
+
<code className="mt-2 inline-block rounded-full bg-foreground/6 px-3 py-1 font-mono text-foreground/75 text-xs">
|
|
272
|
+
{step.code}
|
|
273
|
+
</code>
|
|
274
|
+
</div>
|
|
275
|
+
{step.href ? (
|
|
276
|
+
<Link
|
|
277
|
+
aria-label={step.title}
|
|
278
|
+
className={jellyButtonRecipe({ variant: "ghost", size: "icon" }, "shrink-0 self-center")}
|
|
279
|
+
href={step.href}
|
|
280
|
+
target={step.href.startsWith("http") ? "_blank" : undefined}
|
|
281
|
+
>
|
|
282
|
+
→
|
|
283
|
+
</Link>
|
|
284
|
+
) : null}
|
|
285
|
+
</li>
|
|
286
|
+
))}
|
|
287
|
+
</ol>
|
|
288
|
+
</section>
|
|
289
|
+
|
|
290
|
+
<footer className="py-10 text-center text-foreground/45 text-sm">
|
|
291
|
+
{l.trans({ en: "Made with Akan.js · Powered by Bun", ko: "Akan.js로 만들고 Bun으로 구동합니다" })}
|
|
292
|
+
</footer>
|
|
293
|
+
</div>
|
|
295
294
|
</main>
|
|
296
295
|
);
|
|
297
296
|
});`,
|
|
@@ -30,10 +30,14 @@ export default rootLayout()
|
|
|
30
30
|
],
|
|
31
31
|
},
|
|
32
32
|
])
|
|
33
|
+
.theme("light")
|
|
33
34
|
.head(
|
|
34
35
|
<>
|
|
35
36
|
<title>${dict.appName}</title>
|
|
36
|
-
<link rel="icon" href="/favicon.ico" />
|
|
37
|
+
<link rel="icon" href="/favicon.ico" sizes="32x32" />
|
|
38
|
+
<link rel="icon" type="image/png" sizes="512x512" href="/logo.png" />
|
|
39
|
+
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
40
|
+
<meta name="theme-color" content="#f1f1ee" />
|
|
37
41
|
</>,
|
|
38
42
|
)
|
|
39
43
|
.render(({ children }) => {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AppInfo, LibInfo } from "akanjs";
|
|
2
|
+
|
|
3
|
+
export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { appName: string }) {
|
|
4
|
+
return {
|
|
5
|
+
filename: "_overrides.tsx",
|
|
6
|
+
content: `import { jellyButtonRecipe } from "@apps/${dict.appName}/ui";
|
|
7
|
+
import { override } from "akanjs/ui";
|
|
8
|
+
|
|
9
|
+
export default override({ recipes: { button: jellyButtonRecipe } });
|
|
10
|
+
`,
|
|
11
|
+
};
|
|
12
|
+
}
|