@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.
Files changed (270) hide show
  1. package/dist/api/agent-load.d.ts +1 -2
  2. package/dist/api/agent-load.d.ts.map +1 -1
  3. package/dist/api/agent-load.js +2 -123
  4. package/dist/api/agent-load.js.map +1 -1
  5. package/dist/api/audit-log.d.ts +1 -17
  6. package/dist/api/audit-log.d.ts.map +1 -1
  7. package/dist/api/audit-log.js +2 -162
  8. package/dist/api/audit-log.js.map +1 -1
  9. package/dist/api/background-tasks.d.ts +1 -26
  10. package/dist/api/background-tasks.d.ts.map +1 -1
  11. package/dist/api/background-tasks.js +2 -55
  12. package/dist/api/background-tasks.js.map +1 -1
  13. package/dist/api/bell.d.ts +1 -18
  14. package/dist/api/bell.d.ts.map +1 -1
  15. package/dist/api/bell.js +2 -33
  16. package/dist/api/bell.js.map +1 -1
  17. package/dist/api/code-markers.d.ts +1 -8
  18. package/dist/api/code-markers.d.ts.map +1 -1
  19. package/dist/api/code-markers.js +2 -61
  20. package/dist/api/code-markers.js.map +1 -1
  21. package/dist/api/complexity.d.ts +1 -2
  22. package/dist/api/complexity.d.ts.map +1 -1
  23. package/dist/api/complexity.js +2 -46
  24. package/dist/api/complexity.js.map +1 -1
  25. package/dist/api/context.d.ts +1 -37
  26. package/dist/api/context.d.ts.map +1 -1
  27. package/dist/api/context.js +2 -143
  28. package/dist/api/context.js.map +1 -1
  29. package/dist/api/dead-code.d.ts +1 -2
  30. package/dist/api/dead-code.d.ts.map +1 -1
  31. package/dist/api/dead-code.js +2 -69
  32. package/dist/api/dead-code.js.map +1 -1
  33. package/dist/api/dependencies.d.ts +1 -2
  34. package/dist/api/dependencies.d.ts.map +1 -1
  35. package/dist/api/dependencies.js +2 -42
  36. package/dist/api/dependencies.js.map +1 -1
  37. package/dist/api/evaluation.d.ts +1 -19
  38. package/dist/api/evaluation.d.ts.map +1 -1
  39. package/dist/api/evaluation.js +2 -127
  40. package/dist/api/evaluation.js.map +1 -1
  41. package/dist/api/file-browser.d.ts +1 -8
  42. package/dist/api/file-browser.d.ts.map +1 -1
  43. package/dist/api/file-browser.js +2 -114
  44. package/dist/api/file-browser.js.map +1 -1
  45. package/dist/api/git.d.ts +1 -46
  46. package/dist/api/git.d.ts.map +1 -1
  47. package/dist/api/git.js +2 -354
  48. package/dist/api/git.js.map +1 -1
  49. package/dist/api/health-score.d.ts +1 -2
  50. package/dist/api/health-score.d.ts.map +1 -1
  51. package/dist/api/health-score.js +2 -46
  52. package/dist/api/health-score.js.map +1 -1
  53. package/dist/api/hook-request.d.ts +1 -40
  54. package/dist/api/hook-request.d.ts.map +1 -1
  55. package/dist/api/hook-request.js +2 -277
  56. package/dist/api/hook-request.js.map +1 -1
  57. package/dist/api/hotspots.d.ts +1 -2
  58. package/dist/api/hotspots.d.ts.map +1 -1
  59. package/dist/api/hotspots.js +2 -61
  60. package/dist/api/hotspots.js.map +1 -1
  61. package/dist/api/identity.d.ts +1 -16
  62. package/dist/api/identity.d.ts.map +1 -1
  63. package/dist/api/identity.js +2 -78
  64. package/dist/api/identity.js.map +1 -1
  65. package/dist/api/index.d.ts +1 -34
  66. package/dist/api/index.d.ts.map +1 -1
  67. package/dist/api/index.js +2 -44
  68. package/dist/api/index.js.map +1 -1
  69. package/dist/api/mode.d.ts +1 -22
  70. package/dist/api/mode.d.ts.map +1 -1
  71. package/dist/api/mode.js +2 -37
  72. package/dist/api/mode.js.map +1 -1
  73. package/dist/api/otlp.d.ts +1 -2
  74. package/dist/api/otlp.d.ts.map +1 -1
  75. package/dist/api/otlp.js +2 -46
  76. package/dist/api/otlp.js.map +1 -1
  77. package/dist/api/permissions.d.ts +1 -15
  78. package/dist/api/permissions.d.ts.map +1 -1
  79. package/dist/api/permissions.js +2 -66
  80. package/dist/api/permissions.js.map +1 -1
  81. package/dist/api/persona.d.ts +1 -8
  82. package/dist/api/persona.d.ts.map +1 -1
  83. package/dist/api/persona.js +2 -67
  84. package/dist/api/persona.js.map +1 -1
  85. package/dist/api/portrait.d.ts +1 -5
  86. package/dist/api/portrait.d.ts.map +1 -1
  87. package/dist/api/portrait.js +2 -27
  88. package/dist/api/portrait.js.map +1 -1
  89. package/dist/api/settings.d.ts +1 -53
  90. package/dist/api/settings.d.ts.map +1 -1
  91. package/dist/api/settings.js +2 -464
  92. package/dist/api/settings.js.map +1 -1
  93. package/dist/api/spans.d.ts +1 -16
  94. package/dist/api/spans.d.ts.map +1 -1
  95. package/dist/api/spans.js +2 -244
  96. package/dist/api/spans.js.map +1 -1
  97. package/dist/api/stats.d.ts +1 -12
  98. package/dist/api/stats.d.ts.map +1 -1
  99. package/dist/api/stats.js +2 -84
  100. package/dist/api/stats.js.map +1 -1
  101. package/dist/api/story.d.ts +1 -2
  102. package/dist/api/story.d.ts.map +1 -1
  103. package/dist/api/story.js +2 -14
  104. package/dist/api/story.js.map +1 -1
  105. package/dist/api/telemetry.d.ts +1 -18
  106. package/dist/api/telemetry.d.ts.map +1 -1
  107. package/dist/api/telemetry.js +2 -164
  108. package/dist/api/telemetry.js.map +1 -1
  109. package/dist/api/theme-agents.d.ts +1 -60
  110. package/dist/api/theme-agents.d.ts.map +1 -1
  111. package/dist/api/theme-agents.js +2 -213
  112. package/dist/api/theme-agents.js.map +1 -1
  113. package/dist/api/todos.d.ts +1 -32
  114. package/dist/api/todos.d.ts.map +1 -1
  115. package/dist/api/todos.js +2 -43
  116. package/dist/api/todos.js.map +1 -1
  117. package/dist/api/token-stats.d.ts +1 -7
  118. package/dist/api/token-stats.d.ts.map +1 -1
  119. package/dist/api/token-stats.js +2 -35
  120. package/dist/api/token-stats.js.map +1 -1
  121. package/dist/api/welcome.d.ts +1 -21
  122. package/dist/api/welcome.d.ts.map +1 -1
  123. package/dist/api/welcome.js +2 -34
  124. package/dist/api/welcome.js.map +1 -1
  125. package/dist/env.d.ts +6 -0
  126. package/dist/env.d.ts.map +1 -0
  127. package/dist/env.js +10 -0
  128. package/dist/env.js.map +1 -0
  129. package/dist/focus.d.ts +53 -0
  130. package/dist/focus.d.ts.map +1 -0
  131. package/dist/focus.js +122 -0
  132. package/dist/focus.js.map +1 -0
  133. package/dist/menu-builder.d.ts.map +1 -1
  134. package/dist/menu-builder.js +0 -1
  135. package/dist/menu-builder.js.map +1 -1
  136. package/dist/public/css/react.css +1 -1
  137. package/dist/public/js/react/react.js +51 -59
  138. package/dist/server.d.ts +16 -85
  139. package/dist/server.d.ts.map +1 -1
  140. package/dist/server.js +38 -409
  141. package/dist/server.js.map +1 -1
  142. package/dist/sprint-data.d.ts +1 -1
  143. package/dist/sprint-data.d.ts.map +1 -1
  144. package/dist/sprint-data.js +2 -2
  145. package/dist/sprint-data.js.map +1 -1
  146. package/dist/websocket.d.ts +2 -0
  147. package/dist/websocket.d.ts.map +1 -1
  148. package/dist/websocket.js +42 -75
  149. package/dist/websocket.js.map +1 -1
  150. package/package.json +3 -6
  151. package/portraits/hogans-heroes/large/burkhalter-35312.png +0 -0
  152. package/portraits/hogans-heroes/large/carter-34352.png +0 -0
  153. package/portraits/hogans-heroes/large/hochstetter-45314.png +0 -0
  154. package/portraits/hogans-heroes/large/hogan-44541.png +0 -0
  155. package/portraits/hogans-heroes/large/kinch-35241.png +0 -0
  156. package/portraits/hogans-heroes/large/klink-23434.png +0 -0
  157. package/portraits/hogans-heroes/large/lebeau-45443.png +0 -0
  158. package/portraits/hogans-heroes/large/marya-53543.png +0 -0
  159. package/portraits/hogans-heroes/large/newkirk-54432.png +0 -0
  160. package/portraits/hogans-heroes/large/schultz-42453.png +0 -0
  161. package/portraits/hogans-heroes/large/underground-55131.png +0 -0
  162. package/portraits/hogans-heroes/medium/burkhalter-35312.png +0 -0
  163. package/portraits/hogans-heroes/medium/carter-34352.png +0 -0
  164. package/portraits/hogans-heroes/medium/hochstetter-45314.png +0 -0
  165. package/portraits/hogans-heroes/medium/hogan-44541.png +0 -0
  166. package/portraits/hogans-heroes/medium/kinch-35241.png +0 -0
  167. package/portraits/hogans-heroes/medium/klink-23434.png +0 -0
  168. package/portraits/hogans-heroes/medium/lebeau-45443.png +0 -0
  169. package/portraits/hogans-heroes/medium/marya-53543.png +0 -0
  170. package/portraits/hogans-heroes/medium/newkirk-54432.png +0 -0
  171. package/portraits/hogans-heroes/medium/schultz-42453.png +0 -0
  172. package/portraits/hogans-heroes/medium/underground-55131.png +0 -0
  173. package/portraits/monty-python/large/announcer-44441.png +0 -0
  174. package/portraits/monty-python/large/arguer-35412.png +0 -0
  175. package/portraits/monty-python/large/bicycle-repair-man-35241.png +0 -0
  176. package/portraits/monty-python/large/colonel-35423.png +0 -0
  177. package/portraits/monty-python/large/counsellor-45341.png +0 -0
  178. package/portraits/monty-python/large/gumbys-23524.png +0 -0
  179. package/portraits/monty-python/large/nudge-43533.png +0 -0
  180. package/portraits/monty-python/large/praline-45413.png +0 -0
  181. package/portraits/monty-python/large/silly-walks-55322.png +0 -0
  182. package/portraits/monty-python/large/wensleydale-54451.png +0 -0
  183. package/portraits/monty-python/large/xim-nez-43534.png +0 -0
  184. package/portraits/monty-python/medium/announcer-44441.png +0 -0
  185. package/portraits/monty-python/medium/arguer-35412.png +0 -0
  186. package/portraits/monty-python/medium/bicycle-repair-man-35241.png +0 -0
  187. package/portraits/monty-python/medium/colonel-35423.png +0 -0
  188. package/portraits/monty-python/medium/counsellor-45341.png +0 -0
  189. package/portraits/monty-python/medium/gumbys-23524.png +0 -0
  190. package/portraits/monty-python/medium/nudge-43533.png +0 -0
  191. package/portraits/monty-python/medium/praline-45413.png +0 -0
  192. package/portraits/monty-python/medium/silly-walks-55322.png +0 -0
  193. package/portraits/monty-python/medium/wensleydale-54451.png +0 -0
  194. package/portraits/monty-python/medium/xim-nez-43534.png +0 -0
  195. package/portraits/stephen-king/large/andy-55231.png +0 -0
  196. package/portraits/stephen-king/large/christine-25112.png +0 -0
  197. package/portraits/stephen-king/large/danny-53243.png +0 -0
  198. package/portraits/stephen-king/large/flagg-55311.png +0 -0
  199. package/portraits/stephen-king/large/gaunt-54421.png +0 -0
  200. package/portraits/stephen-king/large/jack-44224.png +0 -0
  201. package/portraits/stephen-king/large/johnny-44353.png +0 -0
  202. package/portraits/stephen-king/large/margaret-15415.png +0 -0
  203. package/portraits/stephen-king/large/paul-45233.png +0 -0
  204. package/portraits/stephen-king/large/pennywise-54411.png +0 -0
  205. package/portraits/stephen-king/large/roland-35121.png +0 -0
  206. package/portraits/stephen-king/medium/andy-55231.png +0 -0
  207. package/portraits/stephen-king/medium/christine-25112.png +0 -0
  208. package/portraits/stephen-king/medium/danny-53243.png +0 -0
  209. package/portraits/stephen-king/medium/flagg-55311.png +0 -0
  210. package/portraits/stephen-king/medium/gaunt-54421.png +0 -0
  211. package/portraits/stephen-king/medium/jack-44224.png +0 -0
  212. package/portraits/stephen-king/medium/johnny-44353.png +0 -0
  213. package/portraits/stephen-king/medium/margaret-15415.png +0 -0
  214. package/portraits/stephen-king/medium/paul-45233.png +0 -0
  215. package/portraits/stephen-king/medium/pennywise-54411.png +0 -0
  216. package/portraits/stephen-king/medium/roland-35121.png +0 -0
  217. package/src/public/App.tsx +21 -5
  218. package/src/public/components/BikeRackIndex.tsx +0 -1
  219. package/src/public/components/BikeRackWorkspace.tsx +86 -11
  220. package/src/public/components/DockviewWorkspace.tsx +19 -8
  221. package/src/public/components/StandalonePanel.tsx +1 -3
  222. package/src/public/components/panel-registry.ts +3 -1
  223. package/src/public/components/panels/AuditLogPanel.tsx +28 -4
  224. package/src/public/components/panels/GitPanel.tsx +1 -20
  225. package/src/public/components/panels/SettingsPanel.tsx +0 -1
  226. package/src/public/components/panels/SprintPanel.tsx +32 -1
  227. package/src/public/components/panels/index.ts +0 -2
  228. package/src/public/hooks/useFocusPanel.ts +137 -0
  229. package/src/public/hooks/useLayoutPersistence.ts +8 -5
  230. package/src/public/styles/dockview-theme.css +1 -84
  231. package/src/public/styles/tailwind.css +27 -32
  232. package/src/public/utils/slash-commands.ts +122 -98
  233. package/dist/hooks/cyclist-pretooluse-hook.d.ts +0 -60
  234. package/dist/hooks/cyclist-pretooluse-hook.d.ts.map +0 -1
  235. package/dist/hooks/cyclist-pretooluse-hook.js +0 -57
  236. package/dist/hooks/cyclist-pretooluse-hook.js.map +0 -1
  237. package/dist/hooks/pretooluse-hook.d.ts +0 -89
  238. package/dist/hooks/pretooluse-hook.d.ts.map +0 -1
  239. package/dist/hooks/pretooluse-hook.js +0 -235
  240. package/dist/hooks/pretooluse-hook.js.map +0 -1
  241. package/dist/notification-sound.d.ts +0 -59
  242. package/dist/notification-sound.d.ts.map +0 -1
  243. package/dist/notification-sound.js +0 -219
  244. package/dist/notification-sound.js.map +0 -1
  245. package/dist/plugin-loader.test.d.ts +0 -17
  246. package/dist/plugin-loader.test.d.ts.map +0 -1
  247. package/dist/plugin-loader.test.js +0 -407
  248. package/dist/plugin-loader.test.js.map +0 -1
  249. package/portraits/star-trek-tng/large/beverly-44352.png +0 -0
  250. package/portraits/star-trek-tng/large/data-55241.png +0 -0
  251. package/portraits/star-trek-tng/large/deanna-43353.png +0 -0
  252. package/portraits/star-trek-tng/large/geordi-54342.png +0 -0
  253. package/portraits/star-trek-tng/large/jean-luc-45342.png +0 -0
  254. package/portraits/star-trek-tng/large/kathryn-45332.png +0 -0
  255. package/portraits/star-trek-tng/large/miles-35342.png +0 -0
  256. package/portraits/star-trek-tng/large/q-53521.png +0 -0
  257. package/portraits/star-trek-tng/large/spock-45231.png +0 -0
  258. package/portraits/star-trek-tng/large/troi-44352.png +0 -0
  259. package/portraits/star-trek-tng/medium/beverly-44352.png +0 -0
  260. package/portraits/star-trek-tng/medium/data-55241.png +0 -0
  261. package/portraits/star-trek-tng/medium/deanna-43353.png +0 -0
  262. package/portraits/star-trek-tng/medium/geordi-54342.png +0 -0
  263. package/portraits/star-trek-tng/medium/jean-luc-45342.png +0 -0
  264. package/portraits/star-trek-tng/medium/kathryn-45332.png +0 -0
  265. package/portraits/star-trek-tng/medium/miles-35342.png +0 -0
  266. package/portraits/star-trek-tng/medium/q-53521.png +0 -0
  267. package/portraits/star-trek-tng/medium/spock-45231.png +0 -0
  268. package/portraits/star-trek-tng/medium/troi-44352.png +0 -0
  269. package/src/public/components/panels/TTYPanel.tsx +0 -299
  270. package/src/public/types/electron.d.ts +0 -18
