dskcode 0.1.19 → 0.1.21
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/dist/index.js +525 -55
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1182,38 +1182,58 @@ function estimateMessageTokens(msg) {
|
|
|
1182
1182
|
}
|
|
1183
1183
|
return estimateTokens(text) + 10;
|
|
1184
1184
|
}
|
|
1185
|
+
function groupIntoTurns(messages) {
|
|
1186
|
+
const turns = [];
|
|
1187
|
+
let current = null;
|
|
1188
|
+
for (const msg of messages) {
|
|
1189
|
+
if (msg.role === "user") {
|
|
1190
|
+
if (current) turns.push(current);
|
|
1191
|
+
current = [msg];
|
|
1192
|
+
} else {
|
|
1193
|
+
if (!current) current = [];
|
|
1194
|
+
current.push(msg);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
if (current && current.length > 0) turns.push(current);
|
|
1198
|
+
return turns;
|
|
1199
|
+
}
|
|
1200
|
+
function estimateTurnTokens(turn) {
|
|
1201
|
+
let sum = 0;
|
|
1202
|
+
for (const msg of turn) sum += estimateMessageTokens(msg);
|
|
1203
|
+
return sum;
|
|
1204
|
+
}
|
|
1185
1205
|
function trimMessages(messages, opts) {
|
|
1186
1206
|
const meta = getModelMeta(opts.model);
|
|
1187
1207
|
const maxInputTokens = meta.contextWindow - opts.reservedForOutput;
|
|
1188
1208
|
const systemTokens = estimateTokens(opts.systemPrompt);
|
|
1189
1209
|
let remaining = maxInputTokens - systemTokens;
|
|
1190
|
-
const
|
|
1210
|
+
const turns = groupIntoTurns(messages);
|
|
1211
|
+
const preservedTurns = [];
|
|
1191
1212
|
let roundsPreserved = 0;
|
|
1192
|
-
for (let i =
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
roundsPreserved++;
|
|
1196
|
-
}
|
|
1213
|
+
for (let i = turns.length - 1; i >= 0 && roundsPreserved < opts.preserveRecentRounds; i--) {
|
|
1214
|
+
preservedTurns.unshift(turns[i]);
|
|
1215
|
+
roundsPreserved++;
|
|
1197
1216
|
}
|
|
1217
|
+
const preserved = preservedTurns.flat();
|
|
1198
1218
|
for (const msg of preserved) {
|
|
1199
1219
|
remaining -= estimateMessageTokens(msg);
|
|
1200
1220
|
}
|
|
1201
1221
|
if (remaining < 0) {
|
|
1202
|
-
while (
|
|
1203
|
-
const
|
|
1204
|
-
remaining +=
|
|
1222
|
+
while (preservedTurns.length > 1 && remaining < 0) {
|
|
1223
|
+
const dropped = preservedTurns.shift();
|
|
1224
|
+
remaining += estimateTurnTokens(dropped);
|
|
1205
1225
|
}
|
|
1206
|
-
return [
|
|
1226
|
+
return [preservedTurns.flat(), true];
|
|
1207
1227
|
}
|
|
1208
|
-
const
|
|
1209
|
-
const
|
|
1210
|
-
for (let i =
|
|
1211
|
-
const cost =
|
|
1228
|
+
const olderTurns = turns.slice(0, turns.length - preservedTurns.length);
|
|
1229
|
+
const keptTurns = [];
|
|
1230
|
+
for (let i = olderTurns.length - 1; i >= 0; i--) {
|
|
1231
|
+
const cost = estimateTurnTokens(olderTurns[i]);
|
|
1212
1232
|
if (remaining - cost < 0) break;
|
|
1213
1233
|
remaining -= cost;
|
|
1214
|
-
|
|
1234
|
+
keptTurns.unshift(olderTurns[i]);
|
|
1215
1235
|
}
|
|
1216
|
-
const result = [...
|
|
1236
|
+
const result = [...keptTurns, ...preservedTurns].flat();
|
|
1217
1237
|
const trimmed = result.length < messages.length;
|
|
1218
1238
|
return [result, trimmed];
|
|
1219
1239
|
}
|
|
@@ -1815,7 +1835,8 @@ ${item.result.diff.patch}`;
|
|
|
1815
1835
|
async #executeBatch(calls) {
|
|
1816
1836
|
const toolCtx = {
|
|
1817
1837
|
cwd: this.#options.cwd,
|
|
1818
|
-
signal: this.#abortController.signal
|
|
1838
|
+
signal: this.#abortController.signal,
|
|
1839
|
+
writeRoots: this.#options.writeRoots
|
|
1819
1840
|
};
|
|
1820
1841
|
const allReadOnly = calls.every((tc) => {
|
|
1821
1842
|
const tool = this.#toolRegistry.get(tc.name);
|
|
@@ -1977,6 +1998,39 @@ function resolvePath(inputPath, cwd) {
|
|
|
1977
1998
|
const resolved = isAbsolute(inputPath) ? inputPath : resolve(cwd, inputPath);
|
|
1978
1999
|
return resolve(resolved);
|
|
1979
2000
|
}
|
|
2001
|
+
async function realPath(target) {
|
|
2002
|
+
try {
|
|
2003
|
+
return await realpath2(target);
|
|
2004
|
+
} catch {
|
|
2005
|
+
const parent = resolve(target, "..");
|
|
2006
|
+
try {
|
|
2007
|
+
const realParent = await realpath2(parent);
|
|
2008
|
+
return resolve(realParent, relative(parent, target));
|
|
2009
|
+
} catch {
|
|
2010
|
+
return resolve(target);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
async function confine(allowedRoots, target) {
|
|
2015
|
+
if (allowedRoots.length === 0) {
|
|
2016
|
+
return { ok: true };
|
|
2017
|
+
}
|
|
2018
|
+
const realTarget = await realPath(target);
|
|
2019
|
+
for (const root of allowedRoots) {
|
|
2020
|
+
const realRoot = await realPath(root);
|
|
2021
|
+
const rel = relative(realRoot, realTarget);
|
|
2022
|
+
if (!rel.startsWith("..") && rel !== "" && !rel.startsWith("/") && !rel.startsWith("\\")) {
|
|
2023
|
+
return { ok: true };
|
|
2024
|
+
}
|
|
2025
|
+
if (realTarget === realRoot) {
|
|
2026
|
+
return { ok: true };
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
return {
|
|
2030
|
+
ok: false,
|
|
2031
|
+
error: `\u8DEF\u5F84 "${target}" \u4E0D\u5728\u5141\u8BB8\u7684\u5199\u5165\u8303\u56F4\u5185 ${allowedRoots.join(", ")}`
|
|
2032
|
+
};
|
|
2033
|
+
}
|
|
1980
2034
|
function truncateOutput(content, maxLength = DEFAULT_MAX_OUTPUT_LENGTH) {
|
|
1981
2035
|
if (content.length <= maxLength) return content;
|
|
1982
2036
|
const truncated = content.slice(0, maxLength);
|
|
@@ -2320,6 +2374,41 @@ function computeFileDiff(oldContent, newContent, filePath) {
|
|
|
2320
2374
|
};
|
|
2321
2375
|
}
|
|
2322
2376
|
|
|
2377
|
+
// src/tool/eol.ts
|
|
2378
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
2379
|
+
function detectEol(text) {
|
|
2380
|
+
if (text.length === 0) return "\n";
|
|
2381
|
+
const crlfIdx = text.indexOf("\r\n");
|
|
2382
|
+
const lfIdx = text.indexOf("\n");
|
|
2383
|
+
if (crlfIdx !== -1 && (lfIdx === -1 || crlfIdx <= lfIdx)) {
|
|
2384
|
+
return "\r\n";
|
|
2385
|
+
}
|
|
2386
|
+
return "\n";
|
|
2387
|
+
}
|
|
2388
|
+
function hasTrailingNewline(text) {
|
|
2389
|
+
return text.endsWith("\n") || text.endsWith("\r\n");
|
|
2390
|
+
}
|
|
2391
|
+
function normalizeEol(originalContent, newContent) {
|
|
2392
|
+
const targetEol = detectEol(originalContent);
|
|
2393
|
+
const lines = newContent.replace(/\r\n/g, "\n").split("\n");
|
|
2394
|
+
const originalHasTrailing = hasTrailingNewline(originalContent);
|
|
2395
|
+
const newHasTrailing = lines.length > 0 && lines[lines.length - 1] === "";
|
|
2396
|
+
let result = lines.join(targetEol);
|
|
2397
|
+
if (originalHasTrailing && !newHasTrailing) {
|
|
2398
|
+
result += targetEol;
|
|
2399
|
+
}
|
|
2400
|
+
if (!originalHasTrailing && newHasTrailing) {
|
|
2401
|
+
if (result.endsWith(targetEol)) {
|
|
2402
|
+
result = result.slice(0, -targetEol.length);
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
return result;
|
|
2406
|
+
}
|
|
2407
|
+
async function writeFileWithEol(filePath, originalContent, newContent) {
|
|
2408
|
+
const content = originalContent.length > 0 ? normalizeEol(originalContent, newContent) : newContent;
|
|
2409
|
+
await writeFile3(filePath, content, "utf-8");
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2323
2412
|
// src/tool/builtins/read-file.ts
|
|
2324
2413
|
import { readFile as readFile4, stat } from "fs/promises";
|
|
2325
2414
|
import { open } from "fs/promises";
|
|
@@ -2427,7 +2516,7 @@ var readFileTool = {
|
|
|
2427
2516
|
};
|
|
2428
2517
|
|
|
2429
2518
|
// src/tool/builtins/write-file.ts
|
|
2430
|
-
import {
|
|
2519
|
+
import { mkdir as mkdir4, readFile as readFile5 } from "fs/promises";
|
|
2431
2520
|
import { dirname, relative as relative3, basename } from "path";
|
|
2432
2521
|
var writeFileSchema = {
|
|
2433
2522
|
type: "object",
|
|
@@ -2459,6 +2548,12 @@ var writeFileTool = {
|
|
|
2459
2548
|
}
|
|
2460
2549
|
const filePath = resolvePath(params.path, ctx.cwd);
|
|
2461
2550
|
try {
|
|
2551
|
+
if (ctx.writeRoots && ctx.writeRoots.length > 0) {
|
|
2552
|
+
const conf = await confine(ctx.writeRoots, filePath);
|
|
2553
|
+
if (!conf.ok) {
|
|
2554
|
+
return { success: false, data: conf.error, error: "OUTSIDE_WRITE_ROOTS" };
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2462
2557
|
let oldContent = "";
|
|
2463
2558
|
let existedBefore = false;
|
|
2464
2559
|
try {
|
|
@@ -2468,7 +2563,7 @@ var writeFileTool = {
|
|
|
2468
2563
|
}
|
|
2469
2564
|
await mkdir4(dirname(filePath), { recursive: true });
|
|
2470
2565
|
const content = String(params.content);
|
|
2471
|
-
await
|
|
2566
|
+
await writeFileWithEol(filePath, oldContent, content);
|
|
2472
2567
|
const diff = computeFileDiff(oldContent, content, filePath);
|
|
2473
2568
|
diff.existedBefore = existedBefore;
|
|
2474
2569
|
const lineCount = content.split("\n").length;
|
|
@@ -2495,7 +2590,7 @@ var writeFileTool = {
|
|
|
2495
2590
|
};
|
|
2496
2591
|
|
|
2497
2592
|
// src/tool/builtins/edit-file.ts
|
|
2498
|
-
import { readFile as readFile6
|
|
2593
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
2499
2594
|
import { basename as basename2 } from "path";
|
|
2500
2595
|
var editFileSchema = {
|
|
2501
2596
|
type: "object",
|
|
@@ -2533,6 +2628,12 @@ var editFileTool = {
|
|
|
2533
2628
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 new_text", error: "INVALID_ARGS" };
|
|
2534
2629
|
}
|
|
2535
2630
|
const filePath = resolvePath(params.path, ctx.cwd);
|
|
2631
|
+
if (ctx.writeRoots && ctx.writeRoots.length > 0) {
|
|
2632
|
+
const conf = await confine(ctx.writeRoots, filePath);
|
|
2633
|
+
if (!conf.ok) {
|
|
2634
|
+
return { success: false, data: conf.error, error: "OUTSIDE_WRITE_ROOTS" };
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2536
2637
|
try {
|
|
2537
2638
|
const content = await readFile6(filePath, "utf-8");
|
|
2538
2639
|
const firstIndex = content.indexOf(params.old_text);
|
|
@@ -2552,7 +2653,7 @@ var editFileTool = {
|
|
|
2552
2653
|
};
|
|
2553
2654
|
}
|
|
2554
2655
|
const newContent = content.replace(params.old_text, params.new_text);
|
|
2555
|
-
await
|
|
2656
|
+
await writeFileWithEol(filePath, content, newContent);
|
|
2556
2657
|
const diff = computeFileDiff(content, newContent, filePath);
|
|
2557
2658
|
diff.existedBefore = true;
|
|
2558
2659
|
const beforeText = content.slice(0, firstIndex);
|
|
@@ -2582,7 +2683,7 @@ ${oldLines} \u884C \u2192 ${newLines} \u884C
|
|
|
2582
2683
|
};
|
|
2583
2684
|
|
|
2584
2685
|
// src/tool/builtins/multi-edit.ts
|
|
2585
|
-
import { readFile as readFile7
|
|
2686
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
2586
2687
|
import { basename as basename3 } from "path";
|
|
2587
2688
|
var multiEditSchema = {
|
|
2588
2689
|
type: "object",
|
|
@@ -2623,6 +2724,12 @@ var multiEditTool = {
|
|
|
2623
2724
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 edits\uFF08\u975E\u7A7A\u6570\u7EC4\uFF09", error: "INVALID_ARGS" };
|
|
2624
2725
|
}
|
|
2625
2726
|
const filePath = resolvePath(params.path, ctx.cwd);
|
|
2727
|
+
if (ctx.writeRoots && ctx.writeRoots.length > 0) {
|
|
2728
|
+
const conf = await confine(ctx.writeRoots, filePath);
|
|
2729
|
+
if (!conf.ok) {
|
|
2730
|
+
return { success: false, data: conf.error, error: "OUTSIDE_WRITE_ROOTS" };
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2626
2733
|
try {
|
|
2627
2734
|
const originalContent = await readFile7(filePath, "utf-8");
|
|
2628
2735
|
let currentContent = originalContent;
|
|
@@ -2669,7 +2776,7 @@ var multiEditTool = {
|
|
|
2669
2776
|
currentContent = currentContent.replace(step.oldText, step.newText);
|
|
2670
2777
|
}
|
|
2671
2778
|
}
|
|
2672
|
-
await
|
|
2779
|
+
await writeFileWithEol(filePath, originalContent, currentContent);
|
|
2673
2780
|
const diff = computeFileDiff(originalContent, currentContent, filePath);
|
|
2674
2781
|
diff.existedBefore = true;
|
|
2675
2782
|
return {
|
|
@@ -2692,7 +2799,7 @@ var multiEditTool = {
|
|
|
2692
2799
|
};
|
|
2693
2800
|
|
|
2694
2801
|
// src/tool/builtins/delete-range.ts
|
|
2695
|
-
import { readFile as readFile8
|
|
2802
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
2696
2803
|
import { basename as basename4 } from "path";
|
|
2697
2804
|
var deleteRangeSchema = {
|
|
2698
2805
|
type: "object",
|
|
@@ -2750,6 +2857,12 @@ var deleteRangeTool = {
|
|
|
2750
2857
|
}
|
|
2751
2858
|
const filePath = resolvePath(params.path, ctx.cwd);
|
|
2752
2859
|
const inclusive = params.inclusive ?? false;
|
|
2860
|
+
if (ctx.writeRoots && ctx.writeRoots.length > 0) {
|
|
2861
|
+
const conf = await confine(ctx.writeRoots, filePath);
|
|
2862
|
+
if (!conf.ok) {
|
|
2863
|
+
return { success: false, data: conf.error, error: "OUTSIDE_WRITE_ROOTS" };
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2753
2866
|
try {
|
|
2754
2867
|
const content = await readFile8(filePath, "utf-8");
|
|
2755
2868
|
const lines = content.split("\n");
|
|
@@ -2781,14 +2894,8 @@ var deleteRangeTool = {
|
|
|
2781
2894
|
}
|
|
2782
2895
|
const newLines = [...lines.slice(0, rangeStart), ...lines.slice(rangeEnd + 1)];
|
|
2783
2896
|
const newContent = newLines.join("\n");
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
writeContent = newContent + "\n";
|
|
2787
|
-
} else if (content.endsWith("\r\n") && !newContent.endsWith("\r\n") && !newContent.endsWith("\n")) {
|
|
2788
|
-
writeContent = newContent + "\r\n";
|
|
2789
|
-
}
|
|
2790
|
-
await writeFile6(filePath, writeContent, "utf-8");
|
|
2791
|
-
const diff = computeFileDiff(content, writeContent, filePath);
|
|
2897
|
+
await writeFileWithEol(filePath, content, newContent);
|
|
2898
|
+
const diff = computeFileDiff(content, newContent, filePath);
|
|
2792
2899
|
diff.existedBefore = true;
|
|
2793
2900
|
const deletedLines = rangeEnd - rangeStart + 1;
|
|
2794
2901
|
const summary = `\u{1F4DD} \u4FEE\u6539: ${basename4(filePath)} (\u5220 ${deletedLines} \u884C, +${diff.additions} -${diff.deletions})`;
|
|
@@ -4224,7 +4331,7 @@ function BrickBreakerGame({ onExit: _onExit }) {
|
|
|
4224
4331
|
const [initialLevel, setInitialLevel] = useState5(1);
|
|
4225
4332
|
const [selectingLevel, setSelectingLevel] = useState5(true);
|
|
4226
4333
|
const stateRef = useRef3(createInitialState(initialLevel));
|
|
4227
|
-
const [
|
|
4334
|
+
const [tick2, setTick] = useState5(0);
|
|
4228
4335
|
const onExitRef = useRef3(_onExit);
|
|
4229
4336
|
onExitRef.current = _onExit;
|
|
4230
4337
|
useEffect4(() => {
|
|
@@ -4279,7 +4386,7 @@ function BrickBreakerGame({ onExit: _onExit }) {
|
|
|
4279
4386
|
const aliveCount = s.bricks.filter((b) => b.alive).length;
|
|
4280
4387
|
const board = buildBoard(s);
|
|
4281
4388
|
const def = getLevel(s.level);
|
|
4282
|
-
void
|
|
4389
|
+
void tick2;
|
|
4283
4390
|
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
4284
4391
|
/* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", children: [
|
|
4285
4392
|
/* @__PURE__ */ jsx10(Box10, { width: 20, children: /* @__PURE__ */ jsxs10(Text11, { children: [
|
|
@@ -4754,7 +4861,7 @@ function buildGameView(s, scoreLines, scoreColor, message) {
|
|
|
4754
4861
|
}
|
|
4755
4862
|
function CoderCheck({ onExit: _onExit }) {
|
|
4756
4863
|
const stateRef = useRef4(createInitialState2());
|
|
4757
|
-
const [
|
|
4864
|
+
const [tick2, setTick] = useState6(0);
|
|
4758
4865
|
const [colorOffset, setColorOffset] = useState6(0);
|
|
4759
4866
|
const onExitRef = useRef4(_onExit);
|
|
4760
4867
|
onExitRef.current = _onExit;
|
|
@@ -4832,7 +4939,7 @@ function CoderCheck({ onExit: _onExit }) {
|
|
|
4832
4939
|
const scoreStr = String(s.score).padStart(5, "0");
|
|
4833
4940
|
const scoreLines = buildScoreLines(scoreStr);
|
|
4834
4941
|
const view = buildGameView(s, scoreLines, scoreColor, s.message);
|
|
4835
|
-
void
|
|
4942
|
+
void tick2;
|
|
4836
4943
|
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", paddingX: 1, children: [
|
|
4837
4944
|
/* @__PURE__ */ jsx11(Box11, { flexDirection: "row", children: /* @__PURE__ */ jsxs11(Text12, { children: [
|
|
4838
4945
|
"\u751F\u547D ",
|
|
@@ -4889,16 +4996,375 @@ var coder_check_default = {
|
|
|
4889
4996
|
}
|
|
4890
4997
|
};
|
|
4891
4998
|
|
|
4999
|
+
// src/game/snake/engine.ts
|
|
5000
|
+
function isOpposite(a, b) {
|
|
5001
|
+
return a === "UP" && b === "DOWN" || a === "DOWN" && b === "UP" || a === "LEFT" && b === "RIGHT" || a === "RIGHT" && b === "LEFT";
|
|
5002
|
+
}
|
|
5003
|
+
function samePoint(a, b) {
|
|
5004
|
+
return a.x === b.x && a.y === b.y;
|
|
5005
|
+
}
|
|
5006
|
+
function movePoint(p, dir) {
|
|
5007
|
+
switch (dir) {
|
|
5008
|
+
case "UP":
|
|
5009
|
+
return { x: p.x, y: p.y - 1 };
|
|
5010
|
+
case "DOWN":
|
|
5011
|
+
return { x: p.x, y: p.y + 1 };
|
|
5012
|
+
case "LEFT":
|
|
5013
|
+
return { x: p.x - 1, y: p.y };
|
|
5014
|
+
case "RIGHT":
|
|
5015
|
+
return { x: p.x + 1, y: p.y };
|
|
5016
|
+
}
|
|
5017
|
+
}
|
|
5018
|
+
function wrapPoint(p, config) {
|
|
5019
|
+
return {
|
|
5020
|
+
x: (p.x % config.width + config.width) % config.width,
|
|
5021
|
+
y: (p.y % config.height + config.height) % config.height
|
|
5022
|
+
};
|
|
5023
|
+
}
|
|
5024
|
+
function generateFood(snake, config, type, excludePositions = []) {
|
|
5025
|
+
const occupied = new Set(snake.map((p) => `${p.x},${p.y}`));
|
|
5026
|
+
for (const p of excludePositions) {
|
|
5027
|
+
occupied.add(`${p.x},${p.y}`);
|
|
5028
|
+
}
|
|
5029
|
+
const totalCells = config.width * config.height;
|
|
5030
|
+
if (occupied.size >= totalCells) return null;
|
|
5031
|
+
let pos;
|
|
5032
|
+
do {
|
|
5033
|
+
pos = {
|
|
5034
|
+
x: Math.floor(Math.random() * config.width),
|
|
5035
|
+
y: Math.floor(Math.random() * config.height)
|
|
5036
|
+
};
|
|
5037
|
+
} while (occupied.has(`${pos.x},${pos.y}`));
|
|
5038
|
+
return {
|
|
5039
|
+
position: pos,
|
|
5040
|
+
type,
|
|
5041
|
+
...type === "special" ? { spawnedAt: Date.now() } : {}
|
|
5042
|
+
};
|
|
5043
|
+
}
|
|
5044
|
+
function createInitialState3(config) {
|
|
5045
|
+
const startX = Math.floor(config.width / 2);
|
|
5046
|
+
const startY = Math.floor(config.height / 2);
|
|
5047
|
+
const snake = [
|
|
5048
|
+
{ x: startX, y: startY },
|
|
5049
|
+
{ x: startX - 1, y: startY },
|
|
5050
|
+
{ x: startX - 2, y: startY }
|
|
5051
|
+
];
|
|
5052
|
+
const food = generateFood(snake, config, "normal");
|
|
5053
|
+
return {
|
|
5054
|
+
snake,
|
|
5055
|
+
direction: "RIGHT",
|
|
5056
|
+
nextDirection: "RIGHT",
|
|
5057
|
+
food,
|
|
5058
|
+
specialFood: null,
|
|
5059
|
+
score: 0,
|
|
5060
|
+
speedLevel: 1,
|
|
5061
|
+
isGameOver: false,
|
|
5062
|
+
isPaused: false,
|
|
5063
|
+
wallWrap: Math.random() < 0.5,
|
|
5064
|
+
tickInterval: config.initialSpeed
|
|
5065
|
+
};
|
|
5066
|
+
}
|
|
5067
|
+
function tick(state, config) {
|
|
5068
|
+
state.direction = state.nextDirection;
|
|
5069
|
+
const head = state.snake[0];
|
|
5070
|
+
let newHead = movePoint(head, state.direction);
|
|
5071
|
+
const outOfBounds = newHead.x < 0 || newHead.x >= config.width || newHead.y < 0 || newHead.y >= config.height;
|
|
5072
|
+
if (outOfBounds) {
|
|
5073
|
+
if (state.wallWrap) {
|
|
5074
|
+
newHead = wrapPoint(newHead, config);
|
|
5075
|
+
} else {
|
|
5076
|
+
state.isGameOver = true;
|
|
5077
|
+
return;
|
|
5078
|
+
}
|
|
5079
|
+
}
|
|
5080
|
+
const ateNormal = samePoint(newHead, state.food.position);
|
|
5081
|
+
const ateSpecial = state.specialFood !== null && samePoint(newHead, state.specialFood.position);
|
|
5082
|
+
const willEat = ateNormal || ateSpecial;
|
|
5083
|
+
const bodyToCheck = willEat ? state.snake : state.snake.slice(0, -1);
|
|
5084
|
+
for (const segment of bodyToCheck) {
|
|
5085
|
+
if (samePoint(newHead, segment)) {
|
|
5086
|
+
state.isGameOver = true;
|
|
5087
|
+
return;
|
|
5088
|
+
}
|
|
5089
|
+
}
|
|
5090
|
+
state.snake.unshift(newHead);
|
|
5091
|
+
if (ateNormal) {
|
|
5092
|
+
state.score += 1;
|
|
5093
|
+
updateSpeed(state, config);
|
|
5094
|
+
const exclude = state.specialFood ? [state.specialFood.position] : [];
|
|
5095
|
+
const newFood = generateFood(state.snake, config, "normal", exclude);
|
|
5096
|
+
if (newFood) {
|
|
5097
|
+
state.food = newFood;
|
|
5098
|
+
} else {
|
|
5099
|
+
state.isGameOver = true;
|
|
5100
|
+
return;
|
|
5101
|
+
}
|
|
5102
|
+
if (state.specialFood === null && Math.random() < 0.25) {
|
|
5103
|
+
state.specialFood = generateFood(state.snake, config, "special", [state.food.position]);
|
|
5104
|
+
}
|
|
5105
|
+
} else if (ateSpecial) {
|
|
5106
|
+
state.score += 3;
|
|
5107
|
+
updateSpeed(state, config);
|
|
5108
|
+
state.specialFood = null;
|
|
5109
|
+
} else {
|
|
5110
|
+
state.snake.pop();
|
|
5111
|
+
}
|
|
5112
|
+
if (state.specialFood?.spawnedAt !== void 0) {
|
|
5113
|
+
if (Date.now() - state.specialFood.spawnedAt > 5e3) {
|
|
5114
|
+
state.specialFood = null;
|
|
5115
|
+
}
|
|
5116
|
+
}
|
|
5117
|
+
}
|
|
5118
|
+
function updateSpeed(state, config) {
|
|
5119
|
+
const newLevel = Math.floor(state.score / config.scorePerLevel) + 1;
|
|
5120
|
+
state.speedLevel = newLevel;
|
|
5121
|
+
state.tickInterval = Math.max(
|
|
5122
|
+
config.minSpeed,
|
|
5123
|
+
config.initialSpeed - (newLevel - 1) * config.speedDecrement
|
|
5124
|
+
);
|
|
5125
|
+
}
|
|
5126
|
+
function changeDirection(state, newDir) {
|
|
5127
|
+
if (!isOpposite(newDir, state.direction)) {
|
|
5128
|
+
state.nextDirection = newDir;
|
|
5129
|
+
}
|
|
5130
|
+
}
|
|
5131
|
+
|
|
5132
|
+
// src/game/snake/renderer.ts
|
|
5133
|
+
import chalk4 from "chalk";
|
|
5134
|
+
var CELL_W = 2;
|
|
5135
|
+
var EMPTY = " ";
|
|
5136
|
+
var BODY_CHAR = "\u2593";
|
|
5137
|
+
var HEAD_CHAR = "\u2593";
|
|
5138
|
+
var NORMAL_FOOD_CHAR = "\u25C6";
|
|
5139
|
+
var SPECIAL_FOOD_CHAR = "\u2605";
|
|
5140
|
+
var CSI = "\x1B[";
|
|
5141
|
+
var CURSOR_HIDE = `${CSI}?25l`;
|
|
5142
|
+
var CURSOR_SHOW = `${CSI}?25h`;
|
|
5143
|
+
var CURSOR_HOME = `${CSI}H`;
|
|
5144
|
+
function hideCursor() {
|
|
5145
|
+
process.stdout.write(CURSOR_HIDE);
|
|
5146
|
+
}
|
|
5147
|
+
function showCursor() {
|
|
5148
|
+
process.stdout.write(CURSOR_SHOW);
|
|
5149
|
+
}
|
|
5150
|
+
function clearScreen() {
|
|
5151
|
+
process.stdout.write(`${CSI}2J${CURSOR_HOME}`);
|
|
5152
|
+
}
|
|
5153
|
+
function cellColor(char, type) {
|
|
5154
|
+
switch (type) {
|
|
5155
|
+
case "snake-head":
|
|
5156
|
+
return chalk4.greenBright(char);
|
|
5157
|
+
case "snake-body":
|
|
5158
|
+
return chalk4.green(char);
|
|
5159
|
+
case "normal-food":
|
|
5160
|
+
return chalk4.red(char);
|
|
5161
|
+
case "special-food":
|
|
5162
|
+
return chalk4.yellow(char);
|
|
5163
|
+
case "empty":
|
|
5164
|
+
return char;
|
|
5165
|
+
}
|
|
5166
|
+
}
|
|
5167
|
+
function renderToTerminal(state, config) {
|
|
5168
|
+
const lines = buildRenderLines(state, config);
|
|
5169
|
+
process.stdout.write(`${CSI}2J${CURSOR_HOME}` + lines.join("\n"));
|
|
5170
|
+
}
|
|
5171
|
+
function buildRenderLines(state, config) {
|
|
5172
|
+
const { width: W, height: H } = config;
|
|
5173
|
+
const lines = [];
|
|
5174
|
+
const grid = Array.from({ length: H }, () => Array(W).fill("empty"));
|
|
5175
|
+
for (let i = 1; i < state.snake.length; i++) {
|
|
5176
|
+
const seg = state.snake[i];
|
|
5177
|
+
if (seg.y >= 0 && seg.y < H && seg.x >= 0 && seg.x < W) {
|
|
5178
|
+
grid[seg.y][seg.x] = "snake-body";
|
|
5179
|
+
}
|
|
5180
|
+
}
|
|
5181
|
+
const head = state.snake[0];
|
|
5182
|
+
if (head.y >= 0 && head.y < H && head.x >= 0 && head.x < W) {
|
|
5183
|
+
grid[head.y][head.x] = "snake-head";
|
|
5184
|
+
}
|
|
5185
|
+
const fp = state.food.position;
|
|
5186
|
+
if (grid[fp.y][fp.x] === "empty") {
|
|
5187
|
+
grid[fp.y][fp.x] = "normal-food";
|
|
5188
|
+
}
|
|
5189
|
+
if (state.specialFood) {
|
|
5190
|
+
const sp = state.specialFood.position;
|
|
5191
|
+
if (grid[sp.y][sp.x] === "empty") {
|
|
5192
|
+
grid[sp.y][sp.x] = "special-food";
|
|
5193
|
+
}
|
|
5194
|
+
}
|
|
5195
|
+
lines.push(" " + chalk4.gray("\u2554" + "\u2550\u2550".repeat(W) + "\u2557"));
|
|
5196
|
+
for (let y = 0; y < H; y++) {
|
|
5197
|
+
let row = " " + chalk4.gray("\u2551");
|
|
5198
|
+
for (let x = 0; x < W; x++) {
|
|
5199
|
+
const cellType = grid[y][x];
|
|
5200
|
+
let char;
|
|
5201
|
+
switch (cellType) {
|
|
5202
|
+
case "snake-head":
|
|
5203
|
+
char = HEAD_CHAR.repeat(CELL_W);
|
|
5204
|
+
break;
|
|
5205
|
+
case "snake-body":
|
|
5206
|
+
char = BODY_CHAR.repeat(CELL_W);
|
|
5207
|
+
break;
|
|
5208
|
+
case "normal-food":
|
|
5209
|
+
char = NORMAL_FOOD_CHAR + " ";
|
|
5210
|
+
break;
|
|
5211
|
+
case "special-food":
|
|
5212
|
+
char = SPECIAL_FOOD_CHAR + " ";
|
|
5213
|
+
break;
|
|
5214
|
+
default:
|
|
5215
|
+
char = EMPTY;
|
|
5216
|
+
}
|
|
5217
|
+
row += cellColor(char, cellType);
|
|
5218
|
+
}
|
|
5219
|
+
row += chalk4.gray("\u2551");
|
|
5220
|
+
lines.push(row);
|
|
5221
|
+
}
|
|
5222
|
+
lines.push(" " + chalk4.gray("\u255A" + "\u2550\u2550".repeat(W) + "\u255D"));
|
|
5223
|
+
lines.push("");
|
|
5224
|
+
lines.push(
|
|
5225
|
+
` ${chalk4.cyan("\u5206\u6570")}: ${state.score} ${chalk4.cyan("\u957F\u5EA6")}: ${state.snake.length} ${chalk4.cyan("\u901F\u5EA6")}: Lv.${state.speedLevel} ${chalk4.cyan("\u8FB9\u754C")}: ${state.wallWrap ? "\u7A7F\u5899" : "\u649E\u5899"}`
|
|
5226
|
+
);
|
|
5227
|
+
if (state.specialFood?.spawnedAt !== void 0) {
|
|
5228
|
+
const remaining = Math.ceil((5e3 - (Date.now() - state.specialFood.spawnedAt)) / 1e3);
|
|
5229
|
+
if (remaining > 0) {
|
|
5230
|
+
lines.push(` ${chalk4.yellow(`\u2B50 \u7279\u6B8A\u98DF\u7269 ${remaining} \u79D2\u540E\u6D88\u5931`)}`);
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
5233
|
+
lines.push(` ${chalk4.gray("W/A/S/D \u6216 \u65B9\u5411\u952E\u79FB\u52A8 | P \u6682\u505C | M \u5207\u6362\u6A21\u5F0F | Q \u9000\u51FA")}`);
|
|
5234
|
+
if (state.isPaused) {
|
|
5235
|
+
lines.push("");
|
|
5236
|
+
lines.push(` ${chalk4.yellow("\u23F8 \u5DF2\u6682\u505C \u2014 \u6309 P \u7EE7\u7EED")}`);
|
|
5237
|
+
}
|
|
5238
|
+
if (state.isGameOver) {
|
|
5239
|
+
lines.push("");
|
|
5240
|
+
lines.push(` ${chalk4.red.bold("\u{1F480} \u6E38\u620F\u7ED3\u675F!")} ${chalk4.cyan("\u6700\u7EC8\u5206\u6570: " + state.score)}`);
|
|
5241
|
+
lines.push(` ${chalk4.gray("\u6309 R \u91CD\u65B0\u5F00\u59CB | \u6309 Q \u9000\u51FA")}`);
|
|
5242
|
+
}
|
|
5243
|
+
return lines;
|
|
5244
|
+
}
|
|
5245
|
+
|
|
5246
|
+
// src/game/snake/input.ts
|
|
5247
|
+
import * as readline from "readline";
|
|
5248
|
+
var KEY_TO_DIR = {
|
|
5249
|
+
up: "UP",
|
|
5250
|
+
w: "UP",
|
|
5251
|
+
W: "UP",
|
|
5252
|
+
down: "DOWN",
|
|
5253
|
+
s: "DOWN",
|
|
5254
|
+
S: "DOWN",
|
|
5255
|
+
left: "LEFT",
|
|
5256
|
+
a: "LEFT",
|
|
5257
|
+
A: "LEFT",
|
|
5258
|
+
right: "RIGHT",
|
|
5259
|
+
d: "RIGHT",
|
|
5260
|
+
D: "RIGHT"
|
|
5261
|
+
};
|
|
5262
|
+
function setupInput(ctx, onQuit, onRestart) {
|
|
5263
|
+
readline.emitKeypressEvents(process.stdin);
|
|
5264
|
+
if (process.stdin.isTTY) {
|
|
5265
|
+
process.stdin.setRawMode(true);
|
|
5266
|
+
}
|
|
5267
|
+
const handler = (_, key) => {
|
|
5268
|
+
if (!key || !key.name) return;
|
|
5269
|
+
const { name, ctrl } = key;
|
|
5270
|
+
if (ctrl && (name === "c" || name === "d")) {
|
|
5271
|
+
onQuit();
|
|
5272
|
+
return;
|
|
5273
|
+
}
|
|
5274
|
+
if (name === "q") {
|
|
5275
|
+
onQuit();
|
|
5276
|
+
return;
|
|
5277
|
+
}
|
|
5278
|
+
const state = ctx.state;
|
|
5279
|
+
if (state.isGameOver) {
|
|
5280
|
+
if (name === "r") {
|
|
5281
|
+
ctx.state = onRestart();
|
|
5282
|
+
}
|
|
5283
|
+
return;
|
|
5284
|
+
}
|
|
5285
|
+
if (name === "p") {
|
|
5286
|
+
state.isPaused = !state.isPaused;
|
|
5287
|
+
return;
|
|
5288
|
+
}
|
|
5289
|
+
if (name === "m") {
|
|
5290
|
+
state.wallWrap = !state.wallWrap;
|
|
5291
|
+
return;
|
|
5292
|
+
}
|
|
5293
|
+
const dir = KEY_TO_DIR[name];
|
|
5294
|
+
if (dir) {
|
|
5295
|
+
changeDirection(state, dir);
|
|
5296
|
+
}
|
|
5297
|
+
};
|
|
5298
|
+
process.stdin.on("keypress", handler);
|
|
5299
|
+
return () => {
|
|
5300
|
+
process.stdin.removeListener("keypress", handler);
|
|
5301
|
+
if (process.stdin.isTTY) {
|
|
5302
|
+
process.stdin.setRawMode(false);
|
|
5303
|
+
}
|
|
5304
|
+
};
|
|
5305
|
+
}
|
|
5306
|
+
|
|
5307
|
+
// src/game/snake/index.ts
|
|
5308
|
+
var CONFIG = {
|
|
5309
|
+
width: 20,
|
|
5310
|
+
height: 16,
|
|
5311
|
+
initialSpeed: 180,
|
|
5312
|
+
minSpeed: 60,
|
|
5313
|
+
speedDecrement: 10,
|
|
5314
|
+
scorePerLevel: 5
|
|
5315
|
+
};
|
|
5316
|
+
var IDLE_INTERVAL = 200;
|
|
5317
|
+
var snakeGame = {
|
|
5318
|
+
id: "snake",
|
|
5319
|
+
name: "Big Snake",
|
|
5320
|
+
description: "\u7ECF\u5178\u8D2A\u5403\u86C7\uFF0C\u53CC\u98DF\u7269\u7CFB\u7EDF\u3001\u96BE\u5EA6\u9012\u589E\u3001\u7A7F\u5899/\u649E\u5899\u6A21\u5F0F",
|
|
5321
|
+
play: async () => {
|
|
5322
|
+
const ctx = { state: createInitialState3(CONFIG) };
|
|
5323
|
+
return new Promise((resolve2) => {
|
|
5324
|
+
const cleanupInput = setupInput(
|
|
5325
|
+
ctx,
|
|
5326
|
+
() => {
|
|
5327
|
+
stop = true;
|
|
5328
|
+
clearTimeout(timerId);
|
|
5329
|
+
showCursor();
|
|
5330
|
+
cleanupInput();
|
|
5331
|
+
resolve2();
|
|
5332
|
+
},
|
|
5333
|
+
() => {
|
|
5334
|
+
return createInitialState3(CONFIG);
|
|
5335
|
+
}
|
|
5336
|
+
);
|
|
5337
|
+
clearScreen();
|
|
5338
|
+
hideCursor();
|
|
5339
|
+
let stop = false;
|
|
5340
|
+
let timerId;
|
|
5341
|
+
function loop() {
|
|
5342
|
+
if (stop) return;
|
|
5343
|
+
const s = ctx.state;
|
|
5344
|
+
if (!s.isGameOver && !s.isPaused) {
|
|
5345
|
+
tick(s, CONFIG);
|
|
5346
|
+
}
|
|
5347
|
+
renderToTerminal(s, CONFIG);
|
|
5348
|
+
const interval = s.isGameOver || s.isPaused ? IDLE_INTERVAL : s.tickInterval;
|
|
5349
|
+
timerId = setTimeout(loop, interval);
|
|
5350
|
+
}
|
|
5351
|
+
timerId = setTimeout(loop, 0);
|
|
5352
|
+
});
|
|
5353
|
+
}
|
|
5354
|
+
};
|
|
5355
|
+
var snake_default = snakeGame;
|
|
5356
|
+
|
|
4892
5357
|
// src/game/registry.ts
|
|
4893
5358
|
function initGames() {
|
|
4894
5359
|
registerGame(brick_breaker_default);
|
|
4895
5360
|
registerGame(coder_check_default);
|
|
5361
|
+
registerGame(snake_default);
|
|
4896
5362
|
return listGames();
|
|
4897
5363
|
}
|
|
4898
5364
|
|
|
4899
5365
|
// src/cli/index.tsx
|
|
4900
5366
|
import { render as render4 } from "ink";
|
|
4901
|
-
import
|
|
5367
|
+
import chalk5 from "chalk";
|
|
4902
5368
|
|
|
4903
5369
|
// src/stock/StockList.tsx
|
|
4904
5370
|
import { Box as Box12, Text as Text13, useInput as useInput5 } from "ink";
|
|
@@ -5021,6 +5487,7 @@ function StockList({ codes, onExit, onBackToChat }) {
|
|
|
5021
5487
|
const [currentTime, setCurrentTime] = useState7(
|
|
5022
5488
|
() => (/* @__PURE__ */ new Date()).toLocaleTimeString("zh-CN", { hour12: false })
|
|
5023
5489
|
);
|
|
5490
|
+
const [dimMode, setDimMode] = useState7(false);
|
|
5024
5491
|
const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(onExit);
|
|
5025
5492
|
useEffect6(() => {
|
|
5026
5493
|
const timer = setInterval(() => {
|
|
@@ -5107,9 +5574,11 @@ function StockList({ codes, onExit, onBackToChat }) {
|
|
|
5107
5574
|
} else if (input === "r") {
|
|
5108
5575
|
setCountdown(5);
|
|
5109
5576
|
loadData();
|
|
5577
|
+
} else if (input === "h") {
|
|
5578
|
+
setDimMode((v) => !v);
|
|
5110
5579
|
}
|
|
5111
5580
|
},
|
|
5112
|
-
[stocks, selectedIndex, detailView, onExit, onBackToChat, loadData, handleCtrlC]
|
|
5581
|
+
[stocks, selectedIndex, detailView, onExit, onBackToChat, loadData, handleCtrlC, setDimMode]
|
|
5113
5582
|
)
|
|
5114
5583
|
);
|
|
5115
5584
|
if (detailView) {
|
|
@@ -5118,9 +5587,10 @@ function StockList({ codes, onExit, onBackToChat }) {
|
|
|
5118
5587
|
}
|
|
5119
5588
|
return renderDetail(detailView, () => setDetailView(null), detailPrices ?? void 0, detailCountdown, currentTime);
|
|
5120
5589
|
}
|
|
5590
|
+
const cp2 = (c) => dimMode ? { dimColor: true } : { color: c };
|
|
5121
5591
|
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
|
|
5122
5592
|
/* @__PURE__ */ jsxs12(Box12, { marginBottom: 1, justifyContent: "space-between", children: [
|
|
5123
|
-
/* @__PURE__ */ jsx12(Text13, { bold: true,
|
|
5593
|
+
/* @__PURE__ */ jsx12(Text13, { bold: true, ...cp2("#00ffff"), children: " \u{1F4C8} \u81EA\u9009\u80A1\u76D1\u63A7" }),
|
|
5124
5594
|
/* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
|
|
5125
5595
|
" \u{1F550} ",
|
|
5126
5596
|
currentTime
|
|
@@ -5144,30 +5614,30 @@ function StockList({ codes, onExit, onBackToChat }) {
|
|
|
5144
5614
|
const isUp = stock.changePercent >= 0;
|
|
5145
5615
|
const color = isUp ? "#ff1493" : "#00ff41";
|
|
5146
5616
|
return /* @__PURE__ */ jsxs12(Box12, { children: [
|
|
5147
|
-
/* @__PURE__ */ jsx12(Box12, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx12(Text13, { bold: true,
|
|
5148
|
-
/* @__PURE__ */ jsx12(Box12, { width: 9, children: /* @__PURE__ */ jsx12(Text13, { bold: true,
|
|
5149
|
-
/* @__PURE__ */ jsx12(Box12, { width: 16, children: /* @__PURE__ */ jsx12(Text13, {
|
|
5150
|
-
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsx12(Text13, { bold: true, color, children: formatPrice(stock.price) }) }),
|
|
5151
|
-
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsxs12(Text13, { color, children: [
|
|
5617
|
+
/* @__PURE__ */ jsx12(Box12, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx12(Text13, { bold: true, ...cp2("#00ffff"), children: "\u25B8 " }) : /* @__PURE__ */ jsx12(Text13, { children: " " }) }),
|
|
5618
|
+
/* @__PURE__ */ jsx12(Box12, { width: 9, children: /* @__PURE__ */ jsx12(Text13, { bold: true, ...cp2(isSelected ? "#00ffff" : "#ffffff"), children: stock.code }) }),
|
|
5619
|
+
/* @__PURE__ */ jsx12(Box12, { width: 16, children: /* @__PURE__ */ jsx12(Text13, { ...cp2(isSelected ? "#ffffff" : "#cccccc"), children: stock.name }) }),
|
|
5620
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsx12(Text13, { bold: true, ...cp2(color), children: formatPrice(stock.price) }) }),
|
|
5621
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsxs12(Text13, { ...cp2(color), children: [
|
|
5152
5622
|
isUp ? "+" : "",
|
|
5153
5623
|
stock.changePercent.toFixed(2),
|
|
5154
5624
|
"%"
|
|
5155
5625
|
] }) }),
|
|
5156
|
-
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsxs12(Text13, { color, children: [
|
|
5626
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsxs12(Text13, { ...cp2(color), children: [
|
|
5157
5627
|
isUp ? "+" : "",
|
|
5158
5628
|
stock.changeAmount.toFixed(3)
|
|
5159
5629
|
] }) }),
|
|
5160
|
-
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsx12(Text13, {
|
|
5161
|
-
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsx12(Text13, {
|
|
5162
|
-
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsx12(Text13, {
|
|
5630
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsx12(Text13, { ...cp2("#cccccc"), children: formatPrice(stock.high) }) }),
|
|
5631
|
+
/* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsx12(Text13, { ...cp2("#888888"), children: formatPrice(stock.low) }) }),
|
|
5632
|
+
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsx12(Text13, { ...cp2("#888888"), children: formatAmount(stock.amount) }) })
|
|
5163
5633
|
] }, stock.code);
|
|
5164
5634
|
}) }),
|
|
5165
|
-
/* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: ` \u2191/\u2193 \u9009\u62E9 Enter \u8BE6\u60C5 r \u624B\u52A8\u5237\u65B0 q \u8FD4\u56DE` }) }),
|
|
5635
|
+
/* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: ` \u2191/\u2193 \u9009\u62E9 Enter \u8BE6\u60C5 r \u624B\u52A8\u5237\u65B0 h \u7F6E\u7070/\u6062\u590D q \u8FD4\u56DE` }) }),
|
|
5166
5636
|
/* @__PURE__ */ jsxs12(Box12, { children: [
|
|
5167
5637
|
/* @__PURE__ */ jsx12(Text13, { dimColor: true, children: ` \u6700\u540E\u66F4\u65B0: ${lastUpdate} \u7F16\u8F91\u81EA\u9009\u80A1: ` }),
|
|
5168
|
-
/* @__PURE__ */ jsx12(Text13, {
|
|
5638
|
+
/* @__PURE__ */ jsx12(Text13, { ...cp2("#c792ea"), children: osc8Link(toFileUrl(SETTINGS_PATH), SETTINGS_PATH) })
|
|
5169
5639
|
] }),
|
|
5170
|
-
doubleCtrlC && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, {
|
|
5640
|
+
doubleCtrlC && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { bold: true, ...cp2("#ff1493"), children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
|
|
5171
5641
|
] });
|
|
5172
5642
|
}
|
|
5173
5643
|
function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
|
|
@@ -5327,7 +5797,7 @@ function createCli() {
|
|
|
5327
5797
|
const key = await promptForApiKey();
|
|
5328
5798
|
if (!key) process.exit(1);
|
|
5329
5799
|
const savedPath = await saveApiKey(key);
|
|
5330
|
-
console.log(` ${
|
|
5800
|
+
console.log(` ${chalk5.green("\u2714")} API Key \u5DF2\u4FDD\u5B58\u5230 ${chalk5.dim(savedPath)}
|
|
5331
5801
|
`);
|
|
5332
5802
|
const result = await loadAndValidate();
|
|
5333
5803
|
ctx = { ...ctx, config: result.config };
|
|
@@ -5474,8 +5944,8 @@ compdef _dskcode_completion dskcode`);
|
|
|
5474
5944
|
{ code: "sh601899" }
|
|
5475
5945
|
];
|
|
5476
5946
|
const savedPath = await saveStockConfig(defaultSymbols);
|
|
5477
|
-
console.log(`${
|
|
5478
|
-
console.log(`${
|
|
5947
|
+
console.log(`${chalk5.green("\u2714")} \u5DF2\u751F\u6210\u81EA\u9009\u80A1\u914D\u7F6E: ${chalk5.dim(savedPath)}`);
|
|
5948
|
+
console.log(`${chalk5.dim(" \u63D0\u793A: \u53EF\u7F16\u8F91\u4E0A\u8FF0\u6587\u4EF6\u81EA\u5B9A\u4E49\u81EA\u9009\u80A1\u5217\u8868")}
|
|
5479
5949
|
`);
|
|
5480
5950
|
}
|
|
5481
5951
|
const freshResult = await loadAndValidate();
|
|
@@ -5529,7 +5999,7 @@ compdef _dskcode_completion dskcode`);
|
|
|
5529
5999
|
});
|
|
5530
6000
|
if (selectedGame) {
|
|
5531
6001
|
console.log(`
|
|
5532
|
-
\u542F\u52A8\u6E38\u620F: ${
|
|
6002
|
+
\u542F\u52A8\u6E38\u620F: ${chalk5.green(selectedGame.name)}
|
|
5533
6003
|
`);
|
|
5534
6004
|
await selectedGame.play();
|
|
5535
6005
|
}
|