@rhseung/ps-cli 1.8.0 → 1.9.0

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.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # ps-cli
2
2
 
3
- 백준 문제 해결을 위한 CLI 도구입니다.
3
+ **대화형** 인터페이스와 **사용자 친화적인 디자인**을 갖춘 백준 문제 해결 CLI 도구입니다.
4
+ 검색부터 제출까지, 모든 과정을 직관적이고 아름다운 터미널 환경에서 해결하세요.
5
+
6
+ Nerd Font 사용을 권장합니다.
4
7
 
5
8
  ## 설치
6
9
 
@@ -13,6 +16,9 @@ bun install -g @rhseung/ps-cli
13
16
  ## 빠른 시작
14
17
 
15
18
  ```bash
19
+ # 0. 도움말 보기
20
+ ps --help
21
+
16
22
  # 1. 프로젝트 초기화
17
23
  ps init
18
24
 
@@ -60,6 +66,7 @@ ps fetch <문제번호> [옵션]
60
66
 
61
67
  **옵션:**
62
68
 
69
+ - `--help`, `-h`: 도움말 표시
63
70
  - `--language`, `-l`: 언어 선택 (python, javascript, typescript, cpp)
64
71
  - 기본값: python 또는 설정 파일의 `default-language`
65
72
 
@@ -92,6 +99,7 @@ ps test [문제번호] [옵션]
92
99
 
93
100
  **옵션:**
94
101
 
102
+ - `--help`, `-h`: 도움말 표시
95
103
  - `--language`, `-l`: 언어 선택 (지정 시 자동 감지 무시)
96
104
  - 지원 언어: python, javascript, typescript, cpp
97
105
  - `--watch`, `-w`: watch 모드 (파일 변경 시 자동 재테스트)
@@ -128,6 +136,7 @@ ps run [문제번호] [옵션]
128
136
 
129
137
  **옵션:**
130
138
 
139
+ - `--help`, `-h`: 도움말 표시
131
140
  - `--language`, `-l`: 언어 선택 (지정 시 자동 감지 무시)
132
141
  - 지원 언어: python, javascript, typescript, cpp
133
142
  - `--input`, `-i`: 입력 파일 지정
@@ -166,6 +175,7 @@ ps submit [문제번호] [옵션]
166
175
 
167
176
  **옵션:**
168
177
 
178
+ - `--help`, `-h`: 도움말 표시
169
179
  - `--language`, `-l`: 언어 선택 (지정 시 자동 감지 무시)
170
180
  - 지원 언어: python, javascript, typescript, cpp
171
181
 
@@ -213,35 +223,40 @@ ps archive # 현재 디렉토리에서 문제 번호 자동
213
223
 
214
224
  ---
215
225
 
216
- ### `open` - 문제 페이지 열기
226
+ ### `open` - 문제 페이지 또는 에디터 열기
217
227
 
218
- 백준 문제 페이지 또는 문제집 페이지를 브라우저로 엽니다.
228
+ 백준 문제 페이지 또는 에디터를 엽니다.
219
229
 
220
230
  **사용법:**
221
231
 
222
232
  ```bash
223
- ps open [문제번호]
233
+ ps open [문제번호] [옵션]
224
234
  ps open --workbook <문제집ID>
225
235
  ps open -w <문제집ID>
226
236
  ```
227
237
 
228
238
  **옵션:**
229
239
 
240
+ - `--help`, `-h`: 도움말 표시
230
241
  - `--workbook`, `-w`: 문제집 ID를 지정하여 해당 문제집 페이지를 엽니다
242
+ - `--editor`, `-e`: 문제 파일을 에디터로 엽니다
231
243
 
232
244
  **예제:**
233
245
 
234
246
  ```bash
235
- ps open 1000 # 1000번 문제 열기
236
- ps open # 문제 디렉토리에서 실행 시 자동 추론
247
+ ps open 1000 # 1000번 문제 브라우저로 열기
248
+ ps open # 현재 문제 브라우저로 열기
249
+ ps open -e # 현재 문제 에디터로 열기
250
+ ps open 1000 -e # 1000번 문제 에디터로 열기
237
251
  ps open --workbook 25052 # 문제집 25052 열기
