@stigmer/ink 3.35.0 → 3.36.0
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/app/SessionApp.d.ts.map +1 -1
- package/app/SessionApp.js.map +1 -1
- package/app/SessionView.d.ts.map +1 -1
- package/app/SessionView.js.map +1 -1
- package/cli/stigmer-ink.d.ts.map +1 -1
- package/cli/stigmer-ink.js.map +1 -1
- package/components/AgentPicker.d.ts.map +1 -1
- package/components/AgentPicker.js.map +1 -1
- package/components/ApprovalPrompt.d.ts.map +1 -1
- package/components/ApprovalPrompt.js.map +1 -1
- package/components/ContextGauge.d.ts.map +1 -1
- package/components/ContextGauge.js.map +1 -1
- package/components/ExecutionProgress.d.ts.map +1 -1
- package/components/ExecutionProgress.js.map +1 -1
- package/components/FileChangeProgressBar.d.ts.map +1 -1
- package/components/FileChangeProgressBar.js.map +1 -1
- package/components/FileDiffBody.d.ts.map +1 -1
- package/components/FileDiffBody.js.map +1 -1
- package/components/FileReviewAtoms.d.ts.map +1 -1
- package/components/FileReviewAtoms.js.map +1 -1
- package/components/FileReviewPrompt.d.ts.map +1 -1
- package/components/FileReviewPrompt.js.map +1 -1
- package/components/FileReviewRecord.d.ts.map +1 -1
- package/components/FileReviewRecord.js.map +1 -1
- package/components/FollowUpInput.d.ts.map +1 -1
- package/components/FollowUpInput.js.map +1 -1
- package/components/MessageEntry.d.ts.map +1 -1
- package/components/MessageEntry.js.map +1 -1
- package/components/MessageThread.d.ts.map +1 -1
- package/components/MessageThread.js.map +1 -1
- package/components/ResourcePicker.d.ts.map +1 -1
- package/components/ResourcePicker.js.map +1 -1
- package/components/SessionPicker.d.ts.map +1 -1
- package/components/SessionPicker.js.map +1 -1
- package/components/SubAgentBlock.d.ts.map +1 -1
- package/components/SubAgentBlock.js.map +1 -1
- package/components/TodoList.d.ts.map +1 -1
- package/components/TodoList.js.map +1 -1
- package/components/ToolCallGroup.d.ts.map +1 -1
- package/components/ToolCallGroup.js.map +1 -1
- package/components/ToolCallItem.d.ts.map +1 -1
- package/components/ToolCallItem.js.map +1 -1
- package/components/UsageWidget.d.ts.map +1 -1
- package/components/UsageWidget.js.map +1 -1
- package/file-review.d.ts.map +1 -1
- package/file-review.js.map +1 -1
- package/index.d.ts.map +1 -1
- package/index.js.map +1 -1
- package/markdown.d.ts.map +1 -1
- package/markdown.js.map +1 -1
- package/package.json +4 -4
- package/provider.d.ts.map +1 -1
- package/provider.js.map +1 -1
- package/src/__tests__/components.test.tsx +0 -378
- package/src/__tests__/context-gauge.test.tsx +0 -176
- package/src/__tests__/data-pickers.test.tsx +0 -147
- package/src/__tests__/file-change-progress.test.tsx +0 -295
- package/src/__tests__/file-review.test.tsx +0 -811
- package/src/__tests__/markdown.test.ts +0 -46
- package/src/__tests__/resource-picker.test.tsx +0 -176
- package/src/__tests__/test-support.tsx +0 -31
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { renderMarkdown } from "../markdown.js";
|
|
3
|
-
|
|
4
|
-
describe("renderMarkdown", () => {
|
|
5
|
-
it("renders plain text without modification", () => {
|
|
6
|
-
const result = renderMarkdown("Hello world");
|
|
7
|
-
expect(result).toContain("Hello world");
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it("renders bold text with ANSI styling", () => {
|
|
11
|
-
const result = renderMarkdown("This is **bold** text");
|
|
12
|
-
expect(result).toContain("bold");
|
|
13
|
-
expect(result).not.toBe("This is **bold** text");
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("renders code blocks", () => {
|
|
17
|
-
const result = renderMarkdown("```\nconst x = 1;\n```");
|
|
18
|
-
expect(result).toContain("const x = 1");
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("renders headings", () => {
|
|
22
|
-
const result = renderMarkdown("# Title");
|
|
23
|
-
expect(result).toContain("Title");
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("renders lists", () => {
|
|
27
|
-
const result = renderMarkdown("- item one\n- item two");
|
|
28
|
-
expect(result).toContain("item one");
|
|
29
|
-
expect(result).toContain("item two");
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("handles empty content", () => {
|
|
33
|
-
const result = renderMarkdown("");
|
|
34
|
-
expect(result).toBe("");
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("trims trailing whitespace", () => {
|
|
38
|
-
const result = renderMarkdown("Hello");
|
|
39
|
-
expect(result).toBe(result.trimEnd());
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("accepts custom width", () => {
|
|
43
|
-
const result = renderMarkdown("A very long sentence that might wrap", 40);
|
|
44
|
-
expect(result).toContain("A very long sentence");
|
|
45
|
-
});
|
|
46
|
-
});
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { describe, it, expect, vi } from "vitest";
|
|
3
|
-
import { render } from "ink-testing-library";
|
|
4
|
-
import { type PickerItem, ResourcePicker } from "../components/ResourcePicker.js";
|
|
5
|
-
|
|
6
|
-
// Key escape sequences understood by ink's input parser.
|
|
7
|
-
const KEY = {
|
|
8
|
-
up: "\u001B[A",
|
|
9
|
-
down: "\u001B[B",
|
|
10
|
-
enter: "\r",
|
|
11
|
-
escape: "\u001B",
|
|
12
|
-
} as const;
|
|
13
|
-
|
|
14
|
-
const ITEMS: PickerItem[] = [
|
|
15
|
-
{ id: "agt_1", title: "acme/alpha", subtitle: "first agent" },
|
|
16
|
-
{ id: "agt_2", title: "acme/beta", subtitle: "second agent" },
|
|
17
|
-
{ id: "agt_3", title: "acme/gamma", subtitle: "third agent", meta: "2 hours ago" },
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
// Ink processes stdin asynchronously; yield a macrotask so the frame settles.
|
|
21
|
-
function tick(): Promise<void> {
|
|
22
|
-
return new Promise((resolve) => setTimeout(resolve, 20));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function noop(): void {}
|
|
26
|
-
|
|
27
|
-
describe("ResourcePicker", () => {
|
|
28
|
-
it("renders the prompt, all items, and the result count", () => {
|
|
29
|
-
const { lastFrame } = render(
|
|
30
|
-
<ResourcePicker
|
|
31
|
-
prompt="Select an agent"
|
|
32
|
-
items={ITEMS}
|
|
33
|
-
query=""
|
|
34
|
-
onQueryChange={noop}
|
|
35
|
-
onSelect={noop}
|
|
36
|
-
onCancel={noop}
|
|
37
|
-
/>,
|
|
38
|
-
);
|
|
39
|
-
const out = lastFrame() ?? "";
|
|
40
|
-
expect(out).toContain("Select an agent");
|
|
41
|
-
expect(out).toContain("acme/alpha");
|
|
42
|
-
expect(out).toContain("acme/gamma");
|
|
43
|
-
expect(out).toContain("first agent");
|
|
44
|
-
expect(out).toContain("2 hours ago");
|
|
45
|
-
expect(out).toContain("3 results");
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("marks the first item active by default", () => {
|
|
49
|
-
const { lastFrame } = render(
|
|
50
|
-
<ResourcePicker items={ITEMS} query="" onQueryChange={noop} onSelect={noop} onCancel={noop} />,
|
|
51
|
-
);
|
|
52
|
-
expect(lastFrame() ?? "").toContain("❯ acme/alpha");
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("selects the active item on Enter", async () => {
|
|
56
|
-
const onSelect = vi.fn();
|
|
57
|
-
const { stdin } = render(
|
|
58
|
-
<ResourcePicker items={ITEMS} query="" onQueryChange={noop} onSelect={onSelect} onCancel={noop} />,
|
|
59
|
-
);
|
|
60
|
-
stdin.write(KEY.enter);
|
|
61
|
-
await tick();
|
|
62
|
-
expect(onSelect).toHaveBeenCalledTimes(1);
|
|
63
|
-
expect(onSelect).toHaveBeenCalledWith(ITEMS[0]);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("moves the cursor down and selects the new active item", async () => {
|
|
67
|
-
const onSelect = vi.fn();
|
|
68
|
-
const { stdin } = render(
|
|
69
|
-
<ResourcePicker items={ITEMS} query="" onQueryChange={noop} onSelect={onSelect} onCancel={noop} />,
|
|
70
|
-
);
|
|
71
|
-
stdin.write(KEY.down);
|
|
72
|
-
await tick();
|
|
73
|
-
stdin.write(KEY.down);
|
|
74
|
-
await tick();
|
|
75
|
-
stdin.write(KEY.enter);
|
|
76
|
-
await tick();
|
|
77
|
-
expect(onSelect).toHaveBeenCalledWith(ITEMS[2]);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("clamps cursor movement at the list boundaries", async () => {
|
|
81
|
-
const onSelect = vi.fn();
|
|
82
|
-
const { stdin } = render(
|
|
83
|
-
<ResourcePicker items={ITEMS} query="" onQueryChange={noop} onSelect={onSelect} onCancel={noop} />,
|
|
84
|
-
);
|
|
85
|
-
// Up at the top stays on the first row.
|
|
86
|
-
stdin.write(KEY.up);
|
|
87
|
-
await tick();
|
|
88
|
-
stdin.write(KEY.enter);
|
|
89
|
-
await tick();
|
|
90
|
-
expect(onSelect).toHaveBeenLastCalledWith(ITEMS[0]);
|
|
91
|
-
// Past the bottom stays on the last row.
|
|
92
|
-
stdin.write(KEY.down);
|
|
93
|
-
await tick();
|
|
94
|
-
stdin.write(KEY.down);
|
|
95
|
-
await tick();
|
|
96
|
-
stdin.write(KEY.down);
|
|
97
|
-
await tick();
|
|
98
|
-
stdin.write(KEY.enter);
|
|
99
|
-
await tick();
|
|
100
|
-
expect(onSelect).toHaveBeenLastCalledWith(ITEMS[2]);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it("cancels on Escape without selecting", async () => {
|
|
104
|
-
const onSelect = vi.fn();
|
|
105
|
-
const onCancel = vi.fn();
|
|
106
|
-
const { stdin } = render(
|
|
107
|
-
<ResourcePicker items={ITEMS} query="" onQueryChange={noop} onSelect={onSelect} onCancel={onCancel} />,
|
|
108
|
-
);
|
|
109
|
-
stdin.write(KEY.escape);
|
|
110
|
-
await tick();
|
|
111
|
-
expect(onCancel).toHaveBeenCalledTimes(1);
|
|
112
|
-
expect(onSelect).not.toHaveBeenCalled();
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it("forwards typed characters to onQueryChange", async () => {
|
|
116
|
-
const onQueryChange = vi.fn();
|
|
117
|
-
const { stdin } = render(
|
|
118
|
-
<ResourcePicker items={ITEMS} query="" onQueryChange={onQueryChange} onSelect={noop} onCancel={noop} />,
|
|
119
|
-
);
|
|
120
|
-
stdin.write("a");
|
|
121
|
-
await tick();
|
|
122
|
-
expect(onQueryChange).toHaveBeenCalledWith("a");
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("does not select when Enter is pressed on an empty list", async () => {
|
|
126
|
-
const onSelect = vi.fn();
|
|
127
|
-
const { stdin, lastFrame } = render(
|
|
128
|
-
<ResourcePicker items={[]} query="zzz" onQueryChange={noop} onSelect={onSelect} onCancel={noop} />,
|
|
129
|
-
);
|
|
130
|
-
expect(lastFrame() ?? "").toContain("no results");
|
|
131
|
-
stdin.write(KEY.enter);
|
|
132
|
-
await tick();
|
|
133
|
-
expect(onSelect).not.toHaveBeenCalled();
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it("shows a loading state while searching", () => {
|
|
137
|
-
const { lastFrame } = render(
|
|
138
|
-
<ResourcePicker items={[]} query="x" onQueryChange={noop} onSelect={noop} onCancel={noop} isLoading />,
|
|
139
|
-
);
|
|
140
|
-
expect(lastFrame() ?? "").toContain("searching...");
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it("shows an error state in place of the list", () => {
|
|
144
|
-
const { lastFrame } = render(
|
|
145
|
-
<ResourcePicker
|
|
146
|
-
items={[]}
|
|
147
|
-
query="x"
|
|
148
|
-
onQueryChange={noop}
|
|
149
|
-
onSelect={noop}
|
|
150
|
-
onCancel={noop}
|
|
151
|
-
error={new Error("backend unreachable")}
|
|
152
|
-
/>,
|
|
153
|
-
);
|
|
154
|
-
expect(lastFrame() ?? "").toContain("Error: backend unreachable");
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("scrolls the visible window to keep the active row in view", async () => {
|
|
158
|
-
const many: PickerItem[] = Array.from({ length: 14 }, (_, i) => ({
|
|
159
|
-
id: `id_${i}`,
|
|
160
|
-
title: `item-${i}`,
|
|
161
|
-
}));
|
|
162
|
-
const { stdin, lastFrame } = render(
|
|
163
|
-
<ResourcePicker items={many} query="" onQueryChange={noop} onSelect={noop} onCancel={noop} />,
|
|
164
|
-
);
|
|
165
|
-
// Initially the top of the list is visible, the tail is not.
|
|
166
|
-
expect(lastFrame() ?? "").toContain("item-0");
|
|
167
|
-
expect(lastFrame() ?? "").not.toContain("item-13");
|
|
168
|
-
// Drive the cursor to the bottom; the window should scroll to reveal it.
|
|
169
|
-
for (let i = 0; i < 13; i++) {
|
|
170
|
-
stdin.write(KEY.down);
|
|
171
|
-
await tick();
|
|
172
|
-
}
|
|
173
|
-
expect(lastFrame() ?? "").toContain("item-13");
|
|
174
|
-
expect(lastFrame() ?? "").not.toContain("item-0");
|
|
175
|
-
}, 20000);
|
|
176
|
-
});
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import React, { type ReactElement } from "react";
|
|
2
|
-
import { render } from "ink-testing-library";
|
|
3
|
-
import type { Stigmer } from "@stigmer/sdk";
|
|
4
|
-
import { InkStigmerProvider } from "../provider.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Builds a minimal Stigmer stub exposing only the methods a test's hooks call.
|
|
8
|
-
* Anything not overridden is absent, so a test that touches an unstubbed method
|
|
9
|
-
* fails loudly rather than silently no-op'ing.
|
|
10
|
-
*/
|
|
11
|
-
export function fakeClient(overrides: Record<string, unknown> = {}): Stigmer {
|
|
12
|
-
return overrides as unknown as Stigmer;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Renders a tree under an {@link InkStigmerProvider} so hooks that read the
|
|
17
|
-
* Stigmer client (e.g. `useFileChangeContent` → `useStigmer`, which throws
|
|
18
|
-
* without a provider) work. Returns the `ink-testing-library` handle.
|
|
19
|
-
*
|
|
20
|
-
* The explicit return type is load-bearing: ink-testing-library does not
|
|
21
|
-
* export its `Instance` type, and its stdout/stderr classes carry private
|
|
22
|
-
* members — under `declaration: true` an inferred return type here fails
|
|
23
|
-
* declaration emit with TS4094 (`ReturnType<typeof render>` stays a named
|
|
24
|
-
* reference the emitter never has to expand).
|
|
25
|
-
*/
|
|
26
|
-
export function renderWithClient(
|
|
27
|
-
node: ReactElement,
|
|
28
|
-
client: Stigmer = fakeClient(),
|
|
29
|
-
): ReturnType<typeof render> {
|
|
30
|
-
return render(<InkStigmerProvider client={client}>{node}</InkStigmerProvider>);
|
|
31
|
-
}
|