@grackle-ai/web-components 0.113.0 → 0.114.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.
Files changed (43) hide show
  1. package/.rush/temp/{b47d67cd3e2d79d0da7f9aef2eb425725d6d2f61.tar.log → 989dfd9dfc66be6288052dccbf2952ddad9066c6.tar.log} +2 -2
  2. package/.rush/temp/{05ec67b10f932bdbe295aab3f4465cf0d26cb485.untar.log → 989dfd9dfc66be6288052dccbf2952ddad9066c6.untar.log} +2 -2
  3. package/.rush/temp/{05ec67b10f932bdbe295aab3f4465cf0d26cb485.tar.log → b1a36bcc314d65fbd843fcff6d3127ebdf827e06.tar.log} +76 -78
  4. package/.rush/temp/{b47d67cd3e2d79d0da7f9aef2eb425725d6d2f61.untar.log → b1a36bcc314d65fbd843fcff6d3127ebdf827e06.untar.log} +2 -2
  5. package/.rush/temp/chunked-rush-logs/web-components._phase_build.chunks.jsonl +6 -6
  6. package/.rush/temp/chunked-rush-logs/web-components._phase_test.chunks.jsonl +25 -23
  7. package/.rush/temp/operation/_phase_build/all.log +6 -6
  8. package/.rush/temp/operation/_phase_build/log-chunks.jsonl +6 -6
  9. package/.rush/temp/operation/_phase_build/state.json +1 -1
  10. package/.rush/temp/operation/_phase_test/all.log +25 -23
  11. package/.rush/temp/operation/_phase_test/log-chunks.jsonl +25 -23
  12. package/.rush/temp/operation/_phase_test/state.json +1 -1
  13. package/README.md +1 -1
  14. package/dist/index.css +1 -1
  15. package/dist/index.js +7577 -7373
  16. package/package.json +2 -2
  17. package/rush-logs/web-components._phase_build.cache.log +1 -1
  18. package/rush-logs/web-components._phase_build.log +6 -6
  19. package/rush-logs/web-components._phase_test.cache.log +1 -1
  20. package/rush-logs/web-components._phase_test.log +25 -23
  21. package/src/components/layout/AppNav.stories.tsx +5 -5
  22. package/src/components/layout/AppNav.tsx +8 -4
  23. package/src/components/panels/KeyboardShortcutsPanel.stories.tsx +1 -1
  24. package/src/components/panels/KeyboardShortcutsPanel.tsx +1 -1
  25. package/src/components/streams/CoordinationList.module.scss +137 -0
  26. package/src/components/streams/CoordinationList.stories.tsx +95 -0
  27. package/src/components/streams/CoordinationList.tsx +153 -0
  28. package/src/components/streams/StreamDetailPanel.module.scss +30 -0
  29. package/src/components/streams/StreamDetailPanel.stories.tsx +3 -0
  30. package/src/components/streams/StreamDetailPanel.tsx +58 -24
  31. package/src/components/streams/index.ts +3 -3
  32. package/src/hooks/types.ts +9 -2
  33. package/src/index.ts +4 -4
  34. package/src/mocks/MockGrackleProvider.tsx +15 -3
  35. package/src/mocks/mockData.ts +4 -0
  36. package/src/mocks/mockStreamsData.ts +80 -0
  37. package/src/utils/navigation.ts +3 -5
  38. package/src/utils/streamCoordination.test.ts +88 -0
  39. package/src/utils/streamCoordination.ts +108 -0
  40. package/temp/build/lint/_eslint-5eVG3S6w.json +30 -18
  41. package/src/components/streams/StreamList.module.scss +0 -92
  42. package/src/components/streams/StreamList.stories.tsx +0 -99
  43. package/src/components/streams/StreamList.tsx +0 -114
@@ -7,7 +7,7 @@
7
7
  ],
8
8
  [
9
9
  "hooks/types.ts",
10
- "Q3IfPXTzOZH/42LKa4mjM52/+X0=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
10
+ "6V+IhDV8stiaMzUcKy7VtyKchXU=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
11
11
  ],
12
12
  [
13
13
  "components/chat/ChatInput.tsx",
@@ -35,7 +35,7 @@
35
35
  ],
