Skip to content

Commit 82937dc

Browse files
Travisuncursoragent
andcommitted
feat: add CYQ chip distribution, market panel title bar, and live chart refresh.
Wire EastMoney-compatible CYQ into the data layer and daily chart overlay, refactor the market sidebar header with draggable title chrome, and poll intraday/minute charts during trading sessions. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent c583739 commit 82937dc

22 files changed

Lines changed: 671 additions & 49 deletions

File tree

client-ui/src/api/client.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ export const research = {
100100
{ signal },
101101
),
102102

103+
stockCyq: (code: string) =>
104+
apiCall<{ code: string; rows: import('../types/market').ChipDistributionPoint[]; latest: import('../types/market').ChipDistributionPoint }>(
105+
'stock_cyq',
106+
{ code },
107+
),
108+
103109
stockDetail: (code: string) =>
104110
apiCall<import('../types/market').StockDetailData>('stock_detail', { code }),
105111

client-ui/src/chat/ChatApp.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,10 @@ export default function ChatApp() {
608608
width={rightPanelWidth}
609609
fullWidth={!chatVisible}
610610
transitionEnabled={!isDragging}
611+
electronChrome={electronChrome}
612+
chatColumnVisible={chatVisible}
613+
onToggleRightPanel={handleToggleRightPanel}
614+
onToggleChatColumn={canToggleChatColumn ? handleToggleChatColumn : undefined}
611615
/>
612616
)}
613617
</div>

