@rhseung/ps-cli 1.5.0 → 1.6.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.
@@ -1,70 +1,346 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ source_default
4
+ } from "../chunk-ASMT3CRD.js";
2
5
  import {
3
6
  searchProblems
4
7
  } from "../chunk-AG6KWWHS.js";
8
+ import {
9
+ getProblem,
10
+ getTierColor,
11
+ getTierName
12
+ } from "../chunk-NH36IFWR.js";
5
13
  import {
6
14
  useOpenBrowser
7
15
  } from "../chunk-GCOFFYJ3.js";
8
16
  import "../chunk-QGMWUOJ3.js";
9
- import {
10
- getTierColor,
11
- getTierName,
12
- source_default
13
- } from "../chunk-HDNNR5OY.js";
14
17
  import {
15
18
  Command,
16
19
  CommandBuilder,
17
20
  CommandDef,
18
21
  getProblemDirPath
19
- } from "../chunk-7SVCS23X.js";
22
+ } from "../chunk-RVD22OUQ.js";
20
23
  import {
21
24
  __decorateClass
22
25
  } from "../chunk-7MQMPJ3X.js";
23
26
 
24
27
  // src/commands/search.tsx
25
28
  import { existsSync } from "fs";
26
- import { Alert, Select, Spinner } from "@inkjs/ui";
27
- import { Box, Text } from "ink";
29
+ import { Alert, Spinner } from "@inkjs/ui";
30
+ import { Box as Box2, Text as Text2 } from "ink";
28
31
  import { useEffect, useState } from "react";
32
+
33
+ // src/components/problem-selector.tsx
34
+ import { Select } from "@inkjs/ui";
35
+ import { Box, Text } from "ink";
29
36
  import { jsx, jsxs } from "react/jsx-runtime";