36
36
  [
37
37
  "utils/navigation.ts",
38
- "VYMJ/hZ95uKS4IXcv2tomXVUoCk=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
38
+ "Jp9ePlbyKrjkctBaIHNfJR9ed74=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
39
39
  ],
40
40
  [
41
41
  "components/dag/DagView.tsx",
@@ -275,7 +275,7 @@
275
275
  ],
276
276
  [
277
277
  "components/layout/AppNav.tsx",
278
- "m+cH8YKux2EB/NGH9BzaikTuEw0=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
278
+ "tBAiCk8HnPO62b+sMkZudZfg/2A=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
279
279
  ],
280
280
  [
281
281
  "components/layout/Sidebar.tsx",
@@ -391,7 +391,7 @@
391
391
  ],
392
392
  [
393
393
  "components/panels/KeyboardShortcutsPanel.tsx",
394
- "0yOtcA8wE4p4ctgFYThvwW0x8Pk=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
394
+ "3edi7irlbtgRuyn2M7Xd0UxNLlM=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
395
395
  ],
396
396
  [
397
397
  "components/panels/CredentialProvidersPanel.tsx",
@@ -414,16 +414,20 @@
414
414
  "jRRfxaaHfygxZpdluJRjfEfHpHw=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
415
415
  ],
416
416
  [
417
- "components/streams/StreamList.tsx",
418
- "LVEmA8wVrbyO2Xt+GtPMhSGC6B8=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
417
+ "utils/streamCoordination.ts",
418
+ "EptFZSARRzENP6IxRt/AyZXBJt8=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
419
+ ],
420
+ [
421
+ "components/streams/CoordinationList.tsx",
422
+ "xDBNKE1Nr/O3BrdHcyQlPea6MtM=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
419
423
  ],
420
424
  [
421
425
  "components/streams/StreamDetailPanel.tsx",
422
- "RGLslyiZm41CPlqP2QpSDWm5uYw=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
426
+ "OnOHSENhCoxiHgkVDZM3a06WeO4=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
423
427
  ],
424
428
  [
425
429
  "components/streams/index.ts",
426
- "wjnffmfMguFqP1a4hC2+XReu4Ss=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
430
+ "6ayws8aPUPY31xaNAyWGY2MkjFY=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
427
431
  ],
428
432
  [
429
433
  "utils/boardColumns.ts",
@@ -465,13 +469,17 @@
465
469
  "mocks/mockKnowledgeData.ts",
466
470
  "K09GMmZqdvBWTt38ZYY1HsIjlLQ=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
467
471
  ],
472
+ [
473
+ "mocks/mockStreamsData.ts",
474
+ "sV8iV8PeiQ+2LXWFB7zKll/xSIs=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
475
+ ],
468
476
  [
469
477
  "mocks/mockData.ts",
470
- "8t66aL3PKQq/G0CwV/82K12tURc=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
478
+ "rwiDVF97QNZ5XtbVvMH8/1e4gBI=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
471
479
  ],
472
480
  [
473
481
  "mocks/MockGrackleProvider.tsx",
474
- "Zrw0hhCq5Bbv4P33XGGTpJjLm/8=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
482
+ "B09K0oXwGXuyVoPp09f6MYqwzBI=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
475
483
  ],
476
484
  [
477
485
  "test-utils/storybook-decorators.tsx",
@@ -483,7 +491,7 @@
483
491
  ],
484
492
  [
485
493
  "index.ts",
486
- "ahBkv6xGAgyekHPIzGpNiJ2T2lI=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
494
+ "GBCOSyIvbIWfMykHVFwaig0ddkA=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
487
495
  ],
488
496
  [
489
497
  "components/index.ts",
@@ -611,7 +619,7 @@
611
619
  ],
612
620
  [
613
621
  "components/layout/AppNav.stories.tsx",
614
- "oYykVWEa7SEp4YtAOIe+xpB1D3k=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
622
+ "Bb31+Z83wts8nmmIRRCzUDj0/q8=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
615
623
  ],
616
624
  [
617
625
  "components/layout/BottomStatusBar.stories.tsx",
@@ -675,7 +683,7 @@
675
683
  ],