client-ui/src/chat/ChatView.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import { fadeInUp } from '../theme/mixins'
1717
import { isElectron } from '../platform/detect'
1818
import ChromeToolButton from '../desktop/ChromeToolButton'
1919
import {
20-
PanelRightContractRegular,
2120
PanelRightExpandRegular,
2221
ArrowMaximizeRegular,
2322
ArrowMinimizeRegular,
@@ -408,7 +407,7 @@ export default function ChatView({
408407
<div className={s.header}>
409408
<div className={s.headerInner}>
410409
<Text className={s.title}>{title || '新对话'}</Text>
411-
{(onToggleRightPanel || onToggleChatColumn) && (
410+
{!rightPanelOpen && (onToggleRightPanel || onToggleChatColumn) && (
412411
<div className={s.headerActions}>
413412
{onToggleChatColumn && (
414413
<ChromeToolButton
@@ -423,14 +422,11 @@ export default function ChatView({
423422
)}
424423
{onToggleRightPanel && (
425424
<ChromeToolButton
426-
label={rightPanelOpen ? '收起右侧面板' : '展开右侧面板'}
425+
label="展开右侧面板"
427426
iconPadding={DESKTOP_SIDEBAR_TOOL_ICON_PADDING}
428-
active={rightPanelOpen}
429427
onClick={onToggleRightPanel}
430428
>
431-
{rightPanelOpen
432-
? <PanelRightContractRegular fontSize={DESKTOP_SIDEBAR_TOOL_ICON_SIZE} />
433-
: <PanelRightExpandRegular fontSize={DESKTOP_SIDEBAR_TOOL_ICON_SIZE} />}
429+
<PanelRightExpandRegular fontSize={DESKTOP_SIDEBAR_TOOL_ICON_SIZE} />
434430
</ChromeToolButton>
435431
)}
436432
</div>

client-ui/src/chat/RightPanel.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { makeStyles, mergeClasses } from '@fluentui/react-components'
22
import {
33
DESKTOP_SIDEBAR_LAYOUT_EASE,
44
DESKTOP_SIDEBAR_LAYOUT_MS,
5+
DESKTOP_TITLEBAR_HEIGHT,
56
WORKSPACE_RIGHT_PANEL_DEFAULT_WIDTH,
67
} from '../desktop/constants'
78
import RightMarketPanel from '../market/RightMarketPanel'
@@ -26,6 +27,10 @@ const useStyles = makeStyles({
2627
panelShellNoTransition: {
2728
transitionProperty: 'none',
2829
},
30+
panelShellElectron: {
31+
marginTop: `-${DESKTOP_TITLEBAR_HEIGHT}px`,
32+
boxSizing: 'border-box',
33+
},
2934
panel: {
3035
height: '100%',
3136
minHeight: 0,
@@ -40,13 +45,21 @@ interface Props {
4045
width?: number
4146
fullWidth?: boolean
4247
transitionEnabled?: boolean
48+
electronChrome?: boolean
49+
chatColumnVisible?: boolean
50+
onToggleRightPanel?: () => void
51+
onToggleChatColumn?: () => void
4352
}
4453

4554
export default function RightPanel({
4655
visible,
4756
width = WORKSPACE_RIGHT_PANEL_DEFAULT_WIDTH,
4857
fullWidth = false,
4958
transitionEnabled = true,
59+
electronChrome = false,
60+
chatColumnVisible = true,
61+
onToggleRightPanel,
62+
onToggleChatColumn,
5063
}: Props) {
5164
const s = useStyles()
5265

@@ -63,6 +76,7 @@ export default function RightPanel({
6376
s.panelShell,
6477
visible && s.panelShellOpen,
6578
!transitionEnabled && s.panelShellNoTransition,
79+
electronChrome && s.panelShellElectron,
6680
)}
6781
style={{ width: typeof shellWidth === 'number' ? `${shellWidth}px` : shellWidth }}
6882
>
@@ -72,7 +86,12 @@ export default function RightPanel({
7286
aria-label="行情侧栏"
7387
aria-hidden={!visible}
7488
>
75-
<RightMarketPanel />
89+
<RightMarketPanel
90+
electronChrome={electronChrome}
91+
chatColumnVisible={chatColumnVisible}
92+
onToggleRightPanel={visible ? onToggleRightPanel : undefined}
93+
onToggleChatColumn={visible ? onToggleChatColumn : undefined}
94+
/>
7695
</aside>
7796
</div>
7897
)

client-ui/src/desktop/DesktopWindowChrome.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import {
2020
import {
2121
PanelLeftContractRegular,
2222
PanelLeftExpandRegular,
23-
PanelRightContractRegular,
2423
PanelRightExpandRegular,
2524
ChatAddRegular,
2625
ArrowMaximizeRegular,
@@ -201,7 +200,7 @@ export default function DesktopWindowChrome({
201200
</div>
202201
</header>
203202

204-
{!isSettings && (onToggleRightPanel || onToggleChatColumn) && (
203+
{!isSettings && !rightPanelOpen && (onToggleRightPanel || onToggleChatColumn) && (
205204
<div
206205
className={s.titleBarActions}
207206
style={{ right: `${titleBarActionsRight}px` }}
@@ -219,14 +218,11 @@ export default function DesktopWindowChrome({
219218
)}
220219
{onToggleRightPanel && (
221220
<ChromeToolButton
222-
label={rightPanelOpen ? '收起右侧面板' : '展开右侧面板'}
221+
label="展开右侧面板"
223222
iconPadding={DESKTOP_SIDEBAR_TOOL_ICON_PADDING}
224-
active={rightPanelOpen}
225223
onClick={onToggleRightPanel}
226224
>
227-
{rightPanelOpen
228-
? <PanelRightContractRegular fontSize={DESKTOP_SIDEBAR_TOOL_ICON_SIZE} />
229-
: <PanelRightExpandRegular fontSize={DESKTOP_SIDEBAR_TOOL_ICON_SIZE} />}
225+
<PanelRightExpandRegular fontSize={DESKTOP_SIDEBAR_TOOL_ICON_SIZE} />
230226
</ChromeToolButton>
231227
)}
232228
</div>

client-ui/src/desktop/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,6 @@ export const WORKSPACE_RIGHT_PANEL_RESTORE_WIDTH =
7373

7474
/** Title bar stacking — overlay sidebar sits between title and toolbar */
7575
export const DESKTOP_Z_TITLE = 1100
76+
export const DESKTOP_Z_PANEL_TITLE = 1200
7677
export const DESKTOP_Z_OVERLAY_SIDEBAR = 1150
7778
export const DESKTOP_Z_CHROME_TOOLS = 1210

client-ui/src/market/RightMarketPanel.tsx

Lines changed: 123 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
import { useCallback, useMemo, useState } from 'react'
2-
import { Tab, TabList, Text, makeStyles, mergeClasses } from '@fluentui/react-components'
2+
import { Tab, TabList, makeStyles, mergeClasses } from '@fluentui/react-components'
33
import WatchlistTab from './WatchlistTab'
44
import StockDetailTab from './StockDetailTab'
55
import { useWatchlist } from './useWatchlist'
66
import type { WatchlistItem } from '../types/market'
77
import { innoTokens } from '../theme/tokens'
8+
import ChromeToolButton from '../desktop/ChromeToolButton'
9+
import {
10+
DESKTOP_SIDEBAR_TOOL_ICON_PADDING,
11+
DESKTOP_SIDEBAR_TOOL_ICON_SIZE,
12+
DESKTOP_TITLEBAR_HEIGHT,
13+
DESKTOP_Z_PANEL_TITLE,
14+
} from '../desktop/constants'
15+
import {
16+
PanelRightContractRegular,
17+
ArrowMaximizeRegular,
18+
ArrowMinimizeRegular,
19+
} from '../chat/chatIcons'
20+
import { electronPlatform } from '../platform/detect'
821

922
type MarketTab = 'watchlist' | 'detail'
1023

@@ -16,23 +29,56 @@ const useStyles = makeStyles({
1629
flexDirection: 'column',
1730
backgroundColor: innoTokens.canvas,
1831
},
19-
header: {
32+
titleBar: {
2033
flexShrink: 0,
21-
padding: '10px 12px 0',
22-
borderBottom: `1px solid ${innoTokens.separator}`,
34+
height: `${DESKTOP_TITLEBAR_HEIGHT}px`,
35+
boxSizing: 'border-box',
2336
display: 'flex',
24-
flexDirection: 'column',
25-
gap: '8px',
37+
alignItems: 'center',
38+
gap: '0',
39+
paddingLeft: '8px',
40+
paddingRight: '8px',
41+
borderBottom: `1px solid ${innoTokens.separator}`,
42+
backgroundColor: innoTokens.canvas,
43+
position: 'relative',
44+
zIndex: DESKTOP_Z_PANEL_TITLE,
45+
pointerEvents: 'auto',
46+
},
47+
titleBarElectron: {
48+
WebkitAppRegion: 'drag',
49+
},
50+
titleBarWeb: {
51+
height: '40px',
52+
zIndex: 1,
2653
},
27-
kicker: {
28-
fontSize: '11px',
29-
fontWeight: 600,
30-
letterSpacing: '0.08em',
31-
color: innoTokens.textTertiary,
32-
textTransform: 'uppercase',
54+
titleBarElectronWin: {
55+
paddingRight: '132px',
56+
},
57+
titleBarElectronMac: {
58+
paddingRight: '12px',
59+
},
60+
tabsWrap: {
61+
flexShrink: 0,
62+
maxWidth: '100%',
63+
WebkitAppRegion: 'no-drag',
64+
pointerEvents: 'auto',
3365
},
3466
tabs: {
35-
minHeight: '34px',
67+
minHeight: 'unset',
68+
},
69+
dragFill: {
70+
flex: 1,
71+
minWidth: '8px',
72+
alignSelf: 'stretch',
73+
WebkitAppRegion: 'drag',
74+
},
75+
titleBarActions: {
76+
flexShrink: 0,
77+
display: 'flex',
78+
alignItems: 'center',
79+
gap: '2px',
80+
WebkitAppRegion: 'no-drag',
81+
pointerEvents: 'auto',
3682
},
3783
content: {
3884
flex: 1,
@@ -42,13 +88,26 @@ const useStyles = makeStyles({
4288
},
4389
})
4490

45-
export default function RightMarketPanel() {
91+
interface Props {
92+
electronChrome?: boolean
93+
chatColumnVisible?: boolean
94+
onToggleRightPanel?: () => void
95+
onToggleChatColumn?: () => void
96+
}
97+
98+
export default function RightMarketPanel({
99+
electronChrome = false,
100+
chatColumnVisible = true,
101+
onToggleRightPanel,
102+
onToggleChatColumn,
103+
}: Props) {
46104
const s = useStyles()
47105
const { items, addItem, removeItem } = useWatchlist()
48106
const [tab, setTab] = useState<MarketTab>('watchlist')
49107
const [selected, setSelected] = useState<WatchlistItem | null>(null)
50108

51109
const selectedCode = selected?.code ?? null
110+
const electronWin = electronChrome && electronPlatform() !== 'darwin'
52111

53112
const handleSelect = useCallback((item: WatchlistItem) => {
54113
setSelected(item)
@@ -64,19 +123,58 @@ export default function RightMarketPanel() {
64123
return items.find(item => item.code === selected.code) ?? selected
65124
}, [items, selected])
66125

126+
const showWorkspaceActions = Boolean(onToggleRightPanel || onToggleChatColumn)
127+
67128
return (
68129
<div className={s.root}>
69-
<div className={s.header}>
70-
<Text className={s.kicker}>MARKET DESK</Text>
71-
<TabList
72-
className={s.tabs}
73-
size="small"
74-
selectedValue={tab}
75-
onTabSelect={(_, data) => setTab(data.value as MarketTab)}
76-
>
77-
<Tab value="watchlist">自选</Tab>
78-
<Tab value="detail" disabled={!selected}>个股</Tab>
79-
</TabList>
130+
<div
131+
className={mergeClasses(
132+
s.titleBar,
133+
!electronChrome && s.titleBarWeb,
134+
electronChrome && s.titleBarElectron,
135+
electronChrome && 'inno-right-panel-title-bar',
136+
electronChrome && (electronWin ? s.titleBarElectronWin : s.titleBarElectronMac),
137+
)}
138+
>
139+
<div className={mergeClasses(s.tabsWrap, 'inno-panel-title-no-drag')}>
140+
<TabList
141+
className={s.tabs}
142+
size="small"
143+
selectedValue={tab}
144+
onTabSelect={(_, data) => setTab(data.value as MarketTab)}
145+
>
146+
<Tab value="watchlist">自选</Tab>
147+
<Tab value="detail" disabled={!selected}>个股</Tab>
148+
</TabList>
149+
</div>
150+
151+
{electronChrome && <div className={s.dragFill} aria-hidden />}
152+
153+
{showWorkspaceActions && (
154+
<div className={mergeClasses(s.titleBarActions, 'inno-panel-title-no-drag')}>
155+
{onToggleChatColumn && (
156+
<ChromeToolButton
157+
label={chatColumnVisible ? '最大化右侧面板' : '恢复聊天区域'}
158+
iconPadding={DESKTOP_SIDEBAR_TOOL_ICON_PADDING}
159+
onClick={onToggleChatColumn}
160+
>
161+
{chatColumnVisible
162+
? <ArrowMaximizeRegular fontSize={DESKTOP_SIDEBAR_TOOL_ICON_SIZE} />
163+
: <ArrowMinimizeRegular fontSize={DESKTOP_SIDEBAR_TOOL_ICON_SIZE} />}
164+
</ChromeToolButton>
165+
)}
166+
{onToggleRightPanel && (
167+
<ChromeToolButton
168+
label="收起右侧面板"
169+
iconPadding={DESKTOP_SIDEBAR_TOOL_ICON_PADDING}
170+
active
171+
onClick={onToggleRightPanel}
172+
>
173+
<PanelRightContractRegular fontSize={DESKTOP_SIDEBAR_TOOL_ICON_SIZE} />
174+
</ChromeToolButton>
175+
)}
176+
</div>
177+
)}
80178
</div>
81179

82180
<div className={s.content}>

0 commit comments

Comments
 (0)