@pennyfarthing/cyclist 10.4.0 → 11.0.0-alpha.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/dist/api/agent-load.d.ts +1 -2
- package/dist/api/agent-load.d.ts.map +1 -1
- package/dist/api/agent-load.js +2 -123
- package/dist/api/agent-load.js.map +1 -1
- package/dist/api/audit-log.d.ts +1 -17
- package/dist/api/audit-log.d.ts.map +1 -1
- package/dist/api/audit-log.js +2 -162
- package/dist/api/audit-log.js.map +1 -1
- package/dist/api/background-tasks.d.ts +1 -26
- package/dist/api/background-tasks.d.ts.map +1 -1
- package/dist/api/background-tasks.js +2 -55
- package/dist/api/background-tasks.js.map +1 -1
- package/dist/api/bell.d.ts +1 -18
- package/dist/api/bell.d.ts.map +1 -1
- package/dist/api/bell.js +2 -33
- package/dist/api/bell.js.map +1 -1
- package/dist/api/code-markers.d.ts +1 -8
- package/dist/api/code-markers.d.ts.map +1 -1
- package/dist/api/code-markers.js +2 -61
- package/dist/api/code-markers.js.map +1 -1
- package/dist/api/complexity.d.ts +1 -2
- package/dist/api/complexity.d.ts.map +1 -1
- package/dist/api/complexity.js +2 -46
- package/dist/api/complexity.js.map +1 -1
- package/dist/api/context.d.ts +1 -37
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/context.js +2 -143
- package/dist/api/context.js.map +1 -1
- package/dist/api/dead-code.d.ts +1 -2
- package/dist/api/dead-code.d.ts.map +1 -1
- package/dist/api/dead-code.js +2 -69
- package/dist/api/dead-code.js.map +1 -1
- package/dist/api/dependencies.d.ts +1 -2
- package/dist/api/dependencies.d.ts.map +1 -1
- package/dist/api/dependencies.js +2 -42
- package/dist/api/dependencies.js.map +1 -1
- package/dist/api/evaluation.d.ts +1 -19
- package/dist/api/evaluation.d.ts.map +1 -1
- package/dist/api/evaluation.js +2 -127
- package/dist/api/evaluation.js.map +1 -1
- package/dist/api/file-browser.d.ts +1 -8
- package/dist/api/file-browser.d.ts.map +1 -1
- package/dist/api/file-browser.js +2 -114
- package/dist/api/file-browser.js.map +1 -1
- package/dist/api/git.d.ts +1 -46
- package/dist/api/git.d.ts.map +1 -1
- package/dist/api/git.js +2 -354
- package/dist/api/git.js.map +1 -1
- package/dist/api/health-score.d.ts +1 -2
- package/dist/api/health-score.d.ts.map +1 -1
- package/dist/api/health-score.js +2 -46
- package/dist/api/health-score.js.map +1 -1
- package/dist/api/hook-request.d.ts +1 -40
- package/dist/api/hook-request.d.ts.map +1 -1
- package/dist/api/hook-request.js +2 -277
- package/dist/api/hook-request.js.map +1 -1
- package/dist/api/hotspots.d.ts +1 -2
- package/dist/api/hotspots.d.ts.map +1 -1
- package/dist/api/hotspots.js +2 -61
- package/dist/api/hotspots.js.map +1 -1
- package/dist/api/identity.d.ts +1 -16
- package/dist/api/identity.d.ts.map +1 -1
- package/dist/api/identity.js +2 -78
- package/dist/api/identity.js.map +1 -1
- package/dist/api/index.d.ts +1 -34
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +2 -44
- package/dist/api/index.js.map +1 -1
- package/dist/api/mode.d.ts +1 -22
- package/dist/api/mode.d.ts.map +1 -1
- package/dist/api/mode.js +2 -37
- package/dist/api/mode.js.map +1 -1
- package/dist/api/otlp.d.ts +1 -2
- package/dist/api/otlp.d.ts.map +1 -1
- package/dist/api/otlp.js +2 -46
- package/dist/api/otlp.js.map +1 -1
- package/dist/api/permissions.d.ts +1 -15
- package/dist/api/permissions.d.ts.map +1 -1
- package/dist/api/permissions.js +2 -66
- package/dist/api/permissions.js.map +1 -1
- package/dist/api/persona.d.ts +1 -8
- package/dist/api/persona.d.ts.map +1 -1
- package/dist/api/persona.js +2 -67
- package/dist/api/persona.js.map +1 -1
- package/dist/api/portrait.d.ts +1 -5
- package/dist/api/portrait.d.ts.map +1 -1
- package/dist/api/portrait.js +2 -27
- package/dist/api/portrait.js.map +1 -1
- package/dist/api/settings.d.ts +1 -53
- package/dist/api/settings.d.ts.map +1 -1
- package/dist/api/settings.js +2 -464
- package/dist/api/settings.js.map +1 -1
- package/dist/api/spans.d.ts +1 -16
- package/dist/api/spans.d.ts.map +1 -1
- package/dist/api/spans.js +2 -244
- package/dist/api/spans.js.map +1 -1
- package/dist/api/stats.d.ts +1 -12
- package/dist/api/stats.d.ts.map +1 -1
- package/dist/api/stats.js +2 -84
- package/dist/api/stats.js.map +1 -1
- package/dist/api/story.d.ts +1 -2
- package/dist/api/story.d.ts.map +1 -1
- package/dist/api/story.js +2 -14
- package/dist/api/story.js.map +1 -1
- package/dist/api/telemetry.d.ts +1 -18
- package/dist/api/telemetry.d.ts.map +1 -1
- package/dist/api/telemetry.js +2 -164
- package/dist/api/telemetry.js.map +1 -1
- package/dist/api/theme-agents.d.ts +1 -60
- package/dist/api/theme-agents.d.ts.map +1 -1
- package/dist/api/theme-agents.js +2 -213
- package/dist/api/theme-agents.js.map +1 -1
- package/dist/api/todos.d.ts +1 -32
- package/dist/api/todos.d.ts.map +1 -1
- package/dist/api/todos.js +2 -43
- package/dist/api/todos.js.map +1 -1
- package/dist/api/token-stats.d.ts +1 -7
- package/dist/api/token-stats.d.ts.map +1 -1
- package/dist/api/token-stats.js +2 -35
- package/dist/api/token-stats.js.map +1 -1
- package/dist/api/welcome.d.ts +1 -21
- package/dist/api/welcome.d.ts.map +1 -1
- package/dist/api/welcome.js +2 -34
- package/dist/api/welcome.js.map +1 -1
- package/dist/env.d.ts +6 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +10 -0
- package/dist/env.js.map +1 -0
- package/dist/focus.d.ts +53 -0
- package/dist/focus.d.ts.map +1 -0
- package/dist/focus.js +122 -0
- package/dist/focus.js.map +1 -0
- package/dist/menu-builder.d.ts.map +1 -1
- package/dist/menu-builder.js +0 -1
- package/dist/menu-builder.js.map +1 -1
- package/dist/public/css/react.css +1 -1
- package/dist/public/js/react/react.js +51 -59
- package/dist/server.d.ts +16 -85
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +38 -409
- package/dist/server.js.map +1 -1
- package/dist/sprint-data.d.ts +1 -1
- package/dist/sprint-data.d.ts.map +1 -1
- package/dist/sprint-data.js +2 -2
- package/dist/sprint-data.js.map +1 -1
- package/dist/websocket.d.ts +2 -0
- package/dist/websocket.d.ts.map +1 -1
- package/dist/websocket.js +42 -75
- package/dist/websocket.js.map +1 -1
- package/package.json +3 -6
- package/portraits/hogans-heroes/large/burkhalter-35312.png +0 -0
- package/portraits/hogans-heroes/large/carter-34352.png +0 -0
- package/portraits/hogans-heroes/large/hochstetter-45314.png +0 -0
- package/portraits/hogans-heroes/large/hogan-44541.png +0 -0
- package/portraits/hogans-heroes/large/kinch-35241.png +0 -0
- package/portraits/hogans-heroes/large/klink-23434.png +0 -0
- package/portraits/hogans-heroes/large/lebeau-45443.png +0 -0
- package/portraits/hogans-heroes/large/marya-53543.png +0 -0
- package/portraits/hogans-heroes/large/newkirk-54432.png +0 -0
- package/portraits/hogans-heroes/large/schultz-42453.png +0 -0
- package/portraits/hogans-heroes/large/underground-55131.png +0 -0
- package/portraits/hogans-heroes/medium/burkhalter-35312.png +0 -0
- package/portraits/hogans-heroes/medium/carter-34352.png +0 -0
- package/portraits/hogans-heroes/medium/hochstetter-45314.png +0 -0
- package/portraits/hogans-heroes/medium/hogan-44541.png +0 -0
- package/portraits/hogans-heroes/medium/kinch-35241.png +0 -0
- package/portraits/hogans-heroes/medium/klink-23434.png +0 -0
- package/portraits/hogans-heroes/medium/lebeau-45443.png +0 -0
- package/portraits/hogans-heroes/medium/marya-53543.png +0 -0
- package/portraits/hogans-heroes/medium/newkirk-54432.png +0 -0
- package/portraits/hogans-heroes/medium/schultz-42453.png +0 -0
- package/portraits/hogans-heroes/medium/underground-55131.png +0 -0
- package/portraits/monty-python/large/announcer-44441.png +0 -0
- package/portraits/monty-python/large/arguer-35412.png +0 -0
- package/portraits/monty-python/large/bicycle-repair-man-35241.png +0 -0
- package/portraits/monty-python/large/colonel-35423.png +0 -0
- package/portraits/monty-python/large/counsellor-45341.png +0 -0
- package/portraits/monty-python/large/gumbys-23524.png +0 -0
- package/portraits/monty-python/large/nudge-43533.png +0 -0
- package/portraits/monty-python/large/praline-45413.png +0 -0
- package/portraits/monty-python/large/silly-walks-55322.png +0 -0
- package/portraits/monty-python/large/wensleydale-54451.png +0 -0
- package/portraits/monty-python/large/xim-nez-43534.png +0 -0
- package/portraits/monty-python/medium/announcer-44441.png +0 -0
- package/portraits/monty-python/medium/arguer-35412.png +0 -0
- package/portraits/monty-python/medium/bicycle-repair-man-35241.png +0 -0
- package/portraits/monty-python/medium/colonel-35423.png +0 -0
- package/portraits/monty-python/medium/counsellor-45341.png +0 -0
- package/portraits/monty-python/medium/gumbys-23524.png +0 -0
- package/portraits/monty-python/medium/nudge-43533.png +0 -0
- package/portraits/monty-python/medium/praline-45413.png +0 -0
- package/portraits/monty-python/medium/silly-walks-55322.png +0 -0
- package/portraits/monty-python/medium/wensleydale-54451.png +0 -0
- package/portraits/monty-python/medium/xim-nez-43534.png +0 -0
- package/portraits/stephen-king/large/andy-55231.png +0 -0
- package/portraits/stephen-king/large/christine-25112.png +0 -0
- package/portraits/stephen-king/large/danny-53243.png +0 -0
- package/portraits/stephen-king/large/flagg-55311.png +0 -0
- package/portraits/stephen-king/large/gaunt-54421.png +0 -0
- package/portraits/stephen-king/large/jack-44224.png +0 -0
- package/portraits/stephen-king/large/johnny-44353.png +0 -0
- package/portraits/stephen-king/large/margaret-15415.png +0 -0
- package/portraits/stephen-king/large/paul-45233.png +0 -0
- package/portraits/stephen-king/large/pennywise-54411.png +0 -0
- package/portraits/stephen-king/large/roland-35121.png +0 -0
- package/portraits/stephen-king/medium/andy-55231.png +0 -0
- package/portraits/stephen-king/medium/christine-25112.png +0 -0
- package/portraits/stephen-king/medium/danny-53243.png +0 -0
- package/portraits/stephen-king/medium/flagg-55311.png +0 -0
- package/portraits/stephen-king/medium/gaunt-54421.png +0 -0
- package/portraits/stephen-king/medium/jack-44224.png +0 -0
- package/portraits/stephen-king/medium/johnny-44353.png +0 -0
- package/portraits/stephen-king/medium/margaret-15415.png +0 -0
- package/portraits/stephen-king/medium/paul-45233.png +0 -0
- package/portraits/stephen-king/medium/pennywise-54411.png +0 -0
- package/portraits/stephen-king/medium/roland-35121.png +0 -0
- package/src/public/App.tsx +21 -5
- package/src/public/components/BikeRackIndex.tsx +0 -1
- package/src/public/components/BikeRackWorkspace.tsx +86 -11
- package/src/public/components/DockviewWorkspace.tsx +19 -8
- package/src/public/components/StandalonePanel.tsx +1 -3
- package/src/public/components/panel-registry.ts +3 -1
- package/src/public/components/panels/AuditLogPanel.tsx +28 -4
- package/src/public/components/panels/GitPanel.tsx +1 -20
- package/src/public/components/panels/SettingsPanel.tsx +0 -1
- package/src/public/components/panels/SprintPanel.tsx +32 -1
- package/src/public/components/panels/index.ts +0 -2
- package/src/public/hooks/useFocusPanel.ts +137 -0
- package/src/public/hooks/useLayoutPersistence.ts +8 -5
- package/src/public/styles/dockview-theme.css +1 -84
- package/src/public/styles/tailwind.css +27 -32
- package/src/public/utils/slash-commands.ts +122 -98
- package/dist/hooks/cyclist-pretooluse-hook.d.ts +0 -60
- package/dist/hooks/cyclist-pretooluse-hook.d.ts.map +0 -1
- package/dist/hooks/cyclist-pretooluse-hook.js +0 -57
- package/dist/hooks/cyclist-pretooluse-hook.js.map +0 -1
- package/dist/hooks/pretooluse-hook.d.ts +0 -89
- package/dist/hooks/pretooluse-hook.d.ts.map +0 -1
- package/dist/hooks/pretooluse-hook.js +0 -235
- package/dist/hooks/pretooluse-hook.js.map +0 -1
- package/dist/notification-sound.d.ts +0 -59
- package/dist/notification-sound.d.ts.map +0 -1
- package/dist/notification-sound.js +0 -219
- package/dist/notification-sound.js.map +0 -1
- package/dist/plugin-loader.test.d.ts +0 -17
- package/dist/plugin-loader.test.d.ts.map +0 -1
- package/dist/plugin-loader.test.js +0 -407
- package/dist/plugin-loader.test.js.map +0 -1
- package/portraits/star-trek-tng/large/beverly-44352.png +0 -0
- package/portraits/star-trek-tng/large/data-55241.png +0 -0
- package/portraits/star-trek-tng/large/deanna-43353.png +0 -0
- package/portraits/star-trek-tng/large/geordi-54342.png +0 -0
- package/portraits/star-trek-tng/large/jean-luc-45342.png +0 -0
- package/portraits/star-trek-tng/large/kathryn-45332.png +0 -0
- package/portraits/star-trek-tng/large/miles-35342.png +0 -0
- package/portraits/star-trek-tng/large/q-53521.png +0 -0
- package/portraits/star-trek-tng/large/spock-45231.png +0 -0
- package/portraits/star-trek-tng/large/troi-44352.png +0 -0
- package/portraits/star-trek-tng/medium/beverly-44352.png +0 -0
- package/portraits/star-trek-tng/medium/data-55241.png +0 -0
- package/portraits/star-trek-tng/medium/deanna-43353.png +0 -0
- package/portraits/star-trek-tng/medium/geordi-54342.png +0 -0
- package/portraits/star-trek-tng/medium/jean-luc-45342.png +0 -0
- package/portraits/star-trek-tng/medium/kathryn-45332.png +0 -0
- package/portraits/star-trek-tng/medium/miles-35342.png +0 -0
- package/portraits/star-trek-tng/medium/q-53521.png +0 -0
- package/portraits/star-trek-tng/medium/spock-45231.png +0 -0
- package/portraits/star-trek-tng/medium/troi-44352.png +0 -0
- package/src/public/components/panels/TTYPanel.tsx +0 -299
- package/src/public/types/electron.d.ts +0 -18
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/public/App.tsx
CHANGED
|
@@ -29,6 +29,13 @@ import ApprovalModal, { useApprovalModal } from './components/ApprovalModal';
|
|
|
29
29
|
import { subscribeToPermissionRequests, sendPermissionResponse, createApprovalResponse } from './components/ApprovalModal';
|
|
30
30
|
import type { ApprovalRequest, GrantScope } from './components/ApprovalModal';
|
|
31
31
|
|
|
32
|
+
// Environment discriminator injected by server.ts (ADR-0024)
|
|
33
|
+
declare global {
|
|
34
|
+
interface Window {
|
|
35
|
+
__CYCLIST_MODE__?: 'cyclist' | 'bikerack';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
// Import all panel components
|
|
33
40
|
// Note: ProgressPanel split into Workflow/AC/Todo panels (MSSCI-14188)
|
|
34
41
|
import {
|
|
@@ -44,7 +51,6 @@ import {
|
|
|
44
51
|
DebugPanel,
|
|
45
52
|
SettingsPanel,
|
|
46
53
|
AuditLogPanel,
|
|
47
|
-
TTYPanel,
|
|
48
54
|
} from './components/panels';
|
|
49
55
|
|
|
50
56
|
// =============================================================================
|
|
@@ -60,7 +66,6 @@ registerPanelComponent(PANEL_INVENTORY.CHANGED, ChangedPanel);
|
|
|
60
66
|
registerPanelComponent(PANEL_INVENTORY.DIFFS, DiffsPanel);
|
|
61
67
|
registerPanelComponent(PANEL_INVENTORY.DEBUG, DebugPanel);
|
|
62
68
|
registerPanelComponent(PANEL_INVENTORY.AUDIT_LOG, AuditLogPanel);
|
|
63
|
-
registerPanelComponent(PANEL_INVENTORY.TTY, TTYPanel);
|
|
64
69
|
|
|
65
70
|
// Right sidebar panels
|
|
66
71
|
// Note: ProgressPanel split into Workflow/AC/Todo panels (MSSCI-14188)
|
|
@@ -202,12 +207,13 @@ function RootErrorFallback(): React.ReactElement {
|
|
|
202
207
|
|
|
203
208
|
export default function App(): React.ReactElement {
|
|
204
209
|
// Detect route mode (computed before hooks, used after)
|
|
205
|
-
const isBikeRackIndex = window.
|
|
210
|
+
const isBikeRackIndex = window.__CYCLIST_MODE__ === 'bikerack';
|
|
206
211
|
const standalonePanelName = getStandalonePanelName();
|
|
207
212
|
|
|
208
213
|
// --- All hooks called unconditionally (React rules of hooks) ---
|
|
209
214
|
|
|
210
|
-
const
|
|
215
|
+
const layoutEndpoint = isBikeRackIndex ? '/api/settings/bikerack-layout' : '/api/settings/layout';
|
|
216
|
+
const { layout, isLoading, saveLayout } = useLayoutPersistence(layoutEndpoint);
|
|
211
217
|
|
|
212
218
|
// Set up reduced motion support
|
|
213
219
|
useReducedMotion();
|
|
@@ -268,6 +274,13 @@ export default function App(): React.ReactElement {
|
|
|
268
274
|
// BikeRack Dockview workspace (MSSCI-14877) — /bikerack renders Dockview layout
|
|
269
275
|
// No-op ClaudeContext: BikeRack has no Claude CLI subprocess, skip WebSocket
|
|
270
276
|
if (isBikeRackIndex) {
|
|
277
|
+
if (isLoading) {
|
|
278
|
+
return (
|
|
279
|
+
<div className="cyclist-loading" style={{ height: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
280
|
+
<div className="loading-spinner" aria-label="Loading layout..." />
|
|
281
|
+
</div>
|
|
282
|
+
);
|
|
283
|
+
}
|
|
271
284
|
const noop = () => () => {};
|
|
272
285
|
return (
|
|
273
286
|
<ClaudeContext.Provider value={{
|
|
@@ -277,7 +290,10 @@ export default function App(): React.ReactElement {
|
|
|
277
290
|
onMessage: noop, onComplete: noop, onError: noop,
|
|
278
291
|
onUserMessage: noop, onClear: noop,
|
|
279
292
|
}}>
|
|
280
|
-
<BikeRackWorkspace
|
|
293
|
+
<BikeRackWorkspace
|
|
294
|
+
initialLayout={layout ?? undefined}
|
|
295
|
+
onLayoutChange={saveLayout}
|
|
296
|
+
/>
|
|
281
297
|
</ClaudeContext.Provider>
|
|
282
298
|
);
|
|
283
299
|
}
|
|
@@ -25,7 +25,6 @@ const PANELS = [
|
|
|
25
25
|
{ id: 'audit', label: 'Audit', description: 'OTEL spans and logs' },
|
|
26
26
|
{ id: 'changed', label: 'Changed', description: 'Changed files' },
|
|
27
27
|
{ id: 'ac', label: 'AC', description: 'Acceptance criteria detail' },
|
|
28
|
-
{ id: 'tty', label: 'TTY', description: 'Terminal output' },
|
|
29
28
|
{ id: 'debug', label: 'Debug', description: 'Debug information' },
|
|
30
29
|
{ id: 'bikelane', label: 'BikeLane', description: 'Workflow visualization' },
|
|
31
30
|
{ id: 'settings', label: 'Settings', description: 'Theme, fonts, and display preferences' },
|
|
@@ -9,17 +9,20 @@
|
|
|
9
9
|
* Single Dockview group — users can freely rearrange panels.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import React, { useCallback, useRef } from 'react';
|
|
12
|
+
import React, { useCallback, useRef, useEffect, useState } from 'react';
|
|
13
13
|
import {
|
|
14
14
|
DockviewReact,
|
|
15
15
|
DockviewReadyEvent,
|
|
16
16
|
DockviewApi,
|
|
17
17
|
IDockviewPanelProps,
|
|
18
|
+
SerializedDockview,
|
|
19
|
+
DockviewDefaultTab,
|
|
18
20
|
} from 'dockview-react';
|
|
19
21
|
import 'dockview-react/dist/styles/dockview.css';
|
|
20
22
|
import { ErrorBoundary } from './ErrorBoundary';
|
|
21
23
|
import { panelRegistry } from './panel-registry';
|
|
22
24
|
import PersonaHeader from './PersonaHeader.js';
|
|
25
|
+
import { useFocusPanel } from '../hooks/useFocusPanel.js';
|
|
23
26
|
import '../styles/dockview-theme.css';
|
|
24
27
|
|
|
25
28
|
// =============================================================================
|
|
@@ -94,12 +97,41 @@ function PanelAdapter({ params }: IDockviewPanelProps<PanelAdapterParams>): Reac
|
|
|
94
97
|
// BikeRackWorkspace Component
|
|
95
98
|
// =============================================================================
|
|
96
99
|
|
|
97
|
-
export
|
|
100
|
+
export interface BikeRackWorkspaceProps {
|
|
101
|
+
initialLayout?: SerializedDockview;
|
|
102
|
+
onLayoutChange?: (layout: SerializedDockview) => void;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function BikeRackWorkspace({
|
|
106
|
+
initialLayout,
|
|
107
|
+
onLayoutChange,
|
|
108
|
+
}: BikeRackWorkspaceProps): React.ReactElement {
|
|
98
109
|
const apiRef = useRef<DockviewApi | null>(null);
|
|
110
|
+
const [dockviewApi, setDockviewApi] = useState<DockviewApi | null>(null);
|
|
111
|
+
const saveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
112
|
+
|
|
113
|
+
// Panel focus mode — stash/restore layout on /bc CLI events
|
|
114
|
+
useFocusPanel(dockviewApi);
|
|
99
115
|
|
|
100
116
|
const onReady = useCallback((event: DockviewReadyEvent) => {
|
|
101
117
|
const api = event.api;
|
|
102
118
|
apiRef.current = api;
|
|
119
|
+
setDockviewApi(api);
|
|
120
|
+
|
|
121
|
+
// Restore saved layout if available (must have actual panels, not just empty {})
|
|
122
|
+
if (initialLayout && initialLayout.grid && initialLayout.panels
|
|
123
|
+
&& Object.keys(initialLayout.panels).length > 0) {
|
|
124
|
+
try {
|
|
125
|
+
api.fromJSON(initialLayout);
|
|
126
|
+
// Verify panels were actually created
|
|
127
|
+
if (api.panels.length > 0) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
console.warn('[BikeRackWorkspace] Restored layout produced no panels, building default');
|
|
131
|
+
} catch (err) {
|
|
132
|
+
console.warn('[BikeRackWorkspace] Failed to restore layout, building default:', err);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
103
135
|
|
|
104
136
|
// Single group — all panels as tabs, user can rearrange freely
|
|
105
137
|
const first = api.addPanel({
|
|
@@ -118,23 +150,66 @@ export function BikeRackWorkspace(): React.ReactElement {
|
|
|
118
150
|
title: PANEL_TITLES[BIKERACK_PANELS[i]],
|
|
119
151
|
});
|
|
120
152
|
}
|
|
153
|
+
}, [initialLayout]);
|
|
154
|
+
|
|
155
|
+
// Subscribe to layout changes for persistence
|
|
156
|
+
const handleLayoutChange = useCallback(() => {
|
|
157
|
+
const api = apiRef.current;
|
|
158
|
+
if (!api || !onLayoutChange) return;
|
|
159
|
+
|
|
160
|
+
// Never save empty layouts — prevents corruption loop
|
|
161
|
+
if (api.panels.length === 0) return;
|
|
162
|
+
|
|
163
|
+
if (saveTimeoutRef.current) {
|
|
164
|
+
clearTimeout(saveTimeoutRef.current);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
saveTimeoutRef.current = setTimeout(() => {
|
|
168
|
+
const serialized = api.toJSON();
|
|
169
|
+
// Double-check: don't persist if serialization produced empty panels
|
|
170
|
+
if (serialized.panels && Object.keys(serialized.panels).length > 0) {
|
|
171
|
+
onLayoutChange(serialized);
|
|
172
|
+
}
|
|
173
|
+
}, 300);
|
|
174
|
+
}, [onLayoutChange]);
|
|
175
|
+
|
|
176
|
+
useEffect(() => {
|
|
177
|
+
const api = apiRef.current;
|
|
178
|
+
if (!api) return;
|
|
179
|
+
|
|
180
|
+
const disposables = [
|
|
181
|
+
api.onDidLayoutChange(() => handleLayoutChange()),
|
|
182
|
+
api.onDidAddPanel(() => handleLayoutChange()),
|
|
183
|
+
api.onDidRemovePanel(() => handleLayoutChange()),
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
return () => disposables.forEach(d => d.dispose());
|
|
187
|
+
}, [handleLayoutChange]);
|
|
188
|
+
|
|
189
|
+
// Cleanup on unmount
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
return () => {
|
|
192
|
+
if (saveTimeoutRef.current) {
|
|
193
|
+
clearTimeout(saveTimeoutRef.current);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
121
196
|
}, []);
|
|
122
197
|
|
|
123
|
-
|
|
198
|
+
// Memoize to prevent DockviewReact from reinitializing on re-render
|
|
199
|
+
const components = React.useMemo(() => ({ PanelAdapter }), []);
|
|
124
200
|
|
|
125
201
|
return (
|
|
126
202
|
<div className="cyclist-app cyclist-dockview" style={{ height: '100vh', width: '100vw', display: 'flex', flexDirection: 'column' }}>
|
|
127
203
|
<div data-testid="bikerack-portrait-anchor" style={{ flexShrink: 0 }}>
|
|
128
204
|
<PersonaHeader />
|
|
129
205
|
</div>
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
</div>
|
|
206
|
+
<DockviewReact
|
|
207
|
+
className="dockview-container"
|
|
208
|
+
onReady={onReady}
|
|
209
|
+
components={components}
|
|
210
|
+
defaultTabComponent={(props) => <DockviewDefaultTab {...props} hideClose />}
|
|
211
|
+
watermarkComponent={() => null}
|
|
212
|
+
/>
|
|
138
213
|
</div>
|
|
139
214
|
);
|
|
140
215
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - Theme integration via CSS custom properties
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import React, { useEffect, useRef, useCallback, useState
|
|
16
|
+
import React, { useEffect, useRef, useCallback, useState } from 'react';
|
|
17
17
|
import { Button } from '@/components/ui/button';
|
|
18
18
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
|
19
19
|
import {
|
|
@@ -23,11 +23,13 @@ import {
|
|
|
23
23
|
DockviewApi,
|
|
24
24
|
IDockviewPanel,
|
|
25
25
|
SerializedDockview,
|
|
26
|
+
DockviewDefaultTab,
|
|
26
27
|
} from 'dockview-react';
|
|
27
28
|
import 'dockview-react/dist/styles/dockview.css';
|
|
28
29
|
import { ErrorBoundary } from './ErrorBoundary';
|
|
29
|
-
import { panelRegistry } from './panel-registry';
|
|
30
|
+
import { panelRegistry, type PanelComponent } from './panel-registry';
|
|
30
31
|
import { useResponsiveLayout, MIN_DIMENSIONS, SIDEBAR_WIDTHS } from '../hooks/useResponsiveLayout';
|
|
32
|
+
import { useFocusPanel } from '../hooks/useFocusPanel.js';
|
|
31
33
|
import '../styles/dockview-theme.css';
|
|
32
34
|
|
|
33
35
|
// =============================================================================
|
|
@@ -40,7 +42,6 @@ export const PANEL_INVENTORY = {
|
|
|
40
42
|
DIFFS: 'diffs',
|
|
41
43
|
DEBUG: 'debug',
|
|
42
44
|
AUDIT_LOG: 'audit-log',
|
|
43
|
-
TTY: 'tty',
|
|
44
45
|
// Center panel (sacred)
|
|
45
46
|
MESSAGE: 'message',
|
|
46
47
|
// Right sidebar panels
|
|
@@ -62,7 +63,7 @@ export type PanelId = typeof PANEL_INVENTORY[keyof typeof PANEL_INVENTORY];
|
|
|
62
63
|
/**
|
|
63
64
|
* Register a panel component by ID
|
|
64
65
|
*/
|
|
65
|
-
export function registerPanelComponent(id: string, component:
|
|
66
|
+
export function registerPanelComponent(id: string, component: PanelComponent): void {
|
|
66
67
|
panelRegistry.set(id, component);
|
|
67
68
|
}
|
|
68
69
|
|
|
@@ -81,7 +82,7 @@ export function getDockviewApi(): DockviewApi | null {
|
|
|
81
82
|
|
|
82
83
|
// Panel group definitions (needed for restore logic)
|
|
83
84
|
// Exported so layout persistence can merge missing panels
|
|
84
|
-
export const LEFT_SIDEBAR_PANELS = [PANEL_INVENTORY.CHANGED, PANEL_INVENTORY.DIFFS, PANEL_INVENTORY.DEBUG, PANEL_INVENTORY.AUDIT_LOG
|
|
85
|
+
export const LEFT_SIDEBAR_PANELS = [PANEL_INVENTORY.CHANGED, PANEL_INVENTORY.DIFFS, PANEL_INVENTORY.DEBUG, PANEL_INVENTORY.AUDIT_LOG] as const;
|
|
85
86
|
export const RIGHT_SIDEBAR_PANELS = [
|
|
86
87
|
PANEL_INVENTORY.SPRINT,
|
|
87
88
|
PANEL_INVENTORY.WORKFLOW,
|
|
@@ -98,7 +99,6 @@ const PANEL_TITLES: Record<string, string> = {
|
|
|
98
99
|
diffs: 'Diffs',
|
|
99
100
|
debug: 'Debug',
|
|
100
101
|
'audit-log': 'Audit Log',
|
|
101
|
-
tty: 'Terminal',
|
|
102
102
|
message: 'Message',
|
|
103
103
|
sprint: 'Sprint',
|
|
104
104
|
workflow: 'Workflow',
|
|
@@ -404,11 +404,15 @@ export function DockviewWorkspace({
|
|
|
404
404
|
onLayoutChange,
|
|
405
405
|
}: DockviewWorkspaceProps): React.ReactElement {
|
|
406
406
|
const apiRef = useRef<DockviewApi | null>(null);
|
|
407
|
+
const [dockviewApi, setDockviewApi] = useState<DockviewApi | null>(null);
|
|
407
408
|
const { isSmall, isBelowMinimum, sidebarWidth } = useResponsiveLayout();
|
|
408
409
|
const [isReady, setIsReady] = useState(false);
|
|
409
410
|
const [closedPanelsList, setClosedPanelsList] = useState<string[]>([]);
|
|
410
411
|
const [showRestoreMenu, setShowRestoreMenu] = useState(false);
|
|
411
412
|
const saveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
413
|
+
// Panel focus mode — stash/restore layout on /bc CLI events
|
|
414
|
+
useFocusPanel(dockviewApi);
|
|
415
|
+
|
|
412
416
|
// Track if responsive effect should apply - skip on initial load to respect saved collapsed state
|
|
413
417
|
const hasAppliedInitialLayout = useRef(false);
|
|
414
418
|
const previousIsSmall = useRef<boolean | null>(null);
|
|
@@ -423,6 +427,7 @@ export function DockviewWorkspace({
|
|
|
423
427
|
const api = event.api;
|
|
424
428
|
apiRef.current = api;
|
|
425
429
|
dockviewApiRef = api;
|
|
430
|
+
setDockviewApi(api);
|
|
426
431
|
|
|
427
432
|
// Use native fromJSON if we have a saved layout, otherwise build default
|
|
428
433
|
if (initialLayout && initialLayout.grid && initialLayout.panels) {
|
|
@@ -526,6 +531,9 @@ export function DockviewWorkspace({
|
|
|
526
531
|
const api = apiRef.current;
|
|
527
532
|
if (!api || !onLayoutChange) return;
|
|
528
533
|
|
|
534
|
+
// Never save empty layouts — prevents corruption loop
|
|
535
|
+
if (api.panels.length === 0) return;
|
|
536
|
+
|
|
529
537
|
// Debounce saves
|
|
530
538
|
if (saveTimeoutRef.current) {
|
|
531
539
|
clearTimeout(saveTimeoutRef.current);
|
|
@@ -534,7 +542,10 @@ export function DockviewWorkspace({
|
|
|
534
542
|
saveTimeoutRef.current = setTimeout(() => {
|
|
535
543
|
// Use native Dockview toJSON for complete layout serialization
|
|
536
544
|
const serializedLayout = api.toJSON();
|
|
537
|
-
|
|
545
|
+
// Double-check: don't persist if serialization produced empty panels
|
|
546
|
+
if (serializedLayout.panels && Object.keys(serializedLayout.panels).length > 0) {
|
|
547
|
+
onLayoutChange(serializedLayout);
|
|
548
|
+
}
|
|
538
549
|
}, 300);
|
|
539
550
|
}, [onLayoutChange]);
|
|
540
551
|
|
|
@@ -659,7 +670,6 @@ export function DockviewWorkspace({
|
|
|
659
670
|
diffs: 'Diffs',
|
|
660
671
|
debug: 'Debug',
|
|
661
672
|
'audit-log': 'Audit Log',
|
|
662
|
-
tty: 'Terminal',
|
|
663
673
|
sprint: 'Sprint',
|
|
664
674
|
workflow: 'Workflow',
|
|
665
675
|
ac: 'AC',
|
|
@@ -728,6 +738,7 @@ export function DockviewWorkspace({
|
|
|
728
738
|
className="dockview-container"
|
|
729
739
|
onReady={onReady}
|
|
730
740
|
components={components}
|
|
741
|
+
defaultTabComponent={(props) => <DockviewDefaultTab {...props} hideClose />}
|
|
731
742
|
watermarkComponent={() => null}
|
|
732
743
|
/>
|
|
733
744
|
</div>
|
|
@@ -24,7 +24,6 @@ import {
|
|
|
24
24
|
AuditLogPanel,
|
|
25
25
|
ChangedPanel,
|
|
26
26
|
ACPanel,
|
|
27
|
-
TTYPanel,
|
|
28
27
|
DebugPanel,
|
|
29
28
|
BikeLanePanel,
|
|
30
29
|
SettingsPanel,
|
|
@@ -44,7 +43,6 @@ export const PANEL_REGISTRY: Record<string, React.ComponentType> = {
|
|
|
44
43
|
audit: AuditLogPanel,
|
|
45
44
|
changed: ChangedPanel,
|
|
46
45
|
ac: ACPanel,
|
|
47
|
-
tty: TTYPanel,
|
|
48
46
|
debug: DebugPanel,
|
|
49
47
|
bikelane: BikeLanePanel,
|
|
50
48
|
settings: SettingsPanel,
|
|
@@ -70,7 +68,7 @@ export function StandalonePanel(): React.ReactElement {
|
|
|
70
68
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100vh', width: '100vw', backgroundColor: 'var(--bg-primary, #1a1a2e)', color: 'var(--text-primary, #e4e4e7)' }}>
|
|
71
69
|
<h1>Panel not found</h1>
|
|
72
70
|
<p>
|
|
73
|
-
<a href="/
|
|
71
|
+
<a href="/" style={{ color: 'var(--accent, #818cf8)' }}>Back to BikeRack</a>
|
|
74
72
|
</p>
|
|
75
73
|
</div>
|
|
76
74
|
);
|
|
@@ -8,12 +8,11 @@
|
|
|
8
8
|
* - Statistics display
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import React, { useState, useEffect, useCallback } from 'react';
|
|
11
|
+
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
|
12
12
|
import { Button } from '@/components/ui/button';
|
|
13
13
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
|
14
14
|
import { Separator } from '@/components/ui/separator';
|
|
15
15
|
import { Skeleton } from '@/components/ui/skeleton';
|
|
16
|
-
import { ScrollArea } from '@/components/ui/scroll-area';
|
|
17
16
|
|
|
18
17
|
// =============================================================================
|
|
19
18
|
// Types
|
|
@@ -125,6 +124,30 @@ export function AuditLogPanel(): React.ReactElement {
|
|
|
125
124
|
const [error, setError] = useState<string | null>(null);
|
|
126
125
|
const [expandedEntry, setExpandedEntry] = useState<number | null>(null);
|
|
127
126
|
|
|
127
|
+
// Auto-scroll refs (using ref instead of state for synchronous updates)
|
|
128
|
+
const autoScrollRef = useRef(true);
|
|
129
|
+
const topRef = useRef<HTMLDivElement>(null);
|
|
130
|
+
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
131
|
+
|
|
132
|
+
// Detect manual scroll to pause/resume auto-scroll
|
|
133
|
+
// Re-runs when loading changes so the listener attaches after the scroll container renders
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
const el = scrollContainerRef.current;
|
|
136
|
+
if (!el) return;
|
|
137
|
+
const handler = () => {
|
|
138
|
+
autoScrollRef.current = el.scrollTop === 0;
|
|
139
|
+
};
|
|
140
|
+
el.addEventListener('scroll', handler);
|
|
141
|
+
return () => el.removeEventListener('scroll', handler);
|
|
142
|
+
}, [loading]);
|
|
143
|
+
|
|
144
|
+
// Auto-scroll to newest entry when entries change
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
if (autoScrollRef.current && topRef.current && entries.length > 0) {
|
|
147
|
+
topRef.current.scrollIntoView();
|
|
148
|
+
}
|
|
149
|
+
}, [entries]);
|
|
150
|
+
|
|
128
151
|
// Fetch entries
|
|
129
152
|
const fetchEntries = useCallback(async () => {
|
|
130
153
|
try {
|
|
@@ -326,7 +349,8 @@ export function AuditLogPanel(): React.ReactElement {
|
|
|
326
349
|
</div>
|
|
327
350
|
|
|
328
351
|
{/* Entries List */}
|
|
329
|
-
<
|
|
352
|
+
<div ref={scrollContainerRef} className="audit-log-entries flex-1" style={{ overflow: 'auto' }}>
|
|
353
|
+
<div ref={topRef} />
|
|
330
354
|
{entries.length === 0 ? (
|
|
331
355
|
<div className="p-4 text-muted text-center">No entries</div>
|
|
332
356
|
) : (
|
|
@@ -456,7 +480,7 @@ export function AuditLogPanel(): React.ReactElement {
|
|
|
456
480
|
</tbody>
|
|
457
481
|
</table>
|
|
458
482
|
)}
|
|
459
|
-
</
|
|
483
|
+
</div>
|
|
460
484
|
</TooltipProvider>
|
|
461
485
|
</div>
|
|
462
486
|
);
|
|
@@ -6,13 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React, { useState } from 'react';
|
|
9
|
-
import { RefreshCw } from 'lucide-react';
|
|
10
9
|
import { Button } from '@/components/ui/button';
|
|
11
10
|
import { Badge } from '@/components/ui/badge';
|
|
12
11
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
|
13
12
|
import { Skeleton } from '@/components/ui/skeleton';
|
|
14
13
|
import { useGitStatus, RepoStatusData, DirtyFile } from '../../hooks/useGitStatus';
|
|
15
|
-
|
|
14
|
+
|
|
16
15
|
|
|
17
16
|
/** Get CSS class for file status */
|
|
18
17
|
function getFileStatusClass(status: string): string {
|
|
@@ -152,11 +151,6 @@ function RepoStatus({ repo }: RepoStatusProps): React.ReactElement {
|
|
|
152
151
|
|
|
153
152
|
export function GitPanel(): React.ReactElement {
|
|
154
153
|
const { repos, isLoading, error } = useGitStatus();
|
|
155
|
-
const { send } = useClaudeContext();
|
|
156
|
-
|
|
157
|
-
const handleSyncAll = () => {
|
|
158
|
-
send('Sync all repos');
|
|
159
|
-
};
|
|
160
154
|
|
|
161
155
|
if (isLoading) {
|
|
162
156
|
return (
|
|
@@ -193,19 +187,6 @@ export function GitPanel(): React.ReactElement {
|
|
|
193
187
|
|
|
194
188
|
return (
|
|
195
189
|
<div className="git-panel stacked" data-testid="git-panel">
|
|
196
|
-
<div className="git-panel-actions">
|
|
197
|
-
<TooltipProvider delayDuration={300}>
|
|
198
|
-
<Tooltip>
|
|
199
|
-
<TooltipTrigger asChild>
|
|
200
|
-
<button className="sync-all-btn" onClick={handleSyncAll} aria-label="Sync all repos">
|
|
201
|
-
<RefreshCw size={14} />
|
|
202
|
-
<span>Sync all repos</span>
|
|
203
|
-
</button>
|
|
204
|
-
</TooltipTrigger>
|
|
205
|
-
<TooltipContent>Pull latest changes for all repos</TooltipContent>
|
|
206
|
-
</Tooltip>
|
|
207
|
-
</TooltipProvider>
|
|
208
|
-
</div>
|
|
209
190
|
{repos.map(repo => (
|
|
210
191
|
<RepoStatus key={repo.name} repo={repo} />
|
|
211
192
|
))}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
|
9
|
-
import { Check, Loader, Circle, AlertTriangle } from 'lucide-react';
|
|
9
|
+
import { Check, Copy, Loader, Circle, AlertTriangle } from 'lucide-react';
|
|
10
10
|
import { Button } from '@/components/ui/button';
|
|
11
11
|
import { Badge } from '@/components/ui/badge';
|
|
12
12
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
|
@@ -240,6 +240,35 @@ function JiraLink({ jiraKey, storyId }: { jiraKey: string; storyId: string }): R
|
|
|
240
240
|
);
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
/**
|
|
244
|
+
* CopyButton - Copy ID + title to clipboard on click
|
|
245
|
+
*/
|
|
246
|
+
function CopyButton({ text }: { text: string }): React.ReactElement {
|
|
247
|
+
const [copied, setCopied] = useState(false);
|
|
248
|
+
|
|
249
|
+
const handleCopy = async (e: React.MouseEvent) => {
|
|
250
|
+
e.stopPropagation();
|
|
251
|
+
try {
|
|
252
|
+
await navigator.clipboard.writeText(text);
|
|
253
|
+
setCopied(true);
|
|
254
|
+
setTimeout(() => setCopied(false), 2000);
|
|
255
|
+
} catch {
|
|
256
|
+
// clipboard API not available
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
return (
|
|
261
|
+
<button
|
|
262
|
+
className={`copy-id-button ${copied ? 'copied' : ''}`}
|
|
263
|
+
onClick={handleCopy}
|
|
264
|
+
aria-label={`Copy ${text}`}
|
|
265
|
+
title="Copy ID + title"
|
|
266
|
+
>
|
|
267
|
+
{copied ? <Check size={12} /> : <Copy size={12} />}
|
|
268
|
+
</button>
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
243
272
|
/**
|
|
244
273
|
* EpicGroup - Renders a single epic with its stories
|
|
245
274
|
*/
|
|
@@ -281,6 +310,7 @@ function EpicGroup({
|
|
|
281
310
|
</Button>
|
|
282
311
|
<span className="epic-title">{epic.title}</span>
|
|
283
312
|
{epic.jiraKey && <span className="epic-jira">{epic.jiraKey}</span>}
|
|
313
|
+
<CopyButton text={`${epic.id} ${epic.title}`} />
|
|
284
314
|
<ContextIndicator hasContext={epic.hasContext ?? false} testIdPrefix="epic" id={epic.id} />
|
|
285
315
|
{completed && epic.hasContext && (
|
|
286
316
|
<Badge variant="default" className="epic-ready-badge" data-testid={`epic-ready-badge-${epic.id}`}>
|
|
@@ -347,6 +377,7 @@ function EpicGroup({
|
|
|
347
377
|
<PriorityDot priority={story.priority} storyId={story.id} />
|
|
348
378
|
<StatusBadge status={story.status} storyId={story.id} />
|
|
349
379
|
{story.jiraKey && <JiraLink jiraKey={story.jiraKey} storyId={story.id} />}
|
|
380
|
+
<CopyButton text={`${story.id} ${story.title}`} />
|
|
350
381
|
<div className="story-info">
|
|
351
382
|
<span className="story-title">{story.title}</span>
|
|
352
383
|
<span className="story-meta">
|
|
@@ -17,8 +17,6 @@ export { DiffsPanel } from './DiffsPanel';
|
|
|
17
17
|
export { DebugPanel } from './DebugPanel';
|
|
18
18
|
export { SettingsPanel } from './SettingsPanel';
|
|
19
19
|
export { AuditLogPanel } from './AuditLogPanel';
|
|
20
|
-
export { TTYPanel } from './TTYPanel';
|
|
21
|
-
|
|
22
20
|
// Legacy exports - kept for backwards compatibility and tests
|
|
23
21
|
export { AcceptanceCriteriaPanel, ConnectedAcceptanceCriteriaPanel } from './AcceptanceCriteriaPanel';
|
|
24
22
|
export { BikeLanePanel, ConnectedBikeLanePanel } from './BikeLanePanel';
|