reasonix 0.33.2 → 0.34.1

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.
Files changed (71) hide show
  1. package/dashboard/dist/app.js +1 -21
  2. package/dashboard/dist/app.js.map +1 -1
  3. package/dist/cli/{chat-ZMSAXE77.js → chat-TD6GR3QK.js} +14 -13
  4. package/dist/cli/chunk-2EBODRRO.js +149 -0
  5. package/dist/cli/chunk-2EBODRRO.js.map +1 -0
  6. package/dist/cli/{chunk-DULSP7JH.js → chunk-5JXXEPDM.js} +34 -1
  7. package/dist/cli/chunk-5JXXEPDM.js.map +1 -0
  8. package/dist/cli/{chunk-OW7IHE6M.js → chunk-EINEIIIW.js} +693 -364
  9. package/dist/cli/chunk-EINEIIIW.js.map +1 -0
  10. package/dist/cli/{chunk-WVJL7ZO2.js → chunk-F3ILWP2L.js} +4 -4
  11. package/dist/cli/{chunk-SDE5U32Z.js → chunk-KZHMKOJH.js} +13 -8
  12. package/dist/cli/{chunk-SDE5U32Z.js.map → chunk-KZHMKOJH.js.map} +1 -1
  13. package/dist/cli/{chunk-G7M3QWEN.js → chunk-LNTORE5K.js} +225 -132
  14. package/dist/cli/chunk-LNTORE5K.js.map +1 -0
  15. package/dist/cli/chunk-MRLXEMZ7.js +26 -0
  16. package/dist/cli/chunk-MRLXEMZ7.js.map +1 -0
  17. package/dist/cli/{chunk-WBDE4IRI.js → chunk-OERAGRJX.js} +2 -2
  18. package/dist/cli/{chunk-QGE6AF76.js → chunk-Q36KBLSU.js} +207 -8
  19. package/dist/cli/chunk-Q36KBLSU.js.map +1 -0
  20. package/dist/cli/{chunk-RZILUXUC.js → chunk-RXGEGA7K.js} +2 -2
  21. package/dist/cli/{chunk-FXGQ5NHE.js → chunk-SA4UGZPG.js} +21 -1
  22. package/dist/cli/chunk-SA4UGZPG.js.map +1 -0
  23. package/dist/cli/{chunk-J5VLP23S.js → chunk-SW3CCXEV.js} +2 -2
  24. package/dist/cli/{chunk-W4LDFAZ6.js → chunk-SX6L4HZZ.js} +2 -2
  25. package/dist/cli/chunk-WUI3P4RA.js +319 -0
  26. package/dist/cli/chunk-WUI3P4RA.js.map +1 -0
  27. package/dist/cli/{code-R4TXQQEE.js → code-TGUOQBRJ.js} +15 -14
  28. package/dist/cli/{code-R4TXQQEE.js.map → code-TGUOQBRJ.js.map} +1 -1
  29. package/dist/cli/{commands-JWT2MWVH.js → commands-MEZPSEHV.js} +4 -3
  30. package/dist/cli/{commands-JWT2MWVH.js.map → commands-MEZPSEHV.js.map} +1 -1
  31. package/dist/cli/{commit-RPZBOZS2.js → commit-CE4EFTUQ.js} +3 -2
  32. package/dist/cli/{commit-RPZBOZS2.js.map → commit-CE4EFTUQ.js.map} +1 -1
  33. package/dist/cli/{doctor-V5HLCMSQ.js → doctor-YASM64X6.js} +7 -6
  34. package/dist/cli/index.js +22 -21
  35. package/dist/cli/index.js.map +1 -1
  36. package/dist/cli/{mcp-ARTNQ24O.js → mcp-LDFK5QJI.js} +3 -2
  37. package/dist/cli/{mcp-ARTNQ24O.js.map → mcp-LDFK5QJI.js.map} +1 -1
  38. package/dist/cli/{mcp-browse-HLO2ENDL.js → mcp-browse-FYHEITCM.js} +3 -2
  39. package/dist/cli/{mcp-browse-HLO2ENDL.js.map → mcp-browse-FYHEITCM.js.map} +1 -1
  40. package/dist/cli/{replay-Q43DSMG6.js → replay-JEDLU7F2.js} +8 -6
  41. package/dist/cli/{replay-Q43DSMG6.js.map → replay-JEDLU7F2.js.map} +1 -1
  42. package/dist/cli/{run-HK3FP266.js → run-NHD2RSTD.js} +7 -6
  43. package/dist/cli/{run-HK3FP266.js.map → run-NHD2RSTD.js.map} +1 -1
  44. package/dist/cli/{server-SYC3OVOP.js → server-MC4A4WAJ.js} +9 -8
  45. package/dist/cli/{server-SYC3OVOP.js.map → server-MC4A4WAJ.js.map} +1 -1
  46. package/dist/cli/{sessions-3XU2GGHX.js → sessions-ZHWJEW4L.js} +7 -6
  47. package/dist/cli/{sessions-3XU2GGHX.js.map → sessions-ZHWJEW4L.js.map} +1 -1
  48. package/dist/cli/{setup-CCJZAWTY.js → setup-DK43MT47.js} +6 -5
  49. package/dist/cli/{setup-CCJZAWTY.js.map → setup-DK43MT47.js.map} +1 -1
  50. package/dist/cli/{version-5SGI2SEE.js → version-O362UKPM.js} +7 -6
  51. package/dist/cli/{version-5SGI2SEE.js.map → version-O362UKPM.js.map} +1 -1
  52. package/dist/index.d.ts +45 -1
  53. package/dist/index.js +569 -27
  54. package/dist/index.js.map +1 -1
  55. package/package.json +1 -1
  56. package/dist/cli/chunk-63KAV5DX.js +0 -106
  57. package/dist/cli/chunk-63KAV5DX.js.map +0 -1
  58. package/dist/cli/chunk-DULSP7JH.js.map +0 -1
  59. package/dist/cli/chunk-FXGQ5NHE.js.map +0 -1
  60. package/dist/cli/chunk-G7M3QWEN.js.map +0 -1
  61. package/dist/cli/chunk-OW7IHE6M.js.map +0 -1
  62. package/dist/cli/chunk-QGE6AF76.js.map +0 -1
  63. package/dist/cli/chunk-ZPTSJGX5.js +0 -88
  64. package/dist/cli/chunk-ZPTSJGX5.js.map +0 -1
  65. /package/dist/cli/{chat-ZMSAXE77.js.map → chat-TD6GR3QK.js.map} +0 -0
  66. /package/dist/cli/{chunk-WVJL7ZO2.js.map → chunk-F3ILWP2L.js.map} +0 -0
  67. /package/dist/cli/{chunk-WBDE4IRI.js.map → chunk-OERAGRJX.js.map} +0 -0
  68. /package/dist/cli/{chunk-RZILUXUC.js.map → chunk-RXGEGA7K.js.map} +0 -0
  69. /package/dist/cli/{chunk-J5VLP23S.js.map → chunk-SW3CCXEV.js.map} +0 -0
  70. /package/dist/cli/{chunk-W4LDFAZ6.js.map → chunk-SX6L4HZZ.js.map} +0 -0
  71. /package/dist/cli/{doctor-V5HLCMSQ.js.map → doctor-YASM64X6.js.map} +0 -0
