@cueloop/client 0.1.0-alpha.68 → 0.1.0-alpha.69
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/package.json +7 -6
- package/src/App.appearance.test.tsx +8 -6
- package/src/App.diff.test.tsx +3 -3
- package/src/App.export.test.tsx +9 -3
- package/src/App.plan-review.test.tsx +10 -8
- package/src/App.prototype-markdown.test.tsx +92 -0
- package/src/App.prototype.test.tsx +17 -28
- package/src/App.readonly.test.tsx +3 -3
- package/src/App.rendering.test.tsx +3 -4
- package/src/App.test.tsx +14 -18
- package/src/App.tsx +117 -42
- package/src/App.walk.test.tsx +2 -2
- package/src/App.workbench.test.tsx +85 -14
- package/src/app-screens.tsx +26 -10
- package/src/app-view-model.ts +17 -3
- package/src/components/AnnotatableFileView.stories.tsx +42 -0
- package/src/components/AnnotatableFileView.test.tsx +102 -0
- package/src/components/AnnotatableFileView.tsx +126 -0
- package/src/components/AnnotationCards.tsx +6 -6
- package/src/components/AppShell.tsx +62 -15
- package/src/components/BareWorkbenchFileView.stories.tsx +27 -0
- package/src/components/BareWorkbenchFileView.tsx +69 -0
- package/src/components/ChangesColumn.tsx +3 -2
- package/src/components/{DiffSheet.scroll.test.tsx → DiffContentView.scroll.test.tsx} +2 -2
- package/src/components/{DiffSheet.split.test.tsx → DiffContentView.split.test.tsx} +3 -3
- package/src/components/{DiffSheet.stories.tsx → DiffContentView.stories.tsx} +3 -3
- package/src/components/{DiffSheet.syntax.test.tsx → DiffContentView.syntax.test.tsx} +2 -2
- package/src/components/{DiffSheet.tsx → DiffContentView.tsx} +187 -28
- package/src/components/DiscussionMarkerRail.tsx +2 -2
- package/src/components/EditorGrid.tabs.test.tsx +17 -4
- package/src/components/EditorGrid.tsx +49 -19
- package/src/components/FileContentsView.test.tsx +3 -3
- package/src/components/FileContentsView.tsx +37 -17
- package/src/components/{SurfaceScrollbar.stories.tsx → OverlayScrollbar.stories.tsx} +5 -6
- package/src/components/{SurfaceScrollbar.test.tsx → OverlayScrollbar.test.tsx} +41 -13
- package/src/components/{SurfaceScrollbar.tsx → OverlayScrollbar.tsx} +49 -13
- package/src/components/ProjectTreeView.tsx +3 -2
- package/src/components/{PrototypeSheet.stories.tsx → PrototypeContentView.stories.tsx} +4 -5
- package/src/components/{PrototypeSheet.tsx → PrototypeContentView.tsx} +58 -58
- package/src/components/ScrollArea.stories.tsx +20 -0
- package/src/components/ScrollArea.tsx +44 -0
- package/src/components/SettingsDialog.tsx +3 -2
- package/src/components/{InboxList.stories.tsx → ThreadTree.stories.tsx} +5 -5
- package/src/components/{InboxList.tsx → ThreadTree.tsx} +6 -6
- package/src/components/ThreadsSidebar.tsx +5 -4
- package/src/components/TreePane.tsx +3 -2
- package/src/components/WelcomePlayground.stories.tsx +12 -0
- package/src/components/WelcomePlayground.test.tsx +47 -0
- package/src/components/WelcomePlayground.tsx +140 -0
- package/src/components/__snapshots__/stories.test.tsx.snap +163 -283
- package/src/components/{diff-sheet-layout.test.ts → diff-content-view-layout.test.ts} +1 -1
- package/src/components/{diff-sheet-layout.ts → diff-content-view-layout.ts} +1 -1
- package/src/config.test.ts +19 -6
- package/src/config.ts +26 -6
- package/src/intent-dispatch.test.ts +2 -0
- package/src/intent-dispatch.ts +1 -1
- package/src/key-bindings.test.ts +1 -1
- package/src/key-bindings.ts +5 -1
- package/src/keymap.ts +1 -1
- package/src/prototype-browser.ts +8 -0
- package/src/prototype-pixels.tsx +35 -0
- package/src/ready-signal.test.ts +85 -0
- package/src/ready-signal.ts +52 -0
- package/src/session-controller.live-diff.test.ts +198 -0
- package/src/session-controller.ts +141 -26
- package/src/slash-palette.test.ts +54 -0
- package/src/slash-palette.ts +32 -18
- package/src/test-support.ts +42 -3
- package/src/theme-presets.ts +20 -0
- package/src/theme.ts +18 -0
- package/src/thread-chords.ts +1 -1
- package/src/use-annotation-surface.tsx +12 -2
- package/src/use-settings-dialog.tsx +4 -4
- package/src/view-diff.test.ts +44 -1
- package/src/view-diff.ts +81 -0
- package/src/components/AnnotationCard.compose-rows.test.ts +0 -49
- package/src/components/AnnotationCard.stories.tsx +0 -94
- package/src/components/AnnotationCard.tsx +0 -250
- package/src/components/MarkerPopover.stories.tsx +0 -58
- package/src/components/MarkerPopover.tsx +0 -109
- package/src/components/WelcomeSurface.stories.tsx +0 -12
- package/src/components/WelcomeSurface.tsx +0 -52
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* stories catalog) drive it without a second key handler inside.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { ScrollArea } from "./ScrollArea";
|
|
10
11
|
import React from "react";
|
|
11
12
|
import { useTerminalDimensions } from "@opentui/react";
|
|
12
13
|
import type { Theme } from "../theme";
|
|
@@ -68,7 +69,7 @@ function KeybindsBody({
|
|
|
68
69
|
tokens: Theme;
|
|
69
70
|
}): React.ReactNode {
|
|
70
71
|
return (
|
|
71
|
-
<
|
|
72
|
+
<ScrollArea>
|
|
72
73
|
{sections.map((section) => (
|
|
73
74
|
<box key={section.title} style={{ flexDirection: "column" }}>
|
|
74
75
|
<text fg={tokens.accent}>{section.title}</text>
|
|
@@ -81,7 +82,7 @@ function KeybindsBody({
|
|
|
81
82
|
<text> </text>
|
|
82
83
|
</box>
|
|
83
84
|
))}
|
|
84
|
-
</
|
|
85
|
+
</ScrollArea>
|
|
85
86
|
);
|
|
86
87
|
}
|
|
87
88
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { DARK } from "../theme";
|
|
3
3
|
import type { Story, StoryMeta } from "./story";
|
|
4
|
-
import {
|
|
4
|
+
import { ThreadTree } from "./ThreadTree";
|
|
5
5
|
import { groupInbox } from "./session-tree";
|
|
6
6
|
import { fixtureDiffSession, fixturePlanSession } from "./story-fixtures";
|
|
7
7
|
|
|
8
|
-
export const meta: StoryMeta = { title: "Surfaces/
|
|
8
|
+
export const meta: StoryMeta = { title: "Surfaces/ThreadTree" };
|
|
9
9
|
|
|
10
|
-
/**
|
|
10
|
+
/** ThreadTree is the body under App's header/menu chrome; a frame gives it height. */
|
|
11
11
|
function BodyFrame({ children }: { children: React.ReactNode }): React.ReactNode {
|
|
12
12
|
return <box style={{ flexDirection: "column", width: "100%", height: "100%" }}>{children}</box>;
|
|
13
13
|
}
|
|
@@ -28,7 +28,7 @@ export const TwoPending: Story = {
|
|
|
28
28
|
|
|
29
29
|
return (
|
|
30
30
|
<BodyFrame>
|
|
31
|
-
<
|
|
31
|
+
<ThreadTree rows={rows} cursor={0} />
|
|
32
32
|
</BodyFrame>
|
|
33
33
|
);
|
|
34
34
|
},
|
|
@@ -39,7 +39,7 @@ export const TwoPending: Story = {
|
|
|
39
39
|
export const Empty: Story = {
|
|
40
40
|
render: () => (
|
|
41
41
|
<BodyFrame>
|
|
42
|
-
<
|
|
42
|
+
<ThreadTree rows={[]} cursor={0} />
|
|
43
43
|
</BodyFrame>
|
|
44
44
|
),
|
|
45
45
|
size: { width: 80, height: 8 },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
2
|
+
* The sidebar thread tree: a Pinned / Projects / Threads listing. Selection stays
|
|
3
3
|
* with the keyboard grammar - the cursor indexes the flat thread order and this
|
|
4
4
|
* component renders the snapshot. A hovered or selected thread reveals a kebab
|
|
5
5
|
* that opens an inline pin / rename / delete menu; long titles fade on the right
|
|
@@ -14,7 +14,7 @@ import { NERD } from "./primitives/icons";
|
|
|
14
14
|
import { fadeTitle } from "./fade-title";
|
|
15
15
|
import { threadTitle, type InboxRow } from "./session-tree";
|
|
16
16
|
|
|
17
|
-
export interface
|
|
17
|
+
export interface ThreadTreeProps {
|
|
18
18
|
rows: InboxRow[];
|
|
19
19
|
cursor: number;
|
|
20
20
|
/** The open thread's id; highlights it instead of the cursor (the left sidebar case). */
|
|
@@ -148,7 +148,7 @@ function ThreadRow(props: ThreadRowProps): React.ReactNode {
|
|
|
148
148
|
);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
export function
|
|
151
|
+
export function ThreadTree({
|
|
152
152
|
rows,
|
|
153
153
|
cursor,
|
|
154
154
|
activeId,
|
|
@@ -159,7 +159,7 @@ export function InboxList({
|
|
|
159
159
|
onPin,
|
|
160
160
|
onRename,
|
|
161
161
|
theme,
|
|
162
|
-
}:
|
|
162
|
+
}: ThreadTreeProps): React.ReactNode {
|
|
163
163
|
const tokens = useComponentTheme(theme);
|
|
164
164
|
const [actionsForId, setActionsForId] = useState<string | null>(null);
|
|
165
165
|
// leave room for the 3-char prefix, the border, and the hover kebab so a row
|
|
@@ -168,9 +168,9 @@ export function InboxList({
|
|
|
168
168
|
const closeMenu = (): void => setActionsForId(null);
|
|
169
169
|
|
|
170
170
|
return (
|
|
171
|
-
<box style={{ flexGrow: 1, flexDirection: "column", paddingLeft: 1
|
|
171
|
+
<box style={{ flexGrow: 1, flexDirection: "column", paddingLeft: 1 }}>
|
|
172
172
|
{rows.length === 0 ? (
|
|
173
|
-
<text fg={tokens.textDim}>
|
|
173
|
+
<text fg={tokens.textDim}>no threads</text>
|
|
174
174
|
) : (
|
|
175
175
|
rows.map((row) => {
|
|
176
176
|
if (row.kind === "section") {
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// thread. A click jumps to that thread; the open thread stays highlighted. Renders
|
|
3
3
|
// nothing when collapsed so the thread reclaims the width.
|
|
4
4
|
|
|
5
|
+
import { ScrollArea } from "./ScrollArea";
|
|
5
6
|
import React from "react";
|
|
6
7
|
import type { Theme } from "../theme";
|
|
7
|
-
import {
|
|
8
|
+
import { ThreadTree } from "./ThreadTree";
|
|
8
9
|
import type { InboxRow } from "./session-tree";
|
|
9
10
|
|
|
10
11
|
export interface ThreadsSidebarProps {
|
|
@@ -51,8 +52,8 @@ export function ThreadsSidebar({
|
|
|
51
52
|
borderColor: theme?.border,
|
|
52
53
|
}}
|
|
53
54
|
>
|
|
54
|
-
<
|
|
55
|
-
<
|
|
55
|
+
<ScrollArea>
|
|
56
|
+
<ThreadTree
|
|
56
57
|
rows={rows}
|
|
57
58
|
cursor={cursor}
|
|
58
59
|
activeId={activeId}
|
|
@@ -64,7 +65,7 @@ export function ThreadsSidebar({
|
|
|
64
65
|
onRename={onRename}
|
|
65
66
|
theme={theme}
|
|
66
67
|
/>
|
|
67
|
-
</
|
|
68
|
+
</ScrollArea>
|
|
68
69
|
</box>
|
|
69
70
|
);
|
|
70
71
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* path to the same primitives the option chords reach.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { ScrollArea } from "./ScrollArea";
|
|
9
10
|
import React, { useEffect, useRef } from "react";
|
|
10
11
|
import { createTextAttributes, type ScrollBoxRenderable } from "@opentui/core";
|
|
11
12
|
import type { Theme } from "../theme";
|
|
@@ -84,7 +85,7 @@ export function TreePane({
|
|
|
84
85
|
|
|
85
86
|
return (
|
|
86
87
|
<box style={{ flexGrow: 1, flexDirection: "column" }}>
|
|
87
|
-
<
|
|
88
|
+
<ScrollArea scrollRef={scrollRef}>
|
|
88
89
|
{rows.map((row) => {
|
|
89
90
|
const selected = row.entryId === selectedEntryId;
|
|
90
91
|
const foreground = row.onPath ? tokens.text : tokens.textDim;
|
|
@@ -112,7 +113,7 @@ export function TreePane({
|
|
|
112
113
|
</box>
|
|
113
114
|
);
|
|
114
115
|
})}
|
|
115
|
-
</
|
|
116
|
+
</ScrollArea>
|
|
116
117
|
{canMove || canFork ? (
|
|
117
118
|
<box style={{ paddingLeft: 1 }}>
|
|
118
119
|
<Toolbar>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DARK } from "../theme";
|
|
3
|
+
import type { Story, StoryMeta } from "./story";
|
|
4
|
+
import { WelcomePlayground } from "./WelcomePlayground";
|
|
5
|
+
|
|
6
|
+
export const meta: StoryMeta = { title: "Surfaces/WelcomePlayground" };
|
|
7
|
+
|
|
8
|
+
export const Welcome: Story = {
|
|
9
|
+
render: () => <WelcomePlayground version="0.1.0-alpha.66" quickActions={[]} theme={DARK} />,
|
|
10
|
+
expectedColors: [DARK.textDim],
|
|
11
|
+
size: { width: 72, height: 20 },
|
|
12
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The welcome page is a real annotation surface, but ephemeral: selecting its copy and typing leaves
|
|
3
|
+
* a comment that shows inline, exactly as a file does, and it lives only in the component - there is
|
|
4
|
+
* no controller or daemon behind it, so nothing is persisted. The copy prompts the two first gestures.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, expect, test } from "bun:test";
|
|
8
|
+
import { testRender } from "@opentui/react/test-utils";
|
|
9
|
+
import React from "react";
|
|
10
|
+
import { WelcomePlayground } from "./WelcomePlayground";
|
|
11
|
+
import { DARK } from "../theme";
|
|
12
|
+
import { dragText, typeText, pressKey, waitForText } from "../test-support";
|
|
13
|
+
|
|
14
|
+
describe("WelcomePlayground", () => {
|
|
15
|
+
test("the copy prompts the select-and-type and the slash gestures", async () => {
|
|
16
|
+
const setup = await testRender(
|
|
17
|
+
<WelcomePlayground version="1.2.3" quickActions={[]} theme={DARK} />,
|
|
18
|
+
{ width: 80, height: 24 },
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
await waitForText(setup, "Getting started");
|
|
22
|
+
const frame = setup.captureCharFrame();
|
|
23
|
+
|
|
24
|
+
expect(frame).toContain("start typing to leave your first comment");
|
|
25
|
+
expect(frame).toContain('type "/"');
|
|
26
|
+
|
|
27
|
+
setup.renderer.destroy();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("selecting the copy and typing leaves a comment inline, exactly like a file", async () => {
|
|
31
|
+
const setup = await testRender(
|
|
32
|
+
<WelcomePlayground version="1.2.3" quickActions={[]} theme={DARK} />,
|
|
33
|
+
{ width: 80, height: 24 },
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Act - select the practice line, type a comment, send it
|
|
37
|
+
await waitForText(setup, "quick brown fox");
|
|
38
|
+
await dragText(setup, "quick brown fox", "quick brown fox", "quick brown fox".length);
|
|
39
|
+
await typeText(setup, "my first note");
|
|
40
|
+
await pressKey(setup, "RETURN", { meta: true });
|
|
41
|
+
|
|
42
|
+
// Assert - the note renders on the surface, no controller or daemon involved
|
|
43
|
+
await waitForText(setup, "my first note");
|
|
44
|
+
|
|
45
|
+
setup.renderer.destroy();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// The welcome page as a live annotation playground: the onboarding copy renders through the same
|
|
2
|
+
// diff sheet a file uses, so selecting text and typing leaves a comment with the identical gesture a
|
|
3
|
+
// real review uses. The notes are ephemeral - there is no file or thread behind onboarding copy, so
|
|
4
|
+
// they live only in this component's state and are never stored or re-anchored. The copy teaches the
|
|
5
|
+
// two first gestures: select-and-type to comment, and "/" for the quick actions and skills.
|
|
6
|
+
|
|
7
|
+
import React, { useMemo, useState } from "react";
|
|
8
|
+
import {
|
|
9
|
+
makeAnchor,
|
|
10
|
+
newAnnotationId,
|
|
11
|
+
SCHEMA_VERSION,
|
|
12
|
+
type Annotation,
|
|
13
|
+
type ReviewSession,
|
|
14
|
+
} from "@cueloop/schema";
|
|
15
|
+
import type { Theme } from "../theme";
|
|
16
|
+
import type { QuickAction } from "../config";
|
|
17
|
+
import { DiffContentView } from "./DiffContentView";
|
|
18
|
+
import { diffRowBlocks, fileContentsRows, marksByRows } from "../view-diff";
|
|
19
|
+
|
|
20
|
+
export interface WelcomePlaygroundProps {
|
|
21
|
+
version: string;
|
|
22
|
+
quickActions: QuickAction[];
|
|
23
|
+
/** Reports whether the playground composer is open, so the shell suspends its inbox keys while typing. */
|
|
24
|
+
onComposingChange?: (composing: boolean) => void;
|
|
25
|
+
theme?: Theme;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const DOCS_URL = "https://cueloop.dev/docs/";
|
|
29
|
+
|
|
30
|
+
/** The playground copy: a heading, the two guided prompts, a line to practice on, and the links. */
|
|
31
|
+
function welcomeCopy(version: string): string {
|
|
32
|
+
return [
|
|
33
|
+
"Getting started",
|
|
34
|
+
"Comment on agent-authored work.",
|
|
35
|
+
"",
|
|
36
|
+
"This page is a playground. Nothing you write here is saved.",
|
|
37
|
+
"",
|
|
38
|
+
"Select the line below and start typing to leave your first comment:",
|
|
39
|
+
"",
|
|
40
|
+
" The quick brown fox jumps over the lazy dog.",
|
|
41
|
+
"",
|
|
42
|
+
'While composing, type "/" to see the quick actions and skills.',
|
|
43
|
+
"",
|
|
44
|
+
"Start",
|
|
45
|
+
" - select a thread",
|
|
46
|
+
" - inspect files and changes",
|
|
47
|
+
"",
|
|
48
|
+
"Learn",
|
|
49
|
+
` - docs ${DOCS_URL}`,
|
|
50
|
+
"",
|
|
51
|
+
"Version",
|
|
52
|
+
` - cueloop v${version}`,
|
|
53
|
+
].join("\n");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** An ephemeral thread that holds nothing but the playground's notes, so the surface groups
|
|
57
|
+
* discussions exactly as a real review does without any of it reaching the daemon. */
|
|
58
|
+
function playgroundThread(annotations: Annotation[]): ReviewSession {
|
|
59
|
+
return {
|
|
60
|
+
schemaVersion: SCHEMA_VERSION,
|
|
61
|
+
id: "welcome",
|
|
62
|
+
workspace: { repoRoot: "", branch: "main" },
|
|
63
|
+
artifact: { type: "plan", content: "", meta: {} },
|
|
64
|
+
revisions: [],
|
|
65
|
+
annotations,
|
|
66
|
+
verdict: null,
|
|
67
|
+
status: "pending",
|
|
68
|
+
createdAt: "1970-01-01T00:00:00.000Z",
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function WelcomePlayground(props: WelcomePlaygroundProps): React.ReactNode {
|
|
73
|
+
const [annotations, setAnnotations] = useState<Annotation[]>([]);
|
|
74
|
+
const rows = useMemo(
|
|
75
|
+
() => fileContentsRows("welcome", welcomeCopy(props.version)),
|
|
76
|
+
[props.version],
|
|
77
|
+
);
|
|
78
|
+
const session = playgroundThread(annotations);
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<DiffContentView
|
|
82
|
+
rows={rows}
|
|
83
|
+
session={session}
|
|
84
|
+
marks={marksByRows(annotations, rows)}
|
|
85
|
+
quickActions={props.quickActions}
|
|
86
|
+
observer={false}
|
|
87
|
+
commentsEnabled
|
|
88
|
+
fileView
|
|
89
|
+
onComposingChange={props.onComposingChange}
|
|
90
|
+
onAnnotate={(span, body) =>
|
|
91
|
+
setAnnotations((current) => [
|
|
92
|
+
...current,
|
|
93
|
+
{
|
|
94
|
+
id: newAnnotationId(),
|
|
95
|
+
kind: "comment",
|
|
96
|
+
anchor: makeAnchor(
|
|
97
|
+
diffRowBlocks(rows),
|
|
98
|
+
span.start.blockIndex,
|
|
99
|
+
span.start.char,
|
|
100
|
+
span.end.char,
|
|
101
|
+
span.end.blockIndex,
|
|
102
|
+
),
|
|
103
|
+
target: { kind: "welcome" },
|
|
104
|
+
body,
|
|
105
|
+
createdAt: new Date().toISOString(),
|
|
106
|
+
},
|
|
107
|
+
])
|
|
108
|
+
}
|
|
109
|
+
onReply={(rootAnnotationId, body) =>
|
|
110
|
+
setAnnotations((current) => {
|
|
111
|
+
const root = current.find((annotation) => annotation.id === rootAnnotationId);
|
|
112
|
+
|
|
113
|
+
if (!root) return current;
|
|
114
|
+
|
|
115
|
+
return [
|
|
116
|
+
...current,
|
|
117
|
+
{
|
|
118
|
+
id: newAnnotationId(),
|
|
119
|
+
kind: "comment",
|
|
120
|
+
anchor: root.anchor,
|
|
121
|
+
target: { kind: "welcome" },
|
|
122
|
+
body,
|
|
123
|
+
replyTo: rootAnnotationId,
|
|
124
|
+
createdAt: new Date().toISOString(),
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
onUpdateAnnotation={(id, body) =>
|
|
130
|
+
setAnnotations((current) =>
|
|
131
|
+
current.map((annotation) =>
|
|
132
|
+
annotation.id === id ? { ...annotation, body } : annotation,
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
onExit={() => {}}
|
|
137
|
+
theme={props.theme}
|
|
138
|
+
/>
|
|
139
|
+
);
|
|
140
|
+
}
|