37
+ function ProblemSelector({
38
+ problems,
39
+ currentPage,
40
+ totalPages,
41
+ showPagination = false,
42
+ onSelect,
43
+ onPageChange,
44
+ header
45
+ }) {
46
+ const options = [];
47
+ problems.forEach((problem) => {
48
+ const solvedText = problem.solvedCount ? ` (${problem.solvedCount.toLocaleString()}\uBA85` : "";
49
+ const triesText = problem.averageTries ? `, \uD3C9\uADE0 ${problem.averageTries}\uD68C` : "";
50
+ const suffix = solvedText + triesText + (solvedText ? ")" : "");
51
+ const solvedMark = problem.isSolved ? " \u2713" : "";
52
+ let tierText = "";
53
+ if (problem.level) {
54
+ const tierName = getTierName(problem.level);
55
+ const tierColor = getTierColor(problem.level);
56
+ if (typeof tierColor === "string") {
57
+ tierText = ` ${source_default.bold.hex(tierColor)(tierName)}`;
58
+ } else {
59
+ tierText = ` ${tierColor(source_default.bold(tierName))}`;
60
+ }
61
+ }
62
+ const problemText = `${problem.problemId} - ${problem.title}`;
63
+ options.push({
64
+ label: `${tierText} ${problemText}${solvedMark}${suffix}`,
65
+ value: `problem:${problem.problemId}`
66
+ });
67
+ });
68
+ if (showPagination && currentPage !== void 0 && totalPages !== void 0) {
69
+ if (currentPage < totalPages) {
70
+ options.push({
71
+ label: `\u2192 \uB2E4\uC74C \uD398\uC774\uC9C0 (${currentPage + 1}/${totalPages})`,
72
+ value: "next-page"
73
+ });
74
+ }
75
+ if (currentPage > 1) {
76
+ options.push({
77
+ label: `\u2190 \uC774\uC804 \uD398\uC774\uC9C0 (${currentPage - 1}/${totalPages})`,
78
+ value: "prev-page"
79
+ });
80
+ }
81
+ }
82
+ const handleSelect = (value) => {
83
+ if (value === "next-page" && onPageChange && currentPage !== void 0) {
84
+ onPageChange(currentPage + 1);
85
+ return;
86
+ }
87
+ if (value === "prev-page" && onPageChange && currentPage !== void 0) {
88
+ onPageChange(currentPage - 1);
89
+ return;
90
+ }
91
+ if (value.startsWith("problem:")) {
92
+ const problemId = parseInt(value.replace("problem:", ""), 10);
93
+ if (!isNaN(problemId)) {
94
+ onSelect(problemId);
95
+ }
96
+ }
97
+ };
98
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
99
+ header && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: header }),
100
+ showPagination && currentPage !== void 0 && totalPages !== void 0 && /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
101
+ "\uD398\uC774\uC9C0 ",
102
+ currentPage,
103
+ "/",
104
+ totalPages
105
+ ] }) }),
106
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Select, { options, onChange: handleSelect }) })
107
+ ] });
108
+ }
109
+
110
+ // src/services/workbook-scraper.ts
111
+ import * as cheerio from "cheerio";
112
+ var BOJ_BASE_URL = "https://www.acmicpc.net";
113
+ async function scrapeWorkbook(workbookId) {
114
+ const url = `${BOJ_BASE_URL}/workbook/view/${workbookId}`;
115
+ const response = await fetch(url, {
116
+ headers: {
117
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
118
+ }
119
+ });
120
+ if (!response.ok) {
121
+ throw new Error(
122
+ `Failed to fetch workbook page: HTTP ${response.status}. \uBB38\uC81C\uC9D1\uC774 \uC874\uC7AC\uD558\uC9C0 \uC54A\uAC70\uB098 \uC811\uADFC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
123
+ );
124
+ }
125
+ const html = await response.text();
126
+ const $ = cheerio.load(html);
127
+ let title = "";
128
+ const titleElement = $("h1").first();
129
+ if (titleElement.length > 0) {
130
+ title = titleElement.text().trim();
131
+ } else {
132
+ const pageTitle = $("title").text().trim();
133
+ const match = pageTitle.match(/문제집[:\s]*(.+)/);
134
+ if (match) {
135
+ title = match[1].trim();
136
+ } else {
137
+ title = pageTitle;
138
+ }
139
+ }
140
+ const problems = [];
141
+ const table = $("table.table.table-striped.table-bordered");
142
+ if (table.length === 0) {
143
+ throw new Error(
144
+ `\uBB38\uC81C\uC9D1 ${workbookId}\uC758 \uBB38\uC81C \uBAA9\uB85D\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD398\uC774\uC9C0 \uAD6C\uC870\uAC00 \uBCC0\uACBD\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.`
145
+ );
146
+ }
147
+ const rows = table.find("tbody tr");
148
+ rows.each((index, row) => {
149
+ const $row = $(row);
150
+ const cells = $row.find("td");
151
+ if (cells.length >= 2) {
152
+ const problemIdText = $(cells[0]).text().trim();
153
+ const problemId = parseInt(problemIdText, 10);
154
+ const title2 = $(cells[1]).text().trim();
155
+ if (!isNaN(problemId) && title2) {
156
+ problems.push({
157
+ problemId,
158
+ title: title2,
159
+ order: index + 1
160
+ // 1부터 시작하는 순서
161
+ });
162
+ }
163
+ }
164
+ });
165
+ if (problems.length === 0) {
166
+ throw new Error(
167
+ `\uBB38\uC81C\uC9D1 ${workbookId}\uC5D0 \uBB38\uC81C\uAC00 \uC5C6\uAC70\uB098 \uBB38\uC81C \uBAA9\uB85D\uC744 \uCD94\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
168
+ );
169
+ }
170
+ if (!title) {
171
+ title = `\uBB38\uC81C\uC9D1 ${workbookId}`;
172
+ }
173
+ return {
174
+ id: workbookId,
175
+ title,
176
+ problems,
177
+ createdAt: /* @__PURE__ */ new Date()
178
+ };
179
+ }
180
+
181
+ // src/commands/search.tsx
182
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
183
+ async function enrichProblemsWithTiers(problems) {
184
+ const BATCH_SIZE = 10;
185
+ const DELAY_MS = 200;
186
+ const enriched = [];
187
+ for (let i = 0; i < problems.length; i += BATCH_SIZE) {
188
+ const batch = problems.slice(i, i + BATCH_SIZE);
189
+ const batchPromises = batch.map(async (problem) => {
190
+ try {
191
+ const solvedAcData = await getProblem(problem.problemId);
192
+ return {
193
+ ...problem,
194
+ level: solvedAcData.level
195
+ };
196
+ } catch (error) {
197
+ console.warn(
198
+ `\uBB38\uC81C ${problem.problemId}\uC758 \uD2F0\uC5B4 \uC815\uBCF4\uB97C \uAC00\uC838\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${error instanceof Error ? error.message : String(error)}`
199
+ );
200
+ return problem;
201
+ }
202
+ });
203
+ const batchResults = await Promise.all(batchPromises);
204
+ enriched.push(...batchResults);
205
+ if (i + BATCH_SIZE < problems.length) {
206
+ await new Promise((resolve) => setTimeout(resolve, DELAY_MS));
207
+ }
208
+ }
209
+ return enriched;
210
+ }
211
+ function WorkbookSearchView({
212
+ workbookId,
213
+ onComplete
214
+ }) {
215
+ const [problems, setProblems] = useState([]);
216
+ const [workbookTitle, setWorkbookTitle] = useState("");
217
+ const [loading, setLoading] = useState(true);
218
+ const [error, setError] = useState(null);
219
+ const [selectedProblemId, setSelectedProblemId] = useState(
220
+ null
221
+ );
222
+ useEffect(() => {
223
+ async function loadWorkbook() {
224
+ try {
225
+ setLoading(true);
226
+ setError(null);
227
+ const workbook = await scrapeWorkbook(workbookId);
228
+ setWorkbookTitle(workbook.title);
229
+ const enriched = await enrichProblemsWithTiers(workbook.problems);
230
+ setProblems(enriched);
231
+ } catch (err) {
232
+ setError(err instanceof Error ? err.message : String(err));
233
+ } finally {
234
+ setLoading(false);
235
+ }
236
+ }
237
+ void loadWorkbook();
238
+ }, [workbookId]);
239
+ if (selectedProblemId) {
240
+ return /* @__PURE__ */ jsx2(OpenBrowserView, { problemId: selectedProblemId, onComplete });
241
+ }
242
+ if (loading) {
243
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
244
+ /* @__PURE__ */ jsx2(Spinner, { label: "\uBB38\uC81C\uC9D1\uC744 \uB85C\uB4DC\uD558\uB294 \uC911..." }),
245
+ /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
246
+ "\uBB38\uC81C\uC9D1 ID: ",
247
+ workbookId
248
+ ] }) })
249
+ ] });
250
+ }
251
+ if (error) {
252
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
253
+ /* @__PURE__ */ jsxs2(Alert, { variant: "error", children: [
254
+ "\uC624\uB958: ",
255
+ error
256
+ ] }),
257
+ /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
258
+ "\uBB38\uC81C\uC9D1 ID: ",
259
+ workbookId
260
+ ] }) })
261
+ ] });
262
+ }
263
+ if (problems.length === 0) {
264
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
265
+ /* @__PURE__ */ jsx2(Alert, { variant: "info", children: "\uBB38\uC81C\uC9D1\uC5D0 \uBB38\uC81C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }),
266
+ /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
267
+ "\uBB38\uC81C\uC9D1 ID: ",
268
+ workbookId
269
+ ] }) })
270
+ ] });
271
+ }
272
+ const problemsWithSolvedStatus = problems.map((problem) => {
273
+ const problemDirPath = getProblemDirPath(problem.problemId);
274
+ const isSolved = existsSync(problemDirPath);
275
+ return {
276
+ problemId: problem.problemId,
277
+ title: problem.title,
278
+ level: problem.level,
279
+ isSolved
280
+ };
281
+ });
282
+ return /* @__PURE__ */ jsx2(
283
+ ProblemSelector,
284
+ {
285
+ problems: problemsWithSolvedStatus,
286
+ onSelect: (problemId) => {
287
+ setSelectedProblemId(problemId);
288
+ },
289
+ header: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
290
+ /* @__PURE__ */ jsx2(Box2, { marginBottom: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "cyan", bold: true, children: [
291
+ "\u{1F4DA} \uBB38\uC81C\uC9D1: ",
292
+ workbookTitle,
293
+ " (ID: ",
294
+ workbookId,
295
+ ")"
296
+ ] }) }),
297
+ /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
298
+ "\uCD1D ",
299
+ problems.length,
300
+ "\uBB38\uC81C"
301
+ ] }) })
302
+ ] })
303
+ }
304
+ );
305
+ }
30
306
  function OpenBrowserView({ problemId, onComplete }) {
31
307
  const { status, error, url } = useOpenBrowser({
32
308
  problemId,
33
309
  onComplete
34
310
  });
35
311
  if (status === "loading") {
36
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
37
- /* @__PURE__ */ jsx(Spinner, { label: "\uBE0C\uB77C\uC6B0\uC800\uB97C \uC5EC\uB294 \uC911..." }),
38
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
312
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
313
+ /* @__PURE__ */ jsx2(Spinner, { label: "\uBE0C\uB77C\uC6B0\uC800\uB97C \uC5EC\uB294 \uC911..." }),
314
+ /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
39
315
  "\uBB38\uC81C #",
40
316
  problemId
41
317
  ] }) })