package/dist/index.js CHANGED
@@ -395,6 +395,264 @@ import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "fs";
395
395
  import { homedir } from "os";
396
396
  import { dirname, join } from "path";
397
397
 
398
+ // src/cli/ui/theme/tokens.ts
399
+ function card(fg, tone) {
400
+ return {
401
+ user: { color: fg.meta, glyph: "\u25C7" },
402
+ reasoning: { color: tone.accent, glyph: "\u25C6" },
403
+ streaming: { color: tone.brand, glyph: "\u25C8" },
404
+ task: { color: tone.warn, glyph: "\u25B6" },
405
+ tool: { color: tone.info, glyph: "\u25A3" },
406
+ plan: { color: tone.accent, glyph: "\u229E" },
407
+ diff: { color: tone.ok, glyph: "\xB1" },
408
+ error: { color: tone.err, glyph: "\u2716" },
409
+ warn: { color: tone.warn, glyph: "\u26A0" },
410
+ usage: { color: fg.meta, glyph: "\u03A3" },
411
+ subagent: { color: tone.violet, glyph: "\u232C" },
412
+ approval: { color: tone.warn, glyph: "?" },
413
+ search: { color: tone.info, glyph: "\u2299" },
414
+ memory: { color: fg.meta, glyph: "\u2311" },
415
+ ctx: { color: tone.brand, glyph: "\u25D4" },
416
+ doctor: { color: fg.meta, glyph: "\u2695" },
417
+ branch: { color: tone.violet, glyph: "\u2387" }
418
+ };
419
+ }
420
+ function defineTheme(base) {
421
+ return { ...base, card: card(base.fg, base.tone) };
422
+ }
423
+ var githubDark = defineTheme({
424
+ fg: {
425
+ strong: "#e6edf3",
426
+ body: "#c9d1d9",
427
+ sub: "#8b949e",
428
+ meta: "#6e7681",
429
+ faint: "#484f58"
430
+ },
431
+ tone: {
432
+ brand: "#79c0ff",
433
+ accent: "#d2a8ff",
434
+ violet: "#b395f5",
435
+ ok: "#7ee787",
436
+ warn: "#f0b07d",
437
+ err: "#ff8b81",
438
+ info: "#79c0ff"
439
+ },
440
+ toneActive: {
441
+ brand: "#a5d6ff",
442
+ accent: "#e2c5ff",
443
+ violet: "#c8aaff",
444
+ ok: "#a8f5ad",
445
+ warn: "#ffc99e",
446
+ err: "#ffaba3",
447
+ info: "#a5d6ff"
448
+ },
449
+ surface: {
450
+ bg: "#0a0c10",
451
+ bgInput: "#0d1015",
452
+ bgCode: "#06080c",
453
+ bgElev: "#11141a"
454
+ }
455
+ });
456
+ var dark = defineTheme({
457
+ fg: {
458
+ strong: "#f4f7fb",
459
+ body: "#d8dee9",
460
+ sub: "#a7b1c2",
461
+ meta: "#778294",
462
+ faint: "#4d5666"
463
+ },
464
+ tone: {
465
+ brand: "#7dd3fc",
466
+ accent: "#c084fc",
467
+ violet: "#a78bfa",
468
+ ok: "#86efac",
469
+ warn: "#fbbf24",
470
+ err: "#f87171",
471
+ info: "#60a5fa"
472
+ },
473
+ toneActive: {
474
+ brand: "#bae6fd",
475
+ accent: "#e9d5ff",
476
+ violet: "#ddd6fe",
477
+ ok: "#bbf7d0",
478
+ warn: "#fde68a",
479
+ err: "#fecaca",
480
+ info: "#bfdbfe"
481
+ },
482
+ surface: {
483
+ bg: "#0b1020",
484
+ bgInput: "#111827",
485
+ bgCode: "#080c16",
486
+ bgElev: "#151d2f"
487
+ }
488
+ });
489
+ var light = defineTheme({
490
+ fg: {
491
+ strong: "#111827",
492
+ body: "#1f2937",
493
+ sub: "#4b5563",
494
+ meta: "#6b7280",
495
+ faint: "#9ca3af"
496
+ },
497
+ tone: {
498
+ brand: "#2563eb",
499
+ accent: "#7c3aed",
500
+ violet: "#6d28d9",
501
+ ok: "#15803d",
502
+ warn: "#b45309",
503
+ err: "#dc2626",
504
+ info: "#0369a1"
505
+ },
506
+ toneActive: {
507
+ brand: "#1d4ed8",
508
+ accent: "#6d28d9",
509
+ violet: "#5b21b6",
510
+ ok: "#166534",
511
+ warn: "#92400e",
512
+ err: "#b91c1c",
513
+ info: "#075985"
514
+ },
515
+ surface: {
516
+ bg: "#ffffff",
517
+ bgInput: "#f8fafc",
518
+ bgCode: "#f3f4f6",
519
+ bgElev: "#eef2f7"
520
+ }
521
+ });
522
+ var tokyoNight = defineTheme({
523
+ fg: {
524
+ strong: "#c0caf5",
525
+ body: "#a9b1d6",
526
+ sub: "#9aa5ce",
527
+ meta: "#565f89",
528
+ faint: "#414868"
529
+ },
530
+ tone: {
531
+ brand: "#7aa2f7",
532
+ accent: "#bb9af7",
533
+ violet: "#9d7cd8",
534
+ ok: "#9ece6a",
535
+ warn: "#e0af68",
536
+ err: "#f7768e",
537
+ info: "#2ac3de"
538
+ },
539
+ toneActive: {
540
+ brand: "#a9c7ff",
541
+ accent: "#d7b9ff",
542
+ violet: "#c6a0f6",
543
+ ok: "#b9f27c",
544
+ warn: "#ffd089",
545
+ err: "#ff9cac",
546
+ info: "#7dcfff"
547
+ },
548
+ surface: {
549
+ bg: "#1a1b26",
550
+ bgInput: "#1f2335",
551
+ bgCode: "#16161e",
552
+ bgElev: "#24283b"
553
+ }
554
+ });
555
+ var githubLight = defineTheme({
556
+ fg: {
557
+ strong: "#1f2328",
558
+ body: "#24292f",
559
+ sub: "#57606a",
560
+ meta: "#6e7781",
561
+ faint: "#8c959f"
562
+ },
563
+ tone: {
564
+ brand: "#0969da",
565
+ accent: "#8250df",
566
+ violet: "#6639ba",
567
+ ok: "#1a7f37",
568
+ warn: "#9a6700",
569
+ err: "#cf222e",
570
+ info: "#0969da"
571
+ },
572
+ toneActive: {
573
+ brand: "#0550ae",
574
+ accent: "#6639ba",
575
+ violet: "#512a97",
576
+ ok: "#116329",
577
+ warn: "#7d4e00",
578
+ err: "#a40e26",
579
+ info: "#0550ae"
580
+ },
581
+ surface: {
582
+ bg: "#ffffff",
583
+ bgInput: "#f6f8fa",
584
+ bgCode: "#f6f8fa",
585
+ bgElev: "#eaeef2"
586
+ }
587
+ });
588
+ var highContrast = defineTheme({
589
+ fg: {
590
+ strong: "#ffffff",
591
+ body: "#f5f5f5",
592
+ sub: "#d4d4d4",
593
+ meta: "#bdbdbd",
594
+ faint: "#8a8a8a"
595
+ },
596
+ tone: {
597
+ brand: "#00e5ff",
598
+ accent: "#ff4dff",
599
+ violet: "#b388ff",
600
+ ok: "#00ff66",
601
+ warn: "#ffdd00",
602
+ err: "#ff4d4d",
603
+ info: "#4da3ff"
604
+ },
605
+ toneActive: {
606
+ brand: "#80f2ff",
607
+ accent: "#ff99ff",
608
+ violet: "#d0b3ff",
609
+ ok: "#80ffb3",
610
+ warn: "#ffee80",
611
+ err: "#ff9999",
612
+ info: "#99c9ff"
613
+ },
614
+ surface: {
615
+ bg: "#000000",
616
+ bgInput: "#0a0a0a",
617
+ bgCode: "#050505",
618
+ bgElev: "#141414"
619
+ }
620
+ });
621
+ var THEMES = {
622
+ default: githubDark,
623
+ dark,
624
+ light,
625
+ "tokyo-night": tokyoNight,
626
+ "github-dark": githubDark,
627
+ "github-light": githubLight,
628
+ "high-contrast": highContrast
629
+ };
630
+ var DEFAULT_THEME_NAME = "default";
631
+ var DEFAULT_THEME = THEMES[DEFAULT_THEME_NAME];
632
+ var activeTheme = DEFAULT_THEME;
633
+ function proxyTokens(select) {
634
+ const target = select(DEFAULT_THEME);
635
+ return new Proxy(target, {
636
+ get(_target, prop) {
637
+ return select(activeTheme)[prop];
638
+ },
639
+ getOwnPropertyDescriptor(_target, prop) {
640
+ return Reflect.getOwnPropertyDescriptor(select(activeTheme), prop);
641
+ },
642
+ has(_target, prop) {
643
+ return prop in select(activeTheme);
644
+ },
645
+ ownKeys() {
646
+ return Reflect.ownKeys(select(activeTheme));
647
+ }
648
+ });
649
+ }
650
+ var FG = proxyTokens((theme) => theme.fg);
651
+ var TONE = proxyTokens((theme) => theme.tone);
652
+ var TONE_ACTIVE = proxyTokens((theme) => theme.toneActive);
653
+ var SURFACE = proxyTokens((theme) => theme.surface);
654
+ var CARD = proxyTokens((theme) => theme.card);
655
+
398
656
  // src/index/config.ts