@@ -0,0 +1,137 @@
1
+ /**
2
+ * useFocusPanel Hook
3
+ *
4
+ * React hook for handling panel focus events via /ws/focus WebSocket.
5
+ * Story MSSCI-14977 - BikeShow client layout stash/restore on panel focus
6
+ * Epic 104: /bc CLI Panel Focus
7
+ *
8
+ * Listens to /ws/focus WebSocket for focus events.
9
+ *
10
+ * Multi-group layouts (Cyclist): maximizeGroup/exitMaximizedGroup
11
+ * Single-group layouts (BikeRack): activate target tab, stash previous
12
+ */
13
+
14
+ import { useState, useEffect, useRef } from 'react';
15
+ import type { DockviewApi } from 'dockview-react';
16
+
17
+ export interface UseFocusPanelResult {
18
+ /** Currently focused panel ID, or null if not in focus mode */
19
+ focusedPanel: string | null;
20
+ /** Whether the workspace is currently in single-panel focus mode */
21
+ isInFocusMode: boolean;
22
+ }
23
+
24
+ /** WebSocket message format from /ws/focus */
25
+ interface FocusMessage {
26
+ type: 'init' | 'update';
27
+ focus: string | null;
28
+ }
29
+
30
+ /**
31
+ * Hook for managing panel focus mode.
32
+ *
33
+ * @param api - Dockview API instance for layout manipulation, or null if not ready
34
+ * @returns Focus state including current focused panel and mode
35
+ */
36
+ export function useFocusPanel(api: DockviewApi | null): UseFocusPanelResult {
37
+ const [focusedPanel, setFocusedPanel] = useState<string | null>(null);
38
+ const [isInFocusMode, setIsInFocusMode] = useState(false);
39
+
40
+ const wsRef = useRef<WebSocket | null>(null);
41
+ const reconnectTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
42
+ const isMountedRef = useRef(true);
43
+
44
+ // Refs to access latest values inside WebSocket callbacks without re-creating the effect
45
+ const apiRef = useRef(api);
46
+ apiRef.current = api;
47
+
48
+ // Stash the previously active panel ID for single-group reset
49
+ const previousActivePanelRef = useRef<string | null>(null);
50
+
51
+ useEffect(() => {
52
+ isMountedRef.current = true;
53
+
54
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
55
+ const wsUrl = `${protocol}//${window.location.host}/ws/focus`;
56
+
57
+ const handleFocusChange = (focus: string | null) => {
58
+ const currentApi = apiRef.current;
59
+ if (!currentApi) return;
60
+
61
+ if (focus !== null) {
62
+ const panel = currentApi.getPanel(focus);
63
+ if (!panel) return;
64
+
65
+ const isMultiGroup = currentApi.groups.length > 1;
66
+
67
+ if (isMultiGroup) {
68
+ // Multi-group (Cyclist): maximize the target panel's group
69
+ currentApi.maximizeGroup(panel);
70
+ } else {
71
+ // Single-group (BikeRack): stash current active, switch tab
72
+ if (!previousActivePanelRef.current) {
73
+ previousActivePanelRef.current = currentApi.activePanel?.id ?? null;
74
+ }
75
+ panel.api.setActive();
76
+ }
77
+
78
+ setIsInFocusMode(true);
79
+ setFocusedPanel(focus);
80
+ } else {
81
+ // Reset
82
+ const isMaximized = currentApi.hasMaximizedGroup();
83
+
84
+ if (isMaximized) {
85
+ // Multi-group: exit maximize
86
+ currentApi.exitMaximizedGroup();
87
+ } else if (previousActivePanelRef.current) {
88
+ // Single-group: restore previous active tab
89
+ const prev = currentApi.getPanel(previousActivePanelRef.current);
90
+ if (prev) prev.api.setActive();
91
+ }
92
+
93
+ previousActivePanelRef.current = null;
94
+ setIsInFocusMode(false);
95
+ setFocusedPanel(null);
96
+ }
97
+ };
98
+
99
+ const connect = () => {
100
+ if (!isMountedRef.current) return;
101
+
102
+ wsRef.current = new WebSocket(wsUrl);
103
+
104
+ wsRef.current.onmessage = (event: MessageEvent) => {
105
+ try {
106
+ const msg = JSON.parse(event.data) as FocusMessage;
107
+ if (msg.type === 'update') {
108
+ // Live /bc commands — apply focus change immediately
109
+ handleFocusChange(msg.focus);
110
+ }
111
+ // 'init' messages are ignored — focus is ephemeral, not persistent.
112
+ // Stale focus values in config would destroy the layout on page load.
113
+ } catch {
114
+ // Ignore malformed messages
115
+ }
116
+ };
117
+
118
+ wsRef.current.onclose = () => {
119
+ reconnectTimeoutRef.current = setTimeout(connect, 2000);
120
+ };
121
+ };
122
+
123
+ connect();
124
+
125
+ return () => {
126
+ isMountedRef.current = false;
127
+ if (reconnectTimeoutRef.current) {
128
+ clearTimeout(reconnectTimeoutRef.current);
129
+ }
130
+ if (wsRef.current) {
131
+ wsRef.current.close();
132
+ }
133
+ };
134
+ }, []);
135
+
136
+ return { focusedPanel, isInFocusMode };
137
+ }
@@ -42,6 +42,9 @@ function isValidDockviewLayout(layout: unknown): layout is SerializedDockview {
42
42
  if (!layoutObj.grid || typeof layoutObj.grid !== 'object') return false;
43
43
  if (!layoutObj.panels || typeof layoutObj.panels !== 'object') return false;
44
44
 
45
+ // A layout with zero panels is empty — treat as invalid so default panels get created
46
+ if (Object.keys(layoutObj.panels as Record<string, unknown>).length === 0) return false;
47
+
45
48
  const grid = layoutObj.grid as Record<string, unknown>;
46
49
  // Grid should have root, width, height, orientation
47
50
  if (!grid.root || typeof grid.width !== 'number' || typeof grid.height !== 'number') return false;
@@ -49,7 +52,7 @@ function isValidDockviewLayout(layout: unknown): layout is SerializedDockview {
49
52
  return true;
50
53
  }
51
54
 
52
- export function useLayoutPersistence(): UseLayoutPersistenceResult {
55
+ export function useLayoutPersistence(endpoint: string = '/api/settings/layout'): UseLayoutPersistenceResult {
53
56
  const [layout, setLayout] = useState<SerializedDockview | null>(null);
54
57
  const [isLoading, setIsLoading] = useState(true);
55
58
  const [isSaving, setIsSaving] = useState(false);
@@ -62,7 +65,7 @@ export function useLayoutPersistence(): UseLayoutPersistenceResult {
62
65
  useEffect(() => {
63
66
  const loadLayout = async () => {
64
67
  try {
65
- const response = await fetch('/api/settings/layout');
68
+ const response = await fetch(endpoint);
66
69
  if (response.ok) {
67
70
  const data = await response.json();
68
71
  if (data.layout && isValidDockviewLayout(data.layout)) {
@@ -87,7 +90,7 @@ export function useLayoutPersistence(): UseLayoutPersistenceResult {
87
90
  };
88
91
 
89
92
  loadLayout();
90
- }, []);
93
+ }, [endpoint]);
91
94
 
92
95
  // Debounced save function via REST API - saves native Dockview format directly
93
96
  const saveLayout = useCallback((newLayout: SerializedDockview) => {
@@ -106,7 +109,7 @@ export function useLayoutPersistence(): UseLayoutPersistenceResult {
106
109
  setIsSaving(true);
107
110
  try {
108
111
  // Save native Dockview format directly - no conversion needed
109
- const response = await fetch('/api/settings/layout', {
112
+ const response = await fetch(endpoint, {
110
113
  method: 'PATCH',
111
114
  headers: { 'Content-Type': 'application/json' },
112
115
  body: JSON.stringify(layoutToSave),
@@ -123,7 +126,7 @@ export function useLayoutPersistence(): UseLayoutPersistenceResult {
123
126
  setIsSaving(false);
124
127
  }
125
128
  }, DEBOUNCE_DELAY);
126
- }, []);
129
+ }, [endpoint]);
127
130
 
128
131
  // Cleanup debounce timer on unmount
129
132
  useEffect(() => {
@@ -78,15 +78,7 @@
78
78
  border-bottom: 2px solid var(--accent, #6366f1);
79
79
  }
80
80
 
81
- /* Tab close button */
82
- .cyclist-dockview .dv-tab .dv-default-tab-content .dv-close-action {
83
- opacity: 0;
84
- transition: opacity 0.15s ease;
85
- }
86
-
87
- .cyclist-dockview .dv-tab:hover .dv-default-tab-content .dv-close-action {
88
- opacity: 1;
89
- }
81
+ /* Tab close button removed via defaultTabComponent with hideClose={true} */
90
82
 
91
83
  /* =============================================================================
92
84
  Group Styles
@@ -366,81 +358,6 @@
366
358
  outline-offset: -2px;
367
359
  }
368
360
 
369
- /* =============================================================================
370
- TTY Panel Styles (MSSCI-14211)
371
- ============================================================================= */
372
-
373
- .tty-panel {
374
- width: 100%;
375
- height: 100%;
376
- position: relative;
377
- display: flex;
378
- flex-direction: column;
379
- background-color: var(--bg-primary, #0a0a0f);
380
- }
381
-
382
- .tty-terminal-container {
383
- flex: 1;
384
- overflow: hidden;
385
- }
386
-
387
- /* Error/Exit overlay */
388
- .tty-overlay {
389
- position: absolute;
390
- top: 0;
391
- left: 0;
392
- right: 0;
393
- bottom: 0;
394
- display: flex;
395
- align-items: center;
396
- justify-content: center;
397
- background-color: color-mix(in srgb, var(--bg-primary, #0a0a0f) 90%, transparent);
398
- z-index: 10;
399
- }
400
-
401
- .tty-overlay-content {
402
- display: flex;
403
- flex-direction: column;
404
- align-items: center;
405
- gap: 12px;
406
- padding: 24px;
407
- background-color: var(--bg-tertiary, #1a1a2e);
408
- border: 1px solid var(--border, #27272a);
409
- border-radius: 8px;
410
- text-align: center;
411
- }
412
-
413
- .tty-overlay-icon {
414
- font-size: 32px;
415
- }
416
-
417
- .tty-overlay-message {
418
- color: var(--text-secondary, #a1a1aa);
419
- font-size: 14px;
420
- max-width: 300px;
421
- }
422
-
423
- .tty-restart-button {
424
- padding: 8px 16px;
425
- background-color: var(--accent, #6366f1);
426
- border: none;
427
- border-radius: 6px;
428
- color: var(--text-primary, #ffffff);
429
- font-size: 14px;
430
- font-weight: 500;
431
- cursor: pointer;
432
- transition: background-color 0.15s ease;
433
- }
434
-
435
- .tty-restart-button:hover {
436
- background-color: var(--accent-hover, #4f46e5);
437
- }
438
-
439
- .tty-restart-button:focus-visible {
440
- outline: 2px solid var(--accent, #6366f1);
441
- outline-offset: 2px;
442
- }
443
-
444
361
  /* =============================================================================
445
362
  Accessibility Utilities
446
363
  ============================================================================= */
@@ -1752,6 +1752,33 @@
1752
1752
  text-decoration: line-through;
1753
1753
  }
1754
1754
 
1755
+ /* Copy ID Button — show on hover, feedback on click */
1756
+ .enhanced-sprint-panel .copy-id-button {
1757
+ opacity: 0;
1758
+ background: none;
1759
+ border: none;
1760
+ color: var(--text-secondary, #8b8b8b);
1761
+ cursor: pointer;
1762
+ padding: 2px;
1763
+ line-height: 1;
1764
+ flex-shrink: 0;
1765
+ transition: opacity 0.15s, color 0.15s;
1766
+ }
1767
+
1768
+ .enhanced-sprint-panel .story-item:hover .copy-id-button,
1769
+ .enhanced-sprint-panel .epic-header:hover .copy-id-button {
1770
+ opacity: 1;
1771
+ }
1772
+
1773
+ .enhanced-sprint-panel .copy-id-button:hover {
1774
+ color: var(--text-primary, #d4d4d4);
1775
+ }
1776
+
1777
+ .enhanced-sprint-panel .copy-id-button.copied {
1778
+ opacity: 1;
1779
+ color: var(--success-color, #4ec9b0);
1780
+ }
1781
+
1755
1782
  /* Future Initiatives Section */
1756
1783
  .enhanced-sprint-panel .future-epic {
1757
1784
  display: flex;
@@ -2000,38 +2027,6 @@
2000
2027
  color: var(--status-warning, #cca700);
2001
2028
  }
2002
2029
 
2003
- /* Sync all repos action bar */
2004
- .git-panel .git-panel-actions {
2005
- padding: 0 0 4px;
2006
- }
2007
-
2008
- .git-panel .sync-all-btn {
2009
- display: flex;
2010
- align-items: center;
2011
- justify-content: center;
2012
- gap: 6px;
2013
- width: 100%;
2014
- padding: 6px 10px;
2015
- border: 1px solid var(--border-secondary, rgba(255, 255, 255, 0.08));
2016
- border-radius: 4px;
2017
- background: var(--bg-tertiary, #2d2d2d);
2018
- color: var(--text-secondary, #8b8b8b);
2019
- font-size: 0.75rem;
2020
- font-family: var(--font-mono, 'SF Mono', Monaco, monospace);
2021
- cursor: pointer;
2022
- transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
2023
- }
2024
-
2025
- .git-panel .sync-all-btn:hover {
2026
- background: var(--bg-hover, #3d3d3d);
2027
- color: var(--text-primary, #e0e0e0);
2028
- border-color: var(--accent-color, #007acc);
2029
- }
2030
-
2031
- .git-panel .sync-all-btn:active {
2032
- background: var(--bg-active, #4d4d4d);
2033
- }
2034
-
2035
2030
  .git-panel .repo-status .file-status {
2036
2031
  gap: 6px;
2037
2032
  }