42
318
  ] });
43
319
  }
44
320
  if (status === "error") {
45
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
46
- /* @__PURE__ */ jsxs(Alert, { variant: "error", children: [
321
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
322
+ /* @__PURE__ */ jsxs2(Alert, { variant: "error", children: [
47
323
  "\uBE0C\uB77C\uC6B0\uC800\uB97C \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ",
48
324
  error
49
325
  ] }),
50
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
326
+ /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
51
327
  "URL: ",
52
328
  url
53
329
  ] }) })
54
330
  ] });
55
331
  }
56
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
57
- /* @__PURE__ */ jsx(Alert, { variant: "success", children: "\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uBB38\uC81C \uD398\uC774\uC9C0\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4!" }),
58
- /* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
59
- /* @__PURE__ */ jsxs(Text, { children: [
60
- /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\uBB38\uC81C \uBC88\uD638:" }),
332
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
333
+ /* @__PURE__ */ jsx2(Alert, { variant: "success", children: "\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uBB38\uC81C \uD398\uC774\uC9C0\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4!" }),
334
+ /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
335
+ /* @__PURE__ */ jsxs2(Text2, { children: [
336
+ /* @__PURE__ */ jsx2(Text2, { color: "cyan", bold: true, children: "\uBB38\uC81C \uBC88\uD638:" }),
61
337
  " ",
62
338
  problemId
63
339
  ] }),
64
- /* @__PURE__ */ jsxs(Text, { children: [
65
- /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "URL:" }),
340
+ /* @__PURE__ */ jsxs2(Text2, { children: [
341
+ /* @__PURE__ */ jsx2(Text2, { color: "cyan", bold: true, children: "URL:" }),
66
342
  " ",
67
- /* @__PURE__ */ jsx(Text, { color: "blue", underline: true, children: url })
343
+ /* @__PURE__ */ jsx2(Text2, { color: "blue", underline: true, children: url })
68
344
  ] })
69
345
  ] })
70
346
  ] });