399
657
  import picomatch from "picomatch";
400
658
  var DEFAULT_INDEX_EXCLUDES = {
@@ -610,7 +868,100 @@ var EN = {
610
868
  ephemeralSession: "\u25B8 ephemeral chat (no session persistence) \u2014 drop --no-session to enable",
611
869
  restoredEdits: "\u25B8 restored {count} pending edit block(s) from an interrupted prior run \u2014 /apply to commit or /discard to drop.",
612
870
  resumedPlan: "Resumed plan \xB7 {when}{summary}",
613
- tipEditBindings: "\u25B8 TIP: edit-gate keybindings\n y / n accept or drop pending edits\n Shift+Tab switch review \u2194 AUTO (persisted; AUTO applies instantly)\n u undo the last auto-applied batch (within the 5s banner)\n Current mode is shown in the bottom status bar. Run /keys anytime for the full list.\n (This tip shows once \u2014 suppressed after.)",
871
+ tipEditBindings: {
872
+ topic: "edit-gate keybindings",
873
+ sections: [
874
+ {
875
+ rows: [
876
+ { key: "y / n", text: "accept or drop pending edits" },
877
+ {
878
+ key: "Shift+Tab",
879
+ text: "switch review \u2194 AUTO (persisted; AUTO applies instantly)"
880
+ },
881
+ { key: "u", text: "undo the last auto-applied batch (within the 5s banner)" }
882
+ ]
883
+ }
884
+ ],
885
+ footer: "Current mode shown in the bottom status bar \xB7 /keys for the full reference"
886
+ },
887
+ tipMouseClipboard: {
888
+ topic: "mouse + clipboard",
889
+ sections: [
890
+ {
891
+ rows: [
892
+ { key: "wheel", text: "scrolls the chat history above" },
893
+ {
894
+ key: "right-click",
895
+ text: "captured by the app \u2014 use Ctrl+V (Win/Linux) or Cmd+V (macOS) to paste"
896
+ },
897
+ {
898
+ key: "Shift + drag",
899
+ text: "selects text natively (Option on iTerm2, Shift on Win Term / Alacritty / WezTerm)"
900
+ }
901
+ ]
902
+ }
903
+ ],
904
+ footer: "Run /keys for the full keyboard + mouse reference"
905
+ },
906
+ keysReference: {
907
+ topic: "Reasonix keys + mouse reference",
908
+ sections: [
909
+ {
910
+ title: "keyboard",
911
+ rows: [
912
+ { key: "Enter", text: "submit the prompt" },
913
+ { key: "Shift+Enter", text: "insert a newline in the prompt" },
914
+ { key: "Ctrl+P / Ctrl+N", text: "recall previous / next prompt from history" },
915
+ { key: "Ctrl+A / Ctrl+E", text: "jump to start / end of the current line" },
916
+ { key: "Ctrl+W", text: "delete the word before the cursor" },
917
+ { key: "Ctrl+U", text: "clear the entire prompt buffer" },
918
+ { key: "Tab", text: "complete @-mention \xB7 drill folder \xB7 accept slash command" },
919
+ { key: "Shift+Tab", text: "edit-gate: toggle review \u2194 AUTO mode" },
920
+ { key: "Esc", text: "dismiss picker \xB7 abort the running model turn" },
921
+ { key: "Ctrl+C", text: "abort the running model turn (NOT copy \u2014 see clipboard)" },
922
+ {
923
+ key: "\u2191 / \u2193",
924
+ text: "scroll chat history (PromptInput cursor when buffer non-empty)"
925
+ },
926
+ { key: "PgUp / PgDn", text: "scroll chat history a page at a time" },
927
+ { key: "End", text: "jump chat to the most recent line" }
928
+ ]
929
+ },
930
+ {
931
+ title: "mouse",
932
+ rows: [
933
+ { key: "wheel", text: "scrolls the chat history" },
934
+ { key: "right-click", text: "captured by the app \u2014 use Ctrl+V / Cmd+V to paste" },
935
+ { key: "left-click", text: "captured (no action yet \u2014 reserved for future use)" }
936
+ ]
937
+ },
938
+ {
939
+ title: "copy / paste",
940
+ rows: [
941
+ { key: "select text", text: "hold Shift while dragging (Option on iTerm2)" },
942
+ {
943
+ key: "copy",
944
+ text: "Ctrl+Shift+C (Win/Linux) \xB7 Cmd+C (macOS) \u2014 terminal-native after selection"
945
+ },
946
+ { key: "paste", text: "Ctrl+V or Ctrl+Shift+V (Win/Linux) \xB7 Cmd+V (macOS)" },
947
+ {
948
+ key: "bracketed paste",
949
+ text: "multi-line pastes stay one block \u2014 no auto-submit on intermediate newlines"
950
+ }
951
+ ]
952
+ },
953
+ {
954
+ title: "edit-gate (code mode)",
955
+ rows: [
956
+ { key: "y / n", text: "accept or drop pending edits in the review modal" },
957
+ { key: "Shift+Tab", text: "toggle review \u2194 AUTO (persisted across sessions)" },
958
+ { key: "u", text: "undo the last auto-applied batch (within the 5s banner)" }
959
+ ]
960
+ }
961
+ ],
962
+ footer: "Mouse tracking is on so the wheel scrolls chat instead of moving the cursor \u2014 that's why right-click no longer does the terminal's native paste."
963
+ },
964
+ tipShownOnce: "shown once",
614
965
  modelOverride: "override the default model",
615
966
  noSession: "disable session persistence for this run",
616
967
  resumeHint: "force-resume the named session (even if idle)",
@@ -735,7 +1086,7 @@ var EN = {
735
1086
  description: "shrink oversized tool results AND tool-call args (edit_file search/replace) in the log; cap in tokens, default 4000",
736
1087
  argsHint: "[tokens]"
737
1088
  },
738
- keys: { description: "show all keyboard shortcuts and prompt prefixes" },
1089
+ keys: { description: "keyboard + mouse + copy/paste reference" },
739
1090
  plans: { description: "list this session's active + archived plans, newest first" },
740
1091
  replay: {
741
1092
  description: "load an archived plan as a read-only Time Travel snapshot (default: newest)",
@@ -964,7 +1315,8 @@ var EN = {
964
1315
  loopStopped: "\u25B8 loop stopped.",
965
1316
  loopNoActive: "no active loop to stop.",
966
1317
  loopNoActiveHint: "no active loop. Start one with `/loop <interval> <prompt>` (e.g. /loop 30s npm test).\nCancels on: /loop stop \xB7 Esc \xB7 /clear /new \xB7 any user-typed prompt.",
967
- loopStarted: '\u25B8 loop started \u2014 re-submitting "{prompt}" every {duration}. Type anything (or /loop stop) to cancel.'
1318
+ loopStarted: '\u25B8 loop started \u2014 re-submitting "{prompt}" every {duration}. Type anything (or /loop stop) to cancel.',
1319
+ keysNeedsTui: "/keys needs a TUI context (postKeys wired)."
968
1320
  },
969
1321
  admin: {
970
1322
  doctorNeedsTui: "/doctor needs a TUI context (postDoctor wired).",
@@ -1303,7 +1655,94 @@ var zhCN = {
1303
1655
  ephemeralSession: "\u25B8 \u4E34\u65F6\u804A\u5929 (\u4E0D\u4FDD\u5B58\u4F1A\u8BDD) \u2014 \u53BB\u6389 --no-session \u4EE5\u542F\u7528\u4FDD\u5B58",
1304
1656
  restoredEdits: "\u25B8 \u4ECE\u4E2D\u65AD\u7684\u8FD0\u884C\u4E2D\u6062\u590D\u4E86 {count} \u4E2A\u5F85\u5904\u7406\u7684\u7F16\u8F91\u5757 \u2014 /apply \u63D0\u4EA4\u6216 /discard \u653E\u5F03\u3002",
1305
1657
  resumedPlan: "\u5DF2\u6062\u590D\u8BA1\u5212 \xB7 {when}{summary}",
1306
- tipEditBindings: "\u25B8 \u63D0\u793A\uFF1A\u7F16\u8F91\u95E8\u63A7\u5FEB\u6377\u952E\n y / n \u63A5\u53D7\u6216\u653E\u5F03\u5F85\u5904\u7406\u7684\u7F16\u8F91\n Shift+Tab \u5207\u6362 \u9884\u89C8 \u2194 \u81EA\u52A8 (\u6301\u4E45\u5316\uFF1B\u81EA\u52A8\u6A21\u5F0F\u7ACB\u5373\u5E94\u7528)\n u \u64A4\u6D88\u4E0A\u6B21\u81EA\u52A8\u5E94\u7528\u7684\u6279\u5904\u7406 (\u5728 5 \u79D2\u6A2A\u5E45\u5185)\n \u5F53\u524D\u6A21\u5F0F\u663E\u793A\u5728\u5E95\u90E8\u72B6\u6001\u680F\u3002\u968F\u65F6\u8FD0\u884C /keys \u67E5\u770B\u5B8C\u6574\u5217\u8868\u3002\n (\u6B64\u63D0\u793A\u4EC5\u663E\u793A\u4E00\u6B21 \u2014 \u4E4B\u540E\u5C06\u9690\u85CF\u3002)",
1658
+ tipEditBindings: {
1659
+ topic: "\u7F16\u8F91\u95E8\u63A7\u5FEB\u6377\u952E",
1660
+ sections: [
1661
+ {
1662
+ rows: [
1663
+ { key: "y / n", text: "\u63A5\u53D7\u6216\u653E\u5F03\u5F85\u5904\u7406\u7684\u7F16\u8F91" },
1664
+ { key: "Shift+Tab", text: "\u5207\u6362 \u9884\u89C8 \u2194 \u81EA\u52A8\uFF08\u6301\u4E45\u5316\uFF1B\u81EA\u52A8\u6A21\u5F0F\u7ACB\u5373\u5E94\u7528\uFF09" },
1665
+ { key: "u", text: "\u64A4\u9500\u4E0A\u6B21\u81EA\u52A8\u5E94\u7528\u7684\u6279\u5904\u7406\uFF085 \u79D2\u6A2A\u5E45\u5185\uFF09" }
1666
+ ]
1667
+ }
1668
+ ],
1669
+ footer: "\u5F53\u524D\u6A21\u5F0F\u663E\u793A\u5728\u5E95\u90E8\u72B6\u6001\u680F \xB7 /keys \u67E5\u770B\u5B8C\u6574\u5FEB\u6377\u952E\u53C2\u8003"
1670
+ },
1671
+ tipMouseClipboard: {
1672
+ topic: "\u9F20\u6807 + \u526A\u8D34\u677F",
1673
+ sections: [
1674
+ {
1675
+ rows: [
1676
+ { key: "\u6EDA\u8F6E", text: "\u6EDA\u52A8\u4E0A\u65B9\u7684\u804A\u5929\u8BB0\u5F55" },
1677
+ {
1678
+ key: "\u53F3\u952E",
1679
+ text: "\u5DF2\u88AB\u5E94\u7528\u63A5\u7BA1 \u2014 \u7528 Ctrl+V\uFF08Win/Linux\uFF09\u6216 Cmd+V\uFF08macOS\uFF09\u7C98\u8D34"
1680
+ },
1681
+ {
1682
+ key: "Shift + \u62D6\u52A8",
1683
+ text: "\u539F\u751F\u9009\u4E2D\u6587\u672C\uFF08iTerm2 \u7528 Option\uFF0CWin Term / Alacritty / WezTerm \u7528 Shift\uFF09"
1684
+ }
1685
+ ]
1686
+ }
1687
+ ],
1688
+ footer: "\u8FD0\u884C /keys \u67E5\u770B\u5B8C\u6574\u952E\u76D8 + \u9F20\u6807\u53C2\u8003"
1689
+ },
1690
+ keysReference: {
1691
+ topic: "Reasonix \u952E\u76D8 + \u9F20\u6807\u53C2\u8003",
1692
+ sections: [
1693
+ {
1694
+ title: "\u952E\u76D8",
1695
+ rows: [
1696
+ { key: "Enter", text: "\u63D0\u4EA4\u8F93\u5165" },
1697
+ { key: "Shift+Enter", text: "\u5728\u8F93\u5165\u6846\u4E2D\u63D2\u5165\u6362\u884C" },
1698
+ { key: "Ctrl+P / Ctrl+N", text: "\u8C03\u51FA\u4E0A\u4E00\u6761 / \u4E0B\u4E00\u6761\u5386\u53F2\u63D0\u793A" },
1699
+ { key: "Ctrl+A / Ctrl+E", text: "\u8DF3\u5230\u5F53\u524D\u884C\u7684\u5F00\u5934 / \u7ED3\u5C3E" },
1700
+ { key: "Ctrl+W", text: "\u5220\u9664\u5149\u6807\u524D\u7684\u4E00\u4E2A\u8BCD" },
1701
+ { key: "Ctrl+U", text: "\u6E05\u7A7A\u6574\u4E2A\u8F93\u5165\u7F13\u51B2\u533A" },
1702
+ { key: "Tab", text: "\u8865\u5168 @-mention \xB7 \u8FDB\u5165\u6587\u4EF6\u5939 \xB7 \u63A5\u53D7 slash \u547D\u4EE4" },
1703
+ { key: "Shift+Tab", text: "\u7F16\u8F91\u95E8\u63A7\uFF1A\u5207\u6362 \u9884\u89C8 \u2194 \u81EA\u52A8 \u6A21\u5F0F" },
1704
+ { key: "Esc", text: "\u5173\u95ED\u5F39\u51FA\u9009\u62E9\u5668 \xB7 \u4E2D\u6B62\u5F53\u524D\u6A21\u578B\u56DE\u5408" },
1705
+ { key: "Ctrl+C", text: "\u4E2D\u6B62\u5F53\u524D\u6A21\u578B\u56DE\u5408\uFF08\u4E0D\u662F\u590D\u5236 \u2014 \u89C1\u526A\u8D34\u677F\u6BB5\uFF09" },
1706
+ { key: "\u2191 / \u2193", text: "\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55\uFF08\u7F13\u51B2\u533A\u975E\u7A7A\u65F6\u4E3A\u5149\u6807\u79FB\u52A8\uFF09" },
1707
+ { key: "PgUp / PgDn", text: "\u6574\u9875\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55" },
1708
+ { key: "End", text: "\u8DF3\u5230\u804A\u5929\u7684\u6700\u65B0\u4E00\u884C" }
1709
+ ]
1710
+ },
1711
+ {
1712
+ title: "\u9F20\u6807",
1713
+ rows: [
1714
+ { key: "\u6EDA\u8F6E", text: "\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55" },
1715
+ { key: "\u53F3\u952E", text: "\u88AB\u5E94\u7528\u63A5\u7BA1 \u2014 \u7528 Ctrl+V / Cmd+V \u7C98\u8D34" },
1716
+ { key: "\u5DE6\u952E", text: "\u88AB\u63A5\u7BA1\uFF08\u6682\u65E0\u52A8\u4F5C \u2014 \u9884\u7559\u7ED9\u540E\u7EED\u529F\u80FD\uFF09" }
1717
+ ]
1718
+ },
1719
+ {
1720
+ title: "\u590D\u5236 / \u7C98\u8D34",
1721
+ rows: [
1722
+ { key: "\u9009\u4E2D\u6587\u5B57", text: "\u62D6\u52A8\u65F6\u6309\u4F4F Shift\uFF08iTerm2 \u7528 Option\uFF09" },
1723
+ {
1724
+ key: "\u590D\u5236",
1725
+ text: "Ctrl+Shift+C\uFF08Win/Linux\uFF09\xB7 Cmd+C\uFF08macOS\uFF09\u2014 \u9009\u4E2D\u540E\u7531\u7EC8\u7AEF\u539F\u751F\u5904\u7406"
1726
+ },
1727
+ { key: "\u7C98\u8D34", text: "Ctrl+V \u6216 Ctrl+Shift+V\uFF08Win/Linux\uFF09\xB7 Cmd+V\uFF08macOS\uFF09" },
1728
+ {
1729
+ key: "bracketed paste",
1730
+ text: "\u591A\u884C\u7C98\u8D34\u6574\u4F53\u8FDB\u5165 \u2014 \u4E2D\u95F4\u6362\u884C\u4E0D\u4F1A\u89E6\u53D1\u63D0\u4EA4"
1731
+ }
1732
+ ]
1733
+ },
1734
+ {
1735
+ title: "\u7F16\u8F91\u95E8\u63A7\uFF08\u4EC5 code \u6A21\u5F0F\uFF09",
1736
+ rows: [
1737
+ { key: "y / n", text: "\u5728\u9884\u89C8\u6A21\u6001\u4E2D\u63A5\u53D7\u6216\u653E\u5F03\u5F85\u5904\u7406\u7684\u7F16\u8F91" },
1738
+ { key: "Shift+Tab", text: "\u5207\u6362 \u9884\u89C8 \u2194 \u81EA\u52A8\uFF08\u6301\u4E45\u5316\uFF09" },
1739
+ { key: "u", text: "\u64A4\u9500\u4E0A\u6B21\u81EA\u52A8\u5E94\u7528\u7684\u6279\u5904\u7406\uFF085 \u79D2\u6A2A\u5E45\u5185\uFF09" }
1740
+ ]
1741
+ }
1742
+ ],
1743
+ footer: "\u9F20\u6807\u8FFD\u8E2A\u5DF2\u5F00\u542F\uFF0C\u6EDA\u8F6E\u7528\u6765\u6EDA\u52A8\u804A\u5929\u800C\u4E0D\u662F\u79FB\u52A8\u5149\u6807 \u2014 \u8FD9\u4E5F\u662F\u53F3\u952E\u4E0D\u518D\u89E6\u53D1\u7EC8\u7AEF\u539F\u751F\u7C98\u8D34\u7684\u539F\u56E0\u3002"
1744
+ },
1745
+ tipShownOnce: "\u4EC5\u663E\u793A\u4E00\u6B21",
1307
1746
  modelOverride: "\u8986\u76D6\u9ED8\u8BA4\u6A21\u578B",
1308
1747
  noSession: "\u7981\u7528\u672C\u6B21\u8FD0\u884C\u7684\u4F1A\u8BDD\u6301\u4E45\u5316",
1309
1748
  resumeHint: "\u5F3A\u5236\u6062\u590D\u6307\u5B9A\u4F1A\u8BDD\uFF08\u5373\u4F7F\u7A7A\u95F2\uFF09",
@@ -1430,7 +1869,7 @@ var zhCN = {
1430
1869
  description: "\u7F29\u5C0F\u65E5\u5FD7\u4E2D\u8FC7\u5927\u7684\u5DE5\u5177\u7ED3\u679C\u548C\u5DE5\u5177\u8C03\u7528\u53C2\u6570\uFF1B\u4E0A\u9650\u4E3A tokens\uFF0C\u9ED8\u8BA4 4000",
1431
1870
  argsHint: "[tokens]"
1432
1871
  },
1433
- keys: { description: "\u663E\u793A\u6240\u6709\u952E\u76D8\u5FEB\u6377\u952E\u548C\u63D0\u793A\u524D\u7F00" },
1872
+ keys: { description: "\u952E\u76D8 + \u9F20\u6807 + \u590D\u5236\u7C98\u8D34\u53C2\u8003" },
1434
1873
  plans: { description: "\u5217\u51FA\u6B64\u4F1A\u8BDD\u7684\u6D3B\u8DC3 + \u5F52\u6863\u8BA1\u5212\uFF08\u6700\u65B0\u5728\u524D\uFF09" },
1435
1874
  replay: {
1436
1875
  description: "\u52A0\u8F7D\u5F52\u6863\u8BA1\u5212\u4E3A\u53EA\u8BFB\u7684\u65F6\u95F4\u65C5\u884C\u5FEB\u7167\uFF08\u9ED8\u8BA4\uFF1A\u6700\u65B0\uFF09",
@@ -1661,7 +2100,8 @@ var zhCN = {
1661
2100
  loopStopped: "\u25B8 \u5FAA\u73AF\u5DF2\u505C\u6B62\u3002",
1662
2101
  loopNoActive: "\u6CA1\u6709\u6D3B\u52A8\u7684\u5FAA\u73AF\u53EF\u505C\u6B62\u3002",
1663
2102
  loopNoActiveHint: "\u6CA1\u6709\u6D3B\u52A8\u7684\u5FAA\u73AF\u3002\u4F7F\u7528 `/loop <interval> <prompt>` \u542F\u52A8\u4E00\u4E2A\uFF08\u4F8B\u5982 /loop 30s npm test\uFF09\u3002\n\u53D6\u6D88\u65B9\u5F0F\uFF1A/loop stop \xB7 Esc \xB7 /clear /new \xB7 \u4EFB\u4F55\u7528\u6237\u8F93\u5165\u7684\u63D0\u793A\u3002",
1664
- loopStarted: '\u25B8 \u5FAA\u73AF\u5DF2\u542F\u52A8 \u2014 \u6BCF {duration} \u91CD\u65B0\u63D0\u4EA4 "{prompt}"\u3002\u8F93\u5165\u4EFB\u4F55\u5185\u5BB9\uFF08\u6216 /loop stop\uFF09\u53D6\u6D88\u3002'
2103
+ loopStarted: '\u25B8 \u5FAA\u73AF\u5DF2\u542F\u52A8 \u2014 \u6BCF {duration} \u91CD\u65B0\u63D0\u4EA4 "{prompt}"\u3002\u8F93\u5165\u4EFB\u4F55\u5185\u5BB9\uFF08\u6216 /loop stop\uFF09\u53D6\u6D88\u3002',
2104
+ keysNeedsTui: "/keys \u9700\u8981 TUI \u4E0A\u4E0B\u6587\uFF08postKeys \u5DF2\u8FDE\u63A5\uFF09\u3002"
1665
2105
  },
1666
2106
  admin: {
1667
2107
  doctorNeedsTui: "/doctor \u9700\u8981 TUI \u4E0A\u4E0B\u6587\uFF08postDoctor \u5DF2\u8FDE\u63A5\uFF09\u3002",
@@ -4777,13 +5217,41 @@ function listFilesWithStatsSync(root, opts = {}) {
4777
5217
  return out;
4778
5218
  }
4779
5219
  async function listFilesWithStatsAsync(root, opts = {}) {
5220
+ const out = [];
4780
5221
  const maxResults = Math.max(1, opts.maxResults ?? 2e3);
5222
+ await walkFilesStream(root, {
5223
+ ...opts,
5224
+ onEntry: (e) => {
5225
+ out.push(e);
5226
+ return out.length < maxResults;
5227
+ }
5228
+ });
5229
+ return out;
5230
+ }
5231
+ async function walkFilesStream(root, opts) {
4781
5232
  const ignoreDirs = new Set(opts.ignoreDirs ?? DEFAULT_PICKER_IGNORE_DIRS);
4782
- const rootAbs = resolve(root);
4783
5233
  const respectGi = opts.respectGitignore !== false;
4784
- const out = [];
5234
+ const rootAbs = resolve(root);
5235
+ const progressGap = Math.max(0, opts.progressIntervalMs ?? 100);
5236
+ let scanned = 0;
5237
+ let halted = false;
5238
+ let lastProgress = 0;
5239
+ const reportProgress = (force) => {
5240
+ if (!opts.onProgress) return;
5241
+ const now = Date.now();
5242
+ if (force || now - lastProgress >= progressGap) {
5243
+ lastProgress = now;
5244
+ opts.onProgress(scanned);
5245
+ }
5246
+ };
5247
+ const emit = (entry) => {
5248
+ scanned++;
5249
+ if (halted) return;
5250
+ if (opts.onEntry(entry) === false) halted = true;
5251
+ reportProgress(false);
5252
+ };
4785
5253
  const walk2 = async (dirAbs, dirRel, layers) => {
4786
- if (out.length >= maxResults) return;
5254
+ if (halted || opts.signal?.aborted) return;
4787
5255
  let effectiveLayers = layers;
4788
5256
  if (respectGi) {
4789
5257
  const ig = await loadGitignoreAt(dirAbs);
@@ -4798,36 +5266,31 @@ async function listFilesWithStatsAsync(root, opts = {}) {
4798
5266
  entries.sort((a, b) => a.name.localeCompare(b.name));
4799
5267
  const fileEnts = [];
4800
5268
  for (const ent of entries) {
4801
- if (out.length >= maxResults) break;
4802
- const relPath = dirRel ? `${dirRel}/${ent.name}` : ent.name;
5269
+ if (halted || opts.signal?.aborted) break;
4803
5270
  const absPath = join5(dirAbs, ent.name);
4804
5271
  if (ent.isDirectory()) {
4805
5272
  if (ent.name.startsWith(".") || ignoreDirs.has(ent.name)) continue;
4806
5273
  if (ignoredByLayers(effectiveLayers, absPath, true)) continue;
4807
5274
  if (fileEnts.length > 0) {
4808
- await statBatch(fileEnts, dirAbs, dirRel, out, maxResults, effectiveLayers);
5275
+ await flushFiles(fileEnts, dirAbs, dirRel, effectiveLayers, emit);
4809
5276
  fileEnts.length = 0;
4810
- if (out.length >= maxResults) return;
5277
+ if (halted || opts.signal?.aborted) return;
4811
5278
  }
4812
- await walk2(absPath, relPath, effectiveLayers);
5279
+ await walk2(absPath, dirRel ? `${dirRel}/${ent.name}` : ent.name, effectiveLayers);
4813
5280
  } else if (ent.isFile() || ent.isSymbolicLink()) {
4814
5281
  fileEnts.push(ent);
4815
5282
  }
4816
5283
  }
4817
- if (fileEnts.length > 0 && out.length < maxResults) {
4818
- await statBatch(fileEnts, dirAbs, dirRel, out, maxResults, effectiveLayers);
5284
+ if (fileEnts.length > 0 && !halted && !opts.signal?.aborted) {
5285
+ await flushFiles(fileEnts, dirAbs, dirRel, effectiveLayers, emit);
4819
5286
  }
4820
5287
  };
4821
5288
  await walk2(rootAbs, "", []);
4822
- return out;
5289
+ reportProgress(true);
5290
+ return { scanned, cancelled: !!opts.signal?.aborted };
4823
5291
  }
4824
- async function statBatch(ents, dirAbs, dirRel, out, maxResults, layers) {
4825
- const accepted = [];
4826
- for (const e of ents) {
4827
- if (out.length + accepted.length >= maxResults) break;
4828
- if (ignoredByLayers(layers, join5(dirAbs, e.name), false)) continue;
4829
- accepted.push(e);
4830
- }
5292
+ async function flushFiles(ents, dirAbs, dirRel, layers, emit) {
5293
+ const accepted = ents.filter((e) => !ignoredByLayers(layers, join5(dirAbs, e.name), false));
4831
5294
  const stats = await Promise.all(
4832
5295
  accepted.map(
4833
5296
  (e) => stat(join5(dirAbs, e.name)).then((s) => ({ mtimeMs: s.mtimeMs, isFile: s.isFile() })).catch(() => null)
@@ -4836,14 +5299,90 @@ async function statBatch(ents, dirAbs, dirRel, out, maxResults, layers) {
4836
5299
  for (let i = 0; i < accepted.length; i++) {
4837
5300
  const ent = accepted[i];
4838
5301
  const s = stats[i];
4839
- if (ent.isSymbolicLink()) {
4840
- if (!s || !s.isFile) continue;
5302
+ if (ent.isSymbolicLink() && (!s || !s.isFile)) continue;
5303
+ emit({
5304
+ path: dirRel ? `${dirRel}/${ent.name}` : ent.name,
5305
+ mtimeMs: s?.mtimeMs ?? 0
5306
+ });
5307
+ }
5308
+ }
5309
+ async function listDirectory(root, relDir, opts = {}) {
5310
+ const ignoreDirs = new Set(opts.ignoreDirs ?? DEFAULT_PICKER_IGNORE_DIRS);
5311
+ const respectGi = opts.respectGitignore !== false;
5312
+ const rootAbs = resolve(root);
5313
+ const dirAbs = resolve(rootAbs, relDir);
5314
+ const rel = relative(rootAbs, dirAbs);
5315
+ if (rel.startsWith("..") || isAbsolute(rel)) return [];
5316
+ const layers = [];
5317
+ if (respectGi) {
5318
+ const segs = rel ? rel.split(/[\\/]/) : [];
5319
+ let cursor = rootAbs;
5320
+ const ig = await loadGitignoreAt(cursor);
5321
+ if (ig) layers.push({ dirAbs: cursor, ig });
5322
+ for (const seg of segs) {
5323
+ cursor = join5(cursor, seg);
5324
+ const igSeg = await loadGitignoreAt(cursor);
5325
+ if (igSeg) layers.push({ dirAbs: cursor, ig: igSeg });
4841
5326
  }
4842
- out.push({
5327
+ }
5328
+ let raw;
5329
+ try {
5330
+ raw = await readdir(dirAbs, { withFileTypes: true });
5331
+ } catch {
5332
+ return [];
5333
+ }
5334
+ const dirRel = rel.split(/[\\/]/).join("/");
5335
+ const dirs = [];
5336
+ const files = [];
5337
+ for (const ent of raw) {
5338
+ const absPath = join5(dirAbs, ent.name);
5339
+ if (ent.isDirectory()) {
5340
+ if (ent.name.startsWith(".") || ignoreDirs.has(ent.name)) continue;
5341
+ if (ignoredByLayers(layers, absPath, true)) continue;
5342
+ dirs.push({
5343
+ name: ent.name,
5344
+ path: dirRel ? `${dirRel}/${ent.name}` : ent.name,
5345
+ isDir: true,
5346
+ mtimeMs: 0
5347
+ });
5348
+ } else if (ent.isFile() || ent.isSymbolicLink()) {
5349
+ if (ignoredByLayers(layers, absPath, false)) continue;
5350
+ files.push(ent);
5351
+ }
5352
+ }
5353
+ const stats = await Promise.all(
5354
+ files.map(
5355
+ (e) => stat(join5(dirAbs, e.name)).then((s) => ({ mtimeMs: s.mtimeMs, isFile: s.isFile() })).catch(() => null)
5356
+ )
5357
+ );
5358
+ const fileEntries = [];
5359
+ for (let i = 0; i < files.length; i++) {
5360
+ const ent = files[i];
5361
+ const s = stats[i];
5362
+ if (ent.isSymbolicLink() && (!s || !s.isFile)) continue;
5363
+ fileEntries.push({
5364
+ name: ent.name,
4843
5365
  path: dirRel ? `${dirRel}/${ent.name}` : ent.name,
5366
+ isDir: false,
4844
5367
  mtimeMs: s?.mtimeMs ?? 0
4845
5368
  });
4846
5369
  }
5370
+ dirs.sort((a, b) => a.name.localeCompare(b.name));
5371
+ fileEntries.sort((a, b) => a.name.localeCompare(b.name));
5372
+ return [...dirs, ...fileEntries];
5373
+ }
5374
+ function parseAtQuery(query) {
5375
+ const normalized = query.replace(/\\/g, "/");
5376
+ const trailingSlash = normalized.endsWith("/");
5377
+ const trimmed = trailingSlash ? normalized.slice(0, -1) : normalized;
5378
+ const lastSlash = trimmed.lastIndexOf("/");
5379
+ if (trailingSlash) return { dir: trimmed, filter: "", trailingSlash: true };
5380
+ if (lastSlash < 0) return { dir: "", filter: trimmed, trailingSlash: false };
5381
+ return {
5382
+ dir: trimmed.slice(0, lastSlash),
5383
+ filter: trimmed.slice(lastSlash + 1),
5384
+ trailingSlash: false
5385
+ };
4847
5386
  }
4848
5387
  var AT_PICKER_PREFIX = /(?:^|\s)@([a-zA-Z0-9_./\\-]*)$/;
4849
5388
  function detectAtPicker(input) {
@@ -11480,6 +12019,7 @@ export {
11480
12019
  isJsonRpcError,
11481
12020
  isNpxInstall,
11482
12021
  isPlausibleKey,
12022
+ listDirectory,
11483
12023
  listFilesSync,
11484
12024
  listFilesWithStatsAsync,
11485
12025
  listFilesWithStatsSync,
@@ -11493,6 +12033,7 @@ export {
11493
12033
  nestArguments,
11494
12034
  openTranscriptFile,
11495
12035
  outputCostUsd,
12036
+ parseAtQuery,
11496
12037
  parseEditBlocks,
11497
12038
  parseMcpSpec,
11498
12039
  parseMojeekResults,
@@ -11537,6 +12078,7 @@ export {
11537
12078
  tokenizeCommand,
11538
12079
  truncateForModel,
11539
12080
  truncateForModelByTokens,
12081
+ walkFilesStream,
11540
12082
  webFetch,
11541
12083
  webSearch,
11542
12084
  withUtf8Codepage,