@akanjs/devkit 1.0.20 → 2.1.0-rc.1
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/README.ko.md +65 -0
- package/README.md +62 -6
- package/aiEditor.ts +304 -0
- package/akanApp/akanApp.host.ts +393 -0
- package/akanApp/index.ts +1 -0
- package/akanConfig/akanConfig.test.ts +236 -0
- package/akanConfig/akanConfig.ts +384 -0
- package/akanConfig/index.ts +2 -0
- package/akanConfig/types.ts +23 -0
- package/applicationBuildReporter.ts +69 -0
- package/applicationBuildRunner.ts +302 -0
- package/applicationReleasePackager.ts +206 -0
- package/artifact/implicitRootLayout.ts +155 -0
- package/artifact/index.ts +1 -0
- package/artifact/routeSeedIndex.test.ts +98 -0
- package/artifact/routeSeedIndex.ts +130 -0
- package/auth.ts +41 -0
- package/builder.ts +164 -0
- package/capacitor.base.config.ts +88 -0
- package/capacitorApp.ts +440 -0
- package/commandDecorators/argMeta.ts +102 -0
- package/commandDecorators/command.ts +351 -0
- package/commandDecorators/commandBuilder.ts +224 -0
- package/commandDecorators/commandDecorators.test.ts +212 -0
- package/commandDecorators/commandMeta.ts +7 -0
- package/commandDecorators/dependencyBuilder.ts +100 -0
- package/{esm/src/commandDecorators/helpFormatter.js → commandDecorators/helpFormatter.ts} +100 -47
- package/{esm/src/commandDecorators/index.js → commandDecorators/index.ts} +4 -2
- package/commandDecorators/targetMeta.ts +31 -0
- package/commandDecorators/types.ts +10 -0
- package/constants.ts +25 -0
- package/createTunnel.ts +36 -0
- package/dependencyScanner.ts +357 -0
- package/devkitUtils.test.ts +259 -0
- package/executors.test.ts +315 -0
- package/executors.ts +1390 -0
- package/{esm/src/extractDeps.js → extractDeps.ts} +26 -20
- package/{esm/src/fileEditor.js → fileEditor.ts} +51 -32
- package/fileSys.ts +39 -0
- package/frontendBuild/allRoutesBuilder.ts +103 -0
- package/frontendBuild/buildRouteClient.test.ts +190 -0
- package/frontendBuild/clientBuildTypes.ts +114 -0
- package/frontendBuild/clientEntriesBundler.ts +303 -0
- package/frontendBuild/clientEntryDiscovery.ts +199 -0
- package/frontendBuild/csrArtifactBuilder.ts +237 -0
- package/frontendBuild/cssCompiler.ts +286 -0
- package/frontendBuild/cssImportResolver.ts +116 -0
- package/frontendBuild/fontOptimizer.ts +427 -0
- package/frontendBuild/frontendBuild.test.ts +204 -0
- package/frontendBuild/hmrChangeClassifier.ts +28 -0
- package/frontendBuild/hmrWatcher.ts +102 -0
- package/frontendBuild/index.ts +18 -0
- package/frontendBuild/pagesBundleBuilder.ts +137 -0
- package/frontendBuild/pagesEntrySourceGenerator.ts +37 -0
- package/frontendBuild/precompressArtifacts.ts +59 -0
- package/frontendBuild/routeClientBuilder.ts +290 -0
- package/frontendBuild/routesManifestArtifactSerializer.ts +62 -0
- package/frontendBuild/ssrBaseArtifactBuilder.ts +139 -0
- package/frontendBuild/vendorSpecifiers.ts +16 -0
- package/frontendBuild/watchRootResolver.ts +28 -0
- package/getCredentials.ts +19 -0
- package/getDirname.ts +3 -0
- package/getModelFileData.ts +59 -0
- package/getRelatedCnsts.ts +313 -0
- package/guideline.ts +19 -0
- package/incrementalBuilder/incrementalBuilder.host.test.ts +51 -0
- package/incrementalBuilder/incrementalBuilder.host.ts +152 -0
- package/incrementalBuilder/incrementalBuilder.proc.ts +331 -0
- package/incrementalBuilder/index.ts +1 -0
- package/{esm/src/index.js → index.ts} +28 -15
- package/lint/no-deep-internal-import.grit +25 -0
- package/lint/no-import-client-functions.grit +32 -0
- package/lint/no-import-external-library.grit +21 -0
- package/lint/no-js-private-class-method.grit +42 -0
- package/lint/no-use-client-in-server.grit +7 -0
- package/lint/non-scalar-props-restricted.grit +13 -0
- package/linter.ts +271 -0
- package/mobile/index.ts +1 -0
- package/mobile/mobileTarget.test.ts +53 -0
- package/mobile/mobileTarget.ts +88 -0
- package/package.json +48 -31
- package/prompter.ts +72 -0
- package/scanInfo.ts +606 -0
- package/selectModel.ts +11 -0
- package/{esm/src/spinner.js → spinner.ts} +22 -28
- package/{esm/src/capacitorApp.js → src/capacitorApp.ts} +82 -81
- package/sshTunnel.ts +152 -0
- package/{esm/src/streamAi.js → streamAi.ts} +18 -12
- package/transforms/barrelAnalyzer.ts +278 -0
- package/transforms/barrelImportsPlugin.ts +504 -0
- package/transforms/externalizeFrameworkPlugin.ts +185 -0
- package/transforms/index.ts +5 -0
- package/transforms/rscUseClientTransform.ts +59 -0
- package/transforms/transforms.test.ts +208 -0
- package/transforms/useClientBundlePlugin.ts +47 -0
- package/tsconfig.json +37 -0
- package/typeChecker.ts +264 -0
- package/types.ts +44 -0
- package/ui/MultiScrollList.tsx +242 -0
- package/ui/ScrollList.tsx +107 -0
- package/ui/index.ts +2 -0
- package/{esm/src/uploadRelease.js → uploadRelease.ts} +50 -34
- package/{esm/src/useStdoutDimensions.js → useStdoutDimensions.ts} +5 -5
- package/cjs/index.js +0 -21
- package/cjs/src/aiEditor.js +0 -311
- package/cjs/src/auth.js +0 -72
- package/cjs/src/builder.js +0 -114
- package/cjs/src/capacitorApp.js +0 -313
- package/cjs/src/commandDecorators/argMeta.js +0 -88
- package/cjs/src/commandDecorators/command.js +0 -324
- package/cjs/src/commandDecorators/commandMeta.js +0 -30
- package/cjs/src/commandDecorators/helpFormatter.js +0 -211
- package/cjs/src/commandDecorators/index.js +0 -31
- package/cjs/src/commandDecorators/targetMeta.js +0 -57
- package/cjs/src/commandDecorators/types.js +0 -15
- package/cjs/src/constants.js +0 -46
- package/cjs/src/createTunnel.js +0 -49
- package/cjs/src/dependencyScanner.js +0 -220
- package/cjs/src/executors.js +0 -964
- package/cjs/src/extractDeps.js +0 -103
- package/cjs/src/fileEditor.js +0 -120
- package/cjs/src/getCredentials.js +0 -44
- package/cjs/src/getDirname.js +0 -38
- package/cjs/src/getModelFileData.js +0 -66
- package/cjs/src/getRelatedCnsts.js +0 -260
- package/cjs/src/guideline.js +0 -15
- package/cjs/src/index.js +0 -65
- package/cjs/src/linter.js +0 -238
- package/cjs/src/prompter.js +0 -85
- package/cjs/src/scanInfo.js +0 -491
- package/cjs/src/selectModel.js +0 -46
- package/cjs/src/spinner.js +0 -93
- package/cjs/src/streamAi.js +0 -62
- package/cjs/src/typeChecker.js +0 -207
- package/cjs/src/types.js +0 -15
- package/cjs/src/uploadRelease.js +0 -112
- package/cjs/src/useStdoutDimensions.js +0 -43
- package/esm/index.js +0 -1
- package/esm/src/aiEditor.js +0 -282
- package/esm/src/auth.js +0 -42
- package/esm/src/builder.js +0 -81
- package/esm/src/commandDecorators/argMeta.js +0 -54
- package/esm/src/commandDecorators/command.js +0 -290
- package/esm/src/commandDecorators/commandMeta.js +0 -7
- package/esm/src/commandDecorators/targetMeta.js +0 -33
- package/esm/src/commandDecorators/types.js +0 -0
- package/esm/src/constants.js +0 -17
- package/esm/src/createTunnel.js +0 -26
- package/esm/src/dependencyScanner.js +0 -187
- package/esm/src/executors.js +0 -928
- package/esm/src/getCredentials.js +0 -11
- package/esm/src/getDirname.js +0 -5
- package/esm/src/getModelFileData.js +0 -33
- package/esm/src/getRelatedCnsts.js +0 -221
- package/esm/src/guideline.js +0 -0
- package/esm/src/linter.js +0 -205
- package/esm/src/prompter.js +0 -51
- package/esm/src/scanInfo.js +0 -455
- package/esm/src/selectModel.js +0 -13
- package/esm/src/typeChecker.js +0 -174
- package/esm/src/types.js +0 -0
- package/index.d.ts +0 -1
- package/src/aiEditor.d.ts +0 -50
- package/src/auth.d.ts +0 -9
- package/src/builder.d.ts +0 -18
- package/src/capacitorApp.d.ts +0 -39
- package/src/commandDecorators/argMeta.d.ts +0 -67
- package/src/commandDecorators/command.d.ts +0 -2
- package/src/commandDecorators/commandMeta.d.ts +0 -2
- package/src/commandDecorators/helpFormatter.d.ts +0 -3
- package/src/commandDecorators/index.d.ts +0 -6
- package/src/commandDecorators/targetMeta.d.ts +0 -19
- package/src/commandDecorators/types.d.ts +0 -1
- package/src/constants.d.ts +0 -26
- package/src/createTunnel.d.ts +0 -8
- package/src/dependencyScanner.d.ts +0 -23
- package/src/executors.d.ts +0 -296
- package/src/extractDeps.d.ts +0 -7
- package/src/fileEditor.d.ts +0 -16
- package/src/getCredentials.d.ts +0 -12
- package/src/getDirname.d.ts +0 -1
- package/src/getModelFileData.d.ts +0 -16
- package/src/getRelatedCnsts.d.ts +0 -53
- package/src/guideline.d.ts +0 -19
- package/src/index.d.ts +0 -23
- package/src/linter.d.ts +0 -109
- package/src/prompter.d.ts +0 -14
- package/src/scanInfo.d.ts +0 -82
- package/src/selectModel.d.ts +0 -1
- package/src/spinner.d.ts +0 -20
- package/src/streamAi.d.ts +0 -6
- package/src/typeChecker.d.ts +0 -52
- package/src/types.d.ts +0 -31
- package/src/uploadRelease.d.ts +0 -10
- package/src/useStdoutDimensions.d.ts +0 -1
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { useStdoutDimensions } from "../useStdoutDimensions";
|
|
5
|
+
|
|
6
|
+
interface MultiScrollListProps {
|
|
7
|
+
logList: {
|
|
8
|
+
title: string;
|
|
9
|
+
logs: {
|
|
10
|
+
type: string;
|
|
11
|
+
content: string;
|
|
12
|
+
}[];
|
|
13
|
+
color: string;
|
|
14
|
+
}[];
|
|
15
|
+
|
|
16
|
+
maxLength?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param logList 로그 목록
|
|
21
|
+
* @param maxLength 최대 로그 길이 (기본 100)
|
|
22
|
+
*/
|
|
23
|
+
const HEADER_HEIGHT = 1;
|
|
24
|
+
const FOOTER_HEIGHT = 1;
|
|
25
|
+
const OUTER_BORDER_HEIGHT = 2;
|
|
26
|
+
const BORDER_HEIGHT = 2;
|
|
27
|
+
const ANSI_ESCAPE_RE = new RegExp(`${String.fromCharCode(27)}(?:[@-Z\\\\-_]|\\[[\\s\\S]*?[@-~])`, "g");
|
|
28
|
+
|
|
29
|
+
export const MultiScrollList = ({ logList, maxLength = 100 }: MultiScrollListProps) => {
|
|
30
|
+
// 창 너비, 높이
|
|
31
|
+
const [width, height] = useStdoutDimensions();
|
|
32
|
+
// 탭별 로그 렌더링
|
|
33
|
+
|
|
34
|
+
const [focusLog, setFocusLog] = useState<{ type: string; content: string }[]>([]);
|
|
35
|
+
// 탭별 로그 길이 저장 (스크롤시 로그 길이 변화로 위치 조정)
|
|
36
|
+
const [lengthMap, setLengthMap] = useState<Map<number, number>>(new Map());
|
|
37
|
+
// 스크롤 위치
|
|
38
|
+
const [scrollPos, setScrollPos] = useState(0);
|
|
39
|
+
// 현재 포커싱탭 인덱스
|
|
40
|
+
const [tabIndex, setTabIndex] = useState<number>(0);
|
|
41
|
+
// 스크롤 실행 여부
|
|
42
|
+
const [isRunning, setIsRunning] = useState(false);
|
|
43
|
+
// 박스 높이 (보더 사이즈 2, 헤더 사이즈 1, 푸터 사이즈 5 기본 1)
|
|
44
|
+
const [boxHeight, setBoxHeight] = useState(
|
|
45
|
+
height - HEADER_HEIGHT - OUTER_BORDER_HEIGHT - FOOTER_HEIGHT - BORDER_HEIGHT,
|
|
46
|
+
);
|
|
47
|
+
const boxWidth = width - 27;
|
|
48
|
+
|
|
49
|
+
// maxLength에 따라 로그 배열을 제한하는 유틸리티 함수
|
|
50
|
+
const getLimitedLogs = (logs: { type: string; content: string }[]) => {
|
|
51
|
+
const sortedLogs = logs.reduce<{ type: string; content: string }[]>((acc, log) => {
|
|
52
|
+
// log.content.length가 boxWidth보다 큰 경우 잘라서 줄 수 만큼 추가
|
|
53
|
+
// ANSI 코드 보관
|
|
54
|
+
|
|
55
|
+
const content = log.content.replace(ANSI_ESCAPE_RE, "");
|
|
56
|
+
if (content.length > boxWidth) {
|
|
57
|
+
const lines = Math.ceil(content.length / boxWidth);
|
|
58
|
+
for (let i = 0; i < lines; i++) {
|
|
59
|
+
acc.push({ type: log.type, content: content.slice(i * boxWidth, (i + 1) * boxWidth) });
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
acc.push(log);
|
|
63
|
+
}
|
|
64
|
+
return acc;
|
|
65
|
+
}, []);
|
|
66
|
+
return sortedLogs.length > maxLength ? sortedLogs.slice(sortedLogs.length - maxLength) : sortedLogs;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// 입력 이벤트 처리
|
|
70
|
+
useInput((input, key) => {
|
|
71
|
+
// 탭 키 이벤트 처리(포커싱 탭 변경)
|
|
72
|
+
if (key.tab) {
|
|
73
|
+
setTabIndex((prev: number) => (prev + 1) % logList.length);
|
|
74
|
+
setScrollPos(0);
|
|
75
|
+
setIsRunning(false);
|
|
76
|
+
}
|
|
77
|
+
// 스크롤 중지 이벤트 처리 (포커싱까지 완전히 종료)
|
|
78
|
+
if (key.escape) {
|
|
79
|
+
setScrollPos(0);
|
|
80
|
+
setIsRunning(false);
|
|
81
|
+
}
|
|
82
|
+
// 스크롤 중지 이벤트 처리 (포커싱 유지)
|
|
83
|
+
if (input === " ") {
|
|
84
|
+
setScrollPos(0);
|
|
85
|
+
setIsRunning(false);
|
|
86
|
+
}
|
|
87
|
+
// 스크롤 다운 이벤트 처리
|
|
88
|
+
if (key.downArrow && scrollPos > 0) {
|
|
89
|
+
if (key.shift) {
|
|
90
|
+
const newScrollPos = scrollPos - 10;
|
|
91
|
+
// 스크롤 최소 위치에 도달 경우에 대한 예외 처리
|
|
92
|
+
if (newScrollPos < 0) {
|
|
93
|
+
setScrollPos(0);
|
|
94
|
+
} else {
|
|
95
|
+
setScrollPos(newScrollPos);
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
const newScrollPos = scrollPos - 1;
|
|
99
|
+
setScrollPos(newScrollPos);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// 스크롤 업 이벤트 처리
|
|
103
|
+
if (key.upArrow && scrollPos < logList[tabIndex].logs.length - boxHeight) {
|
|
104
|
+
const limitedLogs = getLimitedLogs(logList[tabIndex].logs);
|
|
105
|
+
if (scrollPos < limitedLogs.length - boxHeight) {
|
|
106
|
+
// 스크롤 업 이벤트 처리 (스크롤 속도 증가)
|
|
107
|
+
if (key.shift) {
|
|
108
|
+
const newScrollPos = scrollPos + 10;
|
|
109
|
+
// 스크롤 최대 위치에 도달 경우에 대한 예외 처리
|
|
110
|
+
if (newScrollPos > limitedLogs.length - boxHeight) {
|
|
111
|
+
setScrollPos(limitedLogs.length - boxHeight);
|
|
112
|
+
} else {
|
|
113
|
+
setScrollPos(newScrollPos);
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
setScrollPos(scrollPos + 1);
|
|
117
|
+
}
|
|
118
|
+
if (!isRunning) setIsRunning(true);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
// 공통 로직을 함수로 추출
|
|
125
|
+
const getLogsToRender = (logs: { type: string; content: string }[], index: number) => {
|
|
126
|
+
// maxLength에 따라 로그 제한
|
|
127
|
+
const limitedLogs = getLimitedLogs(logs);
|
|
128
|
+
// 로그 중 boxWidth보다 긴 것이 있는 경우 잘라서 출력
|
|
129
|
+
|
|
130
|
+
// 활성 탭이고 스크롤 중인 특별한 경우
|
|
131
|
+
if (scrollPos > 0 && tabIndex === index) {
|
|
132
|
+
return limitedLogs.slice(limitedLogs.length - boxHeight - scrollPos, limitedLogs.length - scrollPos);
|
|
133
|
+
}
|
|
134
|
+
// 로그가 표시 영역보다 큰 경우 (공통 로직)
|
|
135
|
+
else if (limitedLogs.length > boxHeight) {
|
|
136
|
+
return limitedLogs.slice(limitedLogs.length - boxHeight, limitedLogs.length);
|
|
137
|
+
}
|
|
138
|
+
// 로그가 표시 영역에 모두 들어가는 경우 (공통 로직)
|
|
139
|
+
else {
|
|
140
|
+
return limitedLogs;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// isRunning일 때 로직
|
|
145
|
+
if (isRunning) {
|
|
146
|
+
// 선택된 탭의 로그 길이 변화 확인 및 스크롤 포지션 업데이트
|
|
147
|
+
if (lengthMap.has(tabIndex)) {
|
|
148
|
+
const tabLength = lengthMap.get(tabIndex);
|
|
149
|
+
const limitedLogsLength = Math.min(logList[tabIndex].logs.length, maxLength);
|
|
150
|
+
if (tabLength && tabLength < limitedLogsLength) {
|
|
151
|
+
setScrollPos(scrollPos + 1);
|
|
152
|
+
lengthMap.set(tabIndex, limitedLogsLength);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 스크롤 위치에 따른 로그 렌더링 업데이트
|
|
157
|
+
|
|
158
|
+
setFocusLog(getLogsToRender(logList[tabIndex].logs, tabIndex));
|
|
159
|
+
}
|
|
160
|
+
// isRunning이 아닐 때 로직
|
|
161
|
+
else {
|
|
162
|
+
// lengthMap 업데이트 및 초기 렌더링
|
|
163
|
+
setFocusLog(getLogsToRender(logList[tabIndex].logs, tabIndex));
|
|
164
|
+
}
|
|
165
|
+
}, [logList, isRunning, scrollPos, tabIndex, boxHeight, maxLength]);
|
|
166
|
+
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
setBoxHeight(height - HEADER_HEIGHT - OUTER_BORDER_HEIGHT - FOOTER_HEIGHT - BORDER_HEIGHT);
|
|
169
|
+
}, [height]);
|
|
170
|
+
|
|
171
|
+
// 초기 로그 사이즈 설정
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
setLengthMap(new Map(logList.map((log, index) => [index, Math.min(log.logs.length, maxLength)])));
|
|
174
|
+
}, [logList, maxLength]);
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<Box width={width} height={height} borderStyle="round" borderColor="blackBright" flexDirection="column">
|
|
178
|
+
<Box width={"100%"} height={boxHeight + BORDER_HEIGHT + HEADER_HEIGHT} flexDirection="row">
|
|
179
|
+
<Box width={30} height="100%" flexDirection="column">
|
|
180
|
+
<Box>
|
|
181
|
+
<Text>
|
|
182
|
+
List {tabIndex + 1}/{logList.length}
|
|
183
|
+
</Text>
|
|
184
|
+
</Box>
|
|
185
|
+
<Box borderStyle="round" borderColor="blackBright" width={"100%"} height="100%" flexDirection="column">
|
|
186
|
+
{logList.map((log, index) => {
|
|
187
|
+
return (
|
|
188
|
+
<Text key={index} color={index === tabIndex ? "green" : "white"}>
|
|
189
|
+
<Text>●</Text>
|
|
190
|
+
{log.title.length > 25 ? `${log.title.slice(0, 25)}...` : log.title}
|
|
191
|
+
</Text>
|
|
192
|
+
);
|
|
193
|
+
})}
|
|
194
|
+
</Box>
|
|
195
|
+
</Box>
|
|
196
|
+
<Box width={"100%"} height="100%" flexDirection="column">
|
|
197
|
+
<Box height={1}>
|
|
198
|
+
<Text color={logList[tabIndex].color}>
|
|
199
|
+
{logList[tabIndex].title} {logList[tabIndex].logs.length}
|
|
200
|
+
</Text>
|
|
201
|
+
</Box>
|
|
202
|
+
<Box
|
|
203
|
+
borderStyle={isRunning ? "double" : "round"}
|
|
204
|
+
flexDirection="column"
|
|
205
|
+
borderColor={logList[tabIndex].color}
|
|
206
|
+
width="100%"
|
|
207
|
+
height="100%"
|
|
208
|
+
>
|
|
209
|
+
{scrollPos > 0 ? (
|
|
210
|
+
<>
|
|
211
|
+
{focusLog.slice(0, focusLog.length - 1).map((log, index) => {
|
|
212
|
+
return (
|
|
213
|
+
<Text underline={false} color={log.type === "error" ? "red" : "white"} key={index}>
|
|
214
|
+
{log.content}
|
|
215
|
+
</Text>
|
|
216
|
+
);
|
|
217
|
+
})}
|
|
218
|
+
<Text underline={false} backgroundColor="green">
|
|
219
|
+
Scrolling... +{scrollPos}
|
|
220
|
+
</Text>
|
|
221
|
+
</>
|
|
222
|
+
) : (
|
|
223
|
+
focusLog.map((log, index) => {
|
|
224
|
+
return (
|
|
225
|
+
<Text underline={false} color={log.type === "error" ? "red" : "white"} key={index}>
|
|
226
|
+
{log.content}
|
|
227
|
+
</Text>
|
|
228
|
+
);
|
|
229
|
+
})
|
|
230
|
+
)}
|
|
231
|
+
</Box>
|
|
232
|
+
</Box>
|
|
233
|
+
</Box>
|
|
234
|
+
{/* <Text>{renderMultiLogs[tabIndex][0]}</Text> */}
|
|
235
|
+
<Box width={"100%"} height={FOOTER_HEIGHT}>
|
|
236
|
+
<Text dimColor={true}>
|
|
237
|
+
Tab : switch tab | Up / Down(shift : x10) : scroll | Space : back last position | Esc : stop scrolling
|
|
238
|
+
</Text>
|
|
239
|
+
</Box>
|
|
240
|
+
</Box>
|
|
241
|
+
);
|
|
242
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Box, type BoxProps, Newline, Text, useInput } from "ink";
|
|
3
|
+
import { type ReactNode, useEffect, useState } from "react";
|
|
4
|
+
import { useStdoutDimensions } from "../useStdoutDimensions";
|
|
5
|
+
|
|
6
|
+
interface ScrollListProps extends BoxProps {
|
|
7
|
+
list: ReactNode[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const ScrollList = ({ list, ...props }: ScrollListProps) => {
|
|
11
|
+
const [renderLogs, setRenderLogs] = useState<ReactNode[]>(list);
|
|
12
|
+
const [width, height] = useStdoutDimensions();
|
|
13
|
+
const [scrollPos, setScrollPos] = useState(0);
|
|
14
|
+
const [isRunning, setIsRunning] = useState(false);
|
|
15
|
+
const [boxHeight, setBoxHeight] = useState(height - 3);
|
|
16
|
+
|
|
17
|
+
useInput((input, key) => {
|
|
18
|
+
if (key.escape) {
|
|
19
|
+
setIsRunning(false);
|
|
20
|
+
setScrollPos(0);
|
|
21
|
+
}
|
|
22
|
+
if (input === " " && isRunning) {
|
|
23
|
+
setIsRunning(false);
|
|
24
|
+
setScrollPos(0);
|
|
25
|
+
}
|
|
26
|
+
if (key.downArrow && scrollPos > 0) {
|
|
27
|
+
if (key.shift) {
|
|
28
|
+
setScrollPos(scrollPos - 10);
|
|
29
|
+
} else {
|
|
30
|
+
setScrollPos(scrollPos - 1);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (key.upArrow && scrollPos < list.length - boxHeight) {
|
|
34
|
+
if (key.shift) {
|
|
35
|
+
setScrollPos(scrollPos + 10);
|
|
36
|
+
} else {
|
|
37
|
+
setScrollPos(scrollPos + 1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
//1 로그가 박스 높이보다 크면 스크롤 위치 조정
|
|
44
|
+
//2. scrollPos값에 따라 포커싱 위치 변경하고 pos 활성시 포커싱 위치 고정
|
|
45
|
+
if (isRunning) {
|
|
46
|
+
setScrollPos(scrollPos + 1);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (list.length > boxHeight) {
|
|
50
|
+
setRenderLogs(list.slice(list.length - boxHeight, list.length));
|
|
51
|
+
} else {
|
|
52
|
+
setRenderLogs(list);
|
|
53
|
+
}
|
|
54
|
+
}, [list, isRunning]);
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
setBoxHeight(Math.floor(height * 0.9));
|
|
58
|
+
}, [height]);
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (scrollPos > 0) {
|
|
62
|
+
setRenderLogs(list.slice(list.length - boxHeight - scrollPos, list.length - scrollPos));
|
|
63
|
+
setIsRunning(true);
|
|
64
|
+
} else {
|
|
65
|
+
setRenderLogs(list.slice(list.length - boxHeight, list.length));
|
|
66
|
+
setIsRunning(false);
|
|
67
|
+
}
|
|
68
|
+
}, [scrollPos]);
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<Box {...props} width={width} height={"100%"} flexDirection="column">
|
|
72
|
+
<Box borderStyle="round" width={width} height={height - 3}>
|
|
73
|
+
<Newline />
|
|
74
|
+
<Text>
|
|
75
|
+
{isRunning ? (
|
|
76
|
+
<>
|
|
77
|
+
{renderLogs.slice(0, renderLogs.length - 1).map((log, index) => (
|
|
78
|
+
<>
|
|
79
|
+
<Text key={index}>{log}</Text>
|
|
80
|
+
<Newline />
|
|
81
|
+
</>
|
|
82
|
+
))}
|
|
83
|
+
<Text backgroundColor="green">scrolling... + {scrollPos}</Text>
|
|
84
|
+
</>
|
|
85
|
+
) : (
|
|
86
|
+
<>
|
|
87
|
+
{renderLogs.map((log, index) => (
|
|
88
|
+
<>
|
|
89
|
+
<Text key={index}>{log}</Text>
|
|
90
|
+
<Newline />
|
|
91
|
+
</>
|
|
92
|
+
))}
|
|
93
|
+
</>
|
|
94
|
+
)}
|
|
95
|
+
</Text>
|
|
96
|
+
</Box>
|
|
97
|
+
<Box>
|
|
98
|
+
<Text dimColor={true}>
|
|
99
|
+
You can use the following shortcuts:
|
|
100
|
+
<Newline />* <Text backgroundColor="green">up</Text> and <Text backgroundColor="green">down</Text> to scroll.{" "}
|
|
101
|
+
<Text backgroundColor="green">shift</Text> to scroll faster.
|
|
102
|
+
<Newline />* <Text backgroundColor="green">escape</Text> to stop scrolling.
|
|
103
|
+
</Text>
|
|
104
|
+
</Box>
|
|
105
|
+
</Box>
|
|
106
|
+
);
|
|
107
|
+
};
|
package/ui/index.ts
ADDED
|
@@ -1,71 +1,90 @@
|
|
|
1
|
-
import { Logger } from "
|
|
2
|
-
|
|
3
|
-
import FormData from "form-data";
|
|
4
|
-
import fs from "fs";
|
|
1
|
+
import { HttpClient, Logger } from "akanjs/common";
|
|
2
|
+
|
|
5
3
|
import { Spinner } from "./spinner";
|
|
6
|
-
|
|
4
|
+
|
|
5
|
+
const spinning = (message: string) => {
|
|
7
6
|
const spinner = new Spinner(message, { prefix: message, enableSpin: true }).start();
|
|
8
7
|
return spinner;
|
|
9
8
|
};
|
|
10
|
-
const uploadRelease = async (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
export const uploadRelease = async (
|
|
10
|
+
appName: string,
|
|
11
|
+
{
|
|
12
|
+
workspaceRoot,
|
|
13
|
+
environment,
|
|
14
|
+
buildNum,
|
|
15
|
+
platformVersion,
|
|
16
|
+
os,
|
|
17
|
+
local,
|
|
18
|
+
}: {
|
|
19
|
+
workspaceRoot: string;
|
|
20
|
+
environment: string;
|
|
21
|
+
buildNum: number;
|
|
22
|
+
platformVersion?: string;
|
|
23
|
+
os?: "android" | "ios";
|
|
24
|
+
local?: boolean;
|
|
25
|
+
},
|
|
26
|
+
) => {
|
|
18
27
|
const logger = new Logger("uploadRelease");
|
|
19
|
-
const basePath = local ? "http://localhost:
|
|
28
|
+
const basePath = local ? "http://localhost:8282/backend" : "https://cloud.akanjs.com/backend";
|
|
29
|
+
const httpClient = new HttpClient(basePath);
|
|
20
30
|
const buildPath = `${workspaceRoot}/releases/builds/${appName}-release.tar.gz`;
|
|
21
31
|
const appBuildPath = `${workspaceRoot}/releases/builds/${appName}-appBuild.zip`;
|
|
22
32
|
const sourcePath = `${workspaceRoot}/releases/sources/${appName}-source.tar.gz`;
|
|
33
|
+
|
|
23
34
|
const readingFilesSpinner = spinning("Reading files...");
|
|
24
35
|
try {
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const buildStat =
|
|
29
|
-
const sourceStat =
|
|
30
|
-
const appBuildStat =
|
|
36
|
+
const buildFile = Bun.file(buildPath);
|
|
37
|
+
const sourceFile = Bun.file(sourcePath);
|
|
38
|
+
const appBuildFile = Bun.file(appBuildPath);
|
|
39
|
+
const buildStat = { mtime: new Date(buildFile.lastModified), size: buildFile.size };
|
|
40
|
+
const sourceStat = { mtime: new Date(sourceFile.lastModified), size: sourceFile.size };
|
|
41
|
+
const appBuildStat = { mtime: new Date(appBuildFile.lastModified), size: appBuildFile.size };
|
|
31
42
|
readingFilesSpinner.succeed("Reading files... done");
|
|
43
|
+
|
|
32
44
|
const preparingFormSpinner = spinning("Preparing form data...");
|
|
33
45
|
const formData = new FormData();
|
|
34
|
-
formData.append("files",
|
|
35
|
-
formData.append("files",
|
|
36
|
-
formData.append("files",
|
|
46
|
+
formData.append("files", buildFile, `${appName}-release.tar.gz`);
|
|
47
|
+
formData.append("files", sourceFile, `${appName}-source.tar.gz`);
|
|
48
|
+
formData.append("files", appBuildFile, `${appName}-appBuild.zip`);
|
|
37
49
|
formData.append(
|
|
38
50
|
"metas",
|
|
39
51
|
JSON.stringify([
|
|
40
52
|
{ lastModifiedAt: buildStat.mtime, size: buildStat.size },
|
|
41
53
|
{ lastModifiedAt: sourceStat.mtime, size: sourceStat.size },
|
|
42
|
-
{ lastModifiedAt: appBuildStat.mtime, size: appBuildStat.size }
|
|
43
|
-
])
|
|
54
|
+
{ lastModifiedAt: appBuildStat.mtime, size: appBuildStat.size },
|
|
55
|
+
]),
|
|
44
56
|
);
|
|
45
57
|
formData.append("type", "release");
|
|
46
58
|
preparingFormSpinner.succeed("Preparing form data... done");
|
|
59
|
+
|
|
47
60
|
try {
|
|
48
61
|
const uploadingFilesSpinner = spinning("Uploading files to server...");
|
|
49
|
-
const [buildFile, sourceFile, appBuildFile] =
|
|
62
|
+
const [buildFile, sourceFile, appBuildFile] = await httpClient.post<
|
|
63
|
+
[{ id: string }, { id: string }, { id: string }]
|
|
64
|
+
>("/file/addFilesRestApi", formData);
|
|
50
65
|
uploadingFilesSpinner.succeed("Uploading files to server... done");
|
|
66
|
+
|
|
51
67
|
const fetchingAppSpinner = spinning(`Fetching dev app information for ${appName}...`);
|
|
52
68
|
const major = platformVersion ? parseInt(platformVersion.split(".")[0]) : 1;
|
|
53
69
|
const minor = platformVersion ? parseInt(platformVersion.split(".")[1]) : 0;
|
|
54
70
|
const patch = platformVersion ? parseInt(platformVersion.split(".")[2]) : 0;
|
|
55
|
-
|
|
71
|
+
|
|
72
|
+
const devApp = await httpClient.get<{ id: string }>(`/devApp/devAppInName/${appName}`);
|
|
56
73
|
fetchingAppSpinner.succeed(`Fetching dev app information for ${appName}... done`);
|
|
74
|
+
|
|
57
75
|
const pushingReleaseSpinner = spinning(`Pushing release to ${environment} environment...`);
|
|
58
|
-
const release =
|
|
59
|
-
|
|
60
|
-
)
|
|
76
|
+
const release = await httpClient.post<{ id: string }>(
|
|
77
|
+
`/release/pushRelease/${devApp.id}/${environment}/${major}/${minor}/${patch}/${sourceFile.id}/${buildFile.id}/${appBuildFile.id}${os ? `/${os}` : ""}`,
|
|
78
|
+
);
|
|
61
79
|
pushingReleaseSpinner.succeed(`Pushing release to ${environment} environment... done`);
|
|
62
80
|
new Spinner(`Successfully pushed release to ${appName}-${environment} server. `, {
|
|
63
81
|
prefix: `Successfully pushed release to ${appName}-${environment} server. `,
|
|
64
|
-
enableSpin: false
|
|
82
|
+
enableSpin: false,
|
|
65
83
|
}).succeed(`Successfully pushed release to ${appName}-${environment} server. `);
|
|
66
84
|
return release;
|
|
67
85
|
} catch (e) {
|
|
68
86
|
const errorMessage = e instanceof Error ? e.message : "Unknown error";
|
|
87
|
+
logger.error(`Upload release failed: ${errorMessage}`);
|
|
69
88
|
return null;
|
|
70
89
|
}
|
|
71
90
|
} catch (e) {
|
|
@@ -74,6 +93,3 @@ const uploadRelease = async (appName, {
|
|
|
74
93
|
return null;
|
|
75
94
|
}
|
|
76
95
|
};
|
|
77
|
-
export {
|
|
78
|
-
uploadRelease
|
|
79
|
-
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useStdout } from "ink";
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
export const useStdoutDimensions = (): [number, number] => {
|
|
5
6
|
const { stdout } = useStdout();
|
|
6
|
-
const [dimensions, setDimensions] = useState([stdout.columns, stdout.rows]);
|
|
7
|
+
const [dimensions, setDimensions] = useState<[number, number]>([stdout.columns, stdout.rows]);
|
|
8
|
+
|
|
7
9
|
useEffect(() => {
|
|
8
10
|
const handler = () => {
|
|
9
11
|
setDimensions([stdout.columns, stdout.rows]);
|
|
@@ -13,8 +15,6 @@ const useStdoutDimensions = () => {
|
|
|
13
15
|
stdout.off("resize", handler);
|
|
14
16
|
};
|
|
15
17
|
}, [stdout]);
|
|
18
|
+
|
|
16
19
|
return dimensions;
|
|
17
20
|
};
|
|
18
|
-
export {
|
|
19
|
-
useStdoutDimensions
|
|
20
|
-
};
|
package/cjs/index.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
var __copyProps = (to, from, except, desc) => {
|
|
6
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
7
|
-
for (let key of __getOwnPropNames(from))
|
|
8
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
9
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
10
|
-
}
|
|
11
|
-
return to;
|
|
12
|
-
};
|
|
13
|
-
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
14
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
-
var devkit_exports = {};
|
|
16
|
-
module.exports = __toCommonJS(devkit_exports);
|
|
17
|
-
__reExport(devkit_exports, require("./src"), module.exports);
|
|
18
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
19
|
-
0 && (module.exports = {
|
|
20
|
-
...require("./src")
|
|
21
|
-
});
|