@@ -105,114 +381,92 @@ function SearchView({ query, onComplete }) {
105
381
  void performSearch();
106
382
  }, [query, currentPage]);
107
383
  if (loading && !selectedProblemId) {
108
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
109
- /* @__PURE__ */ jsx(Spinner, { label: "\uAC80\uC0C9 \uC911..." }),
110
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
384
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
385
+ /* @__PURE__ */ jsx2(Spinner, { label: "\uAC80\uC0C9 \uC911..." }),
386
+ /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
111
387
  "\uCFFC\uB9AC: ",
112
388
  query
113
389
  ] }) })
114
390
  ] });
115
391
  }
116
392
  if (error && !selectedProblemId) {
117
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
118
- /* @__PURE__ */ jsxs(Alert, { variant: "error", children: [
393
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
394
+ /* @__PURE__ */ jsxs2(Alert, { variant: "error", children: [
119
395
  "\uAC80\uC0C9 \uC2E4\uD328: ",
120
396
  error
121
397
  ] }),
122
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
398
+ /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
123
399
  "\uCFFC\uB9AC: ",
124
400
  query
125
401
  ] }) })
126
402
  ] });
127
403
  }
128
404
  if (selectedProblemId) {
129
- return /* @__PURE__ */ jsx(OpenBrowserView, { problemId: selectedProblemId, onComplete });
405
+ return /* @__PURE__ */ jsx2(OpenBrowserView, { problemId: selectedProblemId, onComplete });
130
406
  }
