@rhseung/ps-cli 1.9.8 → 1.10.2

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.
@@ -7,14 +7,16 @@ import {
7
7
  getArchiveDir,
8
8
  getArchiveStrategy,
9
9
  getAutoOpenEditor,
10
+ getConfigMetadata,
10
11
  getDefaultLanguage,
11
12
  getEditor,
12
13
  getIncludeTag,
13
14
  getSolvedAcHandle,
14
15
  getSolvingDir,
15
16
  getSupportedLanguages,
17
+ getVersion,
16
18
  icons
17
- } from "../chunk-Q5NECGFA.js";
19
+ } from "../chunk-AHE4QHJD.js";
18
20
 
19
21
  // src/commands/init.tsx
20
22
  import {
@@ -27,23 +29,118 @@ import {
27
29
  import { Text, Box } from "ink";
28
30
 
29
31
  // src/hooks/use-init.ts
30
- import { mkdir, readFile, writeFile, access } from "fs/promises";
31
- import { join } from "path";
32
+ import { existsSync } from "fs";
33
+ import {
34
+ mkdir,
35
+ readFile,
36
+ writeFile,
37
+ access,
38
+ copyFile,
39
+ readdir
40
+ } from "fs/promises";
41
+ import { join, dirname } from "path";
42
+ import { fileURLToPath } from "url";
32
43
  import { execaCommand, execa } from "execa";
33
44
  import { useEffect, useState, useCallback } from "react";
45
+ function getCliRoot() {
46
+ const __filename = fileURLToPath(import.meta.url);
47
+ const __dirname = dirname(__filename);
48
+ let current = __dirname;
49
+ while (current !== dirname(current)) {
50
+ const templatesDir = join(current, "templates");
51
+ try {
52
+ const stats = existsSync(templatesDir);
53
+ if (stats) return current;
54
+ } catch {
55
+ }
56
+ current = dirname(current);
57
+ }
58
+ return join(__dirname, "../..");
59
+ }
60
+ function buildDefaultConfigYaml({
61
+ language,
62
+ editor,
63
+ autoOpen,
64
+ solvingDir,
65
+ archiveDir,
66
+ archiveStrategy,
67
+ includeTag,
68
+ handle
69
+ }) {
70
+ return `
71
+ # ps-cli \uC124\uC815 \uD30C\uC77C
72
+ # \uB354 \uC790\uC138\uD55C \uC815\uBCF4\uB294 \uB2E4\uC74C\uC744 \uCC38\uACE0\uD558\uC138\uC694: https://github.com/rhseung/ps-cli
73
+
74
+ general:
75
+ # \uC0C8\uB85C\uC6B4 \uBB38\uC81C\uB97C \uAC00\uC838\uC62C \uB54C \uC0AC\uC6A9\uD560 \uAE30\uBCF8 \uD504\uB85C\uADF8\uB798\uBC0D \uC5B8\uC5B4\uC785\uB2C8\uB2E4.
76
+ default_language: ${language}
77
+ # \uD1B5\uACC4 \uC870\uD68C\uB97C \uC704\uD55C Solved.ac \uD578\uB4E4(\uB2C9\uB124\uC784)\uC785\uB2C8\uB2E4.
78
+ solved_ac_handle: "${handle}"
79
+
80
+ editor:
81
+ # \uC5D0\uB514\uD130\uB97C \uC5F4 \uB54C \uC0AC\uC6A9\uD560 \uBA85\uB839\uC5B4\uC785\uB2C8\uB2E4 (\uC608: code, cursor, vim).
82
+ command: ${editor}
83
+ # \uBB38\uC81C\uB97C \uAC00\uC838\uC628 \uD6C4 \uC790\uB3D9\uC73C\uB85C \uC5D0\uB514\uD130\uB97C \uC5F4\uC9C0 \uC5EC\uBD80\uC785\uB2C8\uB2E4.
84
+ auto_open: ${autoOpen}
85
+
86
+ paths:
87
+ # \uD604\uC7AC \uD480\uACE0 \uC788\uB294 \uBB38\uC81C\uB4E4\uC744 \uB2F4\uC744 \uB514\uB809\uD1A0\uB9AC \uACBD\uB85C\uC785\uB2C8\uB2E4.
88
+ solving: ${solvingDir}
89
+ # \uD574\uACB0\uD55C \uBB38\uC81C\uB97C \uBCF4\uAD00\uD560 \uB514\uB809\uD1A0\uB9AC \uACBD\uB85C\uC785\uB2C8\uB2E4.
90
+ archive: ${archiveDir}
91
+ # \uC544\uCE74\uC774\uBE59 \uC804\uB7B5\uC785\uB2C8\uB2E4 (flat, by-range, by-tier, by-tag).
92
+ archive_strategy: ${archiveStrategy}
93
+
94
+ archive:
95
+ # \uC544\uCE74\uC774\uBE0C \uC2DC \uC790\uB3D9\uC73C\uB85C Git \uCEE4\uBC0B\uC744 \uC218\uD589\uD560\uC9C0 \uC5EC\uBD80\uC785\uB2C8\uB2E4.
96
+ auto_commit: true
97
+ # Git \uCEE4\uBC0B \uBA54\uC2DC\uC9C0 \uD15C\uD50C\uB9BF\uC785\uB2C8\uB2E4 ({id}, {title} \uC0AC\uC6A9 \uAC00\uB2A5).
98
+ commit_message: "feat: solve {id} {title}"
99
+
100
+ markdown:
101
+ # \uBB38\uC81C README\uC5D0 \uC54C\uACE0\uB9AC\uC998 \uBD84\uB958(\uD0DC\uADF8)\uB97C \uD3EC\uD568\uD560\uC9C0 \uC5EC\uBD80\uC785\uB2C8\uB2E4.
102
+ include_tag: ${includeTag}
103
+
104
+ # \uC5B8\uC5B4\uBCC4 \uC124\uC815
105
+ # \uC774\uACF3\uC5D0\uC11C \uCEF4\uD30C\uC77C/\uC2E4\uD589 \uBA85\uB839\uC5B4\uB098 \uD15C\uD50C\uB9BF \uD30C\uC77C\uBA85\uC744 \uC218\uC815\uD558\uAC70\uB098 \uC0C8\uB85C\uC6B4 \uC5B8\uC5B4\uB97C \uCD94\uAC00\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
106
+ languages:
107
+ python:
108
+ extension: py
109
+ # \uD15C\uD50C\uB9BF \uD30C\uC77C\uBA85 (\uC635\uC158, \uAE30\uBCF8\uAC12: solution.py)
110
+ template_file: "solution.py"
111
+ # \uC2E4\uD589 \uBA85\uB839\uC5B4 (\uD544\uC218)
112
+ run: python3
113
+ cpp:
114
+ extension: cpp
115
+ # \uD15C\uD50C\uB9BF \uD30C\uC77C\uBA85 (\uC635\uC158, \uAE30\uBCF8\uAC12: solution.cpp)
116
+ template_file: "solution.cpp"
117
+ # \uCEF4\uD30C\uC77C \uBA85\uB839\uC5B4 (\uC635\uC158)
118
+ compile: "g++ -fdiagnostics-absolute-paths -o solution solution.cpp"
119
+ # \uC2E4\uD589 \uBA85\uB839\uC5B4 (\uD544\uC218)
120
+ run: "./solution"
121
+ # rust:
122
+ # extension: rs
123
+ # # \uD15C\uD50C\uB9BF \uD30C\uC77C\uBA85\uC744 \uC9C1\uC811 \uC9C0\uC815\uD558\uB824\uBA74 template_file\uC744 \uC0AC\uC6A9\uD558\uC138\uC694 (\uAE30\uBCF8: solution.{extension})
124
+ # template_file: "solution.rs"
125
+ # compile: "rustc {file}"
126
+ # run: "./{file_no_ext}"
127
+ `.trim();
128
+ }
34
129
  function useInit({ onComplete }) {
35
130
  const [currentStep, setCurrentStep] = useState("archive-dir");
36
131
  const [completedSteps, setCompletedSteps] = useState([]);
37
132
  const [confirmExit, setConfirmExit] = useState(false);
38
133
  const [initialized, setInitialized] = useState(false);
39
- const [archiveDir, setArchiveDirValue] = useState(getArchiveDir());
40
- const [solvingDir, setSolvingDirValue] = useState(getSolvingDir());
41
- const [archiveStrategy, setArchiveStrategy] = useState(getArchiveStrategy());
42
- const [language, setLanguage] = useState(getDefaultLanguage());
43
- const [editor, setEditorValue] = useState(getEditor());
44
- const [autoOpen, setAutoOpen] = useState(getAutoOpenEditor());
45
- const [includeTag, setIncludeTag] = useState(getIncludeTag());
46
- const [handle, setHandle] = useState(getSolvedAcHandle() || "");
134
+ const [form, setForm] = useState({
135
+ archiveDir: getArchiveDir(),
136
+ solvingDir: getSolvingDir(),
137
+ archiveStrategy: getArchiveStrategy(),
138
+ language: getDefaultLanguage(),
139
+ editor: getEditor(),
140
+ autoOpen: getAutoOpenEditor(),
141
+ includeTag: getIncludeTag(),
142
+ handle: getSolvedAcHandle() || ""
143
+ });
47
144
  const [handleInputMode, setHandleInputMode] = useState(false);
48
145
  const [created, setCreated] = useState([]);
49
146
  const [cancelled, setCancelled] = useState(false);
@@ -72,21 +169,17 @@ function useInit({ onComplete }) {
72
169
  await access(projectConfigPath);
73
170
  const configContent = await readFile(projectConfigPath, "utf-8");
74
171
  const projectConfig = JSON.parse(configContent);
75
- if (projectConfig.archiveDir)
76
- setArchiveDirValue(projectConfig.archiveDir);
77
- if (projectConfig.solvingDir)
78
- setSolvingDirValue(projectConfig.solvingDir);
79
- if (projectConfig.archiveStrategy)
80
- setArchiveStrategy(projectConfig.archiveStrategy);
81
- if (projectConfig.defaultLanguage)
82
- setLanguage(projectConfig.defaultLanguage);
83
- if (projectConfig.editor) setEditorValue(projectConfig.editor);
84
- if (projectConfig.autoOpenEditor !== void 0)
85
- setAutoOpen(projectConfig.autoOpenEditor);
86
- if (projectConfig.includeTag !== void 0)
87
- setIncludeTag(projectConfig.includeTag);
88
- if (projectConfig.solvedAcHandle)
89
- setHandle(projectConfig.solvedAcHandle);
172
+ setForm((prev) => ({
173
+ ...prev,
174
+ archiveDir: projectConfig.archiveDir ?? prev.archiveDir,
175
+ solvingDir: projectConfig.solvingDir ?? prev.solvingDir,
176
+ archiveStrategy: projectConfig.archiveStrategy ?? prev.archiveStrategy,
177
+ language: projectConfig.defaultLanguage ?? prev.language,
178
+ editor: projectConfig.editor ?? prev.editor,
179
+ autoOpen: projectConfig.autoOpenEditor !== void 0 ? projectConfig.autoOpenEditor : prev.autoOpen,
180
+ includeTag: projectConfig.includeTag !== void 0 ? projectConfig.includeTag : prev.includeTag,
181
+ handle: projectConfig.solvedAcHandle ?? prev.handle
182
+ }));
90
183
  } catch {
91
184
  } finally {
92
185
  setInitialized(true);
@@ -95,34 +188,28 @@ function useInit({ onComplete }) {
95
188
  void loadProjectConfig();
96
189
  }, []);
97
190
  const getStepLabel = useCallback((step) => {
98
- switch (step) {
99
- case "archive-dir":
100
- return "\uC544\uCE74\uC774\uBE0C \uB514\uB809\uD1A0\uB9AC \uC124\uC815 (\uC544\uCE74\uC774\uBE0C\uB41C \uBB38\uC81C)";
101
- case "solving-dir":
102
- return "Solving \uB514\uB809\uD1A0\uB9AC \uC124\uC815 (\uD478\uB294 \uC911\uC778 \uBB38\uC81C)";
103
- case "archive-strategy":
104
- return "\uC544\uCE74\uC774\uBE59 \uC804\uB7B5 \uC124\uC815";
105
- case "language":
106
- return "\uAE30\uBCF8 \uC5B8\uC5B4 \uC124\uC815";
107
- case "editor":
108
- return "\uC5D0\uB514\uD130 \uC124\uC815";
109
- case "auto-open":
110
- return "\uC790\uB3D9 \uC5D0\uB514\uD130 \uC5F4\uAE30";
111
- case "include-tag":
112
- return "README\uC5D0 \uC54C\uACE0\uB9AC\uC998 \uBD84\uB958 \uD3EC\uD568";
113
- case "handle":
114
- return "Solved.ac \uD578\uB4E4 (\uC120\uD0DD)";
115
- default:
116
- return "";
117
- }
191
+ const stepToConfigKey = {
192
+ "archive-dir": "paths.archive",
193
+ "solving-dir": "paths.solving",
194
+ "archive-strategy": "paths.archive-strategy",
195
+ language: "general.default-language",
196
+ editor: "editor.command",
197
+ "auto-open": "editor.auto-open",
198
+ "include-tag": "markdown.include-tag",
199
+ handle: "general.solved-ac-handle"
200
+ };
201
+ const configKey = stepToConfigKey[step];
202
+ if (!configKey) return "";
203
+ const meta = getConfigMetadata().find((m) => m.key === configKey);
204
+ return meta?.label ?? "";
118
205
  }, []);
119
206
  const getStepValue = useCallback(
120
207
  (step) => {
121
208
  switch (step) {
122
209
  case "archive-dir":
123
- return archiveDir === "." ? "\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8" : archiveDir;
210
+ return form.archiveDir === "." ? "\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8" : form.archiveDir;
124
211
  case "solving-dir":
125
- return solvingDir === "." ? "\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8" : solvingDir;
212
+ return form.solvingDir === "." ? "\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8" : form.solvingDir;
126
213
  case "archive-strategy": {
127
214
  const strategyLabels = {
128
215
  flat: "\uD3C9\uBA74 (\uC804\uBD80 \uB098\uC5F4)",
@@ -130,62 +217,66 @@ function useInit({ onComplete }) {
130
217
  "by-tier": "\uD2F0\uC5B4\uBCC4",
131
218
  "by-tag": "\uD0DC\uADF8\uBCC4"
132
219
  };
133
- return strategyLabels[archiveStrategy] || archiveStrategy;
220
+ return strategyLabels[form.archiveStrategy] || form.archiveStrategy;
134
221
  }
135
222
  case "language":
136
- return language;
223
+ return form.language;
137
224
  case "editor":
138
- return editor;
225
+ return form.editor;
139
226
  case "auto-open":
140
- return autoOpen ? "\uC608" : "\uC544\uB2C8\uC624";
227
+ return form.autoOpen ? "\uC608" : "\uC544\uB2C8\uC624";
141
228
  case "include-tag":
142
- return includeTag ? "\uC608" : "\uC544\uB2C8\uC624";
229
+ return form.includeTag ? "\uC608" : "\uC544\uB2C8\uC624";
143
230
  case "handle":
144
- return handle || "(\uC2A4\uD0B5)";
231
+ return form.handle || "(\uC2A4\uD0B5)";
145
232
  default:
146
233
  return "";
147
234
  }
148
235
  },
149
- [
150
- archiveDir,
151
- solvingDir,
152
- language,
153
- editor,
154
- autoOpen,
155
- includeTag,
156
- handle,
157
- archiveStrategy
158
- ]
236
+ [form]
159
237
  );
160
238
  const executeInit = useCallback(
161
239
  async (overrideHandle) => {
162
240
  try {
163
241
  const cwd = process.cwd();
164
- const projectConfigPath = join(cwd, ".ps-cli.json");
165
- const projectConfig = {
166
- archiveDir,
167
- solvingDir,
168
- archiveStrategy,
169
- defaultLanguage: language,
170
- editor,
171
- autoOpenEditor: autoOpen,
172
- includeTag
173
- };
174
- const handleToUse = overrideHandle ?? handle;
175
- if (handleToUse && typeof handleToUse === "string" && handleToUse.trim().length > 0) {
176
- projectConfig.solvedAcHandle = handleToUse.trim();
242
+ const cliRoot = getCliRoot();
243
+ const psCliDir = join(cwd, ".ps-cli");
244
+ const templatesDir = join(psCliDir, "templates");
245
+ await mkdir(psCliDir, { recursive: true });
246
+ await mkdir(templatesDir, { recursive: true });
247
+ setCreated((prev) => [...prev, ".ps-cli/"]);
248
+ const handleToUse = (overrideHandle ?? form.handle)?.trim() || "";
249
+ const configYaml = buildDefaultConfigYaml({
250
+ language: form.language,
251
+ editor: form.editor,
252
+ autoOpen: form.autoOpen,
253
+ solvingDir: form.solvingDir,
254
+ archiveDir: form.archiveDir,
255
+ archiveStrategy: form.archiveStrategy,
256
+ includeTag: form.includeTag,
257
+ handle: handleToUse
258
+ });
259
+ await writeFile(join(psCliDir, "config.yaml"), configYaml, "utf-8");
260
+ setCreated((prev) => [...prev, ".ps-cli/config.yaml"]);
261
+ const defaultTemplatesDir = join(cliRoot, "templates");
262
+ if (existsSync(defaultTemplatesDir)) {
263
+ const files = await readdir(defaultTemplatesDir);
264
+ for (const file of files) {
265
+ await copyFile(
266
+ join(defaultTemplatesDir, file),
267
+ join(templatesDir, file)
268
+ );
269
+ }
270
+ setCreated((prev) => [
271
+ ...prev,
272
+ ".ps-cli/templates/ (\uAE30\uBCF8 \uD15C\uD50C\uB9BF \uBCF5\uC0AC)"
273
+ ]);
177
274
  }
178
- await writeFile(
179
- projectConfigPath,
180
- JSON.stringify(projectConfig, null, 2),
181
- "utf-8"
182
- );
183
- setCreated((prev) => [...prev, ".ps-cli.json"]);
184
- if (archiveDir !== "." && archiveDir !== "") {
185
- const archiveDirPath = join(cwd, archiveDir);
275
+ if (form.archiveDir !== "." && form.archiveDir !== "") {
276
+ const archiveDirPath = join(cwd, form.archiveDir);
186
277
  try {
187
278
  await mkdir(archiveDirPath, { recursive: true });
188
- setCreated((prev) => [...prev, `${archiveDir}/`]);
279
+ setCreated((prev) => [...prev, `${form.archiveDir}/`]);
189
280
  } catch (err) {
190
281
  const error = err;
191
282
  if (error.code !== "EEXIST") {
@@ -193,11 +284,11 @@ function useInit({ onComplete }) {
193
284
  }
194
285
  }
195
286
  }
196
- if (solvingDir !== "." && solvingDir !== "") {
197
- const solvingDirPath = join(cwd, solvingDir);
287
+ if (form.solvingDir !== "." && form.solvingDir !== "") {
288
+ const solvingDirPath = join(cwd, form.solvingDir);
198
289
  try {
199
290
  await mkdir(solvingDirPath, { recursive: true });
200
- setCreated((prev) => [...prev, `${solvingDir}/`]);
291
+ setCreated((prev) => [...prev, `${form.solvingDir}/`]);
201
292
  } catch (err) {
202
293
  const error = err;
203
294
  if (error.code !== "EEXIST") {
@@ -207,8 +298,8 @@ function useInit({ onComplete }) {
207
298
  }
208
299
  const gitignorePath = join(cwd, ".gitignore");
209
300
  const gitignorePatterns = [];
210
- if (solvingDir !== "." && solvingDir !== "") {
211
- gitignorePatterns.push(`${solvingDir}/`);
301
+ if (form.solvingDir !== "." && form.solvingDir !== "") {
302
+ gitignorePatterns.push(`${form.solvingDir}/`);
212
303
  }
213
304
  if (gitignorePatterns.length > 0) {
214
305
  try {
@@ -217,7 +308,8 @@ function useInit({ onComplete }) {
217
308
  let hasChanges = false;
218
309
  for (const pattern of gitignorePatterns) {
219
310
  if (!gitignoreContent.includes(pattern)) {
220
- updatedContent += (updatedContent ? "\n" : "") + `# ps-cli \uBB38\uC81C \uB514\uB809\uD1A0\uB9AC
311
+ updatedContent += (updatedContent ? "\n" : "") + `
312
+ # ps-cli \uBB38\uC81C \uB514\uB809\uD1A0\uB9AC
221
313
  ${pattern}`;
222
314
  hasChanges = true;
223
315
  }
@@ -251,10 +343,9 @@ ${gitignorePatterns.join("\n")}
251
343
  await execaCommand("git init", { cwd });
252
344
  setCreated((prev) => [...prev, "Git \uC800\uC7A5\uC18C \uCD08\uAE30\uD654"]);
253
345
  }
254
- const filesToAdd = [".ps-cli.json"];
255
- const gitignorePath2 = join(cwd, ".gitignore");
346
+ const filesToAdd = [".ps-cli"];
256
347
  try {
257
- await access(gitignorePath2);
348
+ await access(gitignorePath);
258
349
  filesToAdd.push(".gitignore");
259
350
  } catch {
260
351
  }
@@ -288,17 +379,7 @@ ${gitignorePatterns.join("\n")}
288
379
  }, 2e3);
289
380
  }
290
381
  },
291
- [
292
- archiveDir,
293
- solvingDir,
294
- archiveStrategy,
295
- language,
296
- editor,
297
- autoOpen,
298
- includeTag,
299
- handle,
300
- onComplete
301
- ]
382
+ [form, onComplete]
302
383
  );
303
384
  const moveToNextStep = useCallback(
304
385
  (selectedValue, stepLabel, handleValue) => {
@@ -307,7 +388,10 @@ ${gitignorePatterns.join("\n")}
307
388
  { label: stepLabel, value: selectedValue }
308
389
  ]);
309
390
  if (currentStep === "handle" && handleValue !== void 0) {
310
- setHandle(handleValue);
391
+ setForm((prev) => ({
392
+ ...prev,
393
+ handle: handleValue
394
+ }));
311
395
  }
312
396
  const stepOrder = [
313
397
  "archive-dir",
@@ -340,25 +424,11 @@ ${gitignorePatterns.join("\n")}
340
424
  completedSteps,
341
425
  confirmExit,
342
426
  initialized,
343
- archiveDir,
344
- solvingDir,
345
- archiveStrategy,
346
- language,
347
- editor,
348
- autoOpen,
349
- includeTag,
350
- handle,
427
+ form,
351
428
  handleInputMode,
352
429
  created,
353
430
  cancelled,
354
- setArchiveDirValue,
355
- setSolvingDirValue,
356
- setArchiveStrategy,
357
- setLanguage,
358
- setEditorValue,
359
- setAutoOpen,
360
- setIncludeTag,
361
- setHandle,
431
+ setForm,
362
432
  setHandleInputMode,
363
433
  setConfirmExit,
364
434
  setCurrentStep,
@@ -369,22 +439,6 @@ ${gitignorePatterns.join("\n")}
369
439
  };
370
440
  }
371
441
 
372
- // src/utils/version.ts
373
- import { readFileSync } from "fs";
374
- import { join as join2, dirname } from "path";
375
- import { fileURLToPath } from "url";
376
- function getVersion() {
377
- try {
378
- const __filename = fileURLToPath(import.meta.url);
379
- const __dirname = dirname(__filename);
380
- const packageJsonPath = join2(__dirname, "../../package.json");
381
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
382
- return packageJson.version;
383
- } catch {
384
- return "";
385
- }
386
- }
387
-
388
442
  // src/commands/init.tsx
389
443
  import { jsx, jsxs } from "react/jsx-runtime";
390
444
  function InitView({ onComplete }) {
@@ -393,17 +447,13 @@ function InitView({ onComplete }) {
393
447
  completedSteps,
394
448
  confirmExit,
395
449
  initialized,
450
+ // form은 현재 단계 값 표시 등 확장용으로 남겨두되
451
+ // 아직 직접 사용하지 않으므로 린트 예외 패턴에 맞춰 _ 접두사를 사용한다.
452
+ form: _form,
396
453
  handleInputMode,
397
454
  created,
398
455
  cancelled,
399
- setArchiveDirValue,
400
- setSolvingDirValue,
401
- setArchiveStrategy,
402
- setLanguage,
403
- setEditorValue,
404
- setAutoOpen,
405
- setIncludeTag,
406
- setHandle,
456
+ setForm,
407
457
  setHandleInputMode,
408
458
  setConfirmExit,
409
459
  setCurrentStep,
@@ -456,7 +506,7 @@ function InitView({ onComplete }) {
456
506
  {
457
507
  options,
458
508
  onChange: (value) => {
459
- setArchiveDirValue(value);
509
+ setForm((prev) => ({ ...prev, archiveDir: value }));
460
510
  const displayValue = value === "." ? "\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8" : value;
461
511
  moveToNextStep(displayValue, getStepLabel(currentStep));
462
512
  }
@@ -476,7 +526,7 @@ function InitView({ onComplete }) {
476
526
  {
477
527
  options,
478
528
  onChange: (value) => {
479
- setSolvingDirValue(value);
529
+ setForm((prev) => ({ ...prev, solvingDir: value }));
480
530
  const displayValue = value === "." ? "\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8" : value;
481
531
  moveToNextStep(displayValue, getStepLabel(currentStep));
482
532
  }
@@ -498,7 +548,7 @@ function InitView({ onComplete }) {
498
548
  {
499
549
  options,
500
550
  onChange: (value) => {
501
- setArchiveStrategy(value);
551
+ setForm((prev) => ({ ...prev, archiveStrategy: value }));
502
552
  const strategyLabels = {
503
553
  flat: "\uD3C9\uBA74 (\uC804\uBD80 \uB098\uC5F4)",
504
554
  "by-range": "1000\uBC88\uB300 \uBB36\uAE30",
@@ -527,7 +577,7 @@ function InitView({ onComplete }) {
527
577
  {
528
578
  options,
529
579
  onChange: (value) => {
530
- setLanguage(value);
580
+ setForm((prev) => ({ ...prev, language: value }));
531
581
  moveToNextStep(value, getStepLabel(currentStep));
532
582
  }
533
583
  }
@@ -548,7 +598,7 @@ function InitView({ onComplete }) {
548
598
  {
549
599
  options,
550
600
  onChange: (value) => {
551
- setEditorValue(value);
601
+ setForm((prev) => ({ ...prev, editor: value }));
552
602
  moveToNextStep(value, getStepLabel(currentStep));
553
603
  }
554
604
  }
@@ -567,7 +617,7 @@ function InitView({ onComplete }) {
567
617
  {
568
618
  options,
569
619
  onChange: (value) => {
570
- setAutoOpen(value === "true");
620
+ setForm((prev) => ({ ...prev, autoOpen: value === "true" }));
571
621
  moveToNextStep(
572
622
  value === "true" ? "\uC608" : "\uC544\uB2C8\uC624",
573
623
  getStepLabel(currentStep)
@@ -589,7 +639,7 @@ function InitView({ onComplete }) {
589
639
  {
590
640
  options,
591
641
  onChange: (value) => {
592
- setIncludeTag(value === "true");
642
+ setForm((prev) => ({ ...prev, includeTag: value === "true" }));
593
643
  moveToNextStep(
594
644
  value === "true" ? "\uC608" : "\uC544\uB2C8\uC624",
595
645
  getStepLabel(currentStep)
@@ -609,7 +659,7 @@ function InitView({ onComplete }) {
609
659
  placeholder: "\uD578\uB4E4 \uC785\uB825",
610
660
  onSubmit: (value) => {
611
661
  const handleValue = value.trim();
612
- setHandle(handleValue);
662
+ setForm((prev) => ({ ...prev, handle: handleValue }));
613
663
  setHandleInputMode(false);
614
664
  moveToNextStep(
615
665
  handleValue || "(\uC2A4\uD0B5)",
@@ -633,7 +683,7 @@ function InitView({ onComplete }) {
633
683
  options,
634
684
  onChange: (value) => {
635
685
  if (value === "skip") {
636
- setHandle("");
686
+ setForm((prev) => ({ ...prev, handle: "" }));
637
687
  moveToNextStep("(\uC2A4\uD0B5)", getStepLabel(currentStep));
638
688
  } else {
639
689
  setHandleInputMode(true);
@@ -3,9 +3,9 @@ import {
3
3
  OpenCommand,
4
4
  OpenView,
5
5
  open_default
6
- } from "../chunk-AHVAVNFM.js";
6
+ } from "../chunk-7DZDLCGW.js";
7
7
  import "../chunk-QGMWUOJ3.js";
8
- import "../chunk-Q5NECGFA.js";
8
+ import "../chunk-AHE4QHJD.js";
9
9
  export {
10
10
  OpenCommand,
11
11
  OpenView,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSolution
4
- } from "../chunk-YZUGYJA4.js";
4
+ } from "../chunk-GV265WOR.js";
5
5
  import {
6
6
  Command,
7
7
  CommandBuilder,
@@ -13,7 +13,7 @@ import {
13
13
  icons,
14
14
  resolveLanguage,
15
15
  resolveProblemContext
16
- } from "../chunk-Q5NECGFA.js";
16
+ } from "../chunk-AHE4QHJD.js";
17
17
 
18
18
  // src/commands/run.tsx
19
19
  import { join } from "path";
@@ -1,26 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  SubmitView
4
- } from "../chunk-3H74PQRX.js";
4
+ } from "../chunk-NYMNFORB.js";
5
5
  import {
6
6
  TestView
7
- } from "../chunk-2YSOO6AM.js";
7
+ } from "../chunk-CUSSKX2F.js";
8
8
  import {
9
9
  ArchiveView
10
- } from "../chunk-457JZK3K.js";
10
+ } from "../chunk-HCUVCES6.js";
11
11
  import {
12
12
  FetchView
13
- } from "../chunk-M4LNYKJF.js";
13
+ } from "../chunk-XUDGIZ2U.js";
14
14
  import {
15
15
  fetchWithRetry,
16
16
  getProblem,
17
17
  searchProblems
18
- } from "../chunk-YCFY6UCA.js";
18
+ } from "../chunk-S7IL7OXF.js";
19
19
  import {
20
20
  OpenView
21
- } from "../chunk-AHVAVNFM.js";
21
+ } from "../chunk-7DZDLCGW.js";
22
22
  import "../chunk-QGMWUOJ3.js";
23
- import "../chunk-YZUGYJA4.js";
23
+ import "../chunk-GV265WOR.js";
24
24
  import {
25
25
  Command,
26
26
  CommandBuilder,
@@ -37,7 +37,7 @@ import {
37
37
  logger,
38
38
  resolveLanguage,
39
39
  source_default
40
- } from "../chunk-Q5NECGFA.js";
40
+ } from "../chunk-AHE4QHJD.js";
41
41
 
42
42
  // src/commands/search.tsx
43
43
  import { existsSync } from "fs";
@@ -5,7 +5,7 @@ import {
5
5
  getUserTagRatings,
6
6
  getUserTop100,
7
7
  scrapeUserStats
8
- } from "../chunk-YCFY6UCA.js";
8
+ } from "../chunk-S7IL7OXF.js";
9
9
  import {
10
10
  Command,
11
11
  CommandBuilder,
@@ -25,7 +25,7 @@ import {
25
25
  icons,
26
26
  logger,
27
27
  source_default
28
- } from "../chunk-Q5NECGFA.js";
28
+ } from "../chunk-AHE4QHJD.js";
29
29
 
30
30
  // src/commands/stats.tsx
31
31
  import { Alert, Spinner } from "@inkjs/ui";
@@ -3,9 +3,9 @@ import {
3
3
  SubmitCommand,
4
4
  SubmitView,
5
5
  submit_default
6
- } from "../chunk-3H74PQRX.js";
6
+ } from "../chunk-NYMNFORB.js";
7
7
  import "../chunk-QGMWUOJ3.js";
8
- import "../chunk-Q5NECGFA.js";
8
+ import "../chunk-AHE4QHJD.js";
9
9
  export {
10
10
  SubmitCommand,
11
11
  SubmitView,