@grackle-ai/web-components 0.115.2 → 0.117.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/.rush/temp/{49e5384757b767ffca6c218faf139f6813911f4a.untar.log → 8d9be4152bfcbf796b578ac87621b34484202bd0.untar.log} +2 -2
- package/.rush/temp/{b32d9c7748f6c2c43df816a4bdd427ae0c7f1e32.untar.log → be1751e9cb123b206e39fdb59b24fd82523d77e2.untar.log} +2 -2
- package/.rush/temp/operation/_phase_build/all.log +13 -6
- package/.rush/temp/operation/_phase_build/log-chunks.jsonl +13 -6
- package/.rush/temp/operation/_phase_build/state.json +1 -1
- package/.rush/temp/operation/_phase_test/all.log +29 -28
- package/.rush/temp/operation/_phase_test/log-chunks.jsonl +29 -28
- package/.rush/temp/operation/_phase_test/state.json +1 -1
- package/.rush/temp/shrinkwrap-deps.json +13 -1
- package/README.md +3 -3
- package/config/rush-project.json +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +9068 -9498
- package/package.json +4 -2
- package/rush-logs/web-components._phase_build.cache.log +2 -2
- package/rush-logs/web-components._phase_test.cache.log +1 -1
- package/src/components/display/EventRenderer.stories.tsx +22 -0
- package/src/components/display/EventRenderer.tsx +28 -4
- package/src/components/index.ts +0 -3
- package/src/components/knowledge/KnowledgeDetailPanel.tsx +1 -4
- package/src/components/layout/AppNav.stories.tsx +3 -6
- package/src/components/layout/AppNav.tsx +3 -7
- package/src/components/layout/BottomStatusBar.tsx +4 -6
- package/src/components/lists/index.ts +0 -1
- package/src/components/panels/KeyboardShortcutsPanel.tsx +0 -1
- package/src/components/panels/index.ts +0 -1
- package/src/components/personas/McpToolSelector.stories.tsx +12 -12
- package/src/components/tools/ToolCard.stories.tsx +0 -26
- package/src/components/tools/ToolCard.tsx +0 -3
- package/src/components/tools/ToolSearchCard.stories.tsx +8 -8
- package/src/components/tools/WorkpadCard.stories.tsx +5 -5
- package/src/components/tools/classifyTool.test.ts +0 -1
- package/src/components/tools/classifyTool.ts +2 -7
- package/src/context/GrackleContextTypes.ts +1 -3
- package/src/hooks/types.ts +1 -44
- package/src/index.ts +4 -8
- package/src/mcp-runtime/index.tsx +99 -0
- package/src/mocks/MockGrackleProvider.tsx +0 -75
- package/src/mocks/mockData.ts +8 -99
- package/src/test-utils/storybook-helpers.ts +0 -19
- package/src/utils/breadcrumbs.test.ts +0 -43
- package/src/utils/breadcrumbs.ts +1 -37
- package/src/utils/navigation.ts +1 -20
- package/src/utils/route-config.test.ts +0 -31
- package/vite.config.ts +46 -2
- package/.rush/temp/49e5384757b767ffca6c218faf139f6813911f4a.tar.log +0 -12
- package/.rush/temp/b32d9c7748f6c2c43df816a4bdd427ae0c7f1e32.tar.log +0 -236
- package/.rush/temp/chunked-rush-logs/web-components._phase_build.chunks.jsonl +0 -19
- package/.rush/temp/chunked-rush-logs/web-components._phase_test.chunks.jsonl +0 -126
- package/rush-logs/web-components._phase_build.log +0 -19
- package/rush-logs/web-components._phase_test.log +0 -126
- package/src/components/lists/FindingsNav.module.scss +0 -126
- package/src/components/lists/FindingsNav.tsx +0 -146
- package/src/components/panels/FindingsPanel.module.scss +0 -94
- package/src/components/panels/FindingsPanel.stories.tsx +0 -109
- package/src/components/panels/FindingsPanel.tsx +0 -76
- package/src/components/tools/FindingCard.stories.tsx +0 -124
- package/src/components/tools/FindingCard.tsx +0 -178
- package/src/utils/findingCategory.ts +0 -33
- package/temp/build/lint/_eslint-5eVG3S6w.json +0 -850
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { expect } from "@storybook/test";
|
|
3
|
-
import { FindingCard } from "./FindingCard.js";
|
|
4
|
-
|
|
5
|
-
const meta: Meta<typeof FindingCard> = {
|
|
6
|
-
component: FindingCard,
|
|
7
|
-
title: "Tools/FindingCard",
|
|
8
|
-
};
|
|
9
|
-
export default meta;
|
|
10
|
-
type Story = StoryObj<typeof FindingCard>;
|
|
11
|
-
|
|
12
|
-
export const PostInProgress: Story = {
|
|
13
|
-
name: "finding_post - in progress",
|
|
14
|
-
args: {
|
|
15
|
-
tool: "mcp__grackle__finding_post",
|
|
16
|
-
args: {
|
|
17
|
-
title: "Auth middleware stores tokens insecurely",
|
|
18
|
-
category: "bug",
|
|
19
|
-
tags: ["security", "auth"],
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
play: async ({ canvas }) => {
|
|
23
|
-
await expect(canvas.getByTestId("tool-card-finding")).toBeInTheDocument();
|
|
24
|
-
await expect(canvas.getByTestId("tool-card-finding-title")).toHaveTextContent("Auth middleware");
|
|
25
|
-
await expect(canvas.getByTestId("tool-card-finding-category")).toHaveTextContent("bug");
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export const PostCompleted: Story = {
|
|
30
|
-
name: "finding_post - completed",
|
|
31
|
-
args: {
|
|
32
|
-
tool: "mcp__grackle__finding_post",
|
|
33
|
-
args: {
|
|
34
|
-
title: "Qdrant catalog naming convention",
|
|
35
|
-
category: "insight",
|
|
36
|
-
tags: ["search", "worktree", "qdrant"],
|
|
37
|
-
},
|
|
38
|
-
result: JSON.stringify({
|
|
39
|
-
id: "589f1e83",
|
|
40
|
-
workspaceId: "default",
|
|
41
|
-
category: "insight",
|
|
42
|
-
title: "Qdrant catalog naming convention",
|
|
43
|
-
content: "The qdrant-search MCP server indexes the codebase under the catalog name \"grackle\". All worktrees share the same codebase, so every semantic search call must pass catalog: \"grackle\".",
|
|
44
|
-
tags: ["search", "worktree", "qdrant"],
|
|
45
|
-
createdAt: "2026-03-28 03:49:15",
|
|
46
|
-
}),
|
|
47
|
-
},
|
|
48
|
-
play: async ({ canvas }) => {
|
|
49
|
-
await expect(canvas.getByTestId("tool-card-finding")).toBeInTheDocument();
|
|
50
|
-
await expect(canvas.getByTestId("tool-card-finding-category")).toHaveTextContent("insight");
|
|
51
|
-
await expect(canvas.getByTestId("tool-card-finding-tags")).toBeInTheDocument();
|
|
52
|
-
await expect(canvas.getByTestId("tool-card-finding-content")).toBeInTheDocument();
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export const PostCopilotFormat: Story = {
|
|
57
|
-
name: "finding_post - Copilot tool name",
|
|
58
|
-
args: {
|
|
59
|
-
tool: "grackle-finding_post",
|
|
60
|
-
args: {
|
|
61
|
-
title: "Rush worktree usage",
|
|
62
|
-
category: "insight",
|
|
63
|
-
tags: ["workflow"],
|
|
64
|
-
},
|
|
65
|
-
result: JSON.stringify({
|
|
66
|
-
id: "e7091ea6",
|
|
67
|
-
category: "insight",
|
|
68
|
-
title: "Rush worktree usage",
|
|
69
|
-
content: "This codebase uses Rush worktrees for all feature development.",
|
|
70
|
-
tags: ["workflow"],
|
|
71
|
-
createdAt: "2026-03-28 03:53:07",
|
|
72
|
-
}),
|
|
73
|
-
},
|
|
74
|
-
play: async ({ canvas }) => {
|
|
75
|
-
await expect(canvas.getByTestId("tool-card-finding")).toBeInTheDocument();
|
|
76
|
-
// Should show bare tool name, not the full prefixed name
|
|
77
|
-
await expect(canvas.getByText("finding_post")).toBeInTheDocument();
|
|
78
|
-
},
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
export const ListWithResults: Story = {
|
|
82
|
-
name: "finding_list - multiple results",
|
|
83
|
-
args: {
|
|
84
|
-
tool: "mcp__grackle__finding_list",
|
|
85
|
-
args: { limit: 20 },
|
|
86
|
-
result: JSON.stringify([
|
|
87
|
-
{ id: "f1", category: "insight", title: "Qdrant catalog naming" },
|
|
88
|
-
{ id: "f2", category: "bug", title: "Auth token storage issue" },
|
|
89
|
-
{ id: "f3", category: "decision", title: "Use ConnectRPC over ws-bridge" },
|
|
90
|
-
]),
|
|
91
|
-
},
|
|
92
|
-
play: async ({ canvas }) => {
|
|
93
|
-
await expect(canvas.getByTestId("tool-card-finding")).toBeInTheDocument();
|
|
94
|
-
await expect(canvas.getByTestId("tool-card-finding-count")).toHaveTextContent("3 findings");
|
|
95
|
-
await expect(canvas.getByTestId("tool-card-finding-list")).toBeInTheDocument();
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
export const ListEmpty: Story = {
|
|
100
|
-
name: "finding_list - no results",
|
|
101
|
-
args: {
|
|
102
|
-
tool: "mcp__grackle__finding_list",
|
|
103
|
-
args: { category: "bug" },
|
|
104
|
-
result: JSON.stringify([]),
|
|
105
|
-
},
|
|
106
|
-
play: async ({ canvas }) => {
|
|
107
|
-
await expect(canvas.getByTestId("tool-card-finding")).toBeInTheDocument();
|
|
108
|
-
await expect(canvas.getByTestId("tool-card-finding-count")).toHaveTextContent("0 findings");
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
export const ErrorState: Story = {
|
|
113
|
-
name: "finding_post - error",
|
|
114
|
-
args: {
|
|
115
|
-
tool: "mcp__grackle__finding_post",
|
|
116
|
-
args: { title: "Test finding" },
|
|
117
|
-
result: "gRPC error [Internal]: database connection failed",
|
|
118
|
-
isError: true,
|
|
119
|
-
},
|
|
120
|
-
play: async ({ canvas }) => {
|
|
121
|
-
await expect(canvas.getByTestId("tool-card-finding")).toBeInTheDocument();
|
|
122
|
-
await expect(canvas.getByTestId("tool-card-error")).toBeInTheDocument();
|
|
123
|
-
},
|
|
124
|
-
};
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import { useState, type JSX } from "react";
|
|
2
|
-
import type { ToolCardProps } from "./ToolCardProps.js";
|
|
3
|
-
import { extractBareName } from "./classifyTool.js";
|
|
4
|
-
import styles from "./toolCards.module.scss";
|
|
5
|
-
|
|
6
|
-
/** Shape of a single finding in MCP results. */
|
|
7
|
-
interface Finding {
|
|
8
|
-
id?: string;
|
|
9
|
-
title?: string;
|
|
10
|
-
category?: string;
|
|
11
|
-
content?: string;
|
|
12
|
-
tags?: string[];
|
|
13
|
-
createdAt?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** Extracts finding-relevant fields from tool args. */
|
|
17
|
-
function getArgs(args: unknown): { title?: string; category?: string; tags?: string[] } {
|
|
18
|
-
if (args === null || args === undefined || typeof args !== "object") {
|
|
19
|
-
return {};
|
|
20
|
-
}
|
|
21
|
-
const a = args as Record<string, unknown>;
|
|
22
|
-
return {
|
|
23
|
-
title: typeof a.title === "string" ? a.title : undefined,
|
|
24
|
-
category: typeof a.category === "string" ? a.category : undefined,
|
|
25
|
-
tags: Array.isArray(a.tags) ? (a.tags as string[]) : undefined,
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Parses MCP result JSON into a finding or array of findings. */
|
|
30
|
-
function parseResult(result: string | undefined): { single?: Finding; list?: Finding[] } {
|
|
31
|
-
if (!result) {
|
|
32
|
-
return {};
|
|
33
|
-
}
|
|
34
|
-
try {
|
|
35
|
-
const parsed: unknown = JSON.parse(result);
|
|
36
|
-
if (Array.isArray(parsed)) {
|
|
37
|
-
return { list: (parsed as unknown[]).filter((v): v is Finding => v !== null && typeof v === "object") };
|
|
38
|
-
}
|
|
39
|
-
if (typeof parsed === "object" && parsed !== null) {
|
|
40
|
-
return { single: parsed as Finding };
|
|
41
|
-
}
|
|
42
|
-
} catch { /* fall through */ }
|
|
43
|
-
return {};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** Number of items shown when collapsed. */
|
|
47
|
-
const PREVIEW_COUNT: number = 5;
|
|
48
|
-
|
|
49
|
-
/** Number of content lines shown when collapsed. */
|
|
50
|
-
const PREVIEW_LINES: number = 5;
|
|
51
|
-
|
|
52
|
-
/** Renders a finding tool call (finding_post, finding_list) with structured display. */
|
|
53
|
-
export function FindingCard({ tool, args, result, isError }: ToolCardProps): JSX.Element {
|
|
54
|
-
const [expanded, setExpanded] = useState(false);
|
|
55
|
-
const bareName = extractBareName(tool);
|
|
56
|
-
const argData = getArgs(args);
|
|
57
|
-
const inProgress = result === undefined;
|
|
58
|
-
const { single, list } = parseResult(result);
|
|
59
|
-
|
|
60
|
-
// Determine title to show in header
|
|
61
|
-
const displayTitle = single?.title ?? argData.title;
|
|
62
|
-
// Only show category badge for single findings, not when displaying a list
|
|
63
|
-
const displayCategory = list ? undefined : (single?.category ?? argData.category);
|
|
64
|
-
const displayTags = list ? undefined : (single?.tags ?? argData.tags);
|
|
65
|
-
|
|
66
|
-
return (
|
|
67
|
-
<div
|
|
68
|
-
className={`${styles.card} ${isError ? styles.cardRed : styles.cardPurple} ${inProgress ? styles.inProgress : ""}`}
|
|
69
|
-
data-testid="tool-card-finding"
|
|
70
|
-
>
|
|
71
|
-
<div className={styles.header}>
|
|
72
|
-
<span className={styles.icon} aria-hidden="true">💡</span>
|
|
73
|
-
<span className={styles.toolName} style={{ color: "var(--accent-purple, #a78bfa)" }}>
|
|
74
|
-
{bareName}
|
|
75
|
-
</span>
|
|
76
|
-
{displayTitle && (
|
|
77
|
-
<span className={styles.fileName} data-testid="tool-card-finding-title">
|
|
78
|
-
"{displayTitle}"
|
|
79
|
-
</span>
|
|
80
|
-
)}
|
|
81
|
-
{displayCategory && (
|
|
82
|
-
<>
|
|
83
|
-
<span className={styles.spacer} />
|
|
84
|
-
<span className={styles.badge} data-testid="tool-card-finding-category">
|
|
85
|
-
{displayCategory}
|
|
86
|
-
</span>
|
|
87
|
-
</>
|
|
88
|
-
)}
|
|
89
|
-
{list && !displayCategory && (
|
|
90
|
-
<>
|
|
91
|
-
<span className={styles.spacer} />
|
|
92
|
-
<span className={styles.badge} data-testid="tool-card-finding-count">
|
|
93
|
-
{list.length} {list.length === 1 ? "finding" : "findings"}
|
|
94
|
-
</span>
|
|
95
|
-
</>
|
|
96
|
-
)}
|
|
97
|
-
</div>
|
|
98
|
-
|
|
99
|
-
{/* Tags */}
|
|
100
|
-
{displayTags && displayTags.length > 0 && (
|
|
101
|
-
<div className={styles.pre} style={{ padding: "4px 8px", whiteSpace: "normal" }} data-testid="tool-card-finding-tags">
|
|
102
|
-
{displayTags.map((tag, i) => (
|
|
103
|
-
<span key={i} style={{ display: "inline-block", marginRight: "6px", opacity: 0.7, fontSize: "0.85em" }}>
|
|
104
|
-
#{tag}
|
|
105
|
-
</span>
|
|
106
|
-
))}
|
|
107
|
-
</div>
|
|
108
|
-
)}
|
|
109
|
-
|
|
110
|
-
{/* In-progress: show args summary */}
|
|
111
|
-
{inProgress && !displayTitle && args !== null && args !== undefined && (
|
|
112
|
-
<pre className={styles.pre} data-testid="tool-card-args">
|
|
113
|
-
{JSON.stringify(args, null, 2)}
|
|
114
|
-
</pre>
|
|
115
|
-
)}
|
|
116
|
-
|
|
117
|
-
{/* Error */}
|
|
118
|
-
{isError && result && (
|
|
119
|
-
<pre className={styles.pre} data-testid="tool-card-error">
|
|
120
|
-
{result}
|
|
121
|
-
</pre>
|
|
122
|
-
)}
|
|
123
|
-
|
|
124
|
-
{/* Single finding result: show content */}
|
|
125
|
-
{!isError && single?.content && (
|
|
126
|
-
<>
|
|
127
|
-
{(() => {
|
|
128
|
-
const lines = single.content.split("\n");
|
|
129
|
-
const hasMore = lines.length > PREVIEW_LINES;
|
|
130
|
-
const displayContent = expanded ? single.content : lines.slice(0, PREVIEW_LINES).join("\n");
|
|
131
|
-
return (
|
|
132
|
-
<>
|
|
133
|
-
<pre className={styles.pre} data-testid="tool-card-finding-content">
|
|
134
|
-
{displayContent}
|
|
135
|
-
</pre>
|
|
136
|
-
{hasMore && (
|
|
137
|
-
<button
|
|
138
|
-
type="button"
|
|
139
|
-
className={styles.bodyToggle}
|
|
140
|
-
onClick={() => { setExpanded((v) => !v); }}
|
|
141
|
-
aria-expanded={expanded}
|
|
142
|
-
data-testid="tool-card-toggle"
|
|
143
|
-
>
|
|
144
|
-
<span className={`${styles.chevron} ${expanded ? styles.chevronExpanded : ""}`}>▸</span>
|
|
145
|
-
{expanded ? "collapse" : `${lines.length - PREVIEW_LINES} more lines`}
|
|
146
|
-
</button>
|
|
147
|
-
)}
|
|
148
|
-
</>
|
|
149
|
-
);
|
|
150
|
-
})()}
|
|
151
|
-
</>
|
|
152
|
-
)}
|
|
153
|
-
|
|
154
|
-
{/* List result: show compact finding titles */}
|
|
155
|
-
{!isError && list && list.length > 0 && (
|
|
156
|
-
<>
|
|
157
|
-
<pre className={styles.pre} data-testid="tool-card-finding-list">
|
|
158
|
-
{(expanded ? list : list.slice(0, PREVIEW_COUNT)).map((f, i) => (
|
|
159
|
-
`${f.category ? `[${f.category}] ` : ""}${f.title ?? f.id ?? `Finding ${i + 1}`}`
|
|
160
|
-
)).join("\n")}
|
|
161
|
-
</pre>
|
|
162
|
-
{list.length > PREVIEW_COUNT && (
|
|
163
|
-
<button
|
|
164
|
-
type="button"
|
|
165
|
-
className={styles.bodyToggle}
|
|
166
|
-
onClick={() => { setExpanded((v) => !v); }}
|
|
167
|
-
aria-expanded={expanded}
|
|
168
|
-
data-testid="tool-card-toggle"
|
|
169
|
-
>
|
|
170
|
-
<span className={`${styles.chevron} ${expanded ? styles.chevronExpanded : ""}`}>▸</span>
|
|
171
|
-
{expanded ? "collapse" : `${list.length - PREVIEW_COUNT} more findings`}
|
|
172
|
-
</button>
|
|
173
|
-
)}
|
|
174
|
-
</>
|
|
175
|
-
)}
|
|
176
|
-
</div>
|
|
177
|
-
);
|
|
178
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared category-to-color mapping for findings.
|
|
3
|
-
*
|
|
4
|
-
* Centralized here so FindingsPanel, FindingsNav, and consuming pages
|
|
5
|
-
* all use the same palette and stay in sync.
|
|
6
|
-
*
|
|
7
|
-
* @module
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/** Color pair for a finding category. */
|
|
11
|
-
export interface CategoryColor {
|
|
12
|
-
/** Foreground / text color (CSS custom property). */
|
|
13
|
-
text: string;
|
|
14
|
-
/** Background / badge color (CSS custom property). */
|
|
15
|
-
bg: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** Category color mapping using CSS custom property values. */
|
|
19
|
-
export const CATEGORY_COLORS: Record<string, CategoryColor> = {
|
|
20
|
-
architecture: { text: "var(--accent-blue)", bg: "var(--accent-blue-dim)" },
|
|
21
|
-
api: { text: "var(--accent-green)", bg: "var(--accent-green-dim)" },
|
|
22
|
-
bug: { text: "var(--accent-red)", bg: "var(--accent-red-dim)" },
|
|
23
|
-
decision: { text: "var(--accent-yellow)", bg: "var(--accent-yellow-dim)" },
|
|
24
|
-
dependency: { text: "var(--accent-purple)", bg: "var(--accent-purple-dim)" },
|
|
25
|
-
pattern: { text: "var(--accent-cyan)", bg: "var(--accent-cyan-dim)" },
|
|
26
|
-
general: { text: "var(--text-secondary)", bg: "var(--bg-elevated)" },
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/** Look up the color pair for a category, falling back to `general`. */
|
|
30
|
-
export function getCategoryColor(category: string): CategoryColor {
|
|
31
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- category may not be in the map
|
|
32
|
-
return CATEGORY_COLORS[category] || CATEGORY_COLORS.general;
|
|
33
|
-
}
|