@rhseung/ps-cli 1.12.2 → 1.13.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
@@ -3,7 +3,12 @@
3
3
  **대화형** 인터페이스와 **사용자 친화적인 디자인**을 갖춘 백준 문제 해결 CLI 도구입니다.
4
4
  검색부터 제출까지, 모든 과정을 직관적이고 아름다운 터미널 환경에서 해결하세요.
5
5
 
6
- Nerd Font 사용을 권장합니다.
6
+ Nerd Font 사용이 필요합니다.
7
+
8
+ <img width="75%" alt="CleanShot 2026-01-31 at 11 36 08@2x" src="https://github.com/user-attachments/assets/5e10b16e-e5d6-4c0d-a036-3bbe7a9d891a" />
9
+
10
+ <img width="75%" alt="CleanShot 2026-01-31 at 11 34 26@2x" src="https://github.com/user-attachments/assets/df352cf2-ac60-4061-adb3-07b1145356af" />
11
+
7
12
 
8
13
  ## 설치
9
14
 
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-QGMWUOJ3.js";
8
8
  import {
9
9
  runSolution
10
- } from "./chunk-CKSAX7KT.js";
10
+ } from "./chunk-6MJPXSAN.js";
11
11
  import {
12
12
  Command,
13
13
  CommandBuilder,
@@ -24,7 +24,7 @@ import {
24
24
  icons,
25
25
  resolveLanguage,
26
26
  resolveProblemContext
27
- } from "./chunk-UDZLWCHF.js";
27
+ } from "./chunk-E5PL24PI.js";
28
28
 
29
29
  // src/commands/test.tsx
30
30
  import { Alert, Badge as Badge2, Spinner, StatusMessage as StatusMessage2 } from "@inkjs/ui";
@@ -3,7 +3,7 @@ import {
3
3
  findSolutionFile,
4
4
  getLanguageConfig,
5
5
  getProblemTimeLimitMs
6
- } from "./chunk-UDZLWCHF.js";
6
+ } from "./chunk-E5PL24PI.js";
7
7
 
8
8
  // src/hooks/use-run-solution.ts
9
9
  import { useEffect, useState } from "react";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  logger
4
- } from "./chunk-UDZLWCHF.js";
4
+ } from "./chunk-E5PL24PI.js";
5
5
 
6
6
  // src/services/scraper.ts
7
7
  import * as cheerio from "cheerio";
@@ -242,8 +242,8 @@ ${preContent}
242
242
  break;
243
243
  }
244
244
  case "ul":
