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.
- package/README.md +4 -4
- package/dist/{app-IW4THTCR.js → app-YAGEJAXF.js} +768 -276
- package/dist/index.js +2 -2
- package/package.json +2 -1
|
@@ -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
|
|
8
|
-
import { useEffect as
|
|
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
|
|
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(
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
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(
|
|
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 [
|
|
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(
|
|
531
|
+
const element = selectedItem && elements.current.get(selectedItem.path);
|
|
526
532
|
if (focused && hasMeasured && element) scroll.current?.reveal(element);
|
|
527
|
-
}, [focused, hasMeasured,
|
|
528
|
-
useInput2(
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
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 {
|
|
647
|
-
import {
|
|
648
|
-
|
|
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
|
|
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(
|
|
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 {
|
|
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,
|
|
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
|
|
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(
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
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(
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
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__ */
|
|
773
|
-
] }, command.id)
|
|
774
|
-
|
|
775
|
-
|
|
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
|
|
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
|
-
|
|
1181
|
+
panel,
|
|
787
1182
|
onCommand,
|
|
788
1183
|
onCancel,
|
|
789
|
-
|
|
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
|
|
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 =
|
|
797
|
-
const
|
|
798
|
-
const
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
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
|
-
|
|
1207
|
+
focus("composer");
|
|
806
1208
|
}
|
|
807
1209
|
if (input === "/" && activeId === "content") {
|
|
808
|
-
|
|
1210
|
+
focus("composer");
|
|
809
1211
|
aui.composer.setText("/");
|
|
810
1212
|
}
|
|
811
1213
|
});
|
|
812
|
-
return /* @__PURE__ */
|
|
813
|
-
/* @__PURE__ */
|
|
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 === "
|
|
821
|
-
|
|
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
|
|
1296
|
+
import { Box as Box12, Text as Text12 } from "ink";
|
|
880
1297
|
import { memo } from "react";
|
|
881
|
-
|
|
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__ */
|
|
890
|
-
return /* @__PURE__ */
|
|
891
|
-
page.path !== "/" && /* @__PURE__ */
|
|
892
|
-
/* @__PURE__ */
|
|
893
|
-
page.path !== "/" && /* @__PURE__ */
|
|
894
|
-
/* @__PURE__ */
|
|
895
|
-
/* @__PURE__ */
|
|
896
|
-
/* @__PURE__ */
|
|
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.
|
|
902
|
-
page.
|
|
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
|
|
909
|
-
import { jsx as
|
|
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 } =
|
|
1370
|
+
const { columns, rows } = useWindowSize3();
|
|
916
1371
|
const { colors } = useTerminalStyle();
|
|
917
1372
|
const directory = `~/miantiao.me${path === "/" ? "" : path.replace(/\/$/, "")}`;
|
|
918
|
-
return /* @__PURE__ */
|
|
919
|
-
|
|
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__ */
|
|
928
|
-
/* @__PURE__ */
|
|
929
|
-
columns >= 70 && nav.map((link) => /* @__PURE__ */
|
|
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
|
|
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
|
-
|
|
1402
|
+
panel,
|
|
947
1403
|
onCommand,
|
|
948
1404
|
onCancel,
|
|
949
|
-
onSelect
|
|
1405
|
+
onSelect,
|
|
1406
|
+
onSearch,
|
|
1407
|
+
onClosePanel,
|
|
1408
|
+
...themePicker
|
|
950
1409
|
}) {
|
|
951
|
-
const scroll =
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
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
|
-
|
|
962
|
-
revision,
|
|
1439
|
+
focusKey,
|
|
963
1440
|
loading,
|
|
964
|
-
|
|
1441
|
+
panel,
|
|
1442
|
+
site,
|
|
965
1443
|
onCommand,
|
|
966
1444
|
onCancel,
|
|
967
|
-
|
|
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(
|
|
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
|
|
1077
|
-
import { useState as
|
|
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
|
|
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
|
-
|
|
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
|
|
1751
|
+
import { useCallback, useEffect as useEffect8, useRef as useRef9, useState as useState9 } from "react";
|
|
1293
1752
|
function useNavigation(client, initial) {
|
|
1294
|
-
const [state, setState] =
|
|
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 =
|
|
1301
|
-
|
|
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
|
|
1314
|
-
setState((current) => ({ ...current,
|
|
1773
|
+
const setPanel = useCallback((panel) => {
|
|
1774
|
+
setState((current) => ({ ...current, panel }));
|
|
1315
1775
|
}, []);
|
|
1316
|
-
const
|
|
1317
|
-
(
|
|
1318
|
-
|
|
1319
|
-
setState((current) => ({ ...current, page, prompt, notice: "" }));
|
|
1776
|
+
const notify = useCallback(
|
|
1777
|
+
(text, tone) => {
|
|
1778
|
+
setPanel({ type: "notice", text, tone });
|
|
1320
1779
|
},
|
|
1321
|
-
[
|
|
1780
|
+
[setPanel]
|
|
1322
1781
|
);
|
|
1323
1782
|
const navigate = useCallback(
|
|
1324
|
-
async (path, prompt = path) => {
|
|
1325
|
-
active.current
|
|
1326
|
-
|
|
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",
|
|
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
|
-
|
|
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(
|
|
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,
|
|
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] =
|
|
1357
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
1371
|
-
|
|
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
|
-
|
|
1862
|
+
case "rss":
|
|
1863
|
+
setPanel({ type: name });
|
|
1382
1864
|
break;
|
|
1383
1865
|
case "theme": {
|
|
1384
1866
|
const preference = parseThemePreference(args);
|
|
1385
|
-
if (!args)
|
|
1386
|
-
else if (preference)
|
|
1387
|
-
|
|
1388
|
-
notify(
|
|
1389
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
1925
|
+
panel,
|
|
1439
1926
|
onCommand,
|
|
1440
1927
|
onCancel,
|
|
1928
|
+
onClosePanel,
|
|
1441
1929
|
onSelect,
|
|
1442
|
-
|
|
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
|
|
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__ */
|
|
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 } =
|
|
1459
|
-
const [client] =
|
|
1460
|
-
const [state, setState] =
|
|
1461
|
-
const [attempt, setAttempt] =
|
|
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
|
-
|
|
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
|
-
|
|
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__ */
|
|
1488
|
-
return /* @__PURE__ */
|
|
1489
|
-
/* @__PURE__ */
|
|
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 : "\
|
|
1983
|
+
state.status === "error" ? state.message : "\u601D\u8003\u4E2D\u2026\u2026"
|
|
1492
1984
|
] }),
|
|
1493
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1989
|
+
const app = render(/* @__PURE__ */ jsx16(App, { site }), {
|
|
1498
1990
|
exitOnCtrlC: false,
|
|
1499
1991
|
alternateScreen: true,
|
|
1500
1992
|
incrementalRendering: true
|