676
684
  [
677
685
  "components/panels/KeyboardShortcutsPanel.stories.tsx",
678
- "gZ9hP3WGlzy58xTJYrj2eHoEpYQ=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
686
+ "V+OspyHv+PfL/AcNDfxj2yloCzE=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
679
687
  ],
680
688
  [
681
689
  "components/panels/TaskActionButtons.stories.tsx",
@@ -714,12 +722,12 @@
714
722
  "/43ghyGqzqkxu1CWV60htj1ImpU=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
715
723
  ],
716
724
  [
717
- "components/streams/StreamDetailPanel.stories.tsx",
718
- "Q8zOB3LMfxtVSD/0uHCjEIs3+ys=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
725
+ "components/streams/CoordinationList.stories.tsx",
726
+ "BlZXoGe7ntz+nxDQbdI+MC4hj/g=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
719
727
  ],
720
728
  [
721
- "components/streams/StreamList.stories.tsx",
722
- "nBRv4JGi+SHcQWldHrRA0y9cyF0=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
729
+ "components/streams/StreamDetailPanel.stories.tsx",
730
+ "wXCDWxvXnuORr2+5yl/rwfioFFE=_7W3TvvwZxl0TGST6/dyQa8DKDDc="
723
731
  ],
724
732
  [
725
733
  "components/tools/AgentToolCard.stories.tsx",
@@ -832,7 +840,11 @@
832
840
  [
833
841
  "utils/sessionEvents.test.ts",
834
842
  "hL9OyAvsoDAPSh4+TPUEgQrdYMI=_orHdc0vDZqoYfD6TIDl1Za3EAL4="
843
+ ],
844
+ [
845
+ "utils/streamCoordination.test.ts",
846
+ "tcFLEiIFvYYLO/1jT41tcJ4CIX4=_orHdc0vDZqoYfD6TIDl1Za3EAL4="
835
847
  ]
836
848
  ],
837
- "filesHash": "wzmDK7nkrsOam3n0gh_j3g"
849
+ "filesHash": "350HaaMGlV7A3LVA-PY0sQ"
838
850
  }