238
- ps open -w 25052 # 문제집 25052 열기 (단축 옵션)
239
252
  ```
240
253
 
241
254
  **설명:**
242
255
 
243
256
  - 문제 번호를 인자로 전달하거나 문제 디렉토리에서 실행하면 자동으로 문제 번호를 추론
244
257
  - `--workbook` 또는 `-w` 옵션으로 문제집 페이지를 열 수 있습니다
258
+ - `--editor` 또는 `-e` 옵션으로 문제의 솔루션 파일이나 디렉토리를 에디터로 열 수 있습니다
259
+ - 에디터는 `ps config set editor <명령어>`로 설정할 수 있습니다 (기본값: `code`)
245
260
 
246
261
  ---
247
262
 
@@ -258,6 +273,7 @@ ps search --workbook <문제집ID>
258
273
 
259
274
  **옵션:**
260
275
 
276
+ - `--help`, `-h`: 도움말 표시
261
277
  - `--workbook`: 문제집 ID를 지정하여 해당 문제집의 문제 목록을 표시
262
278
 
263
279
  **예제:**
@@ -274,7 +290,15 @@ ps search --workbook 25052 # 문제집 25052의 문제 목록 표시
274
290
  **설명:**
275
291
 
276
292
  - solved.ac 검색어 문법을 지원합니다
277
- - 문제 목록에서 선택하면 자동으로 브라우저에서 문제 페이지를 엽니다
293
+ - 문제 목록에서 해결 상태를 아이콘으로 확인할 있습니다
294
+ - **해결됨(``)**: 이미 해결되어 아카이브된 문제
295
+ - **해결 중(``)**: 현재 해결 중인 문제
296
+ - 문제 선택 시 인터랙티브 메뉴를 통해 다양한 작업을 수행할 수 있습니다
297
+ - `󰖟 브라우저에서 열기 (open)`
298
+ - `󰇚 문제 가져오기 (fetch)`
299
+ - ` 로컬 테스트 실행 (test)` (해결 중/완료인 경우)
300
+ - `󰭹 코드 제출하기 (submit)` (해결 중/완료인 경우)
301
+ - ` 문제 아카이브 (archive)` (해결 중인 경우)
278
302
  - 페이지네이션을 통해 여러 페이지의 결과를 탐색할 수 있습니다
279
303
  - `--workbook` 옵션으로 백준 문제집의 문제 목록을 볼 수 있습니다
280
304
 
@@ -292,6 +316,7 @@ ps stats [핸들] [옵션]
292
316
 
293
317
  **옵션:**
294
318
 
319
+ - `--help`, `-h`: 도움말 표시
295
320
  - `--handle`, `-h`: Solved.ac 핸들
296
321
  - 설정에 저장된 값 사용 가능
297
322
  - 인자로 전달하거나 플래그로 지정 가능
@@ -319,7 +344,7 @@ ps stats # 설정에 저장된 핸들 사용
319
344
  **사용법:**
320
345
 
321
346
  ```bash
322
- ps config <명령어> [키] [값]
347
+ ps config <명령어> [키] [값] [옵션]
323
348
  ```
324
349
 
325
350
  **명령어:**
@@ -329,6 +354,10 @@ ps config <명령어> [키] [값]
329
354
  - `list`: 모든 설정 조회
330
355
  - `clear`: .ps-cli.json 파일 삭제
331
356
 
357
+ **옵션:**
358
+
359
+ - `--help`, `-h`: 도움말 표시
360
+
332
361
  **예제:**
333
362
 
334
363
  ```bash
@@ -354,6 +383,7 @@ ps config clear # .ps-cli.json 파일 삭제
354
383
  - `default-language`: 기본 언어 (python, javascript, typescript, cpp)
355
384
  - `editor`: 에디터 명령어 (code, cursor, vim 등)
356
385
  - `auto-open-editor`: fetch 후 자동으로 에디터 열기 (true/false)
386
+ - `include-tag`: README에 알고리즘 분류(태그) 포함 여부 (true/false, 기본값: true)
357
387
  - `solved-ac-handle`: Solved.ac 핸들
358
388
  - `archive-dir`: 아카이브된 문제 디렉토리 (기본값: problems)