131
407
  if (results.length === 0) {
132
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
133
- /* @__PURE__ */ jsx(Alert, { variant: "info", children: "\uAC80\uC0C9 \uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }),
134
- /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
408
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
409
+ /* @__PURE__ */ jsx2(Alert, { variant: "info", children: "\uAC80\uC0C9 \uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }),
410
+ /* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
135
411
  "\uCFFC\uB9AC: ",
136
412
  query
137
413
  ] }) })
138
414
  ] });
139
415
  }
140
- const options = [];
141
- results.forEach((problem) => {
142
- const solvedText = problem.solvedCount ? ` (${problem.solvedCount.toLocaleString()}\uBA85` : "";
143
- const triesText = problem.averageTries ? `, \uD3C9\uADE0 ${problem.averageTries}\uD68C` : "";
144
- const suffix = solvedText + triesText + (solvedText ? ")" : "");
145
- const solvedMark = problem.isSolved ? " \u2713" : "";
146
- let tierText = "";
147
- if (problem.level) {
148
- const tierName = getTierName(problem.level);
149
- const tierColor = getTierColor(problem.level);
150
- if (typeof tierColor === "string") {
151
- tierText = ` ${source_default.bold.hex(tierColor)(tierName)}`;
152
- } else {
153
- tierText = ` ${tierColor(source_default.bold(tierName))}`;
154
- }
155
- }
156
- options.push({
157
- label: `${tierText} ${problem.problemId} - ${problem.title}${solvedMark}${suffix}`,
158
- value: `problem:${problem.problemId}`
159
- });
160
- });
161
- if (currentPage < totalPages) {
162
- options.push({
163
- label: `\u2192 \uB2E4\uC74C \uD398\uC774\uC9C0 (${currentPage + 1}/${totalPages})`,
164
- value: "next-page"
165
- });
166
- }
167
- if (currentPage > 1) {
168
- options.push({
169
- label: `\u2190 \uC774\uC804 \uD398\uC774\uC9C0 (${currentPage - 1}/${totalPages})`,
170
- value: "prev-page"
171
- });
172
- }
173
- const handleSelect = (value) => {
174
- if (value === "next-page") {
175
- setCurrentPage(currentPage + 1);
176
- return;
177
- }
178
- if (value === "prev-page") {
179
- setCurrentPage(currentPage - 1);
180
- return;
181
- }
182
- if (value.startsWith("problem:")) {
183
- const problemId = parseInt(value.replace("problem:", ""), 10);
184
- if (!isNaN(problemId)) {
416
+ return /* @__PURE__ */ jsx2(
417
+ ProblemSelector,
418
+ {
419
+ problems: results.map((problem) => ({
420
+ problemId: problem.problemId,
421
+ title: problem.title,
422
+ level: problem.level,
423
+ solvedCount: problem.solvedCount,
424
+ averageTries: problem.averageTries,
425
+ isSolved: problem.isSolved
426
+ })),
427
+ currentPage,
428
+ totalPages,
429
+ showPagination: true,
430
+ onSelect: (problemId) => {
185
431
  setSelectedProblemId(problemId);
186
- }
187
- }
188
- };
189
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
190
- /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u{1F50D} \uAC80\uC0C9 \uACB0\uACFC" }) }),
191
- /* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
192
- /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
193
- "\uCFFC\uB9AC: ",
194
- query
195
- ] }),
196
- /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
197
- " ",
198
- "(\uD398\uC774\uC9C0 ",
199
- currentPage,
200
- "/",
201
- totalPages,
202
- ")"
432
+ },
433
+ onPageChange: (page) => {
434
+ setCurrentPage(page);
435
+ },
436
+ header: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
437
+ /* @__PURE__ */ jsx2(Box2, { marginBottom: 1, children: /* @__PURE__ */ jsx2(Text2, { color: "cyan", bold: true, children: "\u{1F50D} \uAC80\uC0C9 \uACB0\uACFC" }) }),
438
+ /* @__PURE__ */ jsx2(Box2, { marginBottom: 1, children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
439
+ "\uCFFC\uB9AC: ",
440
+ query
441
+ ] }) })
203
442
  ] })