@@ -1,92 +0,0 @@
1
- @use '../../styles/mixins' as *;
2
-
3
- // =============================================================================
4
- // StreamList — IPC stream sidebar list
5
- // =============================================================================
6
-
7
- .container {
8
- padding: var(--space-sm) 0;
9
- }
10
-
11
- .header {
12
- @include section-label;
13
- padding: var(--space-xs) var(--space-md);
14
- display: flex;
15
- align-items: center;
16
- justify-content: space-between;
17
- }
18
-
19
- .refreshButton {
20
- background: none;
21
- border: none;
22
- color: var(--text-secondary);
23
- cursor: pointer;
24
- padding: 2px;
25
- display: flex;
26
- align-items: center;
27
- border-radius: var(--radius-sm);
28
-
29
- &:hover {
30
- color: var(--text-primary);
31
- background: var(--bg-overlay);
32
- }
33
- }
34
-
35
- .streamRow {
36
- @include hover-accent;
37
- display: flex;
38
- align-items: center;
39
- gap: var(--space-sm);
40
- padding: var(--space-xs) var(--space-md);
41
- cursor: pointer;
42
- min-height: 32px;
43
- user-select: none;
44
-
45
- &.selected {
46
- background: var(--bg-overlay);
47
- }
48
- }
49
-
50
- .systemRow {
51
- composes: streamRow;
52
- border-bottom: 1px solid var(--border-subtle);
53
- margin-bottom: var(--space-xs);
54
- font-weight: 500;
55
- }
56
-
57
- .streamIcon {
58
- flex-shrink: 0;
59
- color: var(--text-secondary);
60
- }
61
-
62
- .streamName {
63
- flex: 1;
64
- font-size: 13px;
65
- color: var(--text-primary);
66
- overflow: hidden;
67
- text-overflow: ellipsis;
68
- white-space: nowrap;
69
- }
70
-
71
- .subscriberBadge {
72
- @include surface-inset;
73
- font-size: 11px;
74
- padding: 1px 6px;
75
- border-radius: var(--radius-sm);
76
- color: var(--text-secondary);
77
- flex-shrink: 0;
78
- }
79
-
80
- .emptyState {
81
- padding: var(--space-md);
82
- font-size: 12px;
83
- color: var(--text-disabled);
84
- text-align: center;
85
- }
86
-
87
- .loading {
88
- padding: var(--space-md);
89
- font-size: 12px;
90
- color: var(--text-disabled);
91
- text-align: center;
92
- }
@@ -1,99 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react";
2
- import { expect, fn } from "@storybook/test";
3
- import type { StreamData } from "../../hooks/types.js";
4
- import { withMockGrackleRoute } from "../../test-utils/storybook-helpers.js";
5
- import { StreamList } from "./StreamList.js";
6
-
7
- // ---------------------------------------------------------------------------
8
- // Mock data
9
- // ---------------------------------------------------------------------------
10
-
11
- const mockStreams: StreamData[] = [
12
- {
13
- id: "stream-001",
14
- name: "agent-chat",
15
- subscriberCount: 2,
16
- messageBufferDepth: 0,
17
- subscribers: [],
18
- },
19
- {
20
- id: "stream-002",
21
- name: "coordinator-bus",
22
- subscriberCount: 1,
23
- messageBufferDepth: 3,
24
- subscribers: [],
25
- },
26
- {
27
- id: "stream-003",
28
- name: "telemetry-feed",
29
- subscriberCount: 0,
30
- messageBufferDepth: 0,
31
- subscribers: [],
32
- },
33
- ];
34
-
35
- // ---------------------------------------------------------------------------
36
- // Story meta
37
- // ---------------------------------------------------------------------------
38
-
39
- const meta: Meta<typeof StreamList> = {
40
- title: "Grackle/Streams/StreamList",
41
- component: StreamList,
42
- parameters: { skipRouter: true },
43
- args: {
44
- streams: mockStreams,
45
- loading: false,
46
- onRefresh: fn(),
47
- },
48
- };
49
-
50
- export default meta;
51
- type Story = StoryObj<typeof StreamList>;
52
-
53
- // ---------------------------------------------------------------------------
54
- // Stories
55
- // ---------------------------------------------------------------------------
56
-
57
- /** Default: shows System pinned row and a list of named streams. */
58
- export const Default: Story = {
59
- decorators: [withMockGrackleRoute(["/chat"], "/chat")],
60
- };
61
-
62
- /** Empty state: no named streams, only the System row. */
63
- export const Empty: Story = {
64
- decorators: [withMockGrackleRoute(["/chat"], "/chat")],
65
- args: {
66
- streams: [],
67
- },
68
- };
69
-
70
- /** Loading state while streams are being fetched. */
71
- export const Loading: Story = {
72
- decorators: [withMockGrackleRoute(["/chat"], "/chat")],
73
- args: {
74
- streams: [],
75
- loading: true,
76
- },
77
- };
78
-
79
- /** System row is visually selected when on the /chat route. */
80
- export const SystemSelected: Story = {
81
- decorators: [withMockGrackleRoute(["/chat"], "/chat")],
82
- play: async ({ canvas }) => {
83
- const systemRow = canvas.getByTestId("stream-list-system-row");
84
- await expect(systemRow).toBeInTheDocument();
85
- await expect(systemRow).toHaveAttribute("aria-current", "page");
86
- },
87
- };
88
-
89
- /** A named stream is selected when on its /chat/:streamId route. */
90
- export const StreamSelected: Story = {
91
- decorators: [withMockGrackleRoute(["/chat/stream-001"], "/chat/:streamId")],
92
- play: async ({ canvas }) => {
93
- const streamRow = canvas.getByTestId("stream-list-row-stream-001");
94
- await expect(streamRow).toBeInTheDocument();
95
- await expect(streamRow).toHaveAttribute("aria-current", "page");
96
- const systemRow = canvas.getByTestId("stream-list-system-row");
97
- await expect(systemRow).not.toHaveAttribute("aria-current");
98
- },
99
- };
@@ -1,114 +0,0 @@
1
- /**
2
- * StreamList — sidebar list of IPC streams with a pinned "System" entry.
3
- *
4
- * @module
5
- */
6
-
7
- import { useCallback, type JSX } from "react";
8
- import { useLocation, useMatch } from "react-router";
9
- import { MessageSquare, Radio, RefreshCw } from "lucide-react";
10
- import type { StreamData } from "../../hooks/types.js";
11
- import { useAppNavigate, chatStreamUrl, CHAT_URL } from "../../utils/navigation.js";
12
- import styles from "./StreamList.module.scss";
13
-
14
- /** Size for row icons. */
15
- const ICON_SIZE: number = 14;
16
-
17
- /** Props for the StreamList sidebar component. */
18
- export interface StreamListProps {
19
- /** All known IPC streams. */
20
- streams: StreamData[];
21
- /** Whether streams are currently loading. */
22
- loading: boolean;
23
- /** True if the most recent load attempt failed. */
24
- streamsLoadError?: boolean;
25
- /** True after at least one load attempt has completed. */
26
- streamsLoadedOnce?: boolean;
27
- /** Optional callback to trigger a stream list refresh. */
28
- onRefresh?: () => void;
29
- }
30
-
31
- /**
32
- * Sidebar list showing IPC streams.
33
- *
34
- * The "System" row is always pinned at the top and links to `/chat`.
35
- * Named streams are listed below, sorted alphabetically.
36
- */
37
- export function StreamList({ streams, loading, streamsLoadError = false, streamsLoadedOnce = true, onRefresh }: StreamListProps): JSX.Element {
38
- const navigate = useAppNavigate();
39
- const location = useLocation();
40
- const streamMatch = useMatch("/chat/:streamId");
41
-
42
- const selectedStreamId = streamMatch?.params.streamId;
43
- const isSystemSelected = !selectedStreamId && location.pathname === CHAT_URL;
44
-
45
- const sortedStreams = [...streams].sort((a, b) => a.name.localeCompare(b.name));
46
-
47
- const handleSystemClick = useCallback(() => {
48
- navigate(CHAT_URL);
49
- }, [navigate]);
50
-
51
- const handleStreamClick = useCallback((streamId: string) => {
52
- navigate(chatStreamUrl(streamId));
53
- }, [navigate]);
54
-
55
- return (
56
- <div className={styles.container} data-testid="stream-list">
57
- <div className={styles.header}>
58
- <span>Streams</span>
59
- {onRefresh && (
60
- <button
61
- className={styles.refreshButton}
62
- onClick={onRefresh}
63
- aria-label="Refresh streams"
64
- data-testid="stream-list-refresh"
65
- >
66
- <RefreshCw size={12} />
67
- </button>
68
- )}
69
- </div>
70
-
71
- {/* Pinned System row */}
72
- <button
73
- type="button"
74
- className={`${styles.systemRow}${isSystemSelected ? ` ${styles.selected}` : ""}`}
75
- onClick={handleSystemClick}
76
- data-testid="stream-list-system-row"
77
- aria-current={isSystemSelected ? "page" : undefined}
78
- >
79
- <MessageSquare size={ICON_SIZE} className={styles.streamIcon} />
80
- <span className={styles.streamName}>System</span>
81
- </button>
82
-
83
- {/* Named streams */}
84
- {loading && sortedStreams.length === 0 && (
85
- <div className={styles.loading}>Loading...</div>
86
- )}
87
- {!loading && streamsLoadError && (
88
- <div className={styles.emptyState} data-testid="stream-list-error">Unable to load streams</div>
89
- )}
90
- {!loading && !streamsLoadError && streamsLoadedOnce && sortedStreams.length === 0 && (
91
- <div className={styles.emptyState}>No streams</div>
92
- )}
93
- {sortedStreams.map((stream) => {
94
- const isSelected = selectedStreamId === stream.id;
95
- return (
96
- <button
97
- key={stream.id}
98
- type="button"
99
- className={`${styles.streamRow}${isSelected ? ` ${styles.selected}` : ""}`}
100
- onClick={() => handleStreamClick(stream.id)}
101
- data-testid={`stream-list-row-${stream.id}`}
102
- aria-current={isSelected ? "page" : undefined}
103
- >
104
- <Radio size={ICON_SIZE} className={styles.streamIcon} />
105
- <span className={styles.streamName}>{stream.name}</span>
106
- {stream.subscriberCount > 0 && (
107
- <span className={styles.subscriberBadge}>{stream.subscriberCount}</span>
108
- )}
109
- </button>
110
- );
111
- })}
112
- </div>
113
- );
114
- }