miantiao.me 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{app-UWAFKI3E.js → app-ZZYXIU6G.js} +474 -225
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# miantiao.me
|
|
2
2
|
|
|
3
|
-
在终端浏览面条的文章、项目和标签,也可以直接向 AI 助手提问。界面使用 assistant-ui Ink
|
|
3
|
+
在终端浏览面条的文章、项目和标签,也可以直接向 AI 助手提问。界面使用 assistant-ui Ink:固定窗口、底部输入框和独立滚动的内容区,与 [miantiao.me](https://miantiao.me) 的主题、欢迎页和会话块保持一致。
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
6
|
npx miantiao.me
|
|
@@ -24,7 +24,7 @@ npx miantiao.me
|
|
|
24
24
|
| `/clear` | 清空 AI 对话上下文 |
|
|
25
25
|
| `/quit` | 退出 |
|
|
26
26
|
|
|
27
|
-
Tab
|
|
27
|
+
PgUp/PgDn 滚动正文,输入框始终固定在底部。Tab 在输入框和内容区之间切换;列表中用 ↑↓ 选择、Enter 打开,文章中用 ↑↓、Home、End 滚动。Esc 取消加载或停止回复,Ctrl+C 退出。编号对应当前页面的结果列表,可用于 `/read 1` 或 `/open 1`。`/clear` 清空 AI 会话。退出后恢复原终端画面。
|
|
28
28
|
|
|
29
29
|
支持 GrokNight、GrokDay、TokyoNight、RosePine Moon、Oscura Midnight 和 `auto`。自动模式参考终端的 `COLORFGBG`;未提供时使用 GrokNight。可用 `/theme light` 或 `/theme dark` 显式选择。
|
|
30
30
|
|
|
@@ -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 {
|
|
7
|
+
import { Box as Box8, render, Text as Text8, useApp as useApp2, useInput as useInput4, useWindowSize as useWindowSize2 } from "ink";
|
|
8
|
+
import { useEffect as useEffect4, useState as useState5 } from "react";
|
|
9
9
|
|
|
10
10
|
// src/terminal/schema.ts
|
|
11
11
|
import { z } from "zod";
|
|
@@ -52,12 +52,22 @@ var terminalIndexSchema = z.object({
|
|
|
52
52
|
tags: z.array(z.object({ name: z.string(), path: z.string(), count: z.number().int().nonnegative() })),
|
|
53
53
|
searchIndexUrl: z.string()
|
|
54
54
|
});
|
|
55
|
+
var sectionSchema = z.discriminatedUnion("kind", [
|
|
56
|
+
z.object({
|
|
57
|
+
kind: z.literal("welcome"),
|
|
58
|
+
markdown: z.string(),
|
|
59
|
+
logo: z.string().optional(),
|
|
60
|
+
menu: z.array(linkSchema.extend({ command: z.string() })).optional()
|
|
61
|
+
}),
|
|
62
|
+
z.object({ kind: z.literal("turn"), prompt: z.string(), markdown: z.string() })
|
|
63
|
+
]);
|
|
55
64
|
var terminalDocumentSchema = z.object({
|
|
56
65
|
schemaVersion: z.literal(1),
|
|
57
66
|
revision: z.string().min(1),
|
|
58
67
|
path: z.string(),
|
|
59
68
|
title: z.string(),
|
|
60
|
-
markdown: z.string().min(1)
|
|
69
|
+
markdown: z.string().min(1),
|
|
70
|
+
sections: z.array(sectionSchema).optional()
|
|
61
71
|
});
|
|
62
72
|
|
|
63
73
|
// src/utils/search-core.ts
|
|
@@ -276,49 +286,12 @@ var TerminalClient = class {
|
|
|
276
286
|
};
|
|
277
287
|
|
|
278
288
|
// packages/cli/src/console.tsx
|
|
279
|
-
import {
|
|
280
|
-
|
|
281
|
-
LoadingPrimitive,
|
|
282
|
-
ThreadPrimitive as ThreadPrimitive2,
|
|
283
|
-
useAui,
|
|
284
|
-
useAuiState as useAuiState2
|
|
285
|
-
} from "@assistant-ui/react-ink";
|
|
286
|
-
import { Box as Box2, Static, Text as Text2, useFocus, useInput } from "ink";
|
|
287
|
-
import { useState } from "react";
|
|
288
|
-
|
|
289
|
-
// src/agent/commands.ts
|
|
290
|
-
var commands = [
|
|
291
|
-
{ id: "help", description: "\u67E5\u770B\u53EF\u7528\u547D\u4EE4" },
|
|
292
|
-
{ id: "theme", args: "[\u4E3B\u9898]", description: "\u5207\u6362\u4E3B\u9898\uFF0C\u4E0D\u5E26\u53C2\u6570\u65F6\u6253\u5F00\u9009\u62E9\u5668" },
|
|
293
|
-
{ id: "clear", description: "\u6E05\u7A7A\u5F53\u524D\u5BF9\u8BDD" },
|
|
294
|
-
{ id: "home", description: "\u6253\u5F00\u9996\u9875" },
|
|
295
|
-
{ id: "blog", description: "\u6D4F\u89C8\u5168\u90E8\u6587\u7AE0" },
|
|
296
|
-
{ id: "projects", description: "\u67E5\u770B\u6298\u817E\u7684\u9879\u76EE" },
|
|
297
|
-
{ id: "tags", description: "\u6309\u6807\u7B7E\u6D4F\u89C8\u6587\u7AE0" },
|
|
298
|
-
{ id: "search", args: "[\u5173\u952E\u8BCD]", description: "\u641C\u7D22\u6587\u7AE0" },
|
|
299
|
-
{ id: "about", description: "\u5173\u4E8E\u672C\u7AD9" },
|
|
300
|
-
{ id: "rss", description: "\u8BA2\u9605 RSS" }
|
|
301
|
-
];
|
|
302
|
-
var pageCommands = {
|
|
303
|
-
home: "/",
|
|
304
|
-
blog: "/blog/",
|
|
305
|
-
projects: "/projects/",
|
|
306
|
-
tags: "/tags/",
|
|
307
|
-
about: "/about/"
|
|
308
|
-
};
|
|
309
|
-
function parseCommand(input) {
|
|
310
|
-
const match = input.trim().match(/^\/(\S+)(?:\s+([\s\S]*))?$/);
|
|
311
|
-
if (!match)
|
|
312
|
-
return null;
|
|
313
|
-
return { name: match[1].toLowerCase(), args: (match[2] ?? "").trim() };
|
|
314
|
-
}
|
|
315
|
-
function getSearchPath(query) {
|
|
316
|
-
return query ? `/search/?q=${encodeURIComponent(query)}` : "/search/";
|
|
317
|
-
}
|
|
289
|
+
import { ThreadPrimitive as ThreadPrimitive2 } from "@assistant-ui/react-ink";
|
|
290
|
+
import { useRef as useRef4 } from "react";
|
|
318
291
|
|
|
319
292
|
// packages/cli/src/chat.tsx
|
|
320
293
|
import { ThreadPrimitive, useAuiState } from "@assistant-ui/react-ink";
|
|
321
|
-
import { Box, Text } from "ink";
|
|
294
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
322
295
|
|
|
323
296
|
// packages/cli/src/theme.tsx
|
|
324
297
|
import { createContext, useContext, useMemo } from "react";
|
|
@@ -423,43 +396,225 @@ function useTerminalStyle() {
|
|
|
423
396
|
if (!style) throw new Error("TerminalTheme is required");
|
|
424
397
|
return style;
|
|
425
398
|
}
|
|
426
|
-
function Markdown({ text }) {
|
|
399
|
+
function Markdown({ text, width: availableWidth }) {
|
|
427
400
|
const { width, markdownTheme, highlighter } = useTerminalStyle();
|
|
428
401
|
return /* @__PURE__ */ jsx(
|
|
429
402
|
MarkdownText,
|
|
430
403
|
{
|
|
431
404
|
text,
|
|
432
|
-
width: Math.max(10, width -
|
|
405
|
+
width: Math.max(10, availableWidth ?? width - 3),
|
|
433
406
|
theme: markdownTheme,
|
|
434
407
|
highlighter,
|
|
435
408
|
codeWrap: true,
|
|
409
|
+
hyperlinks: true,
|
|
436
410
|
tableTruncate: false
|
|
437
411
|
}
|
|
438
412
|
);
|
|
439
413
|
}
|
|
440
414
|
|
|
441
|
-
// packages/cli/src/
|
|
415
|
+
// packages/cli/src/blocks.tsx
|
|
416
|
+
import { Box as Box3, Text as Text3, useBoxMetrics as useBoxMetrics3 } from "ink";
|
|
417
|
+
import { useRef as useRef3 } from "react";
|
|
418
|
+
|
|
419
|
+
// packages/cli/src/page-list.tsx
|
|
420
|
+
import { Box as Box2, Text as Text2, useBoxMetrics as useBoxMetrics2, useInput as useInput2 } from "ink";
|
|
421
|
+
import { useEffect, useRef as useRef2, useState as useState2 } from "react";
|
|
422
|
+
|
|
423
|
+
// packages/cli/src/scroll-area.tsx
|
|
424
|
+
import {
|
|
425
|
+
Box,
|
|
426
|
+
Text,
|
|
427
|
+
measureElement,
|
|
428
|
+
useBoxMetrics,
|
|
429
|
+
useFocus,
|
|
430
|
+
useInput
|
|
431
|
+
} from "ink";
|
|
432
|
+
import {
|
|
433
|
+
createContext as createContext2,
|
|
434
|
+
useContext as useContext2,
|
|
435
|
+
useImperativeHandle,
|
|
436
|
+
useMemo as useMemo2,
|
|
437
|
+
useRef,
|
|
438
|
+
useState
|
|
439
|
+
} from "react";
|
|
442
440
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
441
|
+
var ScrollContext = createContext2(null);
|
|
442
|
+
function useScrollArea() {
|
|
443
|
+
const value = useContext2(ScrollContext);
|
|
444
|
+
if (!value) throw new Error("ScrollArea is required");
|
|
445
|
+
return value;
|
|
446
|
+
}
|
|
447
|
+
function ScrollArea({
|
|
448
|
+
children,
|
|
449
|
+
selection,
|
|
450
|
+
ref
|
|
451
|
+
}) {
|
|
452
|
+
const { colors } = useTerminalStyle();
|
|
453
|
+
const { isFocused } = useFocus({ id: "content" });
|
|
454
|
+
const viewport = useRef(null);
|
|
455
|
+
const content = useRef(null);
|
|
456
|
+
const { clientHeight: height } = useBoxMetrics(viewport);
|
|
457
|
+
const { height: contentHeight } = useBoxMetrics(content);
|
|
458
|
+
const [anchor, setAnchor] = useState(0);
|
|
459
|
+
const maximum = Math.max(0, contentHeight - height);
|
|
460
|
+
const offset = anchor === "end" ? maximum : Math.min(anchor, maximum);
|
|
461
|
+
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
|
+
});
|
|
486
|
+
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 thumbTop = maximum > 0 ? Math.round(offset / maximum * (height - thumbSize)) : 0;
|
|
489
|
+
const before = Array(thumbTop).fill("\u2502").join("\n");
|
|
490
|
+
const thumb = Array(thumbSize).fill("\u2503").join("\n");
|
|
491
|
+
const after = Array(Math.max(0, track - thumbTop - thumbSize)).fill("\u2502").join("\n");
|
|
492
|
+
return /* @__PURE__ */ jsx2(ScrollContext, { value: context, children: /* @__PURE__ */ jsxs(Box, { flexGrow: 1, flexShrink: 1, minHeight: 1, marginX: 2, children: [
|
|
493
|
+
/* @__PURE__ */ jsx2(
|
|
494
|
+
Box,
|
|
495
|
+
{
|
|
496
|
+
ref: viewport,
|
|
497
|
+
flexDirection: "column",
|
|
498
|
+
flexGrow: 1,
|
|
499
|
+
flexShrink: 1,
|
|
500
|
+
overflow: "hidden",
|
|
501
|
+
contentOffsetY: offset,
|
|
502
|
+
children: /* @__PURE__ */ jsx2(Box, { ref: content, flexDirection: "column", flexShrink: 0, width: "100%", children })
|
|
503
|
+
}
|
|
504
|
+
),
|
|
505
|
+
/* @__PURE__ */ jsx2(Box, { width: 1, flexShrink: 0, "aria-hidden": true, children: /* @__PURE__ */ jsxs(Text, { color: colors.line, children: [
|
|
506
|
+
before,
|
|
507
|
+
before && "\n",
|
|
508
|
+
/* @__PURE__ */ jsx2(Text, { color: isFocused ? colors.accent : colors.muted, children: thumb }),
|
|
509
|
+
after && "\n",
|
|
510
|
+
after
|
|
511
|
+
] }) })
|
|
512
|
+
] }) });
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// packages/cli/src/page-list.tsx
|
|
516
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
517
|
+
function PageList({ items, onSelect, scroll, compact = false }) {
|
|
518
|
+
const { focused } = useScrollArea();
|
|
519
|
+
const { colors } = useTerminalStyle();
|
|
520
|
+
const [selected, setSelected] = useState2(0);
|
|
521
|
+
const elements = useRef2(/* @__PURE__ */ new Map());
|
|
522
|
+
const container = useRef2(null);
|
|
523
|
+
const { hasMeasured } = useBoxMetrics2(container);
|
|
524
|
+
useEffect(() => {
|
|
525
|
+
const element = elements.current.get(items[selected].path);
|
|
526
|
+
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 });
|
|
544
|
+
return /* @__PURE__ */ jsx3(Box2, { ref: container, flexDirection: "column", marginTop: 1, children: items.map((item, index) => {
|
|
545
|
+
const active = focused && index === selected;
|
|
546
|
+
return /* @__PURE__ */ jsxs2(
|
|
547
|
+
Box2,
|
|
548
|
+
{
|
|
549
|
+
flexDirection: compact ? "row" : "column",
|
|
550
|
+
justifyContent: compact ? "space-between" : void 0,
|
|
551
|
+
marginBottom: compact ? 0 : 1,
|
|
552
|
+
backgroundColor: active ? colors.band : void 0,
|
|
553
|
+
ref: (element) => {
|
|
554
|
+
if (element) elements.current.set(item.path, element);
|
|
555
|
+
else elements.current.delete(item.path);
|
|
556
|
+
},
|
|
557
|
+
children: [
|
|
558
|
+
/* @__PURE__ */ jsxs2(Text2, { color: active ? colors.accent : colors.fg, bold: active, children: [
|
|
559
|
+
active ? "\u25B8" : compact ? " " : "\u2022",
|
|
560
|
+
" ",
|
|
561
|
+
compact ? "" : `${index + 1}. `,
|
|
562
|
+
cleanText(item.title)
|
|
563
|
+
] }),
|
|
564
|
+
item.description && /* @__PURE__ */ jsx3(Text2, { color: colors.muted, children: cleanText(item.description) })
|
|
565
|
+
]
|
|
566
|
+
},
|
|
567
|
+
item.path
|
|
568
|
+
);
|
|
569
|
+
}) });
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// packages/cli/src/blocks.tsx
|
|
573
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
574
|
+
function Prompt({ text, command = false }) {
|
|
575
|
+
const { colors } = useTerminalStyle();
|
|
576
|
+
return /* @__PURE__ */ jsxs3(Box3, { backgroundColor: colors.band, paddingX: 2, children: [
|
|
577
|
+
/* @__PURE__ */ jsx4(Text3, { color: colors.prompt, children: "\u276F " }),
|
|
578
|
+
/* @__PURE__ */ jsx4(Text3, { color: command ? colors.skill : colors.fg, children: cleanText(text) })
|
|
579
|
+
] });
|
|
580
|
+
}
|
|
581
|
+
function Welcome({ section, ...selection }) {
|
|
582
|
+
const { colors, width } = useTerminalStyle();
|
|
583
|
+
const content = useRef3(null);
|
|
584
|
+
const { clientWidth } = useBoxMetrics3(content);
|
|
585
|
+
return /* @__PURE__ */ jsxs3(Box3, { borderStyle: "round", borderColor: colors.line, paddingX: 2, paddingY: 1, gap: 4, children: [
|
|
586
|
+
section.logo && width >= 90 && /* @__PURE__ */ jsx4(Text3, { color: colors.skill, children: cleanText(section.logo) }),
|
|
587
|
+
/* @__PURE__ */ jsxs3(Box3, { ref: content, flexDirection: "column", flexGrow: 1, flexShrink: 1, minWidth: 0, children: [
|
|
588
|
+
/* @__PURE__ */ jsx4(Markdown, { text: section.markdown, width: clientWidth || width - 6 }),
|
|
589
|
+
section.menu && selection.items.length > 0 && /* @__PURE__ */ jsx4(PageList, { ...selection, compact: true })
|
|
590
|
+
] })
|
|
591
|
+
] });
|
|
592
|
+
}
|
|
593
|
+
function HomeSections({ page, ...selection }) {
|
|
594
|
+
return /* @__PURE__ */ jsx4(Box3, { flexDirection: "column", gap: 1, children: page.sections?.map((section) => section.kind === "welcome" ? /* @__PURE__ */ jsx4(Welcome, { section, items: page.items, ...selection }, "welcome") : /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", gap: 1, children: [
|
|
595
|
+
/* @__PURE__ */ jsx4(Prompt, { text: section.prompt }),
|
|
596
|
+
/* @__PURE__ */ jsx4(Box3, { flexDirection: "column", paddingLeft: 2, paddingRight: 1, children: /* @__PURE__ */ jsx4(Markdown, { text: section.markdown }) })
|
|
597
|
+
] }, section.prompt)) });
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// packages/cli/src/chat.tsx
|
|
601
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
443
602
|
var toolTitles = { set_theme: "Theme", open_page: "Open" };
|
|
444
603
|
function MessageBody({ message }) {
|
|
445
604
|
const { colors } = useTerminalStyle();
|
|
446
605
|
if (message.role === "user") {
|
|
447
|
-
return /* @__PURE__ */
|
|
448
|
-
"\u276F",
|
|
449
|
-
" ",
|
|
450
|
-
message.content.filter((part) => part.type === "text").map((part) => cleanText(part.text)).join("\n")
|
|
451
|
-
] }) });
|
|
606
|
+
return /* @__PURE__ */ jsx5(Box4, { marginY: 1, children: /* @__PURE__ */ jsx5(Prompt, { text: message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n") }) });
|
|
452
607
|
}
|
|
453
608
|
if (message.role !== "assistant") return null;
|
|
454
|
-
return /* @__PURE__ */
|
|
609
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
|
|
455
610
|
message.content.map((part, index) => {
|
|
456
611
|
if (part.type === "text")
|
|
457
|
-
return /* @__PURE__ */
|
|
612
|
+
return /* @__PURE__ */ jsx5(Markdown, { text: part.text }, index);
|
|
458
613
|
if (part.type === "reasoning") {
|
|
459
614
|
const label = message.status.type === "running" ? "\u601D\u8003\u4E2D\u2026" : "\u601D\u8003";
|
|
460
615
|
return (
|
|
461
616
|
// eslint-disable-next-line react-doctor/no-array-index-as-key
|
|
462
|
-
/* @__PURE__ */
|
|
617
|
+
/* @__PURE__ */ jsxs4(Text4, { color: colors.muted, children: [
|
|
463
618
|
"\u25C6 ",
|
|
464
619
|
label
|
|
465
620
|
] }, index)
|
|
@@ -467,7 +622,7 @@ function MessageBody({ message }) {
|
|
|
467
622
|
}
|
|
468
623
|
if (part.type === "tool-call") {
|
|
469
624
|
const title = toolTitles[part.toolName] ?? part.toolName;
|
|
470
|
-
return /* @__PURE__ */
|
|
625
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: colors.skill, children: [
|
|
471
626
|
"\u25C6 ",
|
|
472
627
|
cleanText(title),
|
|
473
628
|
" ",
|
|
@@ -476,116 +631,85 @@ function MessageBody({ message }) {
|
|
|
476
631
|
}
|
|
477
632
|
return null;
|
|
478
633
|
}),
|
|
479
|
-
message.status.type === "incomplete" && message.status.reason === "error" && /* @__PURE__ */
|
|
634
|
+
message.status.type === "incomplete" && message.status.reason === "error" && /* @__PURE__ */ jsx5(Text4, { color: colors.err, children: "\u25C6 AI \u6682\u65F6\u65E0\u6CD5\u56DE\u590D\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002" })
|
|
480
635
|
] });
|
|
481
636
|
}
|
|
482
|
-
function
|
|
637
|
+
function Message() {
|
|
483
638
|
const message = useAuiState((state) => state.message);
|
|
484
|
-
return
|
|
639
|
+
return /* @__PURE__ */ jsx5(MessageBody, { message });
|
|
485
640
|
}
|
|
486
641
|
function ChatThread() {
|
|
487
|
-
return /* @__PURE__ */
|
|
642
|
+
return /* @__PURE__ */ jsx5(ThreadPrimitive.Messages, { components: { Message } });
|
|
488
643
|
}
|
|
489
644
|
|
|
490
|
-
// packages/cli/src/
|
|
491
|
-
import {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
645
|
+
// packages/cli/src/composer.tsx
|
|
646
|
+
import { ComposerPrimitive, LoadingPrimitive, useAui, useAuiState as useAuiState2 } from "@assistant-ui/react-ink";
|
|
647
|
+
import { Box as Box5, Text as Text5, useFocusManager } from "ink";
|
|
648
|
+
|
|
649
|
+
// src/agent/appearance.ts
|
|
650
|
+
var composerPlaceholder = "\u95EE\u70B9\u4EC0\u4E48\uFF0C\u6216\u8F93\u5165 / \u67E5\u770B\u547D\u4EE4";
|
|
651
|
+
var composerBrand = { mark: "<MT/>", model: "V3.14 Pro" };
|
|
652
|
+
|
|
653
|
+
// src/agent/commands.ts
|
|
654
|
+
var commands = [
|
|
655
|
+
{ id: "help", description: "\u67E5\u770B\u53EF\u7528\u547D\u4EE4" },
|
|
656
|
+
{ id: "theme", args: "[\u4E3B\u9898]", description: "\u5207\u6362\u4E3B\u9898\uFF0C\u4E0D\u5E26\u53C2\u6570\u65F6\u6253\u5F00\u9009\u62E9\u5668" },
|
|
657
|
+
{ id: "clear", description: "\u6E05\u7A7A\u5F53\u524D\u5BF9\u8BDD" },
|
|
658
|
+
{ id: "home", description: "\u6253\u5F00\u9996\u9875" },
|
|
659
|
+
{ id: "blog", description: "\u6D4F\u89C8\u5168\u90E8\u6587\u7AE0" },
|
|
660
|
+
{ id: "projects", description: "\u67E5\u770B\u6298\u817E\u7684\u9879\u76EE" },
|
|
661
|
+
{ id: "tags", description: "\u6309\u6807\u7B7E\u6D4F\u89C8\u6587\u7AE0" },
|
|
662
|
+
{ id: "search", args: "[\u5173\u952E\u8BCD]", description: "\u641C\u7D22\u6587\u7AE0" },
|
|
663
|
+
{ id: "about", description: "\u5173\u4E8E\u672C\u7AD9" },
|
|
664
|
+
{ id: "rss", description: "\u8BA2\u9605 RSS" }
|
|
665
|
+
];
|
|
666
|
+
var pageCommands = {
|
|
667
|
+
home: "/",
|
|
668
|
+
blog: "/blog/",
|
|
669
|
+
projects: "/projects/",
|
|
670
|
+
tags: "/tags/",
|
|
671
|
+
about: "/about/"
|
|
672
|
+
};
|
|
673
|
+
function parseCommand(input) {
|
|
674
|
+
const match = input.trim().match(/^\/(\S+)(?:\s+([\s\S]*))?$/);
|
|
675
|
+
if (!match)
|
|
676
|
+
return null;
|
|
677
|
+
return { name: match[1].toLowerCase(), args: (match[2] ?? "").trim() };
|
|
510
678
|
}
|
|
511
|
-
function
|
|
512
|
-
|
|
513
|
-
const { colors } = useTerminalStyle();
|
|
514
|
-
const [selected, setSelected] = useState(0);
|
|
515
|
-
const start = Math.floor(selected / pageSize) * pageSize;
|
|
516
|
-
useInput(
|
|
517
|
-
(_input, key) => {
|
|
518
|
-
if (key.upArrow) setSelected((value) => Math.max(0, value - 1));
|
|
519
|
-
if (key.downArrow) setSelected((value) => Math.min(page.items.length - 1, value + 1));
|
|
520
|
-
if (key.return) {
|
|
521
|
-
const item = page.items[selected];
|
|
522
|
-
if (item) onSelect(item.path, item.external);
|
|
523
|
-
}
|
|
524
|
-
},
|
|
525
|
-
{ isActive: isFocused }
|
|
526
|
-
);
|
|
527
|
-
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
|
|
528
|
-
page.items.slice(start, start + pageSize).map((item, offset) => {
|
|
529
|
-
const active = isFocused && start + offset === selected;
|
|
530
|
-
return /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate-end", color: active ? colors.accent : colors.fg, children: [
|
|
531
|
-
active ? "\u25B8" : "\u2022",
|
|
532
|
-
" ",
|
|
533
|
-
start + offset + 1,
|
|
534
|
-
". ",
|
|
535
|
-
cleanText(item.title),
|
|
536
|
-
item.description && /* @__PURE__ */ jsxs2(Text2, { color: colors.muted, children: [
|
|
537
|
-
"\n ",
|
|
538
|
-
cleanText(item.description)
|
|
539
|
-
] })
|
|
540
|
-
] }, item.path);
|
|
541
|
-
}),
|
|
542
|
-
/* @__PURE__ */ jsxs2(Text2, { color: colors.muted, children: [
|
|
543
|
-
start + 1,
|
|
544
|
-
"\u2013",
|
|
545
|
-
Math.min(start + pageSize, page.items.length),
|
|
546
|
-
" / ",
|
|
547
|
-
page.items.length,
|
|
548
|
-
" \xB7 Tab",
|
|
549
|
-
" ",
|
|
550
|
-
isFocused ? "\u56DE\u5230\u8F93\u5165" : "\u9009\u62E9\u5217\u8868"
|
|
551
|
-
] })
|
|
552
|
-
] });
|
|
679
|
+
function getSearchPath(query) {
|
|
680
|
+
return query ? `/search/?q=${encodeURIComponent(query)}` : "/search/";
|
|
553
681
|
}
|
|
554
|
-
|
|
682
|
+
|
|
683
|
+
// packages/cli/src/composer.tsx
|
|
684
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
685
|
+
function Composer({
|
|
555
686
|
runtime,
|
|
556
687
|
page,
|
|
557
688
|
revision,
|
|
558
|
-
output,
|
|
559
689
|
loading,
|
|
560
690
|
notice,
|
|
561
691
|
onCommand,
|
|
562
|
-
|
|
692
|
+
onSubmit
|
|
563
693
|
}) {
|
|
564
694
|
const { colors } = useTerminalStyle();
|
|
565
695
|
const aui = useAui();
|
|
566
|
-
const
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
/* @__PURE__ */
|
|
571
|
-
|
|
572
|
-
/* @__PURE__ */
|
|
573
|
-
/* @__PURE__ */ jsxs2(Text2, { color: colors.muted, children: [
|
|
574
|
-
" ",
|
|
575
|
-
loading ? "\u6B63\u5728\u8BFB\u53D6\u5B98\u7F51\u2026" : "\u56DE\u590D\u4E2D\u2026",
|
|
576
|
-
" \xB7 Esc \u505C\u6B62"
|
|
577
|
-
] })
|
|
696
|
+
const { activeId } = useFocusManager();
|
|
697
|
+
const running = useAuiState2((state) => state.thread.isRunning);
|
|
698
|
+
const focused = activeId !== "content";
|
|
699
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginX: 2, flexShrink: 0, children: [
|
|
700
|
+
(loading || running || notice) && /* @__PURE__ */ jsxs5(Box5, { paddingLeft: 2, height: 1, overflow: "hidden", children: [
|
|
701
|
+
(loading || running) && /* @__PURE__ */ jsx6(LoadingPrimitive.Spinner, {}),
|
|
702
|
+
/* @__PURE__ */ jsx6(Text5, { 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` })
|
|
578
703
|
] }),
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
/* @__PURE__ */
|
|
582
|
-
/* @__PURE__ */ jsx3(
|
|
704
|
+
/* @__PURE__ */ jsxs5(Box5, { borderStyle: "round", borderColor: focused ? colors["line-strong"] : colors.line, paddingX: 1, children: [
|
|
705
|
+
/* @__PURE__ */ jsx6(Text5, { color: colors.prompt, children: "\u276F " }),
|
|
706
|
+
/* @__PURE__ */ jsx6(
|
|
583
707
|
ComposerPrimitive.Input,
|
|
584
708
|
{
|
|
585
709
|
autoFocus: true,
|
|
586
710
|
submitOnEnter: true,
|
|
587
711
|
flexGrow: 1,
|
|
588
|
-
placeholder:
|
|
712
|
+
placeholder: composerPlaceholder,
|
|
589
713
|
onSubmit: (text) => {
|
|
590
714
|
const input = text.trim();
|
|
591
715
|
if (!input) return;
|
|
@@ -593,19 +717,166 @@ function Console({
|
|
|
593
717
|
if (command) {
|
|
594
718
|
aui.composer.setText("");
|
|
595
719
|
void onCommand(command.name, command.args);
|
|
596
|
-
} else if (!
|
|
720
|
+
} else if (!running) {
|
|
721
|
+
onSubmit();
|
|
722
|
+
runtime.thread.composer.send();
|
|
723
|
+
}
|
|
597
724
|
}
|
|
598
725
|
},
|
|
599
|
-
page.path
|
|
600
|
-
)
|
|
726
|
+
`${page.path}:${revision}`
|
|
727
|
+
),
|
|
728
|
+
/* @__PURE__ */ jsx6(Box5, { position: "absolute", bottom: -1, right: 1, children: /* @__PURE__ */ jsxs5(Text5, { color: colors.muted, backgroundColor: colors.bg, children: [
|
|
729
|
+
" ",
|
|
730
|
+
composerBrand.mark,
|
|
731
|
+
" \xB7 ",
|
|
732
|
+
composerBrand.model,
|
|
733
|
+
" "
|
|
734
|
+
] }) })
|
|
601
735
|
] }),
|
|
602
|
-
/* @__PURE__ */
|
|
603
|
-
|
|
604
|
-
"
|
|
736
|
+
/* @__PURE__ */ jsxs5(Text5, { color: colors.muted, wrap: "truncate-end", children: [
|
|
737
|
+
/* @__PURE__ */ jsx6(Text5, { color: colors["fg-soft"], bold: true, children: "Tab" }),
|
|
738
|
+
":\u5207\u6362 \u2502 ",
|
|
739
|
+
/* @__PURE__ */ jsx6(Text5, { bold: true, children: "PgUp/PgDn" }),
|
|
740
|
+
":\u6EDA\u52A8 \u2502 ",
|
|
741
|
+
/* @__PURE__ */ jsx6(Text5, { bold: true, children: "/help" }),
|
|
742
|
+
" \u2502 Ctrl+C:\u9000\u51FA"
|
|
605
743
|
] })
|
|
606
744
|
] });
|
|
607
745
|
}
|
|
608
746
|
|
|
747
|
+
// packages/cli/src/page-content.tsx
|
|
748
|
+
import { Box as Box6, Text as Text6 } from "ink";
|
|
749
|
+
import { memo } from "react";
|
|
750
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
751
|
+
var PageContent = memo(function PageContent2({
|
|
752
|
+
page,
|
|
753
|
+
prompt,
|
|
754
|
+
onSelect,
|
|
755
|
+
scroll
|
|
756
|
+
}) {
|
|
757
|
+
const { colors } = useTerminalStyle();
|
|
758
|
+
if (page.sections) return /* @__PURE__ */ jsx7(HomeSections, { page, onSelect, scroll });
|
|
759
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginBottom: 1, children: [
|
|
760
|
+
page.path !== "/" && /* @__PURE__ */ jsx7(Prompt, { text: prompt, command: true }),
|
|
761
|
+
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingLeft: 2, paddingRight: 1, paddingTop: 1, children: [
|
|
762
|
+
page.path !== "/" && /* @__PURE__ */ jsxs6(Text6, { children: [
|
|
763
|
+
/* @__PURE__ */ jsx7(Text6, { color: colors.skill, children: "\u25C6 " }),
|
|
764
|
+
/* @__PURE__ */ jsx7(Text6, { color: colors.fg, bold: true, children: page.verb }),
|
|
765
|
+
/* @__PURE__ */ jsxs6(Text6, { color: colors.path, children: [
|
|
766
|
+
" ",
|
|
767
|
+
cleanText(page.title)
|
|
768
|
+
] })
|
|
769
|
+
] }),
|
|
770
|
+
page.markdown && /* @__PURE__ */ jsx7(Markdown, { text: page.markdown }),
|
|
771
|
+
page.items.length > 0 && /* @__PURE__ */ jsx7(PageList, { items: page.items, onSelect, scroll })
|
|
772
|
+
] })
|
|
773
|
+
] });
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
// packages/cli/src/window.tsx
|
|
777
|
+
import { Box as Box7, Text as Text7, useWindowSize } from "ink";
|
|
778
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
779
|
+
function windowWidth(columns) {
|
|
780
|
+
return Math.min(120, columns >= 84 ? columns - 4 : columns);
|
|
781
|
+
}
|
|
782
|
+
function Window({
|
|
783
|
+
name,
|
|
784
|
+
path,
|
|
785
|
+
nav = [],
|
|
786
|
+
children
|
|
787
|
+
}) {
|
|
788
|
+
const { columns, rows } = useWindowSize();
|
|
789
|
+
const { colors } = useTerminalStyle();
|
|
790
|
+
const width = windowWidth(columns);
|
|
791
|
+
const margin = columns >= 80 && rows >= 24 ? 1 : 0;
|
|
792
|
+
const directory = `~/miantiao.me${path === "/" ? "" : path.replace(/\/$/, "")}`;
|
|
793
|
+
return /* @__PURE__ */ jsx8(
|
|
794
|
+
Box7,
|
|
795
|
+
{
|
|
796
|
+
width: columns,
|
|
797
|
+
height: rows,
|
|
798
|
+
flexDirection: "column",
|
|
799
|
+
alignItems: "center",
|
|
800
|
+
paddingY: margin,
|
|
801
|
+
backgroundColor: colors.page,
|
|
802
|
+
children: /* @__PURE__ */ jsxs7(
|
|
803
|
+
Box7,
|
|
804
|
+
{
|
|
805
|
+
width,
|
|
806
|
+
height: Math.max(1, rows - margin * 2),
|
|
807
|
+
flexDirection: "column",
|
|
808
|
+
borderStyle: "round",
|
|
809
|
+
borderColor: colors.line,
|
|
810
|
+
backgroundColor: colors.bg,
|
|
811
|
+
overflow: "hidden",
|
|
812
|
+
children: [
|
|
813
|
+
/* @__PURE__ */ jsxs7(
|
|
814
|
+
Box7,
|
|
815
|
+
{
|
|
816
|
+
paddingX: 2,
|
|
817
|
+
flexShrink: 0,
|
|
818
|
+
borderStyle: "single",
|
|
819
|
+
borderTop: false,
|
|
820
|
+
borderLeft: false,
|
|
821
|
+
borderRight: false,
|
|
822
|
+
borderColor: colors.line,
|
|
823
|
+
children: [
|
|
824
|
+
/* @__PURE__ */ jsx8(Text7, { color: colors.err, children: "\u25CF " }),
|
|
825
|
+
/* @__PURE__ */ jsx8(Text7, { color: colors.warn, children: "\u25CF " }),
|
|
826
|
+
/* @__PURE__ */ jsx8(Text7, { color: colors.ok, children: "\u25CF " }),
|
|
827
|
+
/* @__PURE__ */ jsx8(Box7, { flexGrow: 1, justifyContent: "center", paddingRight: 6, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.muted, wrap: "truncate-end", children: [
|
|
828
|
+
cleanText(name),
|
|
829
|
+
" \u2014 miantiao.me"
|
|
830
|
+
] }) })
|
|
831
|
+
]
|
|
832
|
+
}
|
|
833
|
+
),
|
|
834
|
+
/* @__PURE__ */ jsxs7(Box7, { paddingX: 2, paddingTop: 1, flexShrink: 0, children: [
|
|
835
|
+
/* @__PURE__ */ jsx8(Box7, { flexGrow: 1, flexShrink: 1, children: /* @__PURE__ */ jsx8(Text7, { color: colors.muted, wrap: "truncate-middle", children: cleanText(directory) }) }),
|
|
836
|
+
width >= 70 && nav.map((link) => /* @__PURE__ */ jsx8(Box7, { marginLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsx8(Text7, { color: path.startsWith(link.href) ? colors.fg : colors.muted, children: cleanText(link.title) }) }, link.href))
|
|
837
|
+
] }),
|
|
838
|
+
children
|
|
839
|
+
]
|
|
840
|
+
}
|
|
841
|
+
)
|
|
842
|
+
}
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// packages/cli/src/console.tsx
|
|
847
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
848
|
+
function Console({
|
|
849
|
+
runtime,
|
|
850
|
+
page,
|
|
851
|
+
prompt,
|
|
852
|
+
revision,
|
|
853
|
+
site,
|
|
854
|
+
loading,
|
|
855
|
+
notice,
|
|
856
|
+
onCommand,
|
|
857
|
+
onSelect
|
|
858
|
+
}) {
|
|
859
|
+
const scroll = useRef4(null);
|
|
860
|
+
return /* @__PURE__ */ jsx9(Window, { name: site.name, path: page.path, nav: site.nav, children: /* @__PURE__ */ jsxs8(ThreadPrimitive2.Root, { flexGrow: 1, flexShrink: 1, minHeight: 0, children: [
|
|
861
|
+
/* @__PURE__ */ jsxs8(ScrollArea, { ref: scroll, selection: page.items.length > 0, children: [
|
|
862
|
+
/* @__PURE__ */ jsx9(PageContent, { page, prompt, onSelect, scroll }),
|
|
863
|
+
/* @__PURE__ */ jsx9(ChatThread, {})
|
|
864
|
+
] }, `${page.path}:${revision}`),
|
|
865
|
+
/* @__PURE__ */ jsx9(
|
|
866
|
+
Composer,
|
|
867
|
+
{
|
|
868
|
+
runtime,
|
|
869
|
+
page,
|
|
870
|
+
revision,
|
|
871
|
+
loading,
|
|
872
|
+
notice,
|
|
873
|
+
onCommand,
|
|
874
|
+
onSubmit: () => scroll.current?.end()
|
|
875
|
+
}
|
|
876
|
+
)
|
|
877
|
+
] }) });
|
|
878
|
+
}
|
|
879
|
+
|
|
609
880
|
// packages/cli/src/pages.ts
|
|
610
881
|
var terminalCommands = [
|
|
611
882
|
...commands.map((command) => command.id === "tags" ? { ...command, args: "[\u6807\u7B7E]" } : command),
|
|
@@ -622,7 +893,7 @@ function helpPage() {
|
|
|
622
893
|
items: [],
|
|
623
894
|
markdown: terminalCommands.map(
|
|
624
895
|
(command) => `- \`/${command.id}${command.args ? ` ${command.args}` : ""}\` \u2014 ${command.description}`
|
|
625
|
-
).join("\n") + "\n\
|
|
896
|
+
).join("\n") + "\n\nPgUp/PgDn \u6EDA\u52A8\u6B63\u6587\uFF1BTab \u5207\u6362\u8F93\u5165\u548C\u5185\u5BB9\uFF1B\u2191\u2193 \u9009\u62E9\uFF0CEnter \u6253\u5F00\uFF1BEsc \u505C\u6B62\uFF1BCtrl+C \u9000\u51FA\u3002"
|
|
626
897
|
};
|
|
627
898
|
}
|
|
628
899
|
function themePage() {
|
|
@@ -651,13 +922,23 @@ async function loadPage(client, path, signal) {
|
|
|
651
922
|
page.path = result.document.path + url.search;
|
|
652
923
|
page.title = result.document.title;
|
|
653
924
|
page.markdown = result.document.markdown;
|
|
925
|
+
page.sections = result.document.sections;
|
|
654
926
|
page.verb = "Read";
|
|
655
|
-
if (page.path === index.pages.home.path)
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
927
|
+
if (page.path === index.pages.home.path) {
|
|
928
|
+
const welcome = page.sections?.find((section) => section.kind === "welcome");
|
|
929
|
+
const menu = welcome?.menu ?? index.site.nav.map((link) => ({ ...link, command: void 0 }));
|
|
930
|
+
const siteOrigins = new Set([client.site, index.site.url].map((site) => new URL(site).origin));
|
|
931
|
+
page.items = menu.map((link) => {
|
|
932
|
+
const destination = new URL(link.href, client.site);
|
|
933
|
+
const external = !siteOrigins.has(destination.origin);
|
|
934
|
+
return {
|
|
935
|
+
title: link.title,
|
|
936
|
+
description: link.command,
|
|
937
|
+
path: external ? destination.href : destination.pathname + destination.search,
|
|
938
|
+
external
|
|
939
|
+
};
|
|
940
|
+
});
|
|
941
|
+
}
|
|
661
942
|
} else if (pathname === "/blog/") {
|
|
662
943
|
page.title = "\u5168\u90E8\u6587\u7AE0";
|
|
663
944
|
page.items = index.posts.map((post) => ({
|
|
@@ -706,12 +987,12 @@ async function loadPage(client, path, signal) {
|
|
|
706
987
|
}
|
|
707
988
|
|
|
708
989
|
// packages/cli/src/session.ts
|
|
709
|
-
import { useApp, useInput as
|
|
710
|
-
import {
|
|
990
|
+
import { useApp, useInput as useInput3 } from "ink";
|
|
991
|
+
import { useState as useState4 } from "react";
|
|
711
992
|
|
|
712
993
|
// packages/cli/src/agent.ts
|
|
713
994
|
import { useChatRuntime } from "@assistant-ui/ai-sdk";
|
|
714
|
-
import { useEffect, useMemo as
|
|
995
|
+
import { useEffect as useEffect2, useMemo as useMemo3 } from "react";
|
|
715
996
|
|
|
716
997
|
// src/agent/transport.ts
|
|
717
998
|
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
@@ -857,7 +1138,7 @@ function createAgentTransport(handlers, options) {
|
|
|
857
1138
|
|
|
858
1139
|
// packages/cli/src/agent.ts
|
|
859
1140
|
function useTerminalAgent({ site, index, page, theme, setTheme, navigate }) {
|
|
860
|
-
const transport =
|
|
1141
|
+
const transport = useMemo3(
|
|
861
1142
|
() => createAgentTransport(
|
|
862
1143
|
{
|
|
863
1144
|
getContext: () => {
|
|
@@ -900,7 +1181,7 @@ function useTerminalAgent({ site, index, page, theme, setTheme, navigate }) {
|
|
|
900
1181
|
]
|
|
901
1182
|
);
|
|
902
1183
|
const runtime = useChatRuntime({ transport });
|
|
903
|
-
|
|
1184
|
+
useEffect2(() => () => runtime.thread.cancelRun(), [runtime]);
|
|
904
1185
|
return runtime;
|
|
905
1186
|
}
|
|
906
1187
|
|
|
@@ -948,15 +1229,16 @@ function resolveTagPath(input, index) {
|
|
|
948
1229
|
}
|
|
949
1230
|
|
|
950
1231
|
// packages/cli/src/navigation.ts
|
|
951
|
-
import { useCallback, useEffect as
|
|
952
|
-
function useNavigation(client, initial
|
|
953
|
-
const [state, setState] =
|
|
1232
|
+
import { useCallback, useEffect as useEffect3, useRef as useRef5, useState as useState3 } from "react";
|
|
1233
|
+
function useNavigation(client, initial) {
|
|
1234
|
+
const [state, setState] = useState3({
|
|
954
1235
|
...initial,
|
|
955
1236
|
status: "idle",
|
|
956
|
-
notice: client.warning
|
|
1237
|
+
notice: client.warning,
|
|
1238
|
+
prompt: "/home"
|
|
957
1239
|
});
|
|
958
|
-
const active =
|
|
959
|
-
|
|
1240
|
+
const active = useRef5(void 0);
|
|
1241
|
+
useEffect3(
|
|
960
1242
|
() => () => {
|
|
961
1243
|
active.current?.abort();
|
|
962
1244
|
active.current = void 0;
|
|
@@ -974,10 +1256,9 @@ function useNavigation(client, initial, onPage) {
|
|
|
974
1256
|
const show = useCallback(
|
|
975
1257
|
(page, prompt) => {
|
|
976
1258
|
cancel();
|
|
977
|
-
setState((current) => ({ ...current, page, notice: "" }));
|
|
978
|
-
onPage(page, prompt);
|
|
1259
|
+
setState((current) => ({ ...current, page, prompt, notice: "" }));
|
|
979
1260
|
},
|
|
980
|
-
[cancel
|
|
1261
|
+
[cancel]
|
|
981
1262
|
);
|
|
982
1263
|
const navigate = useCallback(
|
|
983
1264
|
async (path, prompt = path) => {
|
|
@@ -1001,11 +1282,10 @@ function useNavigation(client, initial, onPage) {
|
|
|
1001
1282
|
}
|
|
1002
1283
|
if (active.current !== request) return false;
|
|
1003
1284
|
active.current = void 0;
|
|
1004
|
-
setState({ ...next, status: "idle", notice: client.warning });
|
|
1005
|
-
onPage(next.page, prompt);
|
|
1285
|
+
setState({ ...next, status: "idle", notice: client.warning, prompt });
|
|
1006
1286
|
return true;
|
|
1007
1287
|
},
|
|
1008
|
-
[client
|
|
1288
|
+
[client]
|
|
1009
1289
|
);
|
|
1010
1290
|
return { ...state, navigate, show, cancel, notify };
|
|
1011
1291
|
}
|
|
@@ -1013,39 +1293,14 @@ function useNavigation(client, initial, onPage) {
|
|
|
1013
1293
|
// packages/cli/src/session.ts
|
|
1014
1294
|
function useTerminalSession(client, initial) {
|
|
1015
1295
|
const { exit } = useApp();
|
|
1016
|
-
const [theme, setTheme] =
|
|
1017
|
-
const
|
|
1018
|
-
{ id: "home", page: initial.page, prompt: "/home" }
|
|
1019
|
-
]);
|
|
1020
|
-
const appendPage = useCallback2((page2, prompt) => {
|
|
1021
|
-
const entry = { id: crypto.randomUUID(), page: page2, prompt };
|
|
1022
|
-
setOutput((items) => [...items, entry]);
|
|
1023
|
-
}, []);
|
|
1024
|
-
const { index, page, status, notice, navigate, show, cancel, notify } = useNavigation(
|
|
1025
|
-
client,
|
|
1026
|
-
initial,
|
|
1027
|
-
appendPage
|
|
1028
|
-
);
|
|
1296
|
+
const [theme, setTheme] = useState4("auto");
|
|
1297
|
+
const { index, page, prompt, status, notice, navigate, show, cancel, notify } = useNavigation(client, initial);
|
|
1029
1298
|
const runtime = useTerminalAgent({ site: client.site, index, page, theme, setTheme, navigate });
|
|
1030
|
-
useEffect3(() => {
|
|
1031
|
-
const printed = /* @__PURE__ */ new Set();
|
|
1032
|
-
const publish = () => {
|
|
1033
|
-
const completed = runtime.thread.getState().messages.filter(
|
|
1034
|
-
(message) => !printed.has(message.id) && (message.role !== "assistant" || message.status.type !== "running")
|
|
1035
|
-
);
|
|
1036
|
-
if (!completed.length) return;
|
|
1037
|
-
const entries = completed.map((message) => ({ id: message.id, message }));
|
|
1038
|
-
completed.forEach((message) => printed.add(message.id));
|
|
1039
|
-
setOutput((items) => [...items, ...entries]);
|
|
1040
|
-
};
|
|
1041
|
-
publish();
|
|
1042
|
-
return runtime.thread.subscribe(publish);
|
|
1043
|
-
}, [runtime]);
|
|
1044
1299
|
const stop = () => {
|
|
1045
1300
|
cancel();
|
|
1046
1301
|
runtime.thread.cancelRun();
|
|
1047
1302
|
};
|
|
1048
|
-
|
|
1303
|
+
useInput3((input, key) => {
|
|
1049
1304
|
if (key.escape) {
|
|
1050
1305
|
stop();
|
|
1051
1306
|
notify("\u5DF2\u505C\u6B62\u3002");
|
|
@@ -1117,7 +1372,8 @@ function useTerminalSession(client, initial) {
|
|
|
1117
1372
|
runtime,
|
|
1118
1373
|
page,
|
|
1119
1374
|
revision: index.revision,
|
|
1120
|
-
|
|
1375
|
+
prompt,
|
|
1376
|
+
site: index.site,
|
|
1121
1377
|
loading: status === "loading",
|
|
1122
1378
|
notice,
|
|
1123
1379
|
onCommand,
|
|
@@ -1127,33 +1383,22 @@ function useTerminalSession(client, initial) {
|
|
|
1127
1383
|
}
|
|
1128
1384
|
|
|
1129
1385
|
// packages/cli/src/app.tsx
|
|
1130
|
-
import { jsx as
|
|
1386
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1131
1387
|
function Session({
|
|
1132
1388
|
client,
|
|
1133
1389
|
initial,
|
|
1134
1390
|
width
|
|
1135
1391
|
}) {
|
|
1136
1392
|
const { theme, ...session } = useTerminalSession(client, initial);
|
|
1137
|
-
return /* @__PURE__ */
|
|
1393
|
+
return /* @__PURE__ */ jsx10(TerminalTheme, { preference: theme, width, children: /* @__PURE__ */ jsx10(AssistantRuntimeProvider, { runtime: session.runtime, children: /* @__PURE__ */ jsx10(Console, { ...session }) }) });
|
|
1138
1394
|
}
|
|
1139
1395
|
function App({ site }) {
|
|
1140
1396
|
const { exit } = useApp2();
|
|
1141
|
-
const {
|
|
1142
|
-
const [client] =
|
|
1143
|
-
const [state, setState] =
|
|
1144
|
-
const [attempt, setAttempt] =
|
|
1145
|
-
const width =
|
|
1146
|
-
useCallback3(
|
|
1147
|
-
(notify) => {
|
|
1148
|
-
stdout.on("resize", notify);
|
|
1149
|
-
return () => {
|
|
1150
|
-
stdout.off("resize", notify);
|
|
1151
|
-
};
|
|
1152
|
-
},
|
|
1153
|
-
[stdout]
|
|
1154
|
-
),
|
|
1155
|
-
() => "columns" in stdout && typeof stdout.columns === "number" ? stdout.columns : 80
|
|
1156
|
-
);
|
|
1397
|
+
const { columns } = useWindowSize2();
|
|
1398
|
+
const [client] = useState5(() => new TerminalClient(site));
|
|
1399
|
+
const [state, setState] = useState5({ status: "loading" });
|
|
1400
|
+
const [attempt, setAttempt] = useState5(0);
|
|
1401
|
+
const width = Math.max(10, windowWidth(columns) - 7);
|
|
1157
1402
|
useEffect4(() => {
|
|
1158
1403
|
const request = new AbortController();
|
|
1159
1404
|
loadPage(client, "/", request.signal).then((content) => {
|
|
@@ -1167,7 +1412,7 @@ function App({ site }) {
|
|
|
1167
1412
|
});
|
|
1168
1413
|
return () => request.abort();
|
|
1169
1414
|
}, [client, attempt]);
|
|
1170
|
-
|
|
1415
|
+
useInput4(
|
|
1171
1416
|
(input, key) => {
|
|
1172
1417
|
if (key.ctrl && input === "c" || input === "q") exit();
|
|
1173
1418
|
else if (state.status === "error" && (key.return || input === "r" || input === "/refresh")) {
|
|
@@ -1178,17 +1423,21 @@ function App({ site }) {
|
|
|
1178
1423
|
{ isActive: state.status !== "ready" }
|
|
1179
1424
|
);
|
|
1180
1425
|
if (state.status === "ready")
|
|
1181
|
-
return /* @__PURE__ */
|
|
1182
|
-
return /* @__PURE__ */
|
|
1183
|
-
/* @__PURE__ */
|
|
1426
|
+
return /* @__PURE__ */ jsx10(Session, { client, initial: state.content, width });
|
|
1427
|
+
return /* @__PURE__ */ jsx10(TerminalTheme, { preference: "auto", width, children: /* @__PURE__ */ jsx10(Window, { name: "miantiao.me", path: "/", children: /* @__PURE__ */ jsxs9(Box8, { flexDirection: "column", flexGrow: 1, padding: 2, children: [
|
|
1428
|
+
/* @__PURE__ */ jsxs9(Text8, { children: [
|
|
1184
1429
|
"\u25C6 ",
|
|
1185
1430
|
state.status === "error" ? state.message : "\u6B63\u5728\u8BFB\u53D6\u5B98\u7F51\u2026"
|
|
1186
1431
|
] }),
|
|
1187
|
-
/* @__PURE__ */
|
|
1188
|
-
] });
|
|
1432
|
+
/* @__PURE__ */ jsx10(Text8, { children: state.status === "error" ? "Enter \u91CD\u8BD5 \xB7 q \u9000\u51FA" : "Ctrl+C \u9000\u51FA" })
|
|
1433
|
+
] }) }) });
|
|
1189
1434
|
}
|
|
1190
1435
|
async function run(site) {
|
|
1191
|
-
const app = render(/* @__PURE__ */
|
|
1436
|
+
const app = render(/* @__PURE__ */ jsx10(App, { site }), {
|
|
1437
|
+
exitOnCtrlC: false,
|
|
1438
|
+
alternateScreen: true,
|
|
1439
|
+
incrementalRendering: true
|
|
1440
|
+
});
|
|
1192
1441
|
await app.waitUntilExit();
|
|
1193
1442
|
}
|
|
1194
1443
|
export {
|
package/dist/index.js
CHANGED
|
@@ -16,14 +16,14 @@ try {
|
|
|
16
16
|
"miantiao.me \u2014 \u7EC8\u7AEF\u91CC\u7684\u9762\u6761\n\n\u7528\u6CD5\uFF1Anpx miantiao.me [--site <URL>]\n\n -h, --help \u67E5\u770B\u5E2E\u52A9\n -v, --version \u67E5\u770B\u7248\u672C\n --site <URL> \u5B98\u7F51\u5730\u5740\uFF0C\u4E5F\u53EF\u6307\u5411\u672C\u5730\u9884\u89C8\n\n\u8FDB\u5165\u540E\u8F93\u5165 /help \u67E5\u770B\u547D\u4EE4\uFF0CCtrl+C \u9000\u51FA\u3002\n\u9700\u8981 Node.js 22.14 \u6216\u66F4\u65B0\u7248\u672C\uFF0C\u4EE5\u53CA\u53EF\u4EA4\u4E92\u7684\u7EC8\u7AEF\u3002"
|
|
17
17
|
);
|
|
18
18
|
} else if (values.version) {
|
|
19
|
-
console.log("0.1.
|
|
19
|
+
console.log("0.1.1");
|
|
20
20
|
} else {
|
|
21
21
|
const site = new URL(values.site);
|
|
22
22
|
if (!["https:", "http:"].includes(site.protocol) || site.username || site.password)
|
|
23
23
|
throw new Error("--site \u5FC5\u987B\u4E3A\u4E0D\u542B\u51ED\u8BC1\u7684 HTTP \u6216 HTTPS \u5730\u5740\u3002");
|
|
24
24
|
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
25
25
|
throw new Error("\u8BF7\u5728\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\u8FD0\u884C npx miantiao.me\uFF1B\u4F7F\u7528 --help \u67E5\u770B\u5E2E\u52A9\u3002");
|
|
26
|
-
const { run } = await import("./app-
|
|
26
|
+
const { run } = await import("./app-ZZYXIU6G.js");
|
|
27
27
|
await run(site.origin);
|
|
28
28
|
}
|
|
29
29
|
} catch (error) {
|