204
- ] }),
205
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Select, { options, onChange: handleSelect }) })
206
- ] });
443
+ }
444
+ );
207
445
  }
208
446
  var SearchCommand = class extends Command {
209
- async execute(args, _flags) {
447
+ async execute(args, flags) {
448
+ const workbookId = flags.workbook ? parseInt(String(flags.workbook), 10) : null;
449
+ if (workbookId) {
450
+ if (isNaN(workbookId) || workbookId <= 0) {
451
+ console.error("\uC624\uB958: \uC720\uD6A8\uD55C \uBB38\uC81C\uC9D1 ID\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694.");
452
+ console.error(`\uC0AC\uC6A9\uBC95: ps search --workbook <\uBB38\uC81C\uC9D1ID>`);
453
+ console.error(`\uB3C4\uC6C0\uB9D0: ps search --help`);
454
+ process.exit(1);
455
+ return;
456
+ }
457
+ await this.renderView(WorkbookSearchView, {
458
+ workbookId
459
+ });
460
+ return;
461
+ }
210
462
  const query = args.join(" ").trim();
211
463
  if (!query) {
212
- console.error("\uC624\uB958: \uAC80\uC0C9 \uCFFC\uB9AC\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694.");
464
+ console.error("\uC624\uB958: \uAC80\uC0C9 \uCFFC\uB9AC \uB610\uB294 --workbook \uC635\uC158\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694.");
213
465
  console.error(`\uC0AC\uC6A9\uBC95: ps search <\uCFFC\uB9AC>`);
466
+ console.error(` ps search --workbook <\uBB38\uC81C\uC9D1ID>`);
214
467
  console.error(`\uB3C4\uC6C0\uB9D0: ps search --help`);
215
468
  console.error(`\uC608\uC81C: ps search "*g1...g5"`);
469
+ console.error(` ps search --workbook 25052`);
216
470
  process.exit(1);
217
471
  return;
218
472
  }
@@ -224,10 +478,19 @@ var SearchCommand = class extends Command {
224
478
  SearchCommand = __decorateClass([
225
479
  CommandDef({
226
480
  name: "search",
227
- description: `solved.ac\uC5D0\uC11C \uBB38\uC81C\uB97C \uAC80\uC0C9\uD558\uACE0 \uC120\uD0DD\uD55C \uBB38\uC81C\uB97C \uBE0C\uB77C\uC6B0\uC800\uB85C \uC5FD\uB2C8\uB2E4.
481
+ description: `solved.ac\uC5D0\uC11C \uBB38\uC81C\uB97C \uAC80\uC0C9\uD558\uAC70\uB098 \uBC31\uC900 \uBB38\uC81C\uC9D1\uC758 \uBB38\uC81C \uBAA9\uB85D\uC744 \uD45C\uC2DC\uD569\uB2C8\uB2E4.
228
482
  - solved.ac \uAC80\uC0C9\uC5B4 \uBB38\uBC95\uC744 \uC9C0\uC6D0\uD569\uB2C8\uB2E4.
229
483
  - \uBB38\uC81C \uBAA9\uB85D\uC5D0\uC11C \uC120\uD0DD\uD558\uBA74 \uC790\uB3D9\uC73C\uB85C \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uBB38\uC81C \uD398\uC774\uC9C0\uB97C \uC5FD\uB2C8\uB2E4.
230
- - \uD398\uC774\uC9C0\uB124\uC774\uC158\uC744 \uD1B5\uD574 \uC5EC\uB7EC \uD398\uC774\uC9C0\uC758 \uACB0\uACFC\uB97C \uD0D0\uC0C9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.`,
484
+ - \uD398\uC774\uC9C0\uB124\uC774\uC158\uC744 \uD1B5\uD574 \uC5EC\uB7EC \uD398\uC774\uC9C0\uC758 \uACB0\uACFC\uB97C \uD0D0\uC0C9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
485
+ - --workbook \uC635\uC158\uC73C\uB85C \uBC31\uC900 \uBB38\uC81C\uC9D1\uC758 \uBB38\uC81C \uBAA9\uB85D\uC744 \uBCFC \uC218 \uC788\uC2B5\uB2C8\uB2E4.`,
486
+ flags: [
487
+ {
488
+ name: "workbook",
489
+ options: {
490
+ description: "\uBB38\uC81C\uC9D1 ID\uB97C \uC9C0\uC815\uD558\uC5EC \uD574\uB2F9 \uBB38\uC81C\uC9D1\uC758 \uBB38\uC81C \uBAA9\uB85D\uC744 \uD45C\uC2DC"
491
+ }
492
+ }
493
+ ],
231
494
  autoDetectProblemId: false,
232
495
  requireProblemId: false,
233
496
  examples: [
@@ -235,7 +498,8 @@ SearchCommand = __decorateClass([
235
498
  'search "tier:g1...g5" # Gold 1-5 \uBB38\uC81C \uAC80\uC0C9 (tier: \uBB38\uBC95)',
236
499
  'search "#dp" # DP \uD0DC\uADF8 \uBB38\uC81C \uAC80\uC0C9',
237
500
  'search "tag:dp" # DP \uD0DC\uADF8 \uBB38\uC81C \uAC80\uC0C9 (tag: \uBB38\uBC95)',
238
- 'search "*g1...g5 #dp" # Gold 1-5 \uD2F0\uC5B4\uC758 DP \uD0DC\uADF8 \uBB38\uC81C \uAC80\uC0C9'
501
+ 'search "*g1...g5 #dp" # Gold 1-5 \uD2F0\uC5B4\uC758 DP \uD0DC\uADF8 \uBB38\uC81C \uAC80\uC0C9',
502
+ "search --workbook 25052 # \uBB38\uC81C\uC9D1 25052\uC758 \uBB38\uC81C \uBAA9\uB85D \uD45C\uC2DC"
239
503
  ]
240
504
  })
241
505
  ], SearchCommand);