245
- case "ol":
246
- $node.find("li").each((i, li) => {
245
+ case "ol": {
246
+ $node.children("li").each((i, li) => {
247
247
  const liContent = htmlToMarkdown($, $(li));
248
248
  if (liContent) {
249
249
  result += `- ${liContent}
@@ -252,6 +252,7 @@ ${preContent}
252
252
  });
253
253
  result += "\n";
254
254
  break;
255
+ }
255
256
  case "li":
256
257
  result += htmlToMarkdown($, $node);
257
258
  break;
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  useFetchProblem
4
- } from "./chunk-KUUJ4DBH.js";
4
+ } from "./chunk-ZAIU4LPX.js";
5
5
  import {
6
6
  Command,
7
7
  CommandBuilder,
@@ -14,7 +14,7 @@ import {
14
14
  getTierName,
15
15
  logger,
16
16
  resolveProblemContext
17
- } from "./chunk-UDZLWCHF.js";
17
+ } from "./chunk-E5PL24PI.js";
18
18
 
19
19
  // src/commands/fetch.tsx
20
20
  import { StatusMessage, Alert, Spinner } from "@inkjs/ui";
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getTierColor,
4
+ getTierName,
5
+ icons,
6
+ source_default
7
+ } from "./chunk-E5PL24PI.js";
8
+
9
+ // src/components/problem-selector.tsx
10
+ import { Select } from "@inkjs/ui";
11
+ import { Box, Text } from "ink";
12
+ import { jsx, jsxs } from "react/jsx-runtime";
13
+ function ProblemSelector({
14
+ problems,
15
+ currentPage,
16
+ totalPages,
17
+ showPagination = false,
18
+ onSelect,
19
+ onPageChange,
20
+ header
21
+ }) {
22
+ const options = [];
23
+ problems.forEach((problem) => {
24
+ const solvedText = problem.solvedCount ? ` (${problem.solvedCount.toLocaleString()}\uBA85` : "";
25
+ const triesText = problem.averageTries ? `, \uD3C9\uADE0 ${problem.averageTries}\uD68C` : "";
26
+ const suffix = solvedText + triesText + (solvedText ? ")" : "");
27
+ const solvedMark = problem.isSolved ? ` ${source_default.bold.green(icons.solved)}` : "";
28
+ const solvingMark = problem.isSolving ? ` ${source_default.bold.blue(icons.solving)}` : "";
29
+ let tierText = "";
30
+ if (problem.level) {
31
+ const tierName = getTierName(problem.level);
32
+ const tierColor = getTierColor(problem.level);
33
+ if (typeof tierColor === "string") {
34
+ tierText = `${source_default.bold.hex(tierColor)(tierName)} `;
35
+ } else {
36
+ tierText = `${tierColor(source_default.bold(tierName))} `;
37
+ }
38
+ }
39
+ const problemText = `${problem.problemId} - ${problem.title}`;
40
+ options.push({
41
+ label: `${tierText}${problemText}${suffix}${solvedMark}${solvingMark}`,
42
+ value: `problem:${problem.problemId}`
43
+ });
44
+ });
45
+ if (showPagination && currentPage !== void 0 && totalPages !== void 0) {
46
+ if (currentPage < totalPages) {
47
+ options.push({
48
+ label: `${icons.next} \uB2E4\uC74C \uD398\uC774\uC9C0 (${currentPage + 1}/${totalPages})`,
49
+ value: "next-page"
50
+ });
51
+ }
52
+ if (currentPage > 1) {
53
+ options.push({
54
+ label: `${icons.back} \uC774\uC804 \uD398\uC774\uC9C0 (${currentPage - 1}/${totalPages})`,
55
+ value: "prev-page"
56
+ });
57
+ }
58
+ }
59
+ const handleSelect = (value) => {
60
+ if (value === "next-page" && onPageChange && currentPage !== void 0) {
61
+ onPageChange(currentPage + 1);
62
+ return;
63
+ }
64
+ if (value === "prev-page" && onPageChange && currentPage !== void 0) {
65
+ onPageChange(currentPage - 1);
66
+ return;
67
+ }
68
+ if (value.startsWith("problem:")) {
69
+ const problemId = parseInt(value.replace("problem:", ""), 10);
70
+ if (!isNaN(problemId)) {
71
+ onSelect(problemId);
72
+ }
73
+ }
74
+ };
75
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
76
+ header && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: header }),
77
+ showPagination && currentPage !== void 0 && totalPages !== void 0 && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
78
+ "\uD398\uC774\uC9C0 ",
79
+ currentPage,
80
+ "/",
81
+ totalPages
82
+ ] }) }),
83
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Select, { options, onChange: handleSelect }) })
84
+ ] });
85
+ }
86
+
87
+ export {
88
+ ProblemSelector
89
+ };
@@ -9,7 +9,7 @@ import {
9
9
  getIncludeTag,
10
10
  getSolvedAcHandle,
11
11
  getSolvingDir
12
- } from "./chunk-UDZLWCHF.js";
12
+ } from "./chunk-E5PL24PI.js";
13
13
 
14
14
  // src/hooks/use-init.ts
15
15
  import { existsSync } from "fs";
@@ -10459,7 +10459,7 @@ function getTierImageUrl(level) {
10459
10459
  }
10460
10460
 
10461
10461
  // src/core/problem.ts
10462
- import { readFile } from "fs/promises";
10462
+ import { readFile, readdir, stat } from "fs/promises";
10463
10463
  import { join as join2 } from "path";
10464
10464
  function parseTimeLimitToMs(timeLimit) {
10465
10465
  if (!timeLimit) return void 0;
@@ -10640,10 +10640,46 @@ function getSolvingDirPath(problemId, cwd = process.cwd(), _) {
10640
10640
  }
10641
10641
  return join2(baseDir, solvingDir, problemId.toString());
10642
10642
  }
10643
+ async function getSolvingProblems(cwd = process.cwd()) {
10644
+ const projectRoot = findProjectRoot(cwd);
10645
+ if (!projectRoot) {
10646
+ return [];
10647
+ }
10648
+ const solvingDir = getSolvingDir();
10649
+ const solvingBasePath = solvingDir === "." || solvingDir === "" ? projectRoot : join2(projectRoot, solvingDir);
10650
+ const results = [];
10651
+ try {
10652
+ const entries = await readdir(solvingBasePath);
10653
+ for (const entry of entries) {
10654
+ if (entry.startsWith(".")) continue;
10655
+ const fullPath = join2(solvingBasePath, entry);
10656
+ const s = await stat(fullPath);
10657
+ if (!s.isDirectory()) continue;
10658
+ const metaPath = join2(fullPath, "meta.json");
10659
+ try {
10660
+ const metaRaw = await readFile(metaPath, "utf-8");
10661
+ const meta = JSON.parse(metaRaw);
10662
+ const problemId = meta.id ?? parseInt(entry, 10);
10663
+ if (isNaN(problemId) || problemId <= 0) continue;
10664
+ results.push({
10665
+ problemId,
10666
+ title: meta.title ?? `\uBB38\uC81C ${problemId}`,
10667
+ level: meta.level,
10668
+ problemDir: fullPath
10669
+ });
10670
+ } catch {
10671
+ }
10672
+ }
10673
+ } catch {
10674
+ return [];
10675
+ }
10676
+ results.sort((a, b) => a.problemId - b.problemId);
10677
+ return results;
10678
+ }
10643
10679
 
10644
10680
  // src/core/execution-context.ts
10645
- import { access, stat } from "fs/promises";
10646
- import { readdir } from "fs/promises";
10681
+ import { access, stat as stat2 } from "fs/promises";
10682
+ import { readdir as readdir2 } from "fs/promises";
10647
10683
  import { join as join3 } from "path";
10648
10684
  async function directoryExists(dirPath) {
10649
10685
  try {
@@ -10723,7 +10759,7 @@ function parseSolutionIndex(filename) {
10723
10759
  }
10724
10760
  async function findSolutionFiles(problemDir, language) {
10725
10761
  try {
10726
- const files = await readdir(problemDir);
10762
+ const files = await readdir2(problemDir);
10727
10763
  const solutionFiles = [];
10728
10764
  for (const file of files) {
10729
10765
  if (!file.startsWith("solution")) {
@@ -10742,7 +10778,7 @@ async function findSolutionFiles(problemDir, language) {
10742
10778
  }
10743
10779
  const filePath = join3(problemDir, file);
10744
10780
  try {
10745
- const stats = await stat(filePath);
10781
+ const stats = await stat2(filePath);
10746
10782
  solutionFiles.push({
10747
10783
  path: filePath,
10748
10784
  filename: file,
@@ -11635,6 +11671,7 @@ export {
11635
11671
  detectProblemIdFromPath,
11636
11672
  getArchiveDirPath,
11637
11673
  getSolvingDirPath,
11674
+ getSolvingProblems,
11638
11675
  resolveProblemContext,
11639
11676
  resolveLanguage,
11640
11677
  findSolutionFiles,
@@ -16,7 +16,7 @@ import {
16
16
  getSupportedLanguagesString,
17
17
  resolveLanguage,
18
18
  resolveProblemContext
19
- } from "./chunk-UDZLWCHF.js";
19
+ } from "./chunk-E5PL24PI.js";
20
20
 
21
21
  // src/commands/submit.tsx
22
22
  import { Badge, StatusMessage, Alert, Spinner } from "@inkjs/ui";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  useArchive
4
- } from "./chunk-V27D7TPW.js";
4
+ } from "./chunk-ZRNV3W7X.js";
5
5
  import {
6
6
  Command,
7
7
  CommandBuilder,
@@ -9,7 +9,7 @@ import {
9
9
  __decorateClass,
10
10
  logger,
11
11
  resolveProblemContext
12
- } from "./chunk-UDZLWCHF.js";
12
+ } from "./chunk-E5PL24PI.js";
13
13
 
14
14
  // src/commands/archive.tsx
15
15
  import { StatusMessage, Alert, Spinner } from "@inkjs/ui";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  findProjectRoot,
4
4
  getConfigMetadata
5
- } from "./chunk-UDZLWCHF.js";
5
+ } from "./chunk-E5PL24PI.js";
6
6
 
7
7
  // src/hooks/use-config.ts
8
8
  import { existsSync, mkdirSync } from "fs";
@@ -5,12 +5,12 @@ import {
5
5
  getUserTagRatings,
6
6
  getUserTop100,
7
7
  scrapeUserStats
8
- } from "./chunk-G2SUJE4H.js";
8
+ } from "./chunk-6TZSHRNP.js";
9
9
  import {
10
10
  findProjectRoot,
11
11
  getArchiveDir,
12
12
  getSolvingDir
13
- } from "./chunk-UDZLWCHF.js";
13
+ } from "./chunk-E5PL24PI.js";
14
14
 
15
15
  // src/hooks/use-user-stats.ts
16
16
  import { existsSync } from "fs";
@@ -11,7 +11,7 @@ import {
11
11
  getEditor,
12
12
  logger,
13
13
  resolveProblemContext
14
- } from "./chunk-UDZLWCHF.js";
14
+ } from "./chunk-E5PL24PI.js";
15
15
 
16
16
  // src/commands/open.tsx
17
17
  import { StatusMessage, Alert, Spinner } from "@inkjs/ui";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getProblem,
4
4
  scrapeProblem
5
- } from "./chunk-G2SUJE4H.js";
5
+ } from "./chunk-6TZSHRNP.js";
6
6
  import {
7
7
  detectLanguageFromFile,
8
8
  findProjectRoot,
@@ -14,7 +14,7 @@ import {
14
14
  getTierImageUrl,
15
15
  getTierName,
16
16
  parseTimeLimitToMs
17
- } from "./chunk-UDZLWCHF.js";
17
+ } from "./chunk-E5PL24PI.js";
18
18
 
19
19
  // src/hooks/use-fetch-problem.ts
20
20
  import { execaCommand } from "execa";
@@ -6,7 +6,7 @@ import {
6
6
  getArchiveDirPath,
7
7
  getSolvingDir,
8
8
  getSolvingDirPath
9
- } from "./chunk-UDZLWCHF.js";
9
+ } from "./chunk-E5PL24PI.js";
10
10
 
11
11
  // src/hooks/use-archive.ts
12
12
  import { access, readFile, rename, mkdir, readdir, rmdir } from "fs/promises";
@@ -3,9 +3,9 @@ import {
3
3
  ArchiveCommand,
4
4
  ArchiveView,
5
5
  archive_default
6
- } from "../chunk-EQDQI3RC.js";
7
- import "../chunk-V27D7TPW.js";
8
- import "../chunk-UDZLWCHF.js";
6
+ } from "../chunk-LTZQEX6E.js";
7
+ import "../chunk-ZRNV3W7X.js";
8
+ import "../chunk-E5PL24PI.js";
9
9
  export {
10
10
  ArchiveCommand,
11
11
  ArchiveView,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  useConfig
4
- } from "../chunk-RHWSWBUG.js";
4
+ } from "../chunk-NIWHZY4V.js";
5
5
  import {
6
6
  Command,
7
7
  CommandBuilder,
@@ -10,7 +10,7 @@ import {
10
10
  getConfigMetadata,
11
11
  icons,
12
12
  logger
13
- } from "../chunk-UDZLWCHF.js";
13
+ } from "../chunk-E5PL24PI.js";
14
14
 
15
15
  // src/commands/config.tsx
16
16
  import { StatusMessage, Select, TextInput, Alert, Badge } from "@inkjs/ui";
@@ -3,10 +3,10 @@ import {
3
3
  FetchCommand,
4
4
  FetchView,
5
5
  fetch_default
6
- } from "../chunk-4J6PFK66.js";
7
- import "../chunk-KUUJ4DBH.js";
8
- import "../chunk-G2SUJE4H.js";
9
- import "../chunk-UDZLWCHF.js";
6
+ } from "../chunk-BFUQPT6F.js";
7
+ import "../chunk-ZAIU4LPX.js";
8
+ import "../chunk-6TZSHRNP.js";
9
+ import "../chunk-E5PL24PI.js";
10
10
  export {
11
11
  FetchCommand,
12
12
  FetchView,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  useInit
4
- } from "../chunk-ZZ76HNFP.js";
4
+ } from "../chunk-CL6IVA6K.js";
5
5
  import {
6
6
  Command,
7
7
  CommandBuilder,
@@ -10,7 +10,7 @@ import {
10
10
  getSupportedLanguages,
11
11
  getVersion,
12
12
  icons
13
- } from "../chunk-UDZLWCHF.js";
13
+ } from "../chunk-E5PL24PI.js";
14
14
 
15
15
  // src/commands/init.tsx
16
16
  import {
@@ -3,10 +3,10 @@ import {
3
3
  OpenCommand,
4
4
  OpenView,
5
5
  open_default
6
- } from "../chunk-C3IOK7V4.js";
6
+ } from "../chunk-YRPMKMV4.js";
7
7
  import "../chunk-3LR2NGRC.js";
8
8
  import "../chunk-QGMWUOJ3.js";
9
- import "../chunk-UDZLWCHF.js";
9
+ import "../chunk-E5PL24PI.js";
10
10
  export {
11
11
  OpenCommand,
12
12
  OpenView,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  useRunSolution
4
- } from "../chunk-CKSAX7KT.js";
4
+ } from "../chunk-6MJPXSAN.js";
5
5
  import {
6
6
  Command,
7
7
  CommandBuilder,
@@ -15,7 +15,7 @@ import {
15
15
  icons,
16
16
  resolveLanguage,
17
17
  resolveProblemContext
18
- } from "../chunk-UDZLWCHF.js";
18
+ } from "../chunk-E5PL24PI.js";
19
19
 
20
20
  // src/commands/run.tsx
21
21
  import { join } from "path";
@@ -1,33 +1,36 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ ProblemSelector
4
+ } from "../chunk-BHCANIOJ.js";
2
5
  import {
3
6
  SubmitView
4
- } from "../chunk-LGSE3IEE.js";
7
+ } from "../chunk-KVGYJXGH.js";
5
8
  import {
6
9
  TestView
7
- } from "../chunk-K5KMUKVD.js";
8
- import "../chunk-TPIID7I2.js";
10
+ } from "../chunk-3QWJ77VU.js";
11
+ import "../chunk-WJGED2TC.js";
9
12
  import "../chunk-MZUR7SER.js";
10
13
  import {
11
14
  ArchiveView
12
- } from "../chunk-EQDQI3RC.js";
13
- import "../chunk-V27D7TPW.js";
14
- import "../chunk-RHWSWBUG.js";
15
+ } from "../chunk-LTZQEX6E.js";
16
+ import "../chunk-ZRNV3W7X.js";
17
+ import "../chunk-NIWHZY4V.js";
15
18
  import {
16
19
  FetchView
17
- } from "../chunk-4J6PFK66.js";
18
- import "../chunk-KUUJ4DBH.js";
20
+ } from "../chunk-BFUQPT6F.js";
21
+ import "../chunk-ZAIU4LPX.js";
19
22
  import {
20
23
  fetchWithRetry,
21
24
  getProblem,
22
25
  searchProblems
23
- } from "../chunk-G2SUJE4H.js";
24
- import "../chunk-ZZ76HNFP.js";
26
+ } from "../chunk-6TZSHRNP.js";
27
+ import "../chunk-CL6IVA6K.js";
25
28
  import {
26
29
  OpenView
27
- } from "../chunk-C3IOK7V4.js";
30
+ } from "../chunk-YRPMKMV4.js";
28
31
  import "../chunk-3LR2NGRC.js";
29
32
  import "../chunk-QGMWUOJ3.js";
30
- import "../chunk-CKSAX7KT.js";
33
+ import "../chunk-6MJPXSAN.js";
31
34
  import {
32
35
  Command,
33
36
  CommandBuilder,
@@ -38,97 +41,16 @@ import {
38
41
  getArchiveDirPath,
39
42
  getEditor,
40
43
  getSolvingDirPath,
41
- getTierColor,
42
- getTierName,
43
44
  icons,
44
45
  logger,
45
- resolveLanguage,
46
- source_default
47
- } from "../chunk-UDZLWCHF.js";
46
+ resolveLanguage
47
+ } from "../chunk-E5PL24PI.js";
48
48
 
49
49
  // src/commands/search.tsx
50
50
  import { existsSync } from "fs";
51
- import { Select as Select2, Alert, Spinner } from "@inkjs/ui";
52
- import { Box as Box2, Text as Text2 } from "ink";
53
- import { useEffect, useState } from "react";
54
-
55
- // src/components/problem-selector.tsx
56
- import { Select } from "@inkjs/ui";
51
+ import { Select, Alert, Spinner } from "@inkjs/ui";
57
52
  import { Box, Text } from "ink";
58
- import { jsx, jsxs } from "react/jsx-runtime";
59
- function ProblemSelector({
60
- problems,
61
- currentPage,
62
- totalPages,
63
- showPagination = false,
64
- onSelect,
65
- onPageChange,
66
- header
67
- }) {
68
- const options = [];
69
- problems.forEach((problem) => {
70
- const solvedText = problem.solvedCount ? ` (${problem.solvedCount.toLocaleString()}\uBA85` : "";
71
- const triesText = problem.averageTries ? `, \uD3C9\uADE0 ${problem.averageTries}\uD68C` : "";
72
- const suffix = solvedText + triesText + (solvedText ? ")" : "");
73
- const solvedMark = problem.isSolved ? ` ${source_default.bold.green(icons.solved)}` : "";
74
- const solvingMark = problem.isSolving ? ` ${source_default.bold.blue(icons.solving)}` : "";
75
- let tierText = "";
76
- if (problem.level) {
77
- const tierName = getTierName(problem.level);
78
- const tierColor = getTierColor(problem.level);
79
- if (typeof tierColor === "string") {
80
- tierText = `${source_default.bold.hex(tierColor)(tierName)} `;
81
- } else {
82
- tierText = `${tierColor(source_default.bold(tierName))} `;
83
- }
84
- }
85
- const problemText = `${problem.problemId} - ${problem.title}`;
86
- options.push({
87
- label: `${tierText}${problemText}${suffix}${solvedMark}${solvingMark}`,
88
- value: `problem:${problem.problemId}`
89
- });
90
- });
91
- if (showPagination && currentPage !== void 0 && totalPages !== void 0) {
92
- if (currentPage < totalPages) {
93
- options.push({
94
- label: `${icons.next} \uB2E4\uC74C \uD398\uC774\uC9C0 (${currentPage + 1}/${totalPages})`,
95
- value: "next-page"
96
- });
97
- }
98
- if (currentPage > 1) {
99
- options.push({
100
- label: `${icons.back} \uC774\uC804 \uD398\uC774\uC9C0 (${currentPage - 1}/${totalPages})`,
101
- value: "prev-page"
102
- });
103
- }
104
- }
105
- const handleSelect = (value) => {
106
- if (value === "next-page" && onPageChange && currentPage !== void 0) {
107
- onPageChange(currentPage + 1);
108
- return;
109
- }
110
- if (value === "prev-page" && onPageChange && currentPage !== void 0) {
111
- onPageChange(currentPage - 1);
112
- return;
113
- }
114
- if (value.startsWith("problem:")) {
115
- const problemId = parseInt(value.replace("problem:", ""), 10);
116
- if (!isNaN(problemId)) {
117
- onSelect(problemId);
118
- }
119
- }
120
- };
121
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
122
- header && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: header }),
123
- showPagination && currentPage !== void 0 && totalPages !== void 0 && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
124
- "\uD398\uC774\uC9C0 ",
125
- currentPage,
126
- "/",
127
- totalPages
128
- ] }) }),
129
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Select, { options, onChange: handleSelect }) })
130
- ] });
131
- }
53
+ import { useEffect, useState } from "react";
132
54
 
133
55
  // src/services/workbook-scraper.ts
134
56
  import * as cheerio from "cheerio";
@@ -192,7 +114,7 @@ async function scrapeWorkbook(workbookId) {
192
114
  }
193
115
 
194
116
  // src/commands/search.tsx
195
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
117
+ import { jsx, jsxs } from "react/jsx-runtime";
196
118
  var searchFlagsSchema = {
197
119
  workbook: {
198
120
  type: "number",
@@ -275,10 +197,10 @@ function ProblemActionView({
275
197
  void loadInfo();
276
198
  }, [problemId, isSolving, isSolved]);
277
199
  if (selectedAction === "open") {
278
- return /* @__PURE__ */ jsx2(OpenView, { problemId, mode: "browser", onComplete });
200
+ return /* @__PURE__ */ jsx(OpenView, { problemId, mode: "browser", onComplete });
279
201
  }
280
202
  if (selectedAction === "editor") {
281
- return /* @__PURE__ */ jsx2(
203
+ return /* @__PURE__ */ jsx(
282
204
  OpenView,
283
205
  {
284
206
  problemId,
@@ -289,10 +211,10 @@ function ProblemActionView({
289
211
  );
290
212
  }
291
213
  if (selectedAction === "fetch") {
292
- return /* @__PURE__ */ jsx2(FetchView, { problemId, onComplete });
214
+ return /* @__PURE__ */ jsx(FetchView, { problemId, onComplete });
293
215
  }
294
216
  if (selectedAction === "test" && problemDir && language) {
295
- return /* @__PURE__ */ jsx2(
217
+ return /* @__PURE__ */ jsx(
296
218
  TestView,
297
219
  {
298
220
  problemDir,
@@ -303,7 +225,7 @@ function ProblemActionView({
303
225
  );
304
226
  }
305
227
  if (selectedAction === "submit" && language && sourcePath) {
306
- return /* @__PURE__ */ jsx2(
228
+ return /* @__PURE__ */ jsx(
307
229
  SubmitView,
308
230
  {
309
231
  problemId,
@@ -314,7 +236,7 @@ function ProblemActionView({
314
236
  );
315
237
  }
316
238
  if (selectedAction === "archive") {
317
- return /* @__PURE__ */ jsx2(ArchiveView, { problemId, onComplete });
239
+ return /* @__PURE__ */ jsx(ArchiveView, { problemId, onComplete });
318
240
  }
319
241
  const options = [
320
242
  { label: `${icons.open} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uAE30 (open)`, value: "open" }
@@ -351,15 +273,15 @@ function ProblemActionView({
351
273
  }
352
274
  }
353
275
  options.push({ label: `${icons.back} \uB4A4\uB85C \uAC00\uAE30`, value: "back" });
354
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
355
- /* @__PURE__ */ jsx2(Box2, { marginBottom: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "cyan", bold: true, children: [
276
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
277
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { color: "cyan", bold: true, children: [
356
278
  "\uBB38\uC81C #",
357
279
  problemId,
358
280
  " \uC120\uD0DD\uB428. \uC5B4\uB5A4 \uC791\uC5C5\uC744 \uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?"
359
281
  ] }) }),
360
- loading && /* @__PURE__ */ jsx2(Spinner, { label: "\uBB38\uC81C \uC815\uBCF4\uB97C \uD655\uC778\uD558\uB294 \uC911..." }),
361
- !loading && /* @__PURE__ */ jsx2(
362
- Select2,
282
+ loading && /* @__PURE__ */ jsx(Spinner, { label: "\uBB38\uC81C \uC815\uBCF4\uB97C \uD655\uC778\uD558\uB294 \uC911..." }),
283
+ !loading && /* @__PURE__ */ jsx(
284
+ Select,
363
285
  {
364
286
  options,
365
287
  onChange: (value) => {
@@ -400,7 +322,7 @@ function WorkbookSearchView({
400
322
  void loadWorkbook();
401
323
  }, [workbookId]);
402
324
  if (selectedProblem) {
403
- return /* @__PURE__ */ jsx2(
325
+ return /* @__PURE__ */ jsx(
404
326
  ProblemActionView,
405
327
  {
406
328
  problemId: selectedProblem.problemId,
@@ -412,30 +334,30 @@ function WorkbookSearchView({
412
334
  );
413
335
  }
414
336
  if (loading) {
415
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
416
- /* @__PURE__ */ jsx2(Spinner, { label: "\uBB38\uC81C\uC9D1\uC744 \uB85C\uB4DC\uD558\uB294 \uC911..." }),
417
- /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
337
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
338
+ /* @__PURE__ */ jsx(Spinner, { label: "\uBB38\uC81C\uC9D1\uC744 \uB85C\uB4DC\uD558\uB294 \uC911..." }),
339
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
418
340
  "\uBB38\uC81C\uC9D1 ID: ",
419
341
  workbookId
420
342
  ] }) })
421
343
  ] });
422
344
  }
423
345
  if (error) {
424
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
425
- /* @__PURE__ */ jsxs2(Alert, { variant: "error", children: [
346
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
347
+ /* @__PURE__ */ jsxs(Alert, { variant: "error", children: [
426
348
  "\uC624\uB958: ",
427
349
  error
428
350
  ] }),
429
- /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
351
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
430
352
  "\uBB38\uC81C\uC9D1 ID: ",
431
353
  workbookId
432
354
  ] }) })
433
355
  ] });
434
356
  }
435
357
  if (problems.length === 0) {
436
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
437
- /* @__PURE__ */ jsx2(Alert, { variant: "info", children: "\uBB38\uC81C\uC9D1\uC5D0 \uBB38\uC81C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }),
438
- /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
358
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
359
+ /* @__PURE__ */ jsx(Alert, { variant: "info", children: "\uBB38\uC81C\uC9D1\uC5D0 \uBB38\uC81C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }),
360
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
439
361
  "\uBB38\uC81C\uC9D1 ID: ",
440
362
  workbookId
441
363
  ] }) })
@@ -457,7 +379,7 @@ function WorkbookSearchView({
457
379
  isSolving
458
380
  };
459
381
  });
460
- return /* @__PURE__ */ jsx2(
382
+ return /* @__PURE__ */ jsx(
461
383
  ProblemSelector,
462
384
  {
463
385
  problems: problemsWithStatus,
@@ -473,8 +395,8 @@ function WorkbookSearchView({
473
395
  });
474
396
  }
475
397
  },
476
- header: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
477
- /* @__PURE__ */ jsx2(Box2, { marginBottom: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "cyan", bold: true, children: [
398
+ header: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
399
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { color: "cyan", bold: true, children: [
478
400
  icons.workbook,
479
401
  " \uBB38\uC81C\uC9D1: ",
480
402
  workbookTitle,
@@ -482,7 +404,7 @@ function WorkbookSearchView({
482
404
  workbookId,
483
405
  ")"
484
406
  ] }) }),
485
- /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
407
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
486
408
  "\uCD1D ",
487
409
  problems.length,
488
410
  "\uBB38\uC81C"
@@ -548,28 +470,28 @@ function SearchView({ query, onComplete }) {
548
470
  void performSearch();
549
471
  }, [query, currentPage]);
550
472
  if (loading && !selectedProblem) {
551
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
552
- /* @__PURE__ */ jsx2(Spinner, { label: "\uAC80\uC0C9 \uC911..." }),
553
- /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
473
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
474
+ /* @__PURE__ */ jsx(Spinner, { label: "\uAC80\uC0C9 \uC911..." }),
475
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
554
476
  "\uCFFC\uB9AC: ",
555
477
  query
556
478
  ] }) })
557
479
  ] });
558
480
  }
559
481
  if (error && !selectedProblem) {
560
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
561
- /* @__PURE__ */ jsxs2(Alert, { variant: "error", children: [
482
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
483
+ /* @__PURE__ */ jsxs(Alert, { variant: "error", children: [
562
484
  "\uAC80\uC0C9 \uC2E4\uD328: ",
563
485
  error
564
486
  ] }),
565
- /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
487
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
566
488
  "\uCFFC\uB9AC: ",
567
489
  query
568
490
  ] }) })
569
491
  ] });
570
492
  }
571
493
  if (selectedProblem) {
572
- return /* @__PURE__ */ jsx2(
494
+ return /* @__PURE__ */ jsx(
573
495
  ProblemActionView,
574
496
  {
575
497
  problemId: selectedProblem.problemId,
@@ -581,15 +503,15 @@ function SearchView({ query, onComplete }) {
581
503
  );
582
504
  }
583
505
  if (results.length === 0) {
584
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
585
- /* @__PURE__ */ jsx2(Alert, { variant: "info", children: "\uAC80\uC0C9 \uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }),
586
- /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
506
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
507
+ /* @__PURE__ */ jsx(Alert, { variant: "info", children: "\uAC80\uC0C9 \uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }),
508
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
587
509
  "\uCFFC\uB9AC: ",
588
510
  query
589
511
  ] }) })
590
512
  ] });
591
513
  }
592
- return /* @__PURE__ */ jsx2(
514
+ return /* @__PURE__ */ jsx(
593
515
  ProblemSelector,
594
516
  {
595
517
  problems: results.map((problem) => ({
@@ -617,12 +539,12 @@ function SearchView({ query, onComplete }) {
617
539
  onPageChange: (page) => {
618
540
  setCurrentPage(page);
619
541
  },
620
- header: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
621
- /* @__PURE__ */ jsx2(Box2, { marginBottom: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "cyan", bold: true, children: [
542
+ header: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
543
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { color: "cyan", bold: true, children: [
622
544
  icons.search,
623
545
  " \uAC80\uC0C9 \uACB0\uACFC"
624
546
  ] }) }),
625
- /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
547
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
626
548
  "\uCFFC\uB9AC: ",
627
549
  query
628
550
  ] }) })
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ProblemSelector
4
+ } from "../chunk-BHCANIOJ.js";
5
+ import {
6
+ OpenView
7
+ } from "../chunk-YRPMKMV4.js";
8
+ import "../chunk-3LR2NGRC.js";
9
+ import "../chunk-QGMWUOJ3.js";
10
+ import {
11
+ Command,
12
+ CommandBuilder,
13
+ CommandDef,
14
+ __decorateClass,
15
+ findProjectRoot,
16
+ getSolvingProblems,
17
+ logger
18
+ } from "../chunk-E5PL24PI.js";
19
+
20
+ // src/commands/solving.tsx
21
+ import { Box, Text } from "ink";
22
+ import { useState } from "react";
23
+ import { jsx } from "react/jsx-runtime";
24
+ function SolvingListView({ problems, onComplete }) {
25
+ const [selectedProblem, setSelectedProblem] = useState(null);
26
+ if (selectedProblem) {
27
+ return /* @__PURE__ */ jsx(
28
+ OpenView,
29
+ {
30
+ problemDir: selectedProblem.problemDir,
31
+ mode: "editor",
32
+ onComplete
33
+ }
34
+ );
35
+ }
36
+ const selectorProblems = problems.map((p) => ({
37
+ problemId: p.problemId,
38
+ title: p.title,
39
+ level: p.level,
40
+ isSolving: true
41
+ }));
42
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsx(
43
+ ProblemSelector,
44
+ {
45
+ problems: selectorProblems,
46
+ onSelect: (problemId) => {
47
+ const p = problems.find((x) => x.problemId === problemId);
48
+ if (p) setSelectedProblem(p);
49
+ },
50
+ header: /* @__PURE__ */ jsx(Text, { bold: true, children: "\uD480\uC774 \uC911\uC778 \uBB38\uC81C\uB97C \uC120\uD0DD\uD558\uC138\uC694 (\uC5D0\uB514\uD130\uB85C \uC5F4\uAE30):" })
51
+ }
52
+ ) });
53
+ }
54
+ var SolvingCommand = class extends Command {
55
+ async execute(_args, _flags) {
56
+ const projectRoot = findProjectRoot();
57
+ if (!projectRoot) {
58
+ logger.error("\uD604\uC7AC \uB514\uB809\uD130\uB9AC\uAC00 ps-cli \uD504\uB85C\uC81D\uD2B8\uAC00 \uC544\uB2D9\uB2C8\uB2E4.");
59
+ process.exit(1);
60
+ return;
61
+ }
62
+ const problems = await getSolvingProblems();
63
+ if (problems.length === 0) {
64
+ logger.info("\uD480\uC774 \uC911\uC778 \uBB38\uC81C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
65
+ process.exit(0);
66
+ return;
67
+ }
68
+ await this.renderView(SolvingListView, { problems });
69
+ }
70
+ };
71
+ SolvingCommand = __decorateClass([
72
+ CommandDef({
73
+ name: "solving",
74
+ description: `solving \uB514\uB809\uD130\uB9AC\uC5D0 \uC788\uB294 \uD480\uC774 \uC911\uC778 \uBB38\uC81C \uBAA9\uB85D\uC744 \uD45C\uC2DC\uD558\uACE0, \uC120\uD0DD\uD55C \uBB38\uC81C\uB97C \uC5D0\uB514\uD130\uB85C \uC5FD\uB2C8\uB2E4.
75
+ - meta.json\uC5D0\uC11C \uBB38\uC81C \uC81C\uBAA9, \uD2F0\uC5B4 \uB4F1 \uC815\uBCF4\uB97C \uC77D\uC5B4 \uD45C\uC2DC\uD569\uB2C8\uB2E4.
76
+ - \uBB38\uC81C \uBC88\uD638\uB9CC \uBCF4\uB358 ls solving \uB300\uC2E0 \uC9C1\uAD00\uC801\uC73C\uB85C \uBB38\uC81C\uB97C \uACE0\uB97C \uC218 \uC788\uC2B5\uB2C8\uB2E4.`,
77
+ flags: [],
78
+ autoDetectProblemId: false,
79
+ requireProblemId: false,
80
+ examples: [
81
+ "solving # \uD480\uC774 \uC911\uC778 \uBB38\uC81C \uBAA9\uB85D \u2192 \uC120\uD0DD \uC2DC \uC5D0\uB514\uD130\uB85C \uC5F4\uAE30",
82
+ "solving --help # \uB3C4\uC6C0\uB9D0 \uD45C\uC2DC"
83
+ ]
84
+ })
85
+ ], SolvingCommand);
86
+ var solving_default = CommandBuilder.fromClass(SolvingCommand);
87
+ export {
88
+ SolvingCommand,
89
+ solving_default as default
90
+ };
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  useUserStats
4
- } from "../chunk-TPIID7I2.js";
5
- import "../chunk-G2SUJE4H.js";
4
+ } from "../chunk-WJGED2TC.js";
5
+ import "../chunk-6TZSHRNP.js";
6
6
  import {
7
7
  Command,
8
8
  CommandBuilder,
@@ -19,7 +19,7 @@ import {
19
19
  icons,
20
20
  logger,
21
21
  source_default
22
- } from "../chunk-UDZLWCHF.js";
22
+ } from "../chunk-E5PL24PI.js";
23
23
 
24
24
  // src/commands/stats.tsx
25
25
  import { Alert, Spinner } from "@inkjs/ui";
@@ -3,10 +3,10 @@ import {
3
3
  SubmitCommand,
4
4
  SubmitView,
5
5
  submit_default
6
- } from "../chunk-LGSE3IEE.js";
6
+ } from "../chunk-KVGYJXGH.js";
7
7
  import "../chunk-MZUR7SER.js";
8
8
  import "../chunk-QGMWUOJ3.js";
9
- import "../chunk-UDZLWCHF.js";
9
+ import "../chunk-E5PL24PI.js";
10
10
  export {
11
11
  SubmitCommand,
12
12
  SubmitView,
@@ -3,18 +3,18 @@ import {
3
3
  TestCommand,
4
4
  TestView,
5
5
  test_default
6
- } from "../chunk-K5KMUKVD.js";
7
- import "../chunk-TPIID7I2.js";
6
+ } from "../chunk-3QWJ77VU.js";
7
+ import "../chunk-WJGED2TC.js";
8
8
  import "../chunk-MZUR7SER.js";
9
- import "../chunk-V27D7TPW.js";
10
- import "../chunk-RHWSWBUG.js";
11
- import "../chunk-KUUJ4DBH.js";
12
- import "../chunk-G2SUJE4H.js";
13
- import "../chunk-ZZ76HNFP.js";
9
+ import "../chunk-ZRNV3W7X.js";
10
+ import "../chunk-NIWHZY4V.js";
11
+ import "../chunk-ZAIU4LPX.js";
12
+ import "../chunk-6TZSHRNP.js";
13
+ import "../chunk-CL6IVA6K.js";
14
14
  import "../chunk-3LR2NGRC.js";
15
15
  import "../chunk-QGMWUOJ3.js";
16
- import "../chunk-CKSAX7KT.js";
17
- import "../chunk-UDZLWCHF.js";
16
+ import "../chunk-6MJPXSAN.js";
17
+ import "../chunk-E5PL24PI.js";
18
18
  export {
19
19
  TestCommand,
20
20
  TestView,
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  findProjectRoot,
4
4
  generateGlobalHelp,
5
5
  logger
6
- } from "./chunk-UDZLWCHF.js";
6
+ } from "./chunk-E5PL24PI.js";
7
7
 
8
8
  // src/index.ts
9
9
  import { readdir } from "fs/promises";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhseung/ps-cli",
3
- "version": "1.12.2",
3
+ "version": "1.13.0",
4
4
  "description": "백준(BOJ) 문제 해결을 위한 통합 CLI 도구",
5
5
  "type": "module",
6
6
  "bin": {