atcoder-workspace 1.1.0-beta.1 → 1.1.0-beta.3
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/LICENSE +58 -0
- package/README.md +45 -23
- package/dist/atcoder/new.js +6 -1
- package/dist/atcoder/parser/problem-page.d.ts +2 -1
- package/dist/atcoder/parser/problem-page.js +340 -6
- package/dist/cli.js +177 -34
- package/dist/config/config-store.d.ts +2 -0
- package/dist/config/config-store.js +5 -5
- package/dist/utils/i18n.d.ts +64 -0
- package/dist/utils/i18n.js +67 -3
- package/dist/workspace/initializer.d.ts +14 -0
- package/dist/workspace/initializer.js +110 -25
- package/package.json +3 -3
- package/src/atcoder/new.test.ts +140 -0
- package/src/atcoder/new.ts +7 -1
- package/src/atcoder/parser/problem-page.test.ts +125 -0
- package/src/atcoder/parser/problem-page.ts +359 -6
- package/src/cli.ts +207 -36
- package/src/config/config-store.ts +7 -5
- package/src/test-runner/runner.test.ts +2 -0
- package/src/utils/i18n.ts +67 -3
- package/src/workspace/initializer.test.ts +125 -0
- package/src/workspace/initializer.ts +128 -27
- package/THIRD_PARTY_LICENSES +0 -21
package/dist/cli.js
CHANGED
|
@@ -69,8 +69,8 @@ const lang = (0, i18n_1.getLanguage)(workspaceRoot);
|
|
|
69
69
|
const program = new commander_1.Command();
|
|
70
70
|
program
|
|
71
71
|
.name('atc')
|
|
72
|
-
.description('AtCoder
|
|
73
|
-
.version('1.1.0-
|
|
72
|
+
.description('AtCoder Workspace')
|
|
73
|
+
.version('1.1.0-beta.3');
|
|
74
74
|
function handleAction(fn) {
|
|
75
75
|
return async (...args) => {
|
|
76
76
|
try {
|
|
@@ -237,47 +237,97 @@ program
|
|
|
237
237
|
setupSpinner.stop((0, i18n_1.t)('newSetupSuccess', lang, tObj.label.toUpperCase(), res.sampleCount));
|
|
238
238
|
}
|
|
239
239
|
p.outro(picocolors_1.default.green((0, i18n_1.t)('newScaffoldingComplete', lang, selectedTasks.length)));
|
|
240
|
+
if (config.extractProblemStatement) {
|
|
241
|
+
p.note((0, i18n_1.t)('newStatementWarningBody', lang), picocolors_1.default.yellow((0, i18n_1.t)('newStatementWarningTitle', lang)));
|
|
242
|
+
}
|
|
240
243
|
}));
|
|
241
|
-
function resolveArgs(workspaceRoot,
|
|
244
|
+
function resolveArgs(workspaceRoot, arg1, arg2, arg3) {
|
|
245
|
+
const cwd = process.cwd();
|
|
246
|
+
const config = (0, config_store_1.loadConfig)(workspaceRoot);
|
|
247
|
+
const contestDir = config.contestDir || '';
|
|
242
248
|
let resolvedTaskDir = '';
|
|
243
249
|
let resolvedFile;
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
path.resolve(
|
|
249
|
-
path.resolve(workspaceRoot,
|
|
250
|
+
// Helper function to check if relative path parts exist in cwd, workspaceRoot or contestDir
|
|
251
|
+
function checkPath(relativeParts) {
|
|
252
|
+
const pathsToTry = [
|
|
253
|
+
path.resolve(cwd, ...relativeParts),
|
|
254
|
+
path.resolve(workspaceRoot, ...relativeParts),
|
|
255
|
+
path.resolve(workspaceRoot, contestDir, ...relativeParts)
|
|
250
256
|
];
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
break;
|
|
257
|
+
for (const p of pathsToTry) {
|
|
258
|
+
if (fs.existsSync(p)) {
|
|
259
|
+
const stat = fs.statSync(p);
|
|
260
|
+
return {
|
|
261
|
+
isFile: stat.isFile(),
|
|
262
|
+
isDir: stat.isDirectory(),
|
|
263
|
+
path: p
|
|
264
|
+
};
|
|
260
265
|
}
|
|
261
266
|
}
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
// 1. Three arguments provided: (arg1, arg2, arg3)
|
|
270
|
+
if (arg1 && arg2 && arg3) {
|
|
271
|
+
// e.g., abc300 a main.cpp -> check if arg1/arg2 is a directory
|
|
272
|
+
const checkDir = checkPath([arg1, arg2]);
|
|
273
|
+
if (checkDir && checkDir.isDir) {
|
|
274
|
+
resolvedTaskDir = checkDir.path;
|
|
275
|
+
resolvedFile = arg3;
|
|
276
|
+
}
|
|
262
277
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
278
|
+
// 2. Two arguments provided: (arg1, arg2)
|
|
279
|
+
if (!resolvedTaskDir && arg1 && arg2) {
|
|
280
|
+
// Pattern A: arg1/arg2 is a directory (e.g., abc300 a)
|
|
281
|
+
const checkDir = checkPath([arg1, arg2]);
|
|
282
|
+
if (checkDir && checkDir.isDir) {
|
|
283
|
+
resolvedTaskDir = checkDir.path;
|
|
284
|
+
resolvedFile = undefined;
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
// Pattern B: arg1 is a directory, arg2 is a file (e.g., a main.cpp)
|
|
288
|
+
const checkArg1 = checkPath([arg1]);
|
|
289
|
+
if (checkArg1 && checkArg1.isDir) {
|
|
290
|
+
resolvedTaskDir = checkArg1.path;
|
|
291
|
+
resolvedFile = arg2;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
266
294
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
295
|
+
// 3. One argument provided: (arg1)
|
|
296
|
+
if (!resolvedTaskDir && arg1) {
|
|
297
|
+
const checkArg1 = checkPath([arg1]);
|
|
298
|
+
if (checkArg1) {
|
|
299
|
+
if (checkArg1.isFile) {
|
|
300
|
+
// Pattern A: directly specified a file (e.g., main.cpp)
|
|
301
|
+
resolvedTaskDir = path.dirname(checkArg1.path);
|
|
302
|
+
resolvedFile = path.basename(checkArg1.path);
|
|
303
|
+
}
|
|
304
|
+
else if (checkArg1.isDir) {
|
|
305
|
+
// Pattern B: directly specified a directory (e.g., a or abc300/a)
|
|
306
|
+
resolvedTaskDir = checkArg1.path;
|
|
307
|
+
resolvedFile = undefined;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
// Fallback to resolveTaskDirectory (legacy behavior)
|
|
312
|
+
resolvedTaskDir = (0, runner_1.resolveTaskDirectory)(workspaceRoot, arg1);
|
|
313
|
+
resolvedFile = undefined;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// 4. No arguments provided
|
|
317
|
+
if (!resolvedTaskDir) {
|
|
318
|
+
resolvedTaskDir = (0, runner_1.resolveTaskDirectory)(workspaceRoot, undefined);
|
|
319
|
+
resolvedFile = undefined;
|
|
270
320
|
}
|
|
271
321
|
const taskLabel = path.basename(resolvedTaskDir);
|
|
272
322
|
const contestId = path.basename(path.dirname(resolvedTaskDir));
|
|
273
323
|
return { resolvedTaskDir, resolvedFile, taskLabel, contestId };
|
|
274
324
|
}
|
|
275
325
|
program
|
|
276
|
-
.command('test [
|
|
326
|
+
.command('test [arg1] [arg2] [arg3]')
|
|
277
327
|
.description((0, i18n_1.t)('descTest', lang))
|
|
278
|
-
.action(handleAction(async (
|
|
328
|
+
.action(handleAction(async (arg1, arg2, arg3) => {
|
|
279
329
|
const workspaceRoot = (0, finder_1.findWorkspaceRoot)();
|
|
280
|
-
const { resolvedTaskDir, resolvedFile, taskLabel, contestId } = resolveArgs(workspaceRoot,
|
|
330
|
+
const { resolvedTaskDir, resolvedFile, taskLabel, contestId } = resolveArgs(workspaceRoot, arg1, arg2, arg3);
|
|
281
331
|
p.intro(picocolors_1.default.cyan((0, i18n_1.t)('testIntro', lang, contestId, taskLabel)));
|
|
282
332
|
const s = p.spinner();
|
|
283
333
|
s.start((0, i18n_1.t)('testRetrievingLimits', lang));
|
|
@@ -355,11 +405,11 @@ program
|
|
|
355
405
|
}
|
|
356
406
|
}));
|
|
357
407
|
program
|
|
358
|
-
.command('submit [
|
|
408
|
+
.command('submit [arg1] [arg2] [arg3]')
|
|
359
409
|
.description((0, i18n_1.t)('descSubmit', lang))
|
|
360
|
-
.action(handleAction(async (
|
|
410
|
+
.action(handleAction(async (arg1, arg2, arg3) => {
|
|
361
411
|
const workspaceRoot = (0, finder_1.findWorkspaceRoot)();
|
|
362
|
-
const { resolvedTaskDir, resolvedFile, taskLabel, contestId } = resolveArgs(workspaceRoot,
|
|
412
|
+
const { resolvedTaskDir, resolvedFile, taskLabel, contestId } = resolveArgs(workspaceRoot, arg1, arg2, arg3);
|
|
363
413
|
p.intro(picocolors_1.default.cyan((0, i18n_1.t)('submitPreparing', lang, contestId, taskLabel)));
|
|
364
414
|
const s = p.spinner();
|
|
365
415
|
s.start((0, i18n_1.t)('submitRetrievingLimits', lang));
|
|
@@ -491,11 +541,22 @@ program
|
|
|
491
541
|
p.log.error(picocolors_1.default.red((0, i18n_1.t)('langWorkspaceRequired', lang)));
|
|
492
542
|
process.exit(1);
|
|
493
543
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
544
|
+
let selectedLang = targetLanguage;
|
|
545
|
+
if (!selectedLang) {
|
|
546
|
+
const choice = await p.select({
|
|
547
|
+
message: (0, i18n_1.t)('langSelectMessage', lang),
|
|
548
|
+
options: [
|
|
549
|
+
{ value: 'en', label: 'English (en)' },
|
|
550
|
+
{ value: 'ja', label: '日本語 (ja)' }
|
|
551
|
+
]
|
|
552
|
+
});
|
|
553
|
+
if (p.isCancel(choice)) {
|
|
554
|
+
p.cancel((0, i18n_1.t)('langCancelled', lang));
|
|
555
|
+
process.exit(0);
|
|
556
|
+
}
|
|
557
|
+
selectedLang = choice;
|
|
497
558
|
}
|
|
498
|
-
const cleanLang =
|
|
559
|
+
const cleanLang = selectedLang.trim().toLowerCase();
|
|
499
560
|
if (cleanLang !== 'en' && cleanLang !== 'ja') {
|
|
500
561
|
p.log.error(picocolors_1.default.red((0, i18n_1.t)('langInvalid', lang)));
|
|
501
562
|
process.exit(1);
|
|
@@ -505,4 +566,86 @@ program
|
|
|
505
566
|
(0, config_store_1.saveConfig)(workspaceRoot, config);
|
|
506
567
|
p.log.success((0, i18n_1.t)('langSuccess', cleanLang, cleanLang));
|
|
507
568
|
}));
|
|
569
|
+
program
|
|
570
|
+
.command('add-lang [langName]')
|
|
571
|
+
.description((0, i18n_1.t)('descAddLang', lang))
|
|
572
|
+
.action(handleAction(async (langName) => {
|
|
573
|
+
const root = (0, finder_1.findWorkspaceRoot)();
|
|
574
|
+
const config = (0, config_store_1.loadConfig)(root);
|
|
575
|
+
let targetLang = langName;
|
|
576
|
+
if (!targetLang) {
|
|
577
|
+
targetLang = await p.text({
|
|
578
|
+
message: (0, i18n_1.t)('addLangEnterName', lang),
|
|
579
|
+
validate: (val) => (!val.trim() ? (0, i18n_1.t)('addLangNameNotEmpty', lang) : undefined)
|
|
580
|
+
});
|
|
581
|
+
if (p.isCancel(targetLang)) {
|
|
582
|
+
p.cancel((0, i18n_1.t)('addLangCancelled', lang));
|
|
583
|
+
process.exit(0);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
targetLang = targetLang.trim().toLowerCase();
|
|
587
|
+
// Check if already exists
|
|
588
|
+
if (config.languages[targetLang]) {
|
|
589
|
+
throw new errors_1.AtcError((0, i18n_1.t)('addLangAlreadyExists', lang, targetLang));
|
|
590
|
+
}
|
|
591
|
+
const preset = initializer_1.LANGUAGE_PRESETS[targetLang];
|
|
592
|
+
let extension = '';
|
|
593
|
+
let build = '';
|
|
594
|
+
let run = '';
|
|
595
|
+
let template = '';
|
|
596
|
+
if (preset) {
|
|
597
|
+
extension = preset.config.extension;
|
|
598
|
+
build = preset.config.build;
|
|
599
|
+
run = preset.config.run;
|
|
600
|
+
template = preset.template;
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
// If not preset, prompt for parameters
|
|
604
|
+
const extInput = await p.text({
|
|
605
|
+
message: (0, i18n_1.t)('addLangEnterExtension', lang, targetLang),
|
|
606
|
+
placeholder: targetLang,
|
|
607
|
+
validate: (val) => (!val.trim() ? (0, i18n_1.t)('addLangExtNotEmpty', lang) : undefined)
|
|
608
|
+
});
|
|
609
|
+
if (p.isCancel(extInput)) {
|
|
610
|
+
p.cancel((0, i18n_1.t)('addLangCancelled', lang));
|
|
611
|
+
process.exit(0);
|
|
612
|
+
}
|
|
613
|
+
const buildInput = await p.text({
|
|
614
|
+
message: (0, i18n_1.t)('addLangEnterBuildCmd', lang),
|
|
615
|
+
placeholder: 'e.g. g++ -O2 main.cpp (leave empty if not needed)'
|
|
616
|
+
});
|
|
617
|
+
if (p.isCancel(buildInput)) {
|
|
618
|
+
p.cancel((0, i18n_1.t)('addLangCancelled', lang));
|
|
619
|
+
process.exit(0);
|
|
620
|
+
}
|
|
621
|
+
const runInput = await p.text({
|
|
622
|
+
message: (0, i18n_1.t)('addLangEnterRunCmd', lang),
|
|
623
|
+
placeholder: `e.g. python3 main.py or ./a.out`,
|
|
624
|
+
validate: (val) => (!val.trim() ? (0, i18n_1.t)('addLangRunCmdNotEmpty', lang) : undefined)
|
|
625
|
+
});
|
|
626
|
+
if (p.isCancel(runInput)) {
|
|
627
|
+
p.cancel((0, i18n_1.t)('addLangCancelled', lang));
|
|
628
|
+
process.exit(0);
|
|
629
|
+
}
|
|
630
|
+
extension = extInput.trim();
|
|
631
|
+
build = buildInput.trim();
|
|
632
|
+
run = runInput.trim();
|
|
633
|
+
template = `// Solve the problem here\n`;
|
|
634
|
+
}
|
|
635
|
+
const s = p.spinner();
|
|
636
|
+
s.start((0, i18n_1.t)('addLangSpinner', lang));
|
|
637
|
+
try {
|
|
638
|
+
(0, initializer_1.addLanguage)(root, targetLang, {
|
|
639
|
+
extension,
|
|
640
|
+
build,
|
|
641
|
+
run,
|
|
642
|
+
template
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
catch (e) {
|
|
646
|
+
s.stop('Failed');
|
|
647
|
+
throw e;
|
|
648
|
+
}
|
|
649
|
+
s.stop((0, i18n_1.t)('addLangSuccess', lang, targetLang));
|
|
650
|
+
}));
|
|
508
651
|
program.parse(process.argv);
|
|
@@ -10,6 +10,8 @@ export interface Config {
|
|
|
10
10
|
testDirName: string;
|
|
11
11
|
contestDir?: string;
|
|
12
12
|
lang?: 'en' | 'ja';
|
|
13
|
+
extractProblemStatement?: boolean;
|
|
14
|
+
problemLang?: 'en' | 'ja';
|
|
13
15
|
}
|
|
14
16
|
export declare const DEFAULT_CONFIG: Config;
|
|
15
17
|
export declare function getConfigPath(workspaceRoot: string): string;
|
|
@@ -56,7 +56,10 @@ exports.DEFAULT_CONFIG = {
|
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
58
|
testDirName: 'tests',
|
|
59
|
-
contestDir: ''
|
|
59
|
+
contestDir: '',
|
|
60
|
+
lang: 'en',
|
|
61
|
+
extractProblemStatement: false,
|
|
62
|
+
problemLang: 'ja'
|
|
60
63
|
};
|
|
61
64
|
function getConfigPath(workspaceRoot) {
|
|
62
65
|
return path.join(workspaceRoot, '.atcoder-cli', 'config.json');
|
|
@@ -72,10 +75,7 @@ function loadConfig(workspaceRoot) {
|
|
|
72
75
|
return {
|
|
73
76
|
...exports.DEFAULT_CONFIG,
|
|
74
77
|
...parsed,
|
|
75
|
-
languages:
|
|
76
|
-
...exports.DEFAULT_CONFIG.languages,
|
|
77
|
-
...(parsed.languages || {})
|
|
78
|
-
}
|
|
78
|
+
languages: parsed.languages ? parsed.languages : exports.DEFAULT_CONFIG.languages
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
catch (e) {
|
package/dist/utils/i18n.d.ts
CHANGED
|
@@ -46,6 +46,10 @@ export declare const MESSAGES: {
|
|
|
46
46
|
en: string;
|
|
47
47
|
ja: string;
|
|
48
48
|
};
|
|
49
|
+
descAddLang: {
|
|
50
|
+
en: string;
|
|
51
|
+
ja: string;
|
|
52
|
+
};
|
|
49
53
|
initIntro: {
|
|
50
54
|
en: string;
|
|
51
55
|
ja: string;
|
|
@@ -186,6 +190,14 @@ export declare const MESSAGES: {
|
|
|
186
190
|
en: (count: number) => string;
|
|
187
191
|
ja: (count: number) => string;
|
|
188
192
|
};
|
|
193
|
+
newStatementWarningTitle: {
|
|
194
|
+
en: string;
|
|
195
|
+
ja: string;
|
|
196
|
+
};
|
|
197
|
+
newStatementWarningBody: {
|
|
198
|
+
en: string;
|
|
199
|
+
ja: string;
|
|
200
|
+
};
|
|
189
201
|
testIntro: {
|
|
190
202
|
en: (contestId: string, label: string) => string;
|
|
191
203
|
ja: (contestId: string, label: string) => string;
|
|
@@ -330,6 +342,14 @@ export declare const MESSAGES: {
|
|
|
330
342
|
en: string;
|
|
331
343
|
ja: string;
|
|
332
344
|
};
|
|
345
|
+
langSelectMessage: {
|
|
346
|
+
en: string;
|
|
347
|
+
ja: string;
|
|
348
|
+
};
|
|
349
|
+
langCancelled: {
|
|
350
|
+
en: string;
|
|
351
|
+
ja: string;
|
|
352
|
+
};
|
|
333
353
|
submitSessionExpired: {
|
|
334
354
|
en: string;
|
|
335
355
|
ja: string;
|
|
@@ -338,6 +358,50 @@ export declare const MESSAGES: {
|
|
|
338
358
|
en: string;
|
|
339
359
|
ja: string;
|
|
340
360
|
};
|
|
361
|
+
addLangAlreadyExists: {
|
|
362
|
+
en: (lang: string) => string;
|
|
363
|
+
ja: (lang: string) => string;
|
|
364
|
+
};
|
|
365
|
+
addLangEnterName: {
|
|
366
|
+
en: string;
|
|
367
|
+
ja: string;
|
|
368
|
+
};
|
|
369
|
+
addLangNameNotEmpty: {
|
|
370
|
+
en: string;
|
|
371
|
+
ja: string;
|
|
372
|
+
};
|
|
373
|
+
addLangCancelled: {
|
|
374
|
+
en: string;
|
|
375
|
+
ja: string;
|
|
376
|
+
};
|
|
377
|
+
addLangEnterExtension: {
|
|
378
|
+
en: (lang: string) => string;
|
|
379
|
+
ja: (lang: string) => string;
|
|
380
|
+
};
|
|
381
|
+
addLangExtNotEmpty: {
|
|
382
|
+
en: string;
|
|
383
|
+
ja: string;
|
|
384
|
+
};
|
|
385
|
+
addLangEnterBuildCmd: {
|
|
386
|
+
en: string;
|
|
387
|
+
ja: string;
|
|
388
|
+
};
|
|
389
|
+
addLangEnterRunCmd: {
|
|
390
|
+
en: string;
|
|
391
|
+
ja: string;
|
|
392
|
+
};
|
|
393
|
+
addLangRunCmdNotEmpty: {
|
|
394
|
+
en: string;
|
|
395
|
+
ja: string;
|
|
396
|
+
};
|
|
397
|
+
addLangSpinner: {
|
|
398
|
+
en: string;
|
|
399
|
+
ja: string;
|
|
400
|
+
};
|
|
401
|
+
addLangSuccess: {
|
|
402
|
+
en: (lang: string) => string;
|
|
403
|
+
ja: (lang: string) => string;
|
|
404
|
+
};
|
|
341
405
|
};
|
|
342
406
|
/**
|
|
343
407
|
* Translates a key into the active language.
|
package/dist/utils/i18n.js
CHANGED
|
@@ -87,13 +87,17 @@ exports.MESSAGES = {
|
|
|
87
87
|
ja: 'ダウンロードしたサンプルケースに対してローカルテストを実行します'
|
|
88
88
|
},
|
|
89
89
|
descSubmit: {
|
|
90
|
-
en: 'Submit code to AtCoder
|
|
91
|
-
ja: 'コードを AtCoder
|
|
90
|
+
en: 'Submit code to AtCoder',
|
|
91
|
+
ja: 'コードを AtCoder に提出します'
|
|
92
92
|
},
|
|
93
93
|
descLang: {
|
|
94
94
|
en: 'Change the display language (en or ja)',
|
|
95
95
|
ja: '表示言語の切り替え (en または ja)'
|
|
96
96
|
},
|
|
97
|
+
descAddLang: {
|
|
98
|
+
en: 'Add a programming language configuration and template',
|
|
99
|
+
ja: 'プログラミング言語の設定とテンプレートを追加します'
|
|
100
|
+
},
|
|
97
101
|
// init
|
|
98
102
|
initIntro: {
|
|
99
103
|
en: 'AtCoder Workspace - Workspace Initialization',
|
|
@@ -241,6 +245,14 @@ exports.MESSAGES = {
|
|
|
241
245
|
en: (count) => `Scaffolding complete for ${count} task(s).`,
|
|
242
246
|
ja: (count) => `${count} 個の問題のセットアップが完了しました。`
|
|
243
247
|
},
|
|
248
|
+
newStatementWarningTitle: {
|
|
249
|
+
en: '[WARNING] Automatic problem statement extraction is enabled',
|
|
250
|
+
ja: '【警告】問題文の自動抽出が有効化されています'
|
|
251
|
+
},
|
|
252
|
+
newStatementWarningBody: {
|
|
253
|
+
en: '• DO NOT feed the problem statement Markdown to Generative AI during a rated contest (violates rules).\n• DO NOT publish or share the extracted problem statement on the internet (e.g. public GitHub repos).',
|
|
254
|
+
ja: '・コンテスト中に問題文のMarkdownを生成AIに読み込ませないでください(ルール違反となります)。\n・抽出した問題文をそのままインターネット(GitHubパブリックリポジトリ等)に公開・共有しないでください。'
|
|
255
|
+
},
|
|
244
256
|
// test
|
|
245
257
|
testIntro: {
|
|
246
258
|
en: (contestId, label) => `Running tests for ${contestId}/${label}`,
|
|
@@ -284,7 +296,7 @@ exports.MESSAGES = {
|
|
|
284
296
|
},
|
|
285
297
|
testOutroFailed: {
|
|
286
298
|
en: 'Some tests failed. 😢',
|
|
287
|
-
ja: '
|
|
299
|
+
ja: 'テストに失敗しました。 😢'
|
|
288
300
|
},
|
|
289
301
|
// submit
|
|
290
302
|
submitPreparing: {
|
|
@@ -388,6 +400,14 @@ exports.MESSAGES = {
|
|
|
388
400
|
en: 'Usage: atc lang <en|ja>',
|
|
389
401
|
ja: '使い方: atc lang <en|ja>'
|
|
390
402
|
},
|
|
403
|
+
langSelectMessage: {
|
|
404
|
+
en: 'Select display language:',
|
|
405
|
+
ja: '表示言語を選択してください:'
|
|
406
|
+
},
|
|
407
|
+
langCancelled: {
|
|
408
|
+
en: 'Language selection cancelled.',
|
|
409
|
+
ja: '言語選択がキャンセルされました。'
|
|
410
|
+
},
|
|
391
411
|
submitSessionExpired: {
|
|
392
412
|
en: 'Session expired or invalid. Please log in again using "atc login".',
|
|
393
413
|
ja: 'セッションの期限が切れているか無効です。"atc login" を実行して再ログインしてください。'
|
|
@@ -395,6 +415,50 @@ exports.MESSAGES = {
|
|
|
395
415
|
submitLangSelectNotFound: {
|
|
396
416
|
en: 'Language selection element not found on submit page. Please make sure you are logged in and the contest has started.',
|
|
397
417
|
ja: '提出ページに言語選択要素が見つかりませんでした。ログイン状態であること、およびコンテストが開始されていることを確認してください。'
|
|
418
|
+
},
|
|
419
|
+
addLangAlreadyExists: {
|
|
420
|
+
en: (lang) => `Language "${lang}" is already configured.`,
|
|
421
|
+
ja: (lang) => `言語 "${lang}" は既に設定されています。`
|
|
422
|
+
},
|
|
423
|
+
addLangEnterName: {
|
|
424
|
+
en: 'Enter the programming language name to add:',
|
|
425
|
+
ja: '追加するプログラミング言語名を入力してください:'
|
|
426
|
+
},
|
|
427
|
+
addLangNameNotEmpty: {
|
|
428
|
+
en: 'Language name cannot be empty.',
|
|
429
|
+
ja: '言語名は空にすることはできません。'
|
|
430
|
+
},
|
|
431
|
+
addLangCancelled: {
|
|
432
|
+
en: 'Language addition cancelled.',
|
|
433
|
+
ja: '言語の追加がキャンセルされました。'
|
|
434
|
+
},
|
|
435
|
+
addLangEnterExtension: {
|
|
436
|
+
en: (lang) => `Enter file extension for ${lang}:`,
|
|
437
|
+
ja: (lang) => `${lang} のファイル拡張子を入力してください:`
|
|
438
|
+
},
|
|
439
|
+
addLangExtNotEmpty: {
|
|
440
|
+
en: 'Extension cannot be empty.',
|
|
441
|
+
ja: '拡張子は空にすることはできません。'
|
|
442
|
+
},
|
|
443
|
+
addLangEnterBuildCmd: {
|
|
444
|
+
en: 'Enter build command (leave empty if not needed):',
|
|
445
|
+
ja: 'ビルドコマンドを入力してください (不要な場合は空欄のまま):'
|
|
446
|
+
},
|
|
447
|
+
addLangEnterRunCmd: {
|
|
448
|
+
en: 'Enter execution command:',
|
|
449
|
+
ja: '実行コマンドを入力してください:'
|
|
450
|
+
},
|
|
451
|
+
addLangRunCmdNotEmpty: {
|
|
452
|
+
en: 'Execution command cannot be empty.',
|
|
453
|
+
ja: '実行コマンドは空にすることはできません。'
|
|
454
|
+
},
|
|
455
|
+
addLangSpinner: {
|
|
456
|
+
en: 'Adding language configuration...',
|
|
457
|
+
ja: '言語設定を追加中...'
|
|
458
|
+
},
|
|
459
|
+
addLangSuccess: {
|
|
460
|
+
en: (lang) => `Language "${lang}" added successfully!`,
|
|
461
|
+
ja: (lang) => `言語 "${lang}" が正常に追加されました!`
|
|
398
462
|
}
|
|
399
463
|
};
|
|
400
464
|
/**
|
|
@@ -1,4 +1,18 @@
|
|
|
1
|
+
import { LanguageConfig } from '../config/config-store';
|
|
2
|
+
export declare const DEFAULT_CPP_TEMPLATE = "#include <bits/stdc++.h>\n\nusing namespace std;\n\nint main() {\n // Solve the problem here\n return 0;\n}\n";
|
|
3
|
+
export declare const DEFAULT_PYTHON_TEMPLATE = "import sys\n\ndef main():\n # Solve the problem here\n pass\n\nif __name__ == '__main__':\n main()\n";
|
|
4
|
+
export declare const LANGUAGE_PRESETS: Record<string, {
|
|
5
|
+
config: LanguageConfig;
|
|
6
|
+
template: string;
|
|
7
|
+
filename: string;
|
|
8
|
+
}>;
|
|
1
9
|
export declare function initWorkspace(targetDir?: string, defaultLanguage?: string): {
|
|
2
10
|
alreadyInitialized: boolean;
|
|
3
11
|
gitignoreUpdated: boolean;
|
|
4
12
|
};
|
|
13
|
+
export declare function addLanguage(workspaceRoot: string, langName: string, options: {
|
|
14
|
+
extension: string;
|
|
15
|
+
build: string;
|
|
16
|
+
run: string;
|
|
17
|
+
template?: string;
|
|
18
|
+
}): void;
|