359
389
  - `solving-dir`: 푸는 중인 문제 디렉토리 (기본값: solving)
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ Command,
4
+ CommandBuilder,
5
+ CommandDef,
6
+ __decorateClass,
7
+ findProjectRoot,
8
+ getArchiveAutoCommit,
9
+ getArchiveCommitMessage,
10
+ getArchiveDirPath,
11
+ getSolvingDir,
12
+ getSolvingDirPath,
13
+ logger,
14
+ resolveProblemContext
15
+ } from "./chunk-JZK2464U.js";
16
+
17
+ // src/commands/archive.tsx
18
+ import { StatusMessage, Alert, Spinner } from "@inkjs/ui";
19
+ import { Box } from "ink";
20
+
21
+ // src/hooks/use-archive.ts
22
+ import { access, readFile, rename, mkdir, readdir, rmdir } from "fs/promises";
23
+ import { join, dirname } from "path";
24
+ import { execa } from "execa";
25
+ import { useEffect, useState } from "react";
26
+ function useArchive({
27
+ problemId,
28
+ onComplete
29
+ }) {
30
+ const [status, setStatus] = useState(
31
+ "loading"
32
+ );
33
+ const [message, setMessage] = useState("\uBB38\uC81C\uB97C \uC544\uCE74\uC774\uBE0C\uD558\uB294 \uC911...");
34
+ const [error, setError] = useState(null);
35
+ useEffect(() => {
36
+ async function archive() {
37
+ try {
38
+ const projectRoot = findProjectRoot();
39
+ if (!projectRoot) {
40
+ throw new Error("\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
41
+ }
42
+ const solvingDir = getSolvingDirPath(problemId, projectRoot);
43
+ setMessage("solving \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uBB38\uC81C\uB97C \uD655\uC778\uD558\uB294 \uC911...");
44
+ try {
45
+ await access(solvingDir);
46
+ } catch {
47
+ throw new Error(
48
+ `solving \uB514\uB809\uD1A0\uB9AC\uC5D0 \uBB38\uC81C ${problemId}\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
49
+ );
50
+ }
51
+ setMessage("\uBB38\uC81C \uC815\uBCF4\uB97C \uC77D\uB294 \uC911...");
52
+ const metaPath = join(solvingDir, "meta.json");
53
+ let problemTitle = `\uBB38\uC81C ${problemId}`;
54
+ let problem;
55
+ try {
56
+ const metaContent = await readFile(metaPath, "utf-8");
57
+ const meta = JSON.parse(metaContent);
58
+ if (meta.title) {
59
+ problemTitle = meta.title;
60
+ }
61
+ if (meta.id && meta.level !== void 0) {
62
+ problem = {
63
+ id: meta.id,
64
+ title: meta.title || `\uBB38\uC81C ${meta.id}`,
65
+ level: meta.level,
66
+ tier: "",
67
+ // tier는 level에서 계산되므로 빈 문자열로 충분
68
+ tags: meta.tags || [],
69
+ testCases: []
70
+ };
71
+ }
72
+ } catch {
73
+ }
74
+ const archiveDir = getArchiveDirPath(problemId, projectRoot, problem);
75
+ try {
76
+ await access(archiveDir);
77
+ throw new Error(
78
+ `archive \uB514\uB809\uD1A0\uB9AC\uC5D0 \uC774\uBBF8 \uBB38\uC81C ${problemId}\uAC00 \uC874\uC7AC\uD569\uB2C8\uB2E4.`
79
+ );
80
+ } catch (err) {
81
+ if (err instanceof Error && err.message.includes("\uC774\uBBF8")) {
82
+ throw err;
83
+ }
84
+ }
85
+ const autoCommit = getArchiveAutoCommit();
86
+ const template = getArchiveCommitMessage() ?? "solve: {id} - {title}";
87
+ const commitMessage = template.replace("{id}", String(problemId)).replace("{title}", problemTitle);
88
+ const archiveDirParent = dirname(archiveDir);
89
+ setMessage("\uC544\uCE74\uC774\uBE0C \uB514\uB809\uD1A0\uB9AC\uB97C \uC900\uBE44\uD558\uB294 \uC911...");
90
+ await mkdir(archiveDirParent, { recursive: true });
91
+ setMessage("\uBB38\uC81C\uB97C archive \uB514\uB809\uD1A0\uB9AC\uB85C \uC774\uB3D9\uD558\uB294 \uC911...");
92
+ await rename(solvingDir, archiveDir);
93
+ if (autoCommit) {
94
+ try {
95
+ setMessage("Git \uCEE4\uBC0B\uC744 \uC2E4\uD589\uD558\uB294 \uC911...");
96
+ await execa("git", ["add", archiveDir], { cwd: projectRoot });
97
+ await execa("git", ["commit", "-m", commitMessage], {
98
+ cwd: projectRoot
99
+ });
100
+ } catch (gitError) {
101
+ setMessage("\uCEE4\uBC0B \uC2E4\uD328\uB85C \uC778\uD574 \uB864\uBC31\uD558\uB294 \uC911...");
102
+ try {
103
+ await rename(archiveDir, solvingDir);
104
+ } catch (rollbackError) {
105
+ throw new Error(
106
+ `Git \uCEE4\uBC0B \uC2E4\uD328 \uBC0F \uB864\uBC31 \uC2E4\uD328: ${gitError instanceof Error ? gitError.message : String(gitError)}. \uB864\uBC31 \uC5D0\uB7EC: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}. \uC218\uB3D9\uC73C\uB85C \uD30C\uC77C\uC744 \uD655\uC778\uD574\uC8FC\uC138\uC694.`
107
+ );
108
+ }
109
+ throw gitError;
110
+ }
111
+ }
112
+ setMessage("\uBE48 \uB514\uB809\uD1A0\uB9AC \uC815\uB9AC \uC911...");
113
+ try {
114
+ const solvingDirConfig = getSolvingDir();
115
+ if (solvingDirConfig && solvingDirConfig !== "." && solvingDirConfig !== "") {
116
+ let currentParentDir = dirname(solvingDir);
117
+ const solvingBaseDir = join(projectRoot, solvingDirConfig);
118
+ while (currentParentDir !== solvingBaseDir && currentParentDir !== projectRoot) {
119
+ try {
120
+ const entries = await readdir(currentParentDir);
121
+ if (entries.length === 0) {
122
+ await rmdir(currentParentDir);
123
+ currentParentDir = dirname(currentParentDir);
124
+ } else {
125
+ break;
126
+ }
127
+ } catch {
128
+ break;
129
+ }
130
+ }
131
+ }
132
+ } catch {
133
+ }
134
+ setStatus("success");
135
+ setMessage(`\uBB38\uC81C ${problemId}\uB97C \uC544\uCE74\uC774\uBE0C\uD588\uC2B5\uB2C8\uB2E4: ${archiveDir}`);
136
+ setTimeout(() => {
137
+ onComplete?.();
138
+ }, 2e3);
139
+ } catch (err) {
140
+ setStatus("error");
141
+ setError(err instanceof Error ? err.message : String(err));
142
+ setTimeout(() => {
143
+ onComplete?.();
144
+ }, 2e3);
145
+ }
146
+ }
147
+ void archive();
148
+ }, [problemId, onComplete]);
149
+ return {
150
+ status,
151
+ message,
152
+ error
153
+ };
154
+ }
155
+
156
+ // src/commands/archive.tsx
157
+ import { jsx, jsxs } from "react/jsx-runtime";
158
+ function ArchiveView({ problemId, onComplete }) {
159
+ const { status, message, error } = useArchive({
160
+ problemId,
161
+ onComplete
162
+ });
163
+ if (status === "loading") {
164
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsx(Spinner, { label: message }) });
165
+ }
166
+ if (status === "error") {
167
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(Alert, { variant: "error", children: [
168
+ "\uC624\uB958 \uBC1C\uC0DD: ",
169
+ error
170
+ ] }) });
171
+ }
172
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", width: "100%", children: /* @__PURE__ */ jsx(StatusMessage, { variant: "success", children: message }) });
173
+ }
174
+ var ArchiveCommand = class extends Command {
175
+ async execute(args, _) {
176
+ const context = await resolveProblemContext(args, { requireId: false });
177
+ if (context.problemId === null) {
178
+ logger.error("\uBB38\uC81C \uBC88\uD638\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694.");
179
+ console.log(`\uB3C4\uC6C0\uB9D0: ps archive --help`);
180
+ process.exit(1);
181
+ return;
182
+ }
183
+ await this.renderView(ArchiveView, {
184
+ problemId: context.problemId
185
+ });
186
+ }
187
+ };
188
+ ArchiveCommand = __decorateClass([
189
+ CommandDef({
190
+ name: "archive",
191
+ description: `\uBB38\uC81C\uB97C \uC544\uCE74\uC774\uBE0C\uD558\uACE0 Git \uCEE4\uBC0B\uC744 \uC0DD\uC131\uD569\uB2C8\uB2E4.
192
+ - solving \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uBB38\uC81C\uB97C \uCC3E\uC544 archive \uB514\uB809\uD1A0\uB9AC\uB85C \uC774\uB3D9
193
+ - Git add \uBC0F commit \uC2E4\uD589 (\uCEE4\uBC0B \uBA54\uC2DC\uC9C0: "solve: {\uBB38\uC81C\uBC88\uD638} - {\uBB38\uC81C\uC774\uB984}")
194
+ - \uC544\uCE74\uC774\uBE0C \uACBD\uB85C, \uC804\uB7B5, \uC790\uB3D9 \uCEE4\uBC0B \uC5EC\uBD80 \uB4F1\uC740 ps config\uC5D0\uC11C \uC124\uC815 \uAC00\uB2A5\uD569\uB2C8\uB2E4.`,
195
+ flags: [],
196
+ autoDetectProblemId: true,
197
+ requireProblemId: false,
198
+ examples: [
199
+ "archive 1000",
200
+ "archive # \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uBB38\uC81C \uBC88\uD638 \uC790\uB3D9 \uAC10\uC9C0"
201
+ ]
202
+ })
203
+ ], ArchiveCommand);
204
+ var archive_default = CommandBuilder.fromClass(ArchiveCommand);
205
+
206
+ export {
207
+ ArchiveView,
208
+ ArchiveCommand,
209
+ archive_default
210
+ };
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ openBrowser
4
+ } from "./chunk-QGMWUOJ3.js";
5
+ import {
6
+ Command,
7
+ CommandBuilder,
8
+ CommandDef,
9
+ __decorateClass,
10
+ defineFlags,
11
+ detectProblemIdFromPath,
12
+ findSolutionFile,
13
+ getSupportedLanguagesString,
14
+ resolveLanguage,
15
+ resolveProblemContext
16
+ } from "./chunk-JZK2464U.js";
17
+
18
+ // src/commands/submit.tsx
19
+ import { Badge, StatusMessage, Alert, Spinner } from "@inkjs/ui";
20
+ import { Text, Box } from "ink";
21
+
22
+ // src/hooks/use-submit.ts
23
+ import { readFile } from "fs/promises";
24
+ import { useEffect, useState } from "react";
25
+
26
+ // src/utils/clipboard.ts
27
+ import { execa, execaCommand } from "execa";
28
+ async function copyToClipboard(text) {
29
+ try {
30
+ if (process.platform === "win32") {
31
+ await execaCommand("clip", {
32
+ shell: true,
33
+ input: text
34
+ });
35
+ } else if (process.platform === "darwin") {
36
+ await execaCommand("pbcopy", {
37
+ shell: false,
38
+ input: text
39
+ });
40
+ } else {
41
+ try {
42
+ await execa("xclip", ["-selection", "clipboard"], {
43
+ input: text
44
+ });
45
+ } catch {
46
+ try {
47
+ await execa("xsel", ["--clipboard", "--input"], {
48
+ input: text
49
+ });
50
+ } catch {
51
+ return false;
52
+ }
53
+ }
54
+ }
55
+ return true;
56
+ } catch {
57
+ return false;
58
+ }
59
+ }
60
+
61
+ // src/hooks/use-submit.ts
62
+ var BOJ_BASE_URL = "https://www.acmicpc.net";
63
+ function useSubmit({
64
+ problemId,
65
+ language: _language,
66
+ sourcePath,
67
+ onComplete
68
+ }) {
69
+ const [status, setStatus] = useState(
70
+ "loading"
71
+ );
72
+ const [message, setMessage] = useState("\uC81C\uCD9C \uC900\uBE44 \uC911...");
73
+ const [error, setError] = useState(null);
74
+ const [submitUrl, setSubmitUrl] = useState("");
75
+ const [clipboardSuccess, setClipboardSuccess] = useState(false);
76
+ const [clipboardError, setClipboardError] = useState(null);
77
+ useEffect(() => {
78
+ async function submit() {
79
+ try {
80
+ setMessage("\uC18C\uC2A4 \uCF54\uB4DC\uB97C \uC77D\uB294 \uC911...");
81
+ const sourceCode = await readFile(sourcePath, "utf-8");
82
+ setMessage("\uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uBCF5\uC0AC\uD558\uB294 \uC911...");
83
+ const clipboardResult = await copyToClipboard(sourceCode);
84
+ setClipboardSuccess(clipboardResult);
85
+ if (!clipboardResult) {
86
+ setClipboardError("\uD074\uB9BD\uBCF4\uB4DC \uBCF5\uC0AC\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.");
87
+ }
88
+ const url = `${BOJ_BASE_URL}/submit/${problemId}`;
89
+ setSubmitUrl(url);
90
+ setMessage("\uBE0C\uB77C\uC6B0\uC800\uB97C \uC5EC\uB294 \uC911...");
91
+ await openBrowser(url);
92
+ setStatus("success");
93
+ setTimeout(() => {
94
+ onComplete();
95
+ }, 2e3);
96
+ } catch (err) {
97
+ setStatus("error");
98
+ setError(err instanceof Error ? err.message : String(err));
99
+ setTimeout(() => {
100
+ onComplete();
101
+ }, 2e3);
102
+ }
103
+ }
104
+ void submit();
105
+ }, [problemId, sourcePath, onComplete]);
106
+ return {
107
+ status,
108
+ message,
109
+ error,
110
+ submitUrl,
111
+ clipboardSuccess,
112
+ clipboardError
113
+ };
114
+ }
115
+
116
+ // src/commands/submit.tsx
117
+ import { jsx, jsxs } from "react/jsx-runtime";
118
+ var submitFlagsSchema = {
119
+ language: {
120
+ type: "string",
121
+ shortFlag: "l",
122
+ description: `\uC5B8\uC5B4 \uC120\uD0DD (\uC9C0\uC815 \uC2DC \uC790\uB3D9 \uAC10\uC9C0 \uBB34\uC2DC)
123
+ \uC9C0\uC6D0 \uC5B8\uC5B4: ${getSupportedLanguagesString()}`
124
+ }
125
+ };
126
+ function SubmitView({
127
+ problemId,
128
+ language,
129
+ sourcePath,
130
+ onComplete
131
+ }) {
132
+ const {
133
+ status,
134
+ message,
135
+ error,
136
+ submitUrl,
137
+ clipboardSuccess,
138
+ clipboardError
139
+ } = useSubmit({
140
+ problemId,
141
+ language,
142
+ sourcePath,
143
+ onComplete
144
+ });
145
+ if (status === "loading") {
146
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
147
+ /* @__PURE__ */ jsx(Spinner, { label: message }),
148
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
149
+ "\uBB38\uC81C #",
150
+ problemId
151
+ ] }) })
152
+ ] });
153
+ }
154
+ if (status === "error") {
155
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
156
+ /* @__PURE__ */ jsxs(Alert, { variant: "error", children: [
157
+ "\uC624\uB958 \uBC1C\uC0DD: ",
158
+ error
159
+ ] }),
160
+ submitUrl && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
161
+ "URL: ",
162
+ submitUrl
163
+ ] }) })
164
+ ] });
165
+ }
166
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: "100%", children: [
167
+ /* @__PURE__ */ jsx(StatusMessage, { variant: "success", children: "\uC81C\uCD9C \uD398\uC774\uC9C0\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4!" }),
168
+ /* @__PURE__ */ jsxs(
169
+ Box,
170
+ {
171
+ flexDirection: "column",
172
+ borderStyle: "round",
173
+ borderColor: "gray",
174
+ marginTop: 1,
175
+ paddingX: 1,
176
+ paddingY: 0,
177
+ alignSelf: "flex-start",
178
+ children: [
179
+ /* @__PURE__ */ jsxs(Text, { children: [
180
+ /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\uBB38\uC81C \uBC88\uD638:" }),
181
+ " ",
182
+ problemId
183
+ ] }),
184
+ /* @__PURE__ */ jsxs(Text, { children: [
185
+ /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\uC5B8\uC5B4:" }),
186
+ " ",
187
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: language })
188
+ ] }),
189
+ /* @__PURE__ */ jsxs(Text, { children: [
190
+ /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "URL:" }),
191
+ " ",
192
+ /* @__PURE__ */ jsx(Text, { color: "blue", underline: true, children: submitUrl })
193
+ ] }),
194
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: clipboardSuccess ? /* @__PURE__ */ jsx(Badge, { color: "green", children: "\uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uBCF5\uC0AC\uB428" }) : /* @__PURE__ */ jsx(Badge, { color: "yellow", children: "\uD074\uB9BD\uBCF4\uB4DC \uBCF5\uC0AC \uC2E4\uD328" }) }),
195
+ clipboardError && !clipboardSuccess && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Alert, { variant: "warning", children: clipboardError }) })
196
+ ]
197
+ }
198
+ )
199
+ ] });
200
+ }
201
+ var SubmitCommand = class extends Command {
202
+ async execute(args, flags) {
203
+ const context = await resolveProblemContext(args, { requireId: true });
204
+ const sourcePath = await findSolutionFile(context.archiveDir);
205
+ const detectedLanguage = await resolveLanguage(
206
+ context.archiveDir,
207
+ flags.language
208
+ );
209
+ let finalProblemId = context.problemId;
210
+ if (finalProblemId === null) {
211
+ finalProblemId = detectProblemIdFromPath(context.archiveDir);
212
+ }
213
+ if (finalProblemId === null) {
214
+ throw new Error(
215
+ "\uBB38\uC81C \uBC88\uD638\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBB38\uC81C \uBC88\uD638\uB97C \uC778\uC790\uB85C \uC804\uB2EC\uD558\uAC70\uB098 \uBB38\uC81C \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uC2E4\uD589\uD574\uC8FC\uC138\uC694."
216
+ );
217
+ }
218
+ await this.renderView(SubmitView, {
219
+ problemId: finalProblemId,
220
+ language: detectedLanguage,
221
+ sourcePath
222
+ });
223
+ }
224
+ };
225
+ SubmitCommand = __decorateClass([
226
+ CommandDef({
227
+ name: "submit",
228
+ description: `\uBC31\uC900 \uC81C\uCD9C \uD398\uC774\uC9C0\uB97C \uBE0C\uB77C\uC6B0\uC800\uB85C \uC5F4\uACE0 \uC18C\uC2A4 \uCF54\uB4DC\uB97C \uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uBCF5\uC0AC\uD569\uB2C8\uB2E4.
229
+ - \uBB38\uC81C \uBC88\uD638\uB97C \uC778\uC790\uB85C \uC804\uB2EC\uD558\uAC70\uB098 \uBB38\uC81C \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uC2E4\uD589\uD558\uBA74 \uC790\uB3D9\uC73C\uB85C \uBB38\uC81C \uBC88\uD638\uB97C \uCD94\uB860
230
+ - solution.* \uD30C\uC77C\uC744 \uC790\uB3D9\uC73C\uB85C \uCC3E\uC544 \uC5B8\uC5B4 \uAC10\uC9C0
231
+ - \uC18C\uC2A4 \uCF54\uB4DC\uB97C \uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uC790\uB3D9 \uBCF5\uC0AC
232
+ - \uC81C\uCD9C \uD398\uC774\uC9C0\uB97C \uBE0C\uB77C\uC6B0\uC800\uB85C \uC790\uB3D9 \uC5F4\uAE30
233
+ - \uAE30\uBCF8 \uC5B8\uC5B4 \uB4F1\uC740 ps config\uC5D0\uC11C \uC124\uC815 \uAC00\uB2A5\uD569\uB2C8\uB2E4.`,
234
+ flags: defineFlags(submitFlagsSchema),
235
+ autoDetectProblemId: true,
236
+ autoDetectLanguage: true,
237
+ requireProblemId: true,
238
+ examples: [
239
+ "submit # \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C \uC81C\uCD9C",
240
+ "submit 1000 # 1000\uBC88 \uBB38\uC81C \uC81C\uCD9C",
241
+ "submit --language python # Python\uC73C\uB85C \uC81C\uCD9C"
242
+ ]
243
+ })
244
+ ], SubmitCommand);
245
+ var submit_default = CommandBuilder.fromClass(SubmitCommand);
246
+
247
+ export {
248
+ SubmitView,
249
+ SubmitCommand,
250
+ submit_default
251
+ };