@akanjs/devkit 3.0.0-alpha.9 → 3.0.0-alpha.90
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/CHANGELOG.md +15 -0
- package/DEV_RUNTIME_KNOBS.md +27 -0
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/agentsIndex.test.ts +10 -0
- package/agentsIndex.ts +21 -1
- package/aiEditor.ts +1 -1
- package/akanApp/BackendImportGraph.test.ts +120 -0
- package/akanApp/BackendImportGraph.ts +167 -0
- package/akanApp/akanApp.host.test.ts +16 -678
- package/akanApp/akanApp.host.ts +113 -578
- package/akanApp/devHostPolicy.test.ts +542 -0
- package/akanApp/devHostPolicy.ts +442 -0
- package/akanApp/index.ts +2 -0
- package/akanConfig/akanConfig.test.ts +182 -14
- package/akanConfig/akanConfig.ts +143 -47
- package/akanConfig/types.ts +9 -0
- package/akanContext.ts +30 -14
- package/akanMcpContract.ts +25 -4
- package/appSelectionMemory.ts +28 -0
- package/applicationBuildRunner.test.ts +1 -1
- package/applicationBuildRunner.ts +45 -21
- package/artifact/implicitRootLayout.test.ts +67 -0
- package/artifact/implicitRootLayout.ts +27 -7
- package/artifact/routeSeedIndex.test.ts +1 -0
- package/biome.base.json +86 -47
- package/biomeBase.ts +6 -5
- package/capacitorApp.ts +10 -0
- package/cloud/globalConfig.ts +9 -2
- package/codegenLock.test.ts +137 -0
- package/codegenLock.ts +136 -0
- package/commandDecorators/argMeta.ts +9 -1
- package/commandDecorators/command.ts +94 -7
- package/commandDecorators/commandBuilder.ts +20 -5
- package/commandDecorators/commandDecorators.test.ts +115 -3
- package/commandDecorators/helpFormatter.ts +4 -0
- package/dependencyScanner.test.ts +99 -0
- package/dependencyScanner.ts +27 -24
- package/devkitUtils.test.ts +0 -104
- package/executors.test.ts +129 -4
- package/executors.ts +105 -24
- package/fileEditor.ts +19 -19
- package/fileSys.ts +19 -1
- package/formSetterScanner.test.ts +80 -0
- package/formSetterScanner.ts +92 -0
- package/frontendBuild/autoImportSync.test.ts +58 -0
- package/frontendBuild/autoImportSync.ts +7 -2
- package/frontendBuild/buildRouteClient.test.ts +47 -24
- package/frontendBuild/clientBuildTypes.ts +4 -0
- package/frontendBuild/clientEntriesBundler.ts +4 -1
- package/frontendBuild/clientEntryDiscovery.ts +2 -2
- package/frontendBuild/csrArtifactBuilder.ts +116 -84
- package/frontendBuild/cssCompiler.ts +123 -12
- package/frontendBuild/cssImportResolver.ts +8 -7
- package/frontendBuild/devGeneratedIndexSync.ts +3 -2
- package/frontendBuild/fontOptimizer.ts +37 -18
- package/frontendBuild/fontPruner.test.ts +220 -0
- package/frontendBuild/fontPruner.ts +206 -0
- package/frontendBuild/frontendBuild.test.ts +178 -10
- package/frontendBuild/hmrWatcher.ts +1 -1
- package/frontendBuild/index.ts +1 -1
- package/frontendBuild/pagesBundleBuilder.ts +3 -3
- package/frontendBuild/pagesEntrySourceGenerator.ts +11 -88
- package/frontendBuild/routeClientBuilder.ts +12 -5
- package/frontendBuild/sourceMtimeIndex.ts +1 -1
- package/frontendBuild/ssrBaseArtifactBuilder.ts +21 -4
- package/frontendBuild/styleContract.ts +14 -20
- package/frontendBuild/themeValidator.ts +22 -17
- package/frontendBuild/vendorSpecifiers.ts +1 -0
- package/frontendBuild/watchRootResolver.test.ts +67 -0
- package/frontendBuild/watchRootResolver.ts +13 -1
- package/incrementalBuilder/devWatchBatch.test.ts +18 -20
- package/incrementalBuilder/devWatchBatch.ts +6 -2
- package/incrementalBuilder/incrementalBuilder.host.ts +51 -7
- package/incrementalBuilder/incrementalBuilder.proc.ts +17 -6
- package/index.ts +0 -7
- package/integration/devStabilityHarness.ts +2 -10
- package/libSource.test.ts +112 -0
- package/libSource.ts +126 -0
- package/lint/__fixtures__/README.md +40 -0
- package/lint/__fixtures__/no-arbitrary-color/bad.tsx +3 -0
- package/lint/__fixtures__/no-arbitrary-color/good.tsx +4 -0
- package/lint/__fixtures__/no-async-component-in-ui/bad.tsx +4 -0
- package/lint/__fixtures__/no-async-component-in-ui/good.tsx +4 -0
- package/lint/__fixtures__/no-bang-comment-in-client/bad.tsx +4 -0
- package/lint/__fixtures__/no-bang-comment-in-client/fixture.json +1 -0
- package/lint/__fixtures__/no-bang-comment-in-client/good.tsx +3 -0
- package/lint/__fixtures__/no-daisyui-legacy-class/bad.tsx +7 -0
- package/lint/__fixtures__/no-daisyui-legacy-class/good.tsx +5 -0
- package/lint/__fixtures__/no-deep-internal-import/bad.tsx +3 -0
- package/lint/__fixtures__/no-deep-internal-import/fixture.json +1 -0
- package/lint/__fixtures__/no-deep-internal-import/good.tsx +3 -0
- package/lint/__fixtures__/no-deprecated-log-level/bad.tsx +4 -0
- package/lint/__fixtures__/no-deprecated-log-level/good.tsx +4 -0
- package/lint/__fixtures__/no-import-client-functions/bad.tsx +2 -0
- package/lint/__fixtures__/no-import-client-functions/good.tsx +4 -0
- package/lint/__fixtures__/no-import-client-in-server/bad.tsx +6 -0
- package/lint/__fixtures__/no-import-client-in-server/good.tsx +5 -0
- package/lint/__fixtures__/no-import-external-library/bad.tsx +4 -0
- package/lint/__fixtures__/no-import-external-library/good.tsx +5 -0
- package/lint/__fixtures__/no-import-server-in-client/bad.tsx +6 -0
- package/lint/__fixtures__/no-import-server-in-client/good.tsx +4 -0
- package/lint/__fixtures__/no-init-fetch-in-client/store-file/bad.ts +1 -0
- package/lint/__fixtures__/no-init-fetch-in-client/store-file/fixture.json +1 -0
- package/lint/__fixtures__/no-init-fetch-in-client/store-file/good.ts +1 -0
- package/lint/__fixtures__/no-init-fetch-in-client/use-client/bad.tsx +4 -0
- package/lint/__fixtures__/no-init-fetch-in-client/use-client/good.tsx +5 -0
- package/lint/__fixtures__/no-inline-color/bad.tsx +3 -0
- package/lint/__fixtures__/no-inline-color/good.tsx +3 -0
- package/lint/__fixtures__/no-interpolated-arbitrary-class/bad.tsx +3 -0
- package/lint/__fixtures__/no-interpolated-arbitrary-class/good.tsx +3 -0
- package/lint/__fixtures__/no-js-private-class-method/bad.tsx +3 -0
- package/lint/__fixtures__/no-js-private-class-method/good.tsx +3 -0
- package/lint/__fixtures__/no-model-type-in-util-zone/bad.tsx +3 -0
- package/lint/__fixtures__/no-model-type-in-util-zone/good.tsx +6 -0
- package/lint/__fixtures__/no-raw-palette-class/bad.tsx +6 -0
- package/lint/__fixtures__/no-raw-palette-class/good.tsx +8 -0
- package/lint/__fixtures__/no-redeclare-predefined-endpoint/bad.ts +10 -0
- package/lint/__fixtures__/no-redeclare-predefined-endpoint/fixture.json +1 -0
- package/lint/__fixtures__/no-redeclare-predefined-endpoint/good.ts +6 -0
- package/lint/__fixtures__/no-return-in-store-action/bad.tsx +2 -0
- package/lint/__fixtures__/no-return-in-store-action/good.tsx +6 -0
- package/lint/__fixtures__/no-throw-raw-error/bad.tsx +4 -0
- package/lint/__fixtures__/no-throw-raw-error/good.tsx +4 -0
- package/lint/__fixtures__/no-unpublished-form-setter/bad.tsx +3 -0
- package/lint/__fixtures__/no-unpublished-form-setter/good.tsx +5 -0
- package/lint/__fixtures__/no-use-client-in-server/bad.tsx +3 -0
- package/lint/__fixtures__/no-use-client-in-server/good.tsx +2 -0
- package/lint/__fixtures__/non-scalar-props-restricted/bad.tsx +5 -0
- package/lint/__fixtures__/non-scalar-props-restricted/good.tsx +4 -0
- package/lint/gritRules.test.ts +178 -0
- package/lint/no-arbitrary-color.grit +1 -1
- package/lint/no-async-component-in-ui.grit +35 -0
- package/lint/no-bang-comment-in-client.grit +23 -10
- package/lint/no-daisyui-legacy-class.grit +26 -9
- package/lint/no-deprecated-log-level.grit +17 -0
- package/lint/no-init-fetch-in-client.grit +47 -0
- package/lint/no-inline-color.grit +10 -8
- package/lint/no-interpolated-arbitrary-class.grit +3 -3
- package/lint/no-model-type-in-util-zone.grit +58 -0
- package/lint/no-raw-palette-class.grit +3 -3
- package/lint/no-unpublished-form-setter.grit +41 -0
- package/lint/non-scalar-props-restricted.grit +16 -7
- package/linter.test.ts +80 -0
- package/linter.ts +99 -25
- package/package.json +5 -8
- package/prompter.ts +9 -4
- package/qualityScanner.test.ts +116 -0
- package/qualityScanner.ts +89 -21
- package/recipeScanner.ts +4 -1
- package/scanInfo.ts +32 -26
- package/semver.test.ts +26 -0
- package/semver.ts +31 -0
- package/slicePlanner.test.ts +182 -0
- package/slicePlanner.ts +235 -0
- package/ssrScanner.test.ts +301 -0
- package/subspace.test.ts +418 -0
- package/subspace.ts +723 -0
- package/subspaceConfig.ts +76 -0
- package/transforms/asyncDefaultExportDetector.ts +103 -0
- package/transforms/barrelImportsPlugin.ts +1 -1
- package/transforms/externalizeFrameworkPlugin.ts +0 -1
- package/transforms/transforms.test.ts +5 -5
- package/transforms/tsconfigPackageResolver.test.ts +230 -0
- package/tsconfig.json +6 -2
- package/typeChecker.ts +1 -1
- package/types.ts +1 -0
- package/uploadRelease.ts +2 -2
- package/workflow/executor.test.ts +146 -0
- package/workspaceLayout.test.ts +56 -4
- package/workspaceLayout.ts +48 -2
- package/builder.ts +0 -164
- package/extractDeps.ts +0 -86
- package/frontendBuild/styleGuard.test.ts +0 -165
- package/frontendBuild/styleGuard.ts +0 -322
- package/getCredentials.ts +0 -19
- package/getModelFileData.ts +0 -62
- package/src/capacitorApp.ts +0 -282
- package/streamAi.ts +0 -45
- package/ui/MultiScrollList.tsx +0 -242
- package/ui/ScrollList.tsx +0 -107
- package/ui/index.ts +0 -2
- package/useStdoutDimensions.ts +0 -20
package/streamAi.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { PromptTemplate } from "@langchain/core/prompts";
|
|
2
|
-
import { RunnableSequence } from "@langchain/core/runnables";
|
|
3
|
-
import { ChatOpenAI } from "@langchain/openai";
|
|
4
|
-
|
|
5
|
-
interface StreamResponse {
|
|
6
|
-
content: string;
|
|
7
|
-
chunk?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const streamAi = async (
|
|
11
|
-
question: string,
|
|
12
|
-
callback: (chunk: string) => void = (chunk) => {
|
|
13
|
-
process.stdout.write(chunk);
|
|
14
|
-
},
|
|
15
|
-
): Promise<StreamResponse> => {
|
|
16
|
-
const createStreamingModel = (apiKey = process.env.DEEPSEEK_API_KEY) => {
|
|
17
|
-
if (!apiKey) throw new Error(`process.env.DEEPSEEK_API_KEY is not set`);
|
|
18
|
-
return new ChatOpenAI({
|
|
19
|
-
modelName: "deepseek-reasoner",
|
|
20
|
-
temperature: 0.7,
|
|
21
|
-
streaming: true, // Enable streaming
|
|
22
|
-
configuration: { baseURL: "https://api.deepseek.com/v1", apiKey },
|
|
23
|
-
});
|
|
24
|
-
};
|
|
25
|
-
const createProcessingChain = () => {
|
|
26
|
-
return RunnableSequence.from([PromptTemplate.fromTemplate(`Answer concisely: {question}`), createStreamingModel()]);
|
|
27
|
-
};
|
|
28
|
-
try {
|
|
29
|
-
const chain = createProcessingChain();
|
|
30
|
-
const stream = await chain.stream({ question });
|
|
31
|
-
|
|
32
|
-
let fullResponse = "";
|
|
33
|
-
for await (const chunk of stream) {
|
|
34
|
-
const content = chunk.content;
|
|
35
|
-
if (typeof content === "string") {
|
|
36
|
-
fullResponse += content;
|
|
37
|
-
callback(content); // Send individual chunks to callback
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return { content: fullResponse };
|
|
42
|
-
} catch (error) {
|
|
43
|
-
throw new Error("Failed to stream response");
|
|
44
|
-
}
|
|
45
|
-
};
|
package/ui/MultiScrollList.tsx
DELETED
|
@@ -1,242 +0,0 @@
|
|
|
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
|
-
};
|
package/ui/ScrollList.tsx
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
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
DELETED
package/useStdoutDimensions.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { useStdout } from "ink";
|
|
3
|
-
import { useEffect, useState } from "react";
|
|
4
|
-
|
|
5
|
-
export const useStdoutDimensions = (): [number, number] => {
|
|
6
|
-
const { stdout } = useStdout();
|
|
7
|
-
const [dimensions, setDimensions] = useState<[number, number]>([stdout.columns, stdout.rows]);
|
|
8
|
-
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
const handler = () => {
|
|
11
|
-
setDimensions([stdout.columns, stdout.rows]);
|
|
12
|
-
};
|
|
13
|
-
stdout.on("resize", handler);
|
|
14
|
-
return () => {
|
|
15
|
-
stdout.off("resize", handler);
|
|
16
|
-
};
|
|
17
|
-
}, [stdout]);
|
|
18
|
-
|
|
19
|
-
return dimensions;
|
|
20
|
-
};
|