miantiao.me 0.1.2 → 0.1.4

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.
@@ -4,8 +4,8 @@ import {
4
4
 
5
5
  // packages/cli/src/app.tsx
6
6
  import { AssistantRuntimeProvider } from "@assistant-ui/react-ink";
7
- import { Box as Box9, render, Text as Text9, useApp as useApp2, useInput as useInput6, useWindowSize as useWindowSize3 } from "ink";
8
- import { useEffect as useEffect5, useState as useState6 } from "react";
7
+ import { Box as Box14, render, Text as Text14, useApp as useApp2, useInput as useInput9, useWindowSize as useWindowSize4 } from "ink";
8
+ import { useEffect as useEffect9, useState as useState11 } from "react";
9
9
 
10
10
  // src/terminal/schema.ts
11
11
  import { z } from "zod";
@@ -287,7 +287,7 @@ var TerminalClient = class {
287
287
 
288
288
  // packages/cli/src/console.tsx
289
289
  import { ThreadPrimitive as ThreadPrimitive2 } from "@assistant-ui/react-ink";
290
- import { useRef as useRef5 } from "react";
290
+ import { useRef as useRef8 } from "react";
291
291
 
292
292
  // packages/cli/src/chat.tsx
293
293
  import { ThreadPrimitive, useAuiState } from "@assistant-ui/react-ink";
@@ -421,14 +421,7 @@ import { Box as Box2, Text as Text2, useBoxMetrics as useBoxMetrics2, useInput a
421
421
  import { useEffect, useRef as useRef2, useState as useState2 } from "react";
422
422
 
423
423
  // packages/cli/src/scroll-area.tsx
424
- import {
425
- Box,
426
- Text,
427
- measureElement,
428
- useBoxMetrics,
429
- useFocus,
430
- useInput
431
- } from "ink";
424
+ import { Box, Text, measureElement, useBoxMetrics, useFocus, useInput } from "ink";
432
425
  import {
433
426
  createContext as createContext2,
434
427
  useContext as useContext2,
@@ -447,10 +440,11 @@ function useScrollArea() {
447
440
  function ScrollArea({
448
441
  children,
449
442
  selection,
443
+ active = true,
450
444
  ref
451
445
  }) {
452
446
  const { colors } = useTerminalStyle();
453
- const { isFocused } = useFocus({ id: "content" });
447
+ const { isFocused } = useFocus({ id: "content", isActive: active });
454
448
  const viewport = useRef(null);
455
449
  const content = useRef(null);
456
450
  const { clientHeight: height } = useBoxMetrics(viewport);
@@ -459,32 +453,42 @@ function ScrollArea({
459
453
  const maximum = Math.max(0, contentHeight - height);
460
454
  const offset = anchor === "end" ? maximum : Math.min(anchor, maximum);
461
455
  const context = useMemo2(() => ({ focused: isFocused, height }), [isFocused, height]);
462
- useImperativeHandle(ref, () => ({
463
- end: () => setAnchor("end"),
464
- reveal: (element) => {
465
- if (!viewport.current) return false;
466
- const item = measureElement(element);
467
- const top = item.y - measureElement(viewport.current).y;
468
- const visible = top >= offset && top < offset + height;
469
- if (top < offset || item.height > height) setAnchor(Math.max(0, top));
470
- else if (top + item.height > offset + height)
471
- setAnchor(Math.min(maximum, top + item.height - height));
472
- return visible;
473
- }
474
- }), [offset, height, maximum]);
475
- useInput((_input, key) => {
476
- let next = offset;
477
- if (key.pageUp) next -= Math.max(1, height - 1);
478
- else if (key.pageDown) next += Math.max(1, height - 1);
479
- else if (isFocused && !selection && key.upArrow) next--;
480
- else if (isFocused && !selection && key.downArrow) next++;
481
- else if (isFocused && !selection && key.home) next = 0;
482
- else if (isFocused && !selection && key.end) next = maximum;
483
- else return;
484
- setAnchor(next >= maximum ? "end" : Math.max(0, next));
485
- });
456
+ useImperativeHandle(
457
+ ref,
458
+ () => ({
459
+ end: () => setAnchor("end"),
460
+ reveal: (element) => {
461
+ if (!viewport.current) return false;
462
+ const item = measureElement(element);
463
+ const top = item.y - measureElement(viewport.current).y;
464
+ const visible = top >= offset && top < offset + height;
465
+ if (top < offset || item.height > height) setAnchor(Math.max(0, top));
466
+ else if (top + item.height > offset + height)
467
+ setAnchor(Math.min(maximum, top + item.height - height));
468
+ return visible;
469
+ }
470
+ }),
471
+ [offset, height, maximum]
472
+ );
473
+ useInput(
474
+ (_input, key) => {
475
+ let next = offset;
476
+ if (key.pageUp) next -= Math.max(1, height - 1);
477
+ else if (key.pageDown) next += Math.max(1, height - 1);
478
+ else if (isFocused && !selection && key.upArrow) next--;
479
+ else if (isFocused && !selection && key.downArrow) next++;
480
+ else if (isFocused && !selection && key.home) next = 0;
481
+ else if (isFocused && !selection && key.end) next = maximum;
482
+ else return;
483
+ setAnchor(next >= maximum ? "end" : Math.max(0, next));
484
+ },
485
+ { isActive: active }
486
+ );
486
487
  const track = Math.max(0, height);
487
- const thumbSize = Math.min(track, Math.max(1, Math.round(height * height / Math.max(1, contentHeight))));
488
+ const thumbSize = Math.min(
489
+ track,
490
+ Math.max(1, Math.round(height * height / Math.max(1, contentHeight)))
491
+ );
488
492
  const thumbTop = maximum > 0 ? Math.round(offset / maximum * (height - thumbSize)) : 0;
489
493
  const before = Array(thumbTop).fill("\u2502").join("\n");
490
494
  const thumb = Array(thumbSize).fill("\u2503").join("\n");
@@ -517,30 +521,35 @@ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
517
521
  function PageList({ items, onSelect, scroll, compact = false }) {
518
522
  const { focused } = useScrollArea();
519
523
  const { colors } = useTerminalStyle();
520
- const [selected, setSelected] = useState2(0);
524
+ const [position, setSelected] = useState2(0);
525
+ const selected = Math.max(0, Math.min(position, items.length - 1));
526
+ const selectedItem = items[selected];
521
527
  const elements = useRef2(/* @__PURE__ */ new Map());
522
528
  const container = useRef2(null);
523
529
  const { hasMeasured } = useBoxMetrics2(container);
524
530
  useEffect(() => {
525
- const element = elements.current.get(items[selected].path);
531
+ const element = selectedItem && elements.current.get(selectedItem.path);
526
532
  if (focused && hasMeasured && element) scroll.current?.reveal(element);
527
- }, [focused, hasMeasured, selected, items, scroll]);
528
- useInput2((_input, key) => {
529
- let next = selected;
530
- if (key.upArrow) next--;
531
- else if (key.downArrow) next++;
532
- else if (key.home) next = 0;
533
- else if (key.end) next = items.length - 1;
534
- else if (key.return) {
535
- const item = items[selected];
536
- const element = elements.current.get(item.path);
537
- if (element && scroll.current?.reveal(element) === false) return;
538
- onSelect(item.path, item.external);
539
- return;
540
- } else return;
541
- next = Math.max(0, Math.min(items.length - 1, next));
542
- setSelected(next);
543
- }, { isActive: focused });
533
+ }, [focused, hasMeasured, selectedItem, scroll]);
534
+ useInput2(
535
+ (_input, key) => {
536
+ let next = selected;
537
+ if (key.upArrow) next--;
538
+ else if (key.downArrow) next++;
539
+ else if (key.home) next = 0;
540
+ else if (key.end) next = items.length - 1;
541
+ else if (key.return) {
542
+ if (!selectedItem) return;
543
+ const element = elements.current.get(selectedItem.path);
544
+ if (element && scroll.current?.reveal(element) === false) return;
545
+ onSelect(selectedItem.path, selectedItem.external);
546
+ return;
547
+ } else return;
548
+ next = Math.max(0, Math.min(items.length - 1, next));
549
+ setSelected(next);
550
+ },
551
+ { isActive: focused }
552
+ );
544
553
  return /* @__PURE__ */ jsx3(Box2, { ref: container, flexDirection: "column", marginTop: 1, children: items.map((item, index) => {
545
554
  const active = focused && index === selected;
546
555
  return /* @__PURE__ */ jsxs2(
@@ -643,9 +652,17 @@ function ChatThread() {
643
652
  }
644
653
 
645
654
  // packages/cli/src/composer.tsx
646
- import { ComposerPrimitive, LoadingPrimitive, useAui, useAuiState as useAuiState2 } from "@assistant-ui/react-ink";
647
- import { Box as Box6, Text as Text6, useFocusManager, useInput as useInput4 } from "ink";
648
- import { useEffect as useEffect2, useRef as useRef4 } from "react";
655
+ import { useAui, useAuiState as useAuiState2 } from "@assistant-ui/react-ink";
656
+ import {
657
+ Box as Box10,
658
+ Text as Text10,
659
+ useBoxMetrics as useBoxMetrics6,
660
+ useFocus as useFocus3,
661
+ useFocusManager as useFocusManager2,
662
+ useInput as useInput7,
663
+ useWindowSize as useWindowSize2
664
+ } from "ink";
665
+ import { useEffect as useEffect5, useEffectEvent, useRef as useRef6 } from "react";
649
666
 
650
667
  // src/agent/appearance.ts
651
668
  var composerPlaceholder = "\u95EE\u70B9\u4EC0\u4E48\uFF0C\u6216\u8F93\u5165 / \u67E5\u770B\u547D\u4EE4";
@@ -682,8 +699,8 @@ function getSearchPath(query) {
682
699
  }
683
700
 
684
701
  // packages/cli/src/command-menu.tsx
685
- import { Box as Box5, Text as Text5, useInput as useInput3, useWindowSize } from "ink";
686
- import { useImperativeHandle as useImperativeHandle2, useState as useState3 } from "react";
702
+ import { Box as Box5, Text as Text5, useFocusManager, useInput as useInput3 } from "ink";
703
+ import { useEffect as useEffect2, useImperativeHandle as useImperativeHandle2, useState as useState3 } from "react";
687
704
 
688
705
  // packages/cli/src/commands.ts
689
706
  var terminalCommands = [
@@ -697,7 +714,9 @@ function getCommandSuggestions(text) {
697
714
  const match = text.match(/^\/([^\s]*)$/);
698
715
  if (!match) return null;
699
716
  const query = match[1].toLowerCase();
700
- return terminalCommands.filter((command) => command.id.startsWith(query) || command.description.includes(query));
717
+ return terminalCommands.filter(
718
+ (command) => command.id.includes(query) || command.description.toLowerCase().includes(query)
719
+ );
701
720
  }
702
721
  function numberedItem(input, page) {
703
722
  if (!/^\d+$/.test(input)) return void 0;
@@ -729,22 +748,30 @@ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
729
748
  function CommandMenu({
730
749
  text,
731
750
  focused,
751
+ availableHeight,
732
752
  onSelect,
733
753
  ref
734
754
  }) {
735
755
  const { colors } = useTerminalStyle();
736
- const { rows } = useWindowSize();
756
+ const { activeId, disableFocus, enableFocus, focus, focusPrevious } = useFocusManager();
737
757
  const [selected, setSelected] = useState3(0);
738
758
  const [dismissed, setDismissed] = useState3(false);
739
759
  const suggestions = getCommandSuggestions(text);
740
760
  const matches = suggestions ?? [];
741
761
  const open = focused && suggestions !== null && !dismissed;
742
- const visibleCount = Math.max(1, Math.min(8, rows - 10));
762
+ const visibleCount = Math.max(1, Math.min(8, availableHeight - 4));
743
763
  const start = Math.max(0, selected - visibleCount + 1);
764
+ useEffect2(() => {
765
+ if (!open || !activeId) return;
766
+ disableFocus();
767
+ focus(activeId);
768
+ return enableFocus;
769
+ }, [open, activeId, disableFocus, enableFocus, focus]);
744
770
  useImperativeHandle2(ref, () => ({
771
+ isOpen: () => open,
745
772
  submit: (input) => {
746
- if (!open || input !== text || !matches.length) return false;
747
- onSelect(matches[selected]);
773
+ if (!open || input !== text) return false;
774
+ if (matches.length) onSelect(matches[selected]);
748
775
  return true;
749
776
  },
750
777
  dismiss: () => {
@@ -753,170 +780,598 @@ function CommandMenu({
753
780
  return true;
754
781
  }
755
782
  }));
756
- useInput3((_input, key) => {
757
- if (!matches.length) return;
758
- const direction = Number(key.downArrow) - Number(key.upArrow);
759
- if (direction) setSelected((index) => (index + direction + matches.length) % matches.length);
760
- }, { isActive: open });
783
+ useInput3(
784
+ (_input, key) => {
785
+ if (key.tab) {
786
+ if (key.shift) queueMicrotask(focusPrevious);
787
+ else if (matches.length) onSelect(matches[selected]);
788
+ return;
789
+ }
790
+ if (!matches.length) return;
791
+ const direction = Number(key.downArrow) - Number(key.upArrow);
792
+ if (direction) setSelected((index) => (index + direction + matches.length) % matches.length);
793
+ },
794
+ { isActive: open }
795
+ );
761
796
  if (!open) return null;
762
- return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", borderStyle: "round", borderColor: colors.line, paddingX: 1, marginBottom: 1, children: [
763
- matches.length === 0 && /* @__PURE__ */ jsx6(Text5, { color: colors.muted, children: "\u6CA1\u6709\u5339\u914D\u7684\u547D\u4EE4" }),
764
- matches.slice(start, start + visibleCount).map((command, index) => {
765
- const active = start + index === selected;
766
- return /* @__PURE__ */ jsxs5(Box5, { height: 1, backgroundColor: active ? colors.band : void 0, children: [
767
- /* @__PURE__ */ jsx6(Box5, { width: 14, flexShrink: 0, children: /* @__PURE__ */ jsxs5(Text5, { color: active ? colors.accent : colors.skill, bold: active, children: [
768
- active ? "\u25B8" : " ",
769
- " /",
770
- command.id
797
+ return /* @__PURE__ */ jsxs5(
798
+ Box5,
799
+ {
800
+ flexDirection: "column",
801
+ borderStyle: "round",
802
+ borderColor: colors.line,
803
+ backgroundColor: colors.bg,
804
+ paddingX: 1,
805
+ marginBottom: 1,
806
+ children: [
807
+ matches.length === 0 && /* @__PURE__ */ jsx6(Text5, { color: colors.muted, children: "\u6CA1\u6709\u5339\u914D\u7684\u547D\u4EE4" }),
808
+ matches.slice(start, start + visibleCount).map((command, index) => {
809
+ const active = start + index === selected;
810
+ return /* @__PURE__ */ jsxs5(Box5, { height: 1, backgroundColor: active ? colors.band : void 0, children: [
811
+ /* @__PURE__ */ jsx6(Box5, { width: 14, flexShrink: 0, children: /* @__PURE__ */ jsxs5(Text5, { color: colors.skill, children: [
812
+ active ? "\u25B8" : " ",
813
+ " /",
814
+ command.id
815
+ ] }) }),
816
+ /* @__PURE__ */ jsx6(Box5, { flexGrow: 1, flexShrink: 1, children: /* @__PURE__ */ jsx6(Text5, { color: colors.muted, wrap: "truncate-end", children: command.description }) })
817
+ ] }, command.id);
818
+ }),
819
+ /* @__PURE__ */ jsx6(Text5, { color: colors.muted, wrap: "truncate-end", children: "\u2191\u2193 \u9009\u62E9 \xB7 Enter/Tab \u786E\u8BA4 \xB7 Esc \u5173\u95ED" })
820
+ ]
821
+ }
822
+ );
823
+ }
824
+
825
+ // packages/cli/src/composer-input.tsx
826
+ import { Box as Box6, Text as Text6, useBoxMetrics as useBoxMetrics4, useInput as useInput4, useWindowSize } from "ink";
827
+ import { useRef as useRef4, useState as useState4 } from "react";
828
+
829
+ // packages/cli/src/input-editor.ts
830
+ import stringWidth from "string-width";
831
+ var segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
832
+ function editorLines(text, width) {
833
+ const lines = [];
834
+ let start = 0;
835
+ let columns = 0;
836
+ for (const { segment, index } of segmenter.segment(text)) {
837
+ if (segment === "\n") {
838
+ lines.push({ start, end: index, text: text.slice(start, index) });
839
+ if (columns >= width) lines.push({ start: index, end: index, text: "" });
840
+ start = index + 1;
841
+ columns = 0;
842
+ continue;
843
+ }
844
+ const size = stringWidth(segment);
845
+ if (columns && columns + size > width) {
846
+ lines.push({ start, end: index, text: text.slice(start, index) });
847
+ start = index;
848
+ columns = 0;
849
+ }
850
+ columns += size;
851
+ }
852
+ lines.push({ start, end: text.length, text: text.slice(start) });
853
+ if (columns >= width) lines.push({ start: text.length, end: text.length, text: "" });
854
+ return lines;
855
+ }
856
+ function cursorRow(lines, cursor) {
857
+ return lines.findLastIndex((line) => line.start <= cursor);
858
+ }
859
+ function editText(state, start, end, inserted = "") {
860
+ return {
861
+ text: state.text.slice(0, start) + inserted + state.text.slice(end),
862
+ cursor: start + inserted.length
863
+ };
864
+ }
865
+ function moveCursor(state, direction) {
866
+ const boundaries = [...segmenter.segment(state.text)].map((part) => part.index);
867
+ boundaries.push(state.text.length);
868
+ const cursor = direction < 0 ? boundaries.findLast((index) => index < state.cursor) ?? 0 : boundaries.find((index) => index > state.cursor) ?? state.text.length;
869
+ return { text: state.text, cursor };
870
+ }
871
+ function moveVertical(state, width, direction) {
872
+ const lines = editorLines(state.text, width);
873
+ const row = cursorRow(lines, state.cursor);
874
+ const column = state.column ?? stringWidth(state.text.slice(lines[row].start, state.cursor));
875
+ const target = lines[Math.max(0, Math.min(lines.length - 1, row + direction))];
876
+ let cursor = target.start;
877
+ let cells = 0;
878
+ for (const { segment, index } of segmenter.segment(target.text)) {
879
+ cells += stringWidth(segment);
880
+ if (cells > column) break;
881
+ cursor = target.start + index + segment.length;
882
+ }
883
+ return { text: state.text, cursor, column };
884
+ }
885
+ function wordCursor(state, direction) {
886
+ if (direction < 0) {
887
+ return state.cursor - (state.text.slice(0, state.cursor).match(/\S+\s*$|\s+$/u)?.[0].length ?? 0);
888
+ }
889
+ return state.cursor + (state.text.slice(state.cursor).match(/^\s*\S*/u)?.[0].length ?? 0);
890
+ }
891
+ function updateEditor(state, input, key, width) {
892
+ const { text, cursor } = state;
893
+ if (key.escape || key.tab || key.pageUp || key.pageDown) return;
894
+ if (key.upArrow || key.downArrow) return moveVertical(state, width, key.upArrow ? -1 : 1);
895
+ if (key.leftArrow || key.rightArrow) {
896
+ const direction = key.leftArrow ? -1 : 1;
897
+ return key.ctrl || key.meta ? { text, cursor: wordCursor(state, direction) } : moveCursor(state, direction);
898
+ }
899
+ const start = cursor === 0 ? 0 : text.lastIndexOf("\n", cursor - 1) + 1;
900
+ const nextBreak = text.indexOf("\n", cursor);
901
+ const end = nextBreak < 0 ? text.length : nextBreak;
902
+ if (key.home || key.ctrl && input === "a") return { text, cursor: start };
903
+ if (key.end || key.ctrl && input === "e") return { text, cursor: end };
904
+ if (key.return || key.ctrl && input === "j") return editText(state, cursor, cursor, "\n");
905
+ if (key.ctrl) {
906
+ if (input === "u") return editText(state, start, cursor);
907
+ if (input === "k") return editText(state, cursor, end);
908
+ if (input === "w") return editText(state, wordCursor(state, -1), cursor);
909
+ return;
910
+ }
911
+ if (key.backspace) return editText(state, moveCursor(state, -1).cursor, cursor);
912
+ if (key.delete) return editText(state, cursor, moveCursor(state, 1).cursor);
913
+ if (key.meta) return;
914
+ const inserted = input.replace(/\r\n?/g, "\n").replace(/\t/g, " ").replace(new RegExp("\\p{Cc}", "gu"), (character) => character === "\n" ? character : "");
915
+ if (inserted) return editText(state, cursor, cursor, inserted);
916
+ }
917
+
918
+ // packages/cli/src/composer-input.tsx
919
+ import { Fragment, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
920
+ function ComposerInput({
921
+ value,
922
+ focused,
923
+ onChange,
924
+ onSubmit,
925
+ ignoreArrows
926
+ }) {
927
+ const { colors, width: availableWidth } = useTerminalStyle();
928
+ const { rows } = useWindowSize();
929
+ const container = useRef4(null);
930
+ const { clientWidth } = useBoxMetrics4(container);
931
+ const width = Math.max(1, clientWidth || availableWidth - 5);
932
+ const maximum = Math.max(1, Math.min(10, rows - 8));
933
+ const [editor, setEditor] = useState4({ text: value, cursor: value.length });
934
+ const [offset, setOffset] = useState4(0);
935
+ const state = editor.text === value ? editor : { text: value, cursor: value.length };
936
+ if (state !== editor) setEditor(state);
937
+ const lines = editorLines(value, width);
938
+ const row = cursorRow(lines, state.cursor);
939
+ const height = Math.min(maximum, lines.length);
940
+ const start = Math.max(0, Math.min(offset, lines.length - height, row));
941
+ const visibleStart = row >= start + height ? row - height + 1 : start;
942
+ if (visibleStart !== offset) setOffset(visibleStart);
943
+ useInput4(
944
+ (input, key) => {
945
+ if (key.return && !key.shift && !key.meta) {
946
+ onSubmit(value);
947
+ return;
948
+ }
949
+ if (ignoreArrows() && (key.upArrow || key.downArrow)) return;
950
+ const next = updateEditor(state, input, key, width);
951
+ if (!next) return;
952
+ setEditor(next);
953
+ if (next.text !== value) onChange(next.text);
954
+ },
955
+ { isActive: focused }
956
+ );
957
+ return /* @__PURE__ */ jsx7(Box6, { ref: container, flexGrow: 1, minWidth: 0, height, flexDirection: "column", children: lines.slice(visibleStart, visibleStart + height).map((line, index) => {
958
+ const current = visibleStart + index === row;
959
+ const cursor = current ? state.cursor - line.start : -1;
960
+ const after = current ? moveCursor(state, 1).cursor - line.start : -1;
961
+ return /* @__PURE__ */ jsx7(Text6, { color: colors.fg, wrap: "truncate-end", children: !value ? /* @__PURE__ */ jsxs6(Text6, { color: colors.muted, children: [
962
+ /* @__PURE__ */ jsx7(Text6, { inverse: focused, children: composerPlaceholder[0] }),
963
+ composerPlaceholder.slice(1)
964
+ ] }) : current && focused ? /* @__PURE__ */ jsxs6(Fragment, { children: [
965
+ line.text.slice(0, cursor),
966
+ /* @__PURE__ */ jsx7(Text6, { inverse: true, children: line.text.slice(cursor, after) || " " }),
967
+ line.text.slice(after)
968
+ ] }) : line.text || " " }, line.start);
969
+ }) });
970
+ }
971
+
972
+ // packages/cli/src/panels.tsx
973
+ import { Box as Box8, Text as Text8, useBoxMetrics as useBoxMetrics5, useInput as useInput6 } from "ink";
974
+ import { useRef as useRef5, useState as useState6 } from "react";
975
+
976
+ // packages/cli/src/theme-picker.tsx
977
+ import { Box as Box7, Text as Text7, useFocus as useFocus2, useInput as useInput5 } from "ink";
978
+ import { useEffect as useEffect3, useState as useState5 } from "react";
979
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
980
+ function ThemePicker({
981
+ initial,
982
+ visibleCount,
983
+ onPreview,
984
+ onApply
985
+ }) {
986
+ const { colors } = useTerminalStyle();
987
+ const { isFocused, focus } = useFocus2({ id: "theme-picker" });
988
+ const [selected, setSelected] = useState5(
989
+ () => Math.max(
990
+ 0,
991
+ themeOptions.findIndex((option) => option.id === initial)
992
+ )
993
+ );
994
+ useEffect3(() => {
995
+ focus("theme-picker");
996
+ return () => onPreview(void 0);
997
+ }, [focus, onPreview]);
998
+ useInput5(
999
+ (input, key) => {
1000
+ const direction = Number(key.downArrow || input === "j") - Number(key.upArrow || input === "k");
1001
+ if (direction) {
1002
+ const next = (selected + direction + themeOptions.length) % themeOptions.length;
1003
+ setSelected(next);
1004
+ onPreview(themeOptions[next].id);
1005
+ } else if (key.return) {
1006
+ onApply(themeOptions[selected].id);
1007
+ }
1008
+ },
1009
+ { isActive: isFocused }
1010
+ );
1011
+ const start = Math.max(0, selected - visibleCount + 1);
1012
+ return /* @__PURE__ */ jsx8(Box7, { flexDirection: "column", children: themeOptions.slice(start, start + visibleCount).map((option, index) => /* @__PURE__ */ jsxs7(Box7, { backgroundColor: start + index === selected ? colors.band : void 0, children: [
1013
+ /* @__PURE__ */ jsx8(Box7, { width: 20, flexShrink: 0, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.fg, bold: true, children: [
1014
+ start + index === selected ? "\u25B8" : " ",
1015
+ " ",
1016
+ option.name
1017
+ ] }) }),
1018
+ /* @__PURE__ */ jsx8(Box7, { flexGrow: 1, flexShrink: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.muted, wrap: "truncate-end", children: [
1019
+ option.description,
1020
+ option.id === initial ? "\uFF08\u5F53\u524D\uFF09" : ""
1021
+ ] }) })
1022
+ ] }, option.id)) });
1023
+ }
1024
+
1025
+ // packages/cli/src/panels.tsx
1026
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1027
+ function PanelBox({
1028
+ title,
1029
+ availableHeight,
1030
+ children,
1031
+ hints = "Esc:\u5173\u95ED"
1032
+ }) {
1033
+ const { colors } = useTerminalStyle();
1034
+ const viewport = useRef5(null);
1035
+ const content = useRef5(null);
1036
+ const { clientHeight: height } = useBoxMetrics5(viewport);
1037
+ const { height: contentHeight } = useBoxMetrics5(content);
1038
+ const [offset, setOffset] = useState6(0);
1039
+ const maximum = Math.max(0, contentHeight - height);
1040
+ useInput6((_input, key) => {
1041
+ const direction = Number(key.pageDown) - Number(key.pageUp);
1042
+ if (direction)
1043
+ setOffset(
1044
+ (current) => Math.max(0, Math.min(maximum, current + direction * Math.max(1, height - 1)))
1045
+ );
1046
+ });
1047
+ return /* @__PURE__ */ jsxs8(
1048
+ Box8,
1049
+ {
1050
+ flexDirection: "column",
1051
+ borderStyle: "round",
1052
+ borderColor: colors.line,
1053
+ backgroundColor: colors.bg,
1054
+ paddingX: 2,
1055
+ marginBottom: 1,
1056
+ children: [
1057
+ /* @__PURE__ */ jsx9(Text8, { color: colors.fg, bold: true, children: title }),
1058
+ /* @__PURE__ */ jsx9(
1059
+ Box8,
1060
+ {
1061
+ ref: viewport,
1062
+ flexDirection: "column",
1063
+ maxHeight: Math.max(1, availableHeight - 5),
1064
+ overflow: "hidden",
1065
+ contentOffsetY: Math.min(offset, maximum),
1066
+ children: /* @__PURE__ */ jsx9(
1067
+ Box8,
1068
+ {
1069
+ ref: content,
1070
+ flexDirection: "column",
1071
+ flexShrink: 0,
1072
+ marginY: availableHeight >= 8 ? 1 : 0,
1073
+ children
1074
+ }
1075
+ )
1076
+ }
1077
+ ),
1078
+ /* @__PURE__ */ jsxs8(Text8, { color: colors.muted, wrap: "truncate-end", children: [
1079
+ hints,
1080
+ maximum > 0 ? " \u2502 PgUp/PgDn:\u6EDA\u52A8" : ""
1081
+ ] })
1082
+ ]
1083
+ }
1084
+ );
1085
+ }
1086
+ function TerminalPanels({
1087
+ panel,
1088
+ availableHeight,
1089
+ savedTheme,
1090
+ onPreviewTheme,
1091
+ onApplyTheme,
1092
+ site
1093
+ }) {
1094
+ const { colors } = useTerminalStyle();
1095
+ if (!panel) return null;
1096
+ if (panel.type === "theme") {
1097
+ return /* @__PURE__ */ jsx9(
1098
+ PanelBox,
1099
+ {
1100
+ title: "\u9009\u62E9\u4E3B\u9898",
1101
+ availableHeight,
1102
+ hints: "\u2191\u2193:\u9884\u89C8 \u2502 Enter:\u5E94\u7528 \u2502 Esc:\u53D6\u6D88",
1103
+ children: /* @__PURE__ */ jsx9(
1104
+ ThemePicker,
1105
+ {
1106
+ initial: savedTheme,
1107
+ visibleCount: Math.max(1, availableHeight - (availableHeight >= 8 ? 7 : 5)),
1108
+ onPreview: onPreviewTheme,
1109
+ onApply: onApplyTheme
1110
+ }
1111
+ )
1112
+ }
1113
+ );
1114
+ }
1115
+ if (panel.type === "help") {
1116
+ return /* @__PURE__ */ jsxs8(PanelBox, { title: "\u547D\u4EE4", availableHeight, children: [
1117
+ terminalCommands.map((command) => /* @__PURE__ */ jsxs8(Box8, { children: [
1118
+ /* @__PURE__ */ jsx9(Box8, { width: 20, flexShrink: 0, children: /* @__PURE__ */ jsxs8(Text8, { color: colors.skill, children: [
1119
+ "/",
1120
+ command.id,
1121
+ /* @__PURE__ */ jsx9(Text8, { color: colors.muted, children: command.args ? ` ${command.args}` : "" })
771
1122
  ] }) }),
772
- /* @__PURE__ */ jsx6(Box5, { flexGrow: 1, flexShrink: 1, children: /* @__PURE__ */ jsx6(Text5, { color: colors.muted, wrap: "truncate-end", children: command.description }) })
773
- ] }, command.id);
774
- }),
775
- /* @__PURE__ */ jsx6(Text5, { color: colors.muted, wrap: "truncate-end", children: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u786E\u8BA4 \xB7 Esc \u5173\u95ED" })
1123
+ /* @__PURE__ */ jsx9(Box8, { flexGrow: 1, flexShrink: 1, children: /* @__PURE__ */ jsx9(Text8, { color: colors.muted, wrap: "truncate-end", children: command.description }) })
1124
+ ] }, command.id)),
1125
+ /* @__PURE__ */ jsx9(Text8, { color: colors["fg-soft"], children: "\u76F4\u63A5\u8F93\u5165\u95EE\u9898\uFF0CAI \u4F1A\u68C0\u7D22\u5168\u7AD9\u6587\u7AE0\u540E\u56DE\u7B54\uFF0C\u4E5F\u80FD\u5E2E\u4F60\u5207\u6362\u4E3B\u9898\u3001\u6253\u5F00\u9875\u9762\u3002" })
1126
+ ] });
1127
+ }
1128
+ if (panel.type === "rss") {
1129
+ return /* @__PURE__ */ jsx9(PanelBox, { title: "\u8BA2\u9605", availableHeight, children: /* @__PURE__ */ jsx9(Markdown, { text: `RSS [${site.feedUrl}](${site.feedUrl})` }) });
1130
+ }
1131
+ return /* @__PURE__ */ jsxs8(
1132
+ Text8,
1133
+ {
1134
+ color: panel.tone === "error" ? colors.err : colors["fg-soft"],
1135
+ backgroundColor: colors.bg,
1136
+ children: [
1137
+ "\u25C6 ",
1138
+ cleanText(panel.text)
1139
+ ]
1140
+ }
1141
+ );
1142
+ }
1143
+
1144
+ // packages/cli/src/run-status.tsx
1145
+ import { LoadingPrimitive } from "@assistant-ui/react-ink";
1146
+ import { Box as Box9, Text as Text9 } from "ink";
1147
+ import { useEffect as useEffect4, useState as useState7 } from "react";
1148
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1149
+ function RunStatus({ running, loading }) {
1150
+ const { colors } = useTerminalStyle();
1151
+ const [seconds, setSeconds] = useState7(0);
1152
+ useEffect4(() => {
1153
+ if (!running) return;
1154
+ const started = Date.now();
1155
+ setSeconds(0);
1156
+ const timer = setInterval(() => setSeconds(Math.floor((Date.now() - started) / 1e3)), 1e3);
1157
+ return () => clearInterval(timer);
1158
+ }, [running]);
1159
+ if (!running && !loading) return null;
1160
+ return /* @__PURE__ */ jsxs9(Box9, { paddingLeft: 2, height: 1, children: [
1161
+ /* @__PURE__ */ jsx10(LoadingPrimitive.Spinner, {}),
1162
+ /* @__PURE__ */ jsxs9(Text9, { color: colors.fg, children: [
1163
+ " ",
1164
+ loading ? "\u601D\u8003\u4E2D\u2026\u2026" : "\u56DE\u590D\u4E2D\u2026"
1165
+ ] }),
1166
+ running && /* @__PURE__ */ jsxs9(Text9, { color: colors.muted, children: [
1167
+ " ",
1168
+ seconds,
1169
+ "s"
1170
+ ] }),
1171
+ /* @__PURE__ */ jsx10(Box9, { flexGrow: 1 }),
1172
+ /* @__PURE__ */ jsx10(Text9, { color: colors.muted, children: "[Esc \u505C\u6B62]" })
776
1173
  ] });
777
1174
  }
778
1175
 
779
1176
  // packages/cli/src/composer.tsx
780
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1177
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
781
1178
  function Composer({
782
1179
  runtime,
783
- page,
784
- revision,
785
1180
  loading,
786
- notice,
1181
+ panel,
787
1182
  onCommand,
788
1183
  onCancel,
789
- onSubmit
1184
+ focusKey,
1185
+ onSubmit,
1186
+ ...panels
790
1187
  }) {
791
1188
  const { colors } = useTerminalStyle();
1189
+ const { rows } = useWindowSize2();
792
1190
  const aui = useAui();
793
- const { activeId, focus, focusNext } = useFocusManager();
1191
+ const { activeId, focus } = useFocusManager2();
794
1192
  const running = useAuiState2((state) => state.thread.isRunning);
795
1193
  const text = useAuiState2((state) => state.composer.text);
796
- const menu = useRef4(null);
797
- const inputFocusId = useRef4(void 0);
798
- const focused = activeId !== "content";
799
- useEffect2(() => {
800
- if (activeId && activeId !== "content") inputFocusId.current = activeId;
801
- }, [activeId]);
802
- useInput4((input, key) => {
1194
+ const menu = useRef6(null);
1195
+ const dock = useRef6(null);
1196
+ const { height } = useBoxMetrics6(dock);
1197
+ const { isFocused: focused } = useFocus3({ id: "composer", autoFocus: true });
1198
+ const restoreComposerFocus = useEffectEvent(() => {
1199
+ if (panel?.type !== "theme") focus("composer");
1200
+ });
1201
+ useEffect5(() => {
1202
+ restoreComposerFocus();
1203
+ }, [focusKey, panel?.type]);
1204
+ useInput7((input, key) => {
803
1205
  if (key.escape) {
804
1206
  if (!menu.current?.dismiss()) onCancel();
805
- if (inputFocusId.current) focus(inputFocusId.current);
1207
+ focus("composer");
806
1208
  }
807
1209
  if (input === "/" && activeId === "content") {
808
- focusNext();
1210
+ focus("composer");
809
1211
  aui.composer.setText("/");
810
1212
  }
811
1213
  });
812
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginX: 2, flexShrink: 0, children: [
813
- /* @__PURE__ */ jsx7(
1214
+ return /* @__PURE__ */ jsxs10(Box10, { ref: dock, flexDirection: "column", marginX: 2, flexShrink: 0, children: [
1215
+ /* @__PURE__ */ jsx11(RunStatus, { loading, running }),
1216
+ /* @__PURE__ */ jsxs10(
1217
+ Box10,
1218
+ {
1219
+ borderStyle: "round",
1220
+ borderColor: focused ? colors["line-strong"] : colors.line,
1221
+ paddingX: 1,
1222
+ alignItems: "flex-start",
1223
+ children: [
1224
+ /* @__PURE__ */ jsx11(Text10, { color: colors.prompt, children: "\u276F " }),
1225
+ /* @__PURE__ */ jsx11(
1226
+ ComposerInput,
1227
+ {
1228
+ value: text,
1229
+ focused,
1230
+ onChange: (value) => aui.composer.setText(value),
1231
+ ignoreArrows: () => menu.current?.isOpen() ?? false,
1232
+ onSubmit: (text2) => {
1233
+ const input = text2.trim();
1234
+ if (!input) return;
1235
+ if (menu.current?.submit(text2)) return;
1236
+ if (input.startsWith("/")) {
1237
+ const command = parseCommand(input);
1238
+ aui.composer.setText("");
1239
+ void onCommand(command?.name ?? "help", command?.args ?? "");
1240
+ } else if (!running) {
1241
+ onSubmit();
1242
+ runtime.thread.composer.send();
1243
+ }
1244
+ }
1245
+ }
1246
+ ),
1247
+ /* @__PURE__ */ jsx11(Box10, { position: "absolute", bottom: -1, right: 1, children: /* @__PURE__ */ jsxs10(Text10, { color: colors.muted, backgroundColor: colors.bg, children: [
1248
+ " ",
1249
+ composerBrand.mark,
1250
+ " \xB7 ",
1251
+ composerBrand.model,
1252
+ " "
1253
+ ] }) })
1254
+ ]
1255
+ }
1256
+ ),
1257
+ /* @__PURE__ */ jsxs10(Text10, { color: colors.muted, wrap: "truncate-end", children: [
1258
+ /* @__PURE__ */ jsx11(Text10, { bold: true, children: "/" }),
1259
+ ":\u547D\u4EE4 \u2502 ",
1260
+ /* @__PURE__ */ jsx11(Text10, { bold: true, children: "Enter" }),
1261
+ ":\u53D1\u9001 \u2502 ",
1262
+ /* @__PURE__ */ jsx11(Text10, { bold: true, children: "Shift+Enter" }),
1263
+ ":\u6362\u884C \u2502 ",
1264
+ /* @__PURE__ */ jsx11(Text10, { bold: true, children: "Esc" }),
1265
+ ":\u505C\u6B62"
1266
+ ] }),
1267
+ /* @__PURE__ */ jsx11(Box10, { position: "absolute", bottom: height, width: "100%", flexDirection: "column", children: /* @__PURE__ */ jsx11(
1268
+ TerminalPanels,
1269
+ {
1270
+ panel,
1271
+ availableHeight: Math.max(6, rows - height - 1),
1272
+ ...panels
1273
+ }
1274
+ ) }),
1275
+ /* @__PURE__ */ jsx11(Box10, { position: "absolute", bottom: height, width: "100%", flexDirection: "column", children: /* @__PURE__ */ jsx11(
814
1276
  CommandMenu,
815
1277
  {
816
1278
  ref: menu,
817
1279
  text,
818
1280
  focused,
1281
+ availableHeight: Math.max(6, rows - height - 1),
819
1282
  onSelect: (command) => {
820
- if (command.id === "search" || command.id === "read") {
821
- aui.composer.setText(`/${command.id} `);
822
- } else {
1283
+ if (command.id === "read") aui.composer.setText("/read ");
1284
+ else {
823
1285
  aui.composer.setText("");
824
1286
  void onCommand(command.id, "");
825
1287
  }
826
1288
  }
827
1289
  },
828
1290
  text
829
- ),
830
- (loading || running || notice) && /* @__PURE__ */ jsxs6(Box6, { paddingLeft: 2, height: 1, overflow: "hidden", children: [
831
- (loading || running) && /* @__PURE__ */ jsx7(LoadingPrimitive.Spinner, {}),
832
- /* @__PURE__ */ jsx7(Text6, { color: notice ? colors.warn : colors.muted, wrap: "truncate-end", children: notice ? cleanText(notice) : ` ${loading ? "\u6B63\u5728\u8BFB\u53D6\u5B98\u7F51\u2026" : "\u56DE\u590D\u4E2D\u2026"} \xB7 Esc \u505C\u6B62` })
833
- ] }),
834
- /* @__PURE__ */ jsxs6(Box6, { borderStyle: "round", borderColor: focused ? colors["line-strong"] : colors.line, paddingX: 1, children: [
835
- /* @__PURE__ */ jsx7(Text6, { color: colors.prompt, children: "\u276F " }),
836
- /* @__PURE__ */ jsx7(
837
- ComposerPrimitive.Input,
838
- {
839
- autoFocus: true,
840
- submitOnEnter: true,
841
- flexGrow: 1,
842
- placeholder: composerPlaceholder,
843
- onSubmit: (text2) => {
844
- const input = text2.trim();
845
- if (!input) return;
846
- if (menu.current?.submit(text2)) return;
847
- if (input.startsWith("/")) {
848
- const command = parseCommand(input);
849
- aui.composer.setText("");
850
- void onCommand(command?.name ?? "help", command?.args ?? "");
851
- } else if (!running) {
852
- onSubmit();
853
- runtime.thread.composer.send();
854
- }
855
- }
856
- },
857
- `${page.path}:${revision}`
858
- ),
859
- /* @__PURE__ */ jsx7(Box6, { position: "absolute", bottom: -1, right: 1, children: /* @__PURE__ */ jsxs6(Text6, { color: colors.muted, backgroundColor: colors.bg, children: [
860
- " ",
861
- composerBrand.mark,
862
- " \xB7 ",
863
- composerBrand.model,
864
- " "
865
- ] }) })
866
- ] }),
867
- /* @__PURE__ */ jsxs6(Text6, { color: colors.muted, wrap: "truncate-end", children: [
868
- /* @__PURE__ */ jsx7(Text6, { color: colors["fg-soft"], bold: true, children: "Tab" }),
869
- ":\u5207\u6362 \u2502 ",
870
- /* @__PURE__ */ jsx7(Text6, { bold: true, children: "PgUp/PgDn" }),
871
- ":\u6EDA\u52A8 \u2502 ",
872
- /* @__PURE__ */ jsx7(Text6, { bold: true, children: "/help" }),
873
- " \u2502 Ctrl+C:\u9000\u51FA"
874
- ] })
1291
+ ) })
875
1292
  ] });
876
1293
  }
877
1294
 
878
1295
  // packages/cli/src/page-content.tsx
879
- import { Box as Box7, Text as Text7 } from "ink";
1296
+ import { Box as Box12, Text as Text12 } from "ink";
880
1297
  import { memo } from "react";
881
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1298
+
1299
+ // packages/cli/src/search-input.tsx
1300
+ import { TextInput } from "@assistant-ui/react-ink";
1301
+ import { Box as Box11, Text as Text11 } from "ink";
1302
+ import { useEffect as useEffect6, useRef as useRef7, useState as useState8 } from "react";
1303
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1304
+ function SearchInput({
1305
+ path,
1306
+ onSearch
1307
+ }) {
1308
+ const { colors } = useTerminalStyle();
1309
+ const [query, setQuery] = useState8(
1310
+ () => new URL(path, "https://miantiao.me").searchParams.get("q") ?? ""
1311
+ );
1312
+ const timer = useRef7(void 0);
1313
+ useEffect6(() => () => clearTimeout(timer.current), []);
1314
+ return /* @__PURE__ */ jsxs11(Box11, { borderStyle: "round", borderColor: colors.line, paddingX: 1, marginY: 1, children: [
1315
+ /* @__PURE__ */ jsx12(Text11, { color: colors.prompt, children: "\u276F " }),
1316
+ /* @__PURE__ */ jsx12(
1317
+ TextInput,
1318
+ {
1319
+ value: query,
1320
+ autoFocus: false,
1321
+ flexGrow: 1,
1322
+ placeholder: "\u641C\u7D22\u6807\u9898\u3001\u63CF\u8FF0\u3001\u6B63\u6587\u4E0E\u6807\u7B7E",
1323
+ onChange: (value) => {
1324
+ setQuery(value);
1325
+ clearTimeout(timer.current);
1326
+ timer.current = setTimeout(() => onSearch(value.trim()), 300);
1327
+ }
1328
+ }
1329
+ )
1330
+ ] });
1331
+ }
1332
+
1333
+ // packages/cli/src/page-content.tsx
1334
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
882
1335
  var PageContent = memo(function PageContent2({
883
1336
  page,
884
1337
  prompt,
885
1338
  onSelect,
886
- scroll
1339
+ scroll,
1340
+ onSearch
887
1341
  }) {
888
1342
  const { colors } = useTerminalStyle();
889
- if (page.sections) return /* @__PURE__ */ jsx8(HomeSections, { page, onSelect, scroll });
890
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginBottom: 1, children: [
891
- page.path !== "/" && /* @__PURE__ */ jsx8(Prompt, { text: prompt, command: true }),
892
- /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingLeft: 2, paddingRight: 1, paddingTop: 1, children: [
893
- page.path !== "/" && /* @__PURE__ */ jsxs7(Text7, { children: [
894
- /* @__PURE__ */ jsx8(Text7, { color: colors.skill, children: "\u25C6 " }),
895
- /* @__PURE__ */ jsx8(Text7, { color: colors.fg, bold: true, children: page.verb }),
896
- /* @__PURE__ */ jsxs7(Text7, { color: colors.path, children: [
1343
+ if (page.sections) return /* @__PURE__ */ jsx13(HomeSections, { page, onSelect, scroll });
1344
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginBottom: 1, children: [
1345
+ page.path !== "/" && /* @__PURE__ */ jsx13(Prompt, { text: prompt, command: true }),
1346
+ /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingLeft: 2, paddingRight: 1, paddingTop: 1, children: [
1347
+ page.path !== "/" && /* @__PURE__ */ jsxs12(Text12, { children: [
1348
+ /* @__PURE__ */ jsx13(Text12, { color: colors.skill, children: "\u25C6 " }),
1349
+ /* @__PURE__ */ jsx13(Text12, { color: colors.fg, bold: true, children: page.verb }),
1350
+ /* @__PURE__ */ jsxs12(Text12, { color: colors.path, children: [
897
1351
  " ",
898
1352
  cleanText(page.title)
899
1353
  ] })
900
1354
  ] }),
901
- page.markdown && /* @__PURE__ */ jsx8(Markdown, { text: page.markdown }),
902
- page.items.length > 0 && /* @__PURE__ */ jsx8(PageList, { items: page.items, onSelect, scroll })
1355
+ page.path.startsWith("/search/") && /* @__PURE__ */ jsx13(SearchInput, { path: page.path, onSearch }),
1356
+ page.markdown && /* @__PURE__ */ jsx13(Markdown, { text: page.markdown }),
1357
+ page.items.length > 0 && /* @__PURE__ */ jsx13(PageList, { items: page.items, onSelect, scroll })
903
1358
  ] })
904
1359
  ] });
905
1360
  });
906
1361
 
907
1362
  // packages/cli/src/terminal-layout.tsx
908
- import { Box as Box8, Text as Text8, useWindowSize as useWindowSize2 } from "ink";
909
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1363
+ import { Box as Box13, Text as Text13, useWindowSize as useWindowSize3 } from "ink";
1364
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
910
1365
  function TerminalLayout({
911
1366
  path,
912
1367
  nav = [],
913
1368
  children
914
1369
  }) {
915
- const { columns, rows } = useWindowSize2();
1370
+ const { columns, rows } = useWindowSize3();
916
1371
  const { colors } = useTerminalStyle();
917
1372
  const directory = `~/miantiao.me${path === "/" ? "" : path.replace(/\/$/, "")}`;
918
- return /* @__PURE__ */ jsxs8(
919
- Box8,
1373
+ return /* @__PURE__ */ jsxs13(
1374
+ Box13,
920
1375
  {
921
1376
  width: columns,
922
1377
  height: rows,
@@ -924,9 +1379,9 @@ function TerminalLayout({
924
1379
  backgroundColor: colors.bg,
925
1380
  overflow: "hidden",
926
1381
  children: [
927
- /* @__PURE__ */ jsxs8(Box8, { paddingX: 2, flexShrink: 0, children: [
928
- /* @__PURE__ */ jsx9(Box8, { flexGrow: 1, flexShrink: 1, children: /* @__PURE__ */ jsx9(Text8, { color: colors.muted, wrap: "truncate-middle", children: cleanText(directory) }) }),
929
- columns >= 70 && nav.map((link) => /* @__PURE__ */ jsx9(Box8, { marginLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsx9(Text8, { color: path.startsWith(link.href) ? colors.fg : colors.muted, children: cleanText(link.title) }) }, link.href))
1382
+ /* @__PURE__ */ jsxs13(Box13, { paddingX: 2, flexShrink: 0, children: [
1383
+ /* @__PURE__ */ jsx14(Box13, { flexGrow: 1, flexShrink: 1, children: /* @__PURE__ */ jsx14(Text13, { color: colors.muted, wrap: "truncate-middle", children: cleanText(directory) }) }),
1384
+ columns >= 70 && nav.map((link) => /* @__PURE__ */ jsx14(Box13, { marginLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsx14(Text13, { color: path.startsWith(link.href) ? colors.fg : colors.muted, children: cleanText(link.title) }) }, link.href))
930
1385
  ] }),
931
1386
  children
932
1387
  ]
@@ -935,65 +1390,69 @@ function TerminalLayout({
935
1390
  }
936
1391
 
937
1392
  // packages/cli/src/console.tsx
938
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1393
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
939
1394
  function Console({
940
1395
  runtime,
941
1396
  page,
942
1397
  prompt,
943
1398
  revision,
1399
+ locationKey,
944
1400
  site,
945
1401
  loading,
946
- notice,
1402
+ panel,
947
1403
  onCommand,
948
1404
  onCancel,
949
- onSelect
1405
+ onSelect,
1406
+ onSearch,
1407
+ onClosePanel,
1408
+ ...themePicker
950
1409
  }) {
951
- const scroll = useRef5(null);
952
- return /* @__PURE__ */ jsx10(TerminalLayout, { path: page.path, nav: site.nav, children: /* @__PURE__ */ jsxs9(ThreadPrimitive2.Root, { flexGrow: 1, flexShrink: 1, minHeight: 0, children: [
953
- /* @__PURE__ */ jsxs9(ScrollArea, { ref: scroll, selection: page.items.length > 0, children: [
954
- /* @__PURE__ */ jsx10(PageContent, { page, prompt, onSelect, scroll }),
955
- /* @__PURE__ */ jsx10(ChatThread, {})
956
- ] }, `${page.path}:${revision}`),
957
- /* @__PURE__ */ jsx10(
1410
+ const scroll = useRef8(null);
1411
+ const focusKey = `${locationKey}:${revision}`;
1412
+ return /* @__PURE__ */ jsx15(TerminalLayout, { path: page.path, nav: site.nav, children: /* @__PURE__ */ jsxs14(ThreadPrimitive2.Root, { flexGrow: 1, flexShrink: 1, minHeight: 0, children: [
1413
+ /* @__PURE__ */ jsxs14(
1414
+ ScrollArea,
1415
+ {
1416
+ ref: scroll,
1417
+ selection: page.items.length > 0,
1418
+ active: !panel || panel.type === "notice",
1419
+ children: [
1420
+ /* @__PURE__ */ jsx15(
1421
+ PageContent,
1422
+ {
1423
+ page,
1424
+ prompt,
1425
+ onSelect,
1426
+ scroll,
1427
+ onSearch
1428
+ }
1429
+ ),
1430
+ /* @__PURE__ */ jsx15(ChatThread, {})
1431
+ ]
1432
+ },
1433
+ focusKey
1434
+ ),
1435
+ /* @__PURE__ */ jsx15(
958
1436
  Composer,
959
1437
  {
960
1438
  runtime,
961
- page,
962
- revision,
1439
+ focusKey,
963
1440
  loading,
964
- notice,
1441
+ panel,
1442
+ site,
965
1443
  onCommand,
966
1444
  onCancel,
967
- onSubmit: () => scroll.current?.end()
1445
+ ...themePicker,
1446
+ onSubmit: () => {
1447
+ onClosePanel();
1448
+ scroll.current?.end();
1449
+ }
968
1450
  }
969
1451
  )
970
1452
  ] }) });
971
1453
  }
972
1454
 
973
1455
  // packages/cli/src/pages.ts
974
- function helpPage() {
975
- return {
976
- path: "/help/",
977
- title: "\u53EF\u7528\u547D\u4EE4",
978
- verb: "Help",
979
- items: [],
980
- markdown: terminalCommands.map(
981
- (command) => `- \`/${command.id}${command.args ? ` ${command.args}` : ""}\` \u2014 ${command.description}`
982
- ).join("\n") + "\n\n/ \u6253\u5F00\u547D\u4EE4\u9762\u677F\uFF1B\u2191\u2193 \u9009\u62E9\u3001Enter \u786E\u8BA4\u3001Esc \u5173\u95ED\u3002PgUp/PgDn \u6EDA\u52A8\u6B63\u6587\uFF1BTab \u5207\u6362\u8F93\u5165\u548C\u5185\u5BB9\uFF1BEsc \u505C\u6B62\uFF1BCtrl+C \u9000\u51FA\u3002"
983
- };
984
- }
985
- function themePage() {
986
- return {
987
- path: "/theme/",
988
- title: "\u9009\u62E9\u4E3B\u9898",
989
- verb: "Theme",
990
- items: themeOptions.map((theme) => ({
991
- title: theme.name,
992
- description: theme.description,
993
- path: `/theme/${theme.id}`
994
- }))
995
- };
996
- }
997
1456
  async function loadPage(client, path, signal) {
998
1457
  let index = await client.getIndex(signal);
999
1458
  const url = new URL(path, client.site);
@@ -1013,7 +1472,9 @@ async function loadPage(client, path, signal) {
1013
1472
  if (page.path === index.pages.home.path) {
1014
1473
  const welcome = page.sections?.find((section) => section.kind === "welcome");
1015
1474
  const menu = welcome?.menu ?? index.site.nav.map((link) => ({ ...link, command: void 0 }));
1016
- const siteOrigins = new Set([client.site, index.site.url].map((site) => new URL(site).origin));
1475
+ const siteOrigins = new Set(
1476
+ [client.site, index.site.url].map((site) => new URL(site).origin)
1477
+ );
1017
1478
  page.items = menu.map((link) => {
1018
1479
  const destination = new URL(link.href, client.site);
1019
1480
  const external = !siteOrigins.has(destination.origin);
@@ -1059,10 +1520,8 @@ async function loadPage(client, path, signal) {
1059
1520
  description: post.description,
1060
1521
  path: post.url
1061
1522
  }));
1062
- if (!page.items.length) page.markdown = "\u6CA1\u6709\u627E\u5230\u76F8\u5173\u6587\u7AE0\u3002";
1063
- } else {
1064
- page.markdown = "\u8F93\u5165 `/search \u5173\u952E\u8BCD` \u641C\u7D22\u6587\u7AE0\u3002";
1065
1523
  }
1524
+ if (query && !page.items.length) page.markdown = `\u6CA1\u6709\u627E\u5230\u4E0E\u300C${query}\u300D\u76F8\u5173\u7684\u6587\u7AE0`;
1066
1525
  } else if (pathname === "/rss/") {
1067
1526
  page.title = "\u8BA2\u9605 RSS";
1068
1527
  page.items = [{ title: index.site.feedUrl, path: index.site.feedUrl, external: true }];
@@ -1073,12 +1532,12 @@ async function loadPage(client, path, signal) {
1073
1532
  }
1074
1533
 
1075
1534
  // packages/cli/src/session.ts
1076
- import { useApp, useInput as useInput5 } from "ink";
1077
- import { useState as useState5 } from "react";
1535
+ import { useApp, useInput as useInput8 } from "ink";
1536
+ import { useCallback as useCallback2, useState as useState10 } from "react";
1078
1537
 
1079
1538
  // packages/cli/src/agent.ts
1080
1539
  import { useChatRuntime } from "@assistant-ui/ai-sdk";
1081
- import { useEffect as useEffect3, useMemo as useMemo3 } from "react";
1540
+ import { useEffect as useEffect7, useMemo as useMemo3 } from "react";
1082
1541
 
1083
1542
  // src/agent/transport.ts
1084
1543
  import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
@@ -1267,7 +1726,7 @@ function useTerminalAgent({ site, index, page, theme, setTheme, navigate }) {
1267
1726
  ]
1268
1727
  );
1269
1728
  const runtime = useChatRuntime({ transport });
1270
- useEffect3(() => () => runtime.thread.cancelRun(), [runtime]);
1729
+ useEffect7(() => () => runtime.thread.cancelRun(), [runtime]);
1271
1730
  return runtime;
1272
1731
  }
1273
1732
 
@@ -1289,88 +1748,110 @@ async function openBrowser(input, site) {
1289
1748
  }
1290
1749
 
1291
1750
  // packages/cli/src/navigation.ts
1292
- import { useCallback, useEffect as useEffect4, useRef as useRef6, useState as useState4 } from "react";
1751
+ import { useCallback, useEffect as useEffect8, useRef as useRef9, useState as useState9 } from "react";
1293
1752
  function useNavigation(client, initial) {
1294
- const [state, setState] = useState4({
1753
+ const [state, setState] = useState9({
1295
1754
  ...initial,
1296
1755
  status: "idle",
1297
- notice: client.warning,
1298
- prompt: "/home"
1756
+ panel: client.warning ? { type: "notice", text: client.warning } : null,
1757
+ prompt: "/home",
1758
+ locationKey: 0
1299
1759
  });
1300
- const active = useRef6(void 0);
1301
- useEffect4(
1760
+ const active = useRef9(void 0);
1761
+ useEffect8(
1302
1762
  () => () => {
1303
- active.current?.abort();
1763
+ active.current?.controller.abort();
1304
1764
  active.current = void 0;
1305
1765
  },
1306
1766
  []
1307
1767
  );
1308
1768
  const cancel = useCallback(() => {
1309
- active.current?.abort();
1769
+ active.current?.controller.abort();
1310
1770
  active.current = void 0;
1311
1771
  setState((current) => current.status === "idle" ? current : { ...current, status: "idle" });
1312
1772
  }, []);
1313
- const notify = useCallback((notice) => {
1314
- setState((current) => ({ ...current, notice }));
1773
+ const setPanel = useCallback((panel) => {
1774
+ setState((current) => ({ ...current, panel }));
1315
1775
  }, []);
1316
- const show = useCallback(
1317
- (page, prompt) => {
1318
- cancel();
1319
- setState((current) => ({ ...current, page, prompt, notice: "" }));
1776
+ const notify = useCallback(
1777
+ (text, tone) => {
1778
+ setPanel({ type: "notice", text, tone });
1320
1779
  },
1321
- [cancel]
1780
+ [setPanel]
1322
1781
  );
1323
1782
  const navigate = useCallback(
1324
- async (path, prompt = path) => {
1325
- active.current?.abort();
1326
- const request = new AbortController();
1783
+ async (path, prompt = path, replace = false) => {
1784
+ if (replace && active.current && !active.current.replace) return false;
1785
+ active.current?.controller.abort();
1786
+ const request = { controller: new AbortController(), replace };
1327
1787
  active.current = request;
1328
- setState((current) => ({ ...current, status: "loading", notice: "" }));
1788
+ setState((current) => ({ ...current, status: "loading", panel: null }));
1329
1789
  let next;
1330
1790
  try {
1331
- next = await loadPage(client, path, request.signal);
1791
+ next = await loadPage(client, path, request.controller.signal);
1332
1792
  } catch (error) {
1333
1793
  if (active.current === request) {
1334
1794
  active.current = void 0;
1335
1795
  setState((current) => ({
1336
1796
  ...current,
1337
1797
  status: "idle",
1338
- notice: error instanceof Error ? error.message : "\u65E0\u6CD5\u8BFB\u53D6\u5B98\u7F51\u5185\u5BB9\u3002"
1798
+ panel: {
1799
+ type: "notice",
1800
+ tone: "error",
1801
+ text: error instanceof Error ? error.message : "\u65E0\u6CD5\u8BFB\u53D6\u5B98\u7F51\u5185\u5BB9\u3002"
1802
+ }
1339
1803
  }));
1340
1804
  }
1341
1805
  return false;
1342
1806
  }
1343
1807
  if (active.current !== request) return false;
1344
1808
  active.current = void 0;
1345
- setState({ ...next, status: "idle", notice: client.warning, prompt });
1809
+ setState((current) => ({
1810
+ ...next,
1811
+ status: "idle",
1812
+ panel: client.warning ? { type: "notice", text: client.warning } : null,
1813
+ prompt,
1814
+ locationKey: current.locationKey + Number(!replace)
1815
+ }));
1346
1816
  return true;
1347
1817
  },
1348
1818
  [client]
1349
1819
  );
1350
- return { ...state, navigate, show, cancel, notify };
1820
+ return { ...state, navigate, setPanel, cancel, notify };
1351
1821
  }
1352
1822
 
1353
1823
  // packages/cli/src/session.ts
1354
1824
  function useTerminalSession(client, initial) {
1355
1825
  const { exit } = useApp();
1356
- const [theme, setTheme] = useState5("auto");
1357
- const { index, page, prompt, status, notice, navigate, show, cancel, notify } = useNavigation(client, initial);
1826
+ const [theme, setTheme] = useState10("auto");
1827
+ const [previewTheme, setPreviewTheme] = useState10();
1828
+ const { index, page, prompt, locationKey, status, panel, navigate, setPanel, cancel, notify } = useNavigation(client, initial);
1358
1829
  const runtime = useTerminalAgent({ site: client.site, index, page, theme, setTheme, navigate });
1359
1830
  const stop = () => {
1360
1831
  cancel();
1361
1832
  runtime.thread.cancelRun();
1362
1833
  };
1363
- useInput5((input, key) => {
1834
+ useInput8((input, key) => {
1364
1835
  if (key.ctrl && input === "c") {
1365
1836
  stop();
1366
1837
  exit();
1367
1838
  }
1368
1839
  });
1840
+ const onClosePanel = () => {
1841
+ setPreviewTheme(void 0);
1842
+ setPanel(null);
1843
+ };
1369
1844
  const onCancel = () => {
1370
- stop();
1371
- notify("\u5DF2\u505C\u6B62\u3002");
1845
+ if (panel) onClosePanel();
1846
+ else stop();
1847
+ };
1848
+ const onApplyTheme = (preference) => {
1849
+ setPreviewTheme(void 0);
1850
+ setTheme(preference);
1851
+ notify(`\u5DF2\u5207\u6362\u5230 ${getThemeName(preference)}`);
1372
1852
  };
1373
1853
  const onCommand = async (name, args) => {
1854
+ setPreviewTheme(void 0);
1374
1855
  try {
1375
1856
  if (pageCommands[name] && !(name === "tags" && args)) {
1376
1857
  await navigate(pageCommands[name], `/${name}`);
@@ -1378,31 +1859,31 @@ function useTerminalSession(client, initial) {
1378
1859
  }
1379
1860
  switch (name) {
1380
1861
  case "help":
1381
- show(helpPage(), "/help");
1862
+ case "rss":
1863
+ setPanel({ type: name });
1382
1864
  break;
1383
1865
  case "theme": {
1384
1866
  const preference = parseThemePreference(args);
1385
- if (!args) show(themePage(), "/theme");
1386
- else if (preference) {
1387
- setTheme(preference);
1388
- notify(`\u4E3B\u9898\uFF1A${getThemeName(preference)}`);
1389
- } else notify("\u672A\u77E5\u4E3B\u9898\uFF0C\u8F93\u5165 /theme \u67E5\u770B\u53EF\u7528\u4E3B\u9898\u3002");
1867
+ if (!args) setPanel({ type: "theme" });
1868
+ else if (preference) onApplyTheme(preference);
1869
+ else
1870
+ notify(
1871
+ `\u672A\u77E5\u4E3B\u9898\uFF1A${args}\u3002\u53EF\u9009\uFF1A${themeOptions.map((option) => option.id).join("\u3001")}`,
1872
+ "error"
1873
+ );
1390
1874
  break;
1391
1875
  }
1392
1876
  case "tags":
1393
1877
  await navigate(resolveTagPath(args, index), `/tags ${args}`);
1394
1878
  break;
1395
1879
  case "search":
1396
- await navigate(getSearchPath(args), `/search ${args}`);
1880
+ await navigate(getSearchPath(args), `/search ${args}`.trim());
1397
1881
  break;
1398
1882
  case "read":
1399
1883
  await navigate(resolveReadPath(args, page), `/read ${args}`);
1400
1884
  break;
1401
- case "rss":
1402
- await navigate("/rss/", "/rss");
1403
- break;
1404
1885
  case "refresh":
1405
- await navigate(["/help/", "/theme/"].includes(page.path) ? "/" : page.path, "/refresh");
1886
+ await navigate(page.path, "/refresh");
1406
1887
  break;
1407
1888
  case "open":
1408
1889
  await openBrowser(resolveOpenTarget(args, page), client.site);
@@ -1410,57 +1891,68 @@ function useTerminalSession(client, initial) {
1410
1891
  case "clear":
1411
1892
  stop();
1412
1893
  runtime.thread.reset();
1413
- notify("\u5BF9\u8BDD\u5DF2\u6E05\u7A7A\u3002");
1894
+ onClosePanel();
1414
1895
  break;
1415
1896
  case "quit":
1416
1897
  stop();
1417
1898
  exit();
1418
1899
  break;
1419
1900
  default:
1420
- notify(`\u672A\u77E5\u547D\u4EE4 /${name}\uFF0C\u8F93\u5165 /help \u67E5\u770B\u53EF\u7528\u547D\u4EE4\u3002`);
1901
+ notify(`\u672A\u77E5\u547D\u4EE4\uFF1A/${name}\uFF0C\u8F93\u5165 /help \u67E5\u770B\u53EF\u7528\u547D\u4EE4`, "error");
1421
1902
  }
1422
1903
  } catch (error) {
1423
- notify(error instanceof Error ? error.message : "\u65E0\u6CD5\u6267\u884C\u547D\u4EE4\u3002");
1904
+ notify(error instanceof Error ? error.message : "\u65E0\u6CD5\u6267\u884C\u547D\u4EE4\u3002", "error");
1424
1905
  }
1425
1906
  };
1426
1907
  const onSelect = (path, external = false) => {
1427
- if (path.startsWith("/theme/")) void onCommand("theme", path.slice("/theme/".length));
1428
- else if (external) void onCommand("open", path);
1908
+ if (external) void onCommand("open", path);
1429
1909
  else void navigate(path);
1430
1910
  };
1911
+ const onSearch = useCallback2(
1912
+ (query) => {
1913
+ void navigate(getSearchPath(query), `/search ${query}`.trim(), true);
1914
+ },
1915
+ [navigate]
1916
+ );
1431
1917
  return {
1432
1918
  runtime,
1433
1919
  page,
1434
1920
  revision: index.revision,
1921
+ locationKey,
1435
1922
  prompt,
1436
1923
  site: index.site,
1437
1924
  loading: status === "loading",
1438
- notice,
1925
+ panel,
1439
1926
  onCommand,
1440
1927
  onCancel,
1928
+ onClosePanel,
1441
1929
  onSelect,
1442
- theme
1930
+ onSearch,
1931
+ theme: previewTheme ?? theme,
1932
+ savedTheme: theme,
1933
+ onPreviewTheme: setPreviewTheme,
1934
+ onApplyTheme
1443
1935
  };
1444
1936
  }
1445
1937
 
1446
1938
  // packages/cli/src/app.tsx
1447
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1939
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
1448
1940
  function Session({
1449
1941
  client,
1450
1942
  initial,
1451
1943
  width
1452
1944
  }) {
1453
1945
  const { theme, ...session } = useTerminalSession(client, initial);
1454
- return /* @__PURE__ */ jsx11(TerminalTheme, { preference: theme, width, children: /* @__PURE__ */ jsx11(AssistantRuntimeProvider, { runtime: session.runtime, children: /* @__PURE__ */ jsx11(Console, { ...session }) }) });
1946
+ return /* @__PURE__ */ jsx16(TerminalTheme, { preference: theme, width, children: /* @__PURE__ */ jsx16(AssistantRuntimeProvider, { runtime: session.runtime, children: /* @__PURE__ */ jsx16(Console, { ...session }) }) });
1455
1947
  }
1456
1948
  function App({ site }) {
1457
1949
  const { exit } = useApp2();
1458
- const { columns } = useWindowSize3();
1459
- const [client] = useState6(() => new TerminalClient(site));
1460
- const [state, setState] = useState6({ status: "loading" });
1461
- const [attempt, setAttempt] = useState6(0);
1950
+ const { columns } = useWindowSize4();
1951
+ const [client] = useState11(() => new TerminalClient(site));
1952
+ const [state, setState] = useState11({ status: "loading" });
1953
+ const [attempt, setAttempt] = useState11(0);
1462
1954
  const width = Math.max(10, columns - 5);
1463
- useEffect5(() => {
1955
+ useEffect9(() => {
1464
1956
  const request = new AbortController();
1465
1957
  loadPage(client, "/", request.signal).then((content) => {
1466
1958
  if (!request.signal.aborted) setState({ status: "ready", content });
@@ -1473,7 +1965,7 @@ function App({ site }) {
1473
1965
  });
1474
1966
  return () => request.abort();
1475
1967
  }, [client, attempt]);
1476
- useInput6(
1968
+ useInput9(
1477
1969
  (input, key) => {
1478
1970
  if (key.ctrl && input === "c" || input === "q") exit();
1479
1971
  else if (state.status === "error" && (key.return || input === "r" || input === "/refresh")) {
@@ -1484,17 +1976,17 @@ function App({ site }) {
1484
1976
  { isActive: state.status !== "ready" }
1485
1977
  );
1486
1978
  if (state.status === "ready")
1487
- return /* @__PURE__ */ jsx11(Session, { client, initial: state.content, width });
1488
- return /* @__PURE__ */ jsx11(TerminalTheme, { preference: "auto", width, children: /* @__PURE__ */ jsx11(TerminalLayout, { path: "/", children: /* @__PURE__ */ jsxs10(Box9, { flexDirection: "column", flexGrow: 1, padding: 2, children: [
1489
- /* @__PURE__ */ jsxs10(Text9, { children: [
1979
+ return /* @__PURE__ */ jsx16(Session, { client, initial: state.content, width });
1980
+ return /* @__PURE__ */ jsx16(TerminalTheme, { preference: "auto", width, children: /* @__PURE__ */ jsx16(TerminalLayout, { path: "/", children: /* @__PURE__ */ jsxs15(Box14, { flexDirection: "column", flexGrow: 1, padding: 2, children: [
1981
+ /* @__PURE__ */ jsxs15(Text14, { children: [
1490
1982
  "\u25C6 ",
1491
- state.status === "error" ? state.message : "\u6B63\u5728\u8BFB\u53D6\u5B98\u7F51\u2026"
1983
+ state.status === "error" ? state.message : "\u601D\u8003\u4E2D\u2026\u2026"
1492
1984
  ] }),
1493
- /* @__PURE__ */ jsx11(Text9, { children: state.status === "error" ? "Enter \u91CD\u8BD5 \xB7 q \u9000\u51FA" : "Ctrl+C \u9000\u51FA" })
1985
+ /* @__PURE__ */ jsx16(Text14, { children: state.status === "error" ? "Enter \u91CD\u8BD5 \xB7 q \u9000\u51FA" : "Ctrl+C \u9000\u51FA" })
1494
1986
  ] }) }) });
1495
1987
  }
1496
1988
  async function run(site) {
1497
- const app = render(/* @__PURE__ */ jsx11(App, { site }), {
1989
+ const app = render(/* @__PURE__ */ jsx16(App, { site }), {
1498
1990
  exitOnCtrlC: false,
1499
1991
  alternateScreen: true,
1500
1992
  incrementalRendering: true