@zzusp/dingtalk-dsh-observer 0.4.0 → 0.5.1
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/package.json +3 -1
- package/web-client.js +243 -95
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zzusp/dingtalk-dsh-observer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "DingTalk task board client for DeepSeek Harness",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -28,12 +28,14 @@
|
|
|
28
28
|
"inject": [
|
|
29
29
|
"@deepseek-ai/dsh-client-runtime",
|
|
30
30
|
"@deepseek-ai/dsh-client-ui-layout",
|
|
31
|
+
"@deepseek-ai/dsh-client-ui-primitives",
|
|
31
32
|
"@deepseek-ai/dsh-client-ui-sidebar"
|
|
32
33
|
],
|
|
33
34
|
"platform": "web"
|
|
34
35
|
}
|
|
35
36
|
},
|
|
36
37
|
"dependencies": {
|
|
38
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.1-rc.2",
|
|
37
39
|
"react": "^18.2.0"
|
|
38
40
|
}
|
|
39
41
|
}
|
package/web-client.js
CHANGED
|
@@ -3,7 +3,8 @@ window.__ModuleLoader__.load({
|
|
|
3
3
|
factory: (require) => {
|
|
4
4
|
const module = { exports: {} }
|
|
5
5
|
const React = require('react')
|
|
6
|
-
const {
|
|
6
|
+
const { Button, Menu, Pill, StateDot } = require('@deepseek-ai/dsh-client-ui-primitives')
|
|
7
|
+
const { useCallback, useEffect, useLayoutEffect, useRef, useState } = React
|
|
7
8
|
const ENDPOINT = 'http://127.0.0.1:18998'
|
|
8
9
|
const colors = {
|
|
9
10
|
surface: 'var(--dsw-alias-bg-base, #fff)',
|
|
@@ -16,17 +17,71 @@ window.__ModuleLoader__.load({
|
|
|
16
17
|
warning: 'var(--dsw-alias-state-warn-primary, #a56500)'
|
|
17
18
|
}
|
|
18
19
|
let open = false
|
|
19
|
-
|
|
20
|
-
const
|
|
20
|
+
let selectedSessionSnapshot
|
|
21
|
+
const openListeners = new Set()
|
|
22
|
+
const clearSessionSelection = () => {
|
|
23
|
+
const element = document.querySelector('[role="treeitem"][aria-selected="true"]')
|
|
24
|
+
if (!element) return
|
|
25
|
+
const selectedClasses = [...element.classList].filter((name) => name.endsWith('_selected'))
|
|
26
|
+
selectedSessionSnapshot = { element, selectedClasses }
|
|
27
|
+
document.documentElement.style.setProperty('--dingtalk-dsh-observer-selected-bg', getComputedStyle(element).backgroundColor)
|
|
28
|
+
element.setAttribute('aria-selected', 'false')
|
|
29
|
+
for (const name of selectedClasses) element.classList.remove(name)
|
|
30
|
+
}
|
|
31
|
+
const restoreSessionSelection = () => {
|
|
32
|
+
const snapshot = selectedSessionSnapshot
|
|
33
|
+
selectedSessionSnapshot = undefined
|
|
34
|
+
if (!snapshot?.element?.isConnected) return
|
|
35
|
+
snapshot.element.setAttribute('aria-selected', 'true')
|
|
36
|
+
for (const name of snapshot.selectedClasses) snapshot.element.classList.add(name)
|
|
37
|
+
}
|
|
38
|
+
const setOpen = (value) => {
|
|
39
|
+
if (value === open) return
|
|
40
|
+
if (value) clearSessionSelection()
|
|
41
|
+
else restoreSessionSelection()
|
|
42
|
+
open = value
|
|
43
|
+
for (const listener of openListeners) listener(value)
|
|
44
|
+
}
|
|
21
45
|
const useOpen = () => {
|
|
22
46
|
const [value, setValue] = useState(open)
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
return
|
|
47
|
+
useEffect(() => { openListeners.add(setValue); return () => openListeners.delete(setValue) }, [])
|
|
48
|
+
return value
|
|
25
49
|
}
|
|
26
50
|
const fmt = (value) => value ? new Date(value).toLocaleString('zh-CN', { hour12: false }) : '—'
|
|
51
|
+
const fmtTime = (value) => value ? new Date(value).toLocaleTimeString('zh-CN', { hour12: false }) : '—'
|
|
27
52
|
const short = (value) => value ? String(value).replace(/^session-/, '').slice(0, 14) : '—'
|
|
28
53
|
const pill = (tone) => ({ display: 'inline-flex', alignItems: 'center', borderRadius: 999, padding: '3px 8px', fontSize: 12, color: tone, background: `color-mix(in srgb, ${tone} 12%, transparent)` })
|
|
29
54
|
const card = { border: `1px solid ${colors.border}`, borderRadius: 12, background: colors.cardSurface, padding: 16, boxShadow: '0 4px 16px rgba(15,23,42,.07)' }
|
|
55
|
+
const tableFrame = { border: `1px solid ${colors.border}`, borderRadius: 12, background: colors.cardSurface, overflow: 'hidden', boxShadow: 'var(--dsw-shadow-card, 0 1px 2px rgba(0,0,0,.06))' }
|
|
56
|
+
const tableHeadCell = { height: 40, boxSizing: 'border-box', padding: '0 12px', color: colors.muted, fontSize: 11, fontWeight: 500, verticalAlign: 'middle' }
|
|
57
|
+
const tableBodyCell = { padding: '12px', borderTop: `1px solid ${colors.border}`, fontSize: 12, lineHeight: 1.55, verticalAlign: 'top' }
|
|
58
|
+
const tableFooter = { minHeight: 48, boxSizing: 'border-box', display: 'flex', justifyContent: 'flex-end', alignItems: 'center', gap: 8, padding: '8px 12px', borderTop: `1px solid ${colors.border}`, background: colors.surface2 }
|
|
59
|
+
const toolbar = { minHeight: 48, boxSizing: 'border-box', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, padding: '8px 12px', borderBottom: `1px solid ${colors.border}`, background: colors.cardSurface }
|
|
60
|
+
const statusTone = {
|
|
61
|
+
done: 'var(--dsw-alias-state-success-primary, #248a3d)',
|
|
62
|
+
warning: 'var(--dsw-alias-state-warn-primary, #a56500)',
|
|
63
|
+
ongoing: 'var(--dsw-alias-brand-primary, #4d6bfe)',
|
|
64
|
+
error: 'var(--dsw-alias-state-error-primary, #c33)'
|
|
65
|
+
}
|
|
66
|
+
const statusTag = (label, state) => {
|
|
67
|
+
const tone = statusTone[state] || colors.muted
|
|
68
|
+
return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 6, minHeight: 24, boxSizing: 'border-box', border: `1px solid color-mix(in srgb, ${tone} 28%, transparent)`, borderRadius: 999, background: `color-mix(in srgb, ${tone} 10%, transparent)`, color: tone, padding: '2px 8px', fontSize: 11, fontWeight: 500, lineHeight: 1.4, whiteSpace: 'nowrap' } }, React.createElement(StateDot, { state, size: 7 }), label)
|
|
69
|
+
}
|
|
70
|
+
const authorizationTag = (label, state) => {
|
|
71
|
+
const tone = statusTone[state] || colors.muted
|
|
72
|
+
return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 5, minWidth: 58, minHeight: 22, boxSizing: 'border-box', border: `1px solid color-mix(in srgb, ${tone} 18%, transparent)`, borderRadius: 6, background: `color-mix(in srgb, ${tone} 7%, transparent)`, color: tone, padding: '2px 7px', fontSize: 11, fontWeight: 500, lineHeight: 1.35, whiteSpace: 'nowrap' } }, React.createElement(StateDot, { state, size: 6 }), label)
|
|
73
|
+
}
|
|
74
|
+
function SelectMenu({ label, value, options, onChange, minWidth = 150 }) {
|
|
75
|
+
const [open, setOpen] = useState(false)
|
|
76
|
+
const selected = options.find((item) => item.id === value)
|
|
77
|
+
const anchor = React.createElement(Button, { variant: 'outline', size: 'sm', type: 'button', 'aria-label': label, 'aria-expanded': open, onClick: () => setOpen((current) => !current), style: { minWidth, justifyContent: 'space-between', gap: 16, fontWeight: 400 } }, React.createElement('span', { style: { minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, selected?.label || value), React.createElement('span', { 'aria-hidden': true, style: { color: colors.muted, fontSize: 9, transform: open ? 'rotate(180deg)' : 'none', transition: 'transform .15s ease' } }, '▼'))
|
|
78
|
+
return React.createElement(Menu, { open, anchor, items: options, selectedId: value, onSelect: (id) => { onChange(id); setOpen(false) }, onClose: () => setOpen(false), align: 'end', portal: true, dense: true, compact: true })
|
|
79
|
+
}
|
|
80
|
+
const pageHeader = (title, description, count) => React.createElement('div', { style: { minHeight: 44, display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 20, marginBottom: 16 } },
|
|
81
|
+
React.createElement('div', { style: { minWidth: 0 } }, React.createElement('h1', { style: { margin: 0, fontSize: 18, lineHeight: 1.45, fontWeight: 600 } }, title), React.createElement('p', { style: { margin: '4px 0 0', color: colors.muted, fontSize: 12, lineHeight: 1.5 } }, description)),
|
|
82
|
+
count === undefined ? null : React.createElement(Pill, null, String(count)))
|
|
83
|
+
const sectionHeader = (title, meta) => React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16, marginBottom: 12 } }, React.createElement('h2', { style: { margin: 0, fontSize: 15, lineHeight: 1.45, fontWeight: 600 } }, title), meta ? React.createElement('span', { style: { color: colors.muted, fontSize: 11 } }, meta) : null)
|
|
84
|
+
const emptyState = (text) => React.createElement('div', { style: { minHeight: 112, display: 'grid', placeItems: 'center', color: colors.muted, fontSize: 12, border: `1px dashed ${colors.border}`, borderRadius: 10, background: colors.surface2 } }, text)
|
|
30
85
|
async function get(path) {
|
|
31
86
|
const response = await fetch(`${ENDPOINT}${path}`, { method: 'GET', headers: { accept: 'application/json' } })
|
|
32
87
|
const value = await response.json()
|
|
@@ -45,17 +100,16 @@ window.__ModuleLoader__.load({
|
|
|
45
100
|
])
|
|
46
101
|
return { health, groups, tasks, activities, alerts, authorizations }
|
|
47
102
|
}
|
|
48
|
-
function
|
|
49
|
-
const
|
|
50
|
-
const [hovered, setHovered] = useState(false)
|
|
103
|
+
function SidebarAction({ wide }) {
|
|
104
|
+
const isOpen = useOpen()
|
|
51
105
|
const icon = React.createElement('svg', { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 1.8, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': true },
|
|
52
106
|
React.createElement('rect', { x: 3, y: 3, width: 18, height: 18, rx: 3 }),
|
|
53
107
|
React.createElement('path', { d: 'M7 15v2M12 11v6M17 7v10' }))
|
|
54
108
|
return React.createElement('button', {
|
|
55
|
-
type: 'button', title: '钉钉群聊运行看板', 'aria-label': '钉钉群聊运行看板', onClick: () => setOpen(
|
|
109
|
+
type: 'button', title: '钉钉群聊运行看板', 'aria-label': '钉钉群聊运行看板', onClick: () => setOpen(true),
|
|
56
110
|
style: wide
|
|
57
|
-
? { boxSizing: 'border-box', cursor: 'pointer', width: 'calc(100% + 4px)', height: 42, color: 'var(--dsw-alias-label-primary)', background: isOpen ? '
|
|
58
|
-
: { boxSizing: 'border-box', cursor: 'pointer', width: 36, height: 36, color: 'var(--dsw-alias-label-primary)', background: isOpen ? '
|
|
111
|
+
? { boxSizing: 'border-box', cursor: 'pointer', width: 'calc(100% + 4px)', height: 42, color: 'var(--dsw-alias-label-primary)', background: isOpen ? 'var(--dingtalk-dsh-observer-selected-bg, var(--dsw-alias-interactive-bg-selected, var(--dsw-alias-interactive-bg-hover)))' : 'transparent', border: 0, outline: 'none', boxShadow: 'none', borderRadius: 12, flex: '0 0 auto', alignItems: 'center', gap: 8, margin: '4px -2px', padding: '0 10px 0 8px', fontFamily: 'inherit', fontSize: 14, lineHeight: '22px', display: 'flex', overflow: 'hidden' }
|
|
112
|
+
: { boxSizing: 'border-box', cursor: 'pointer', width: 36, height: 36, color: 'var(--dsw-alias-label-primary)', background: isOpen ? 'var(--dingtalk-dsh-observer-selected-bg, var(--dsw-alias-interactive-bg-selected, var(--dsw-alias-interactive-bg-hover)))' : 'transparent', border: 0, outline: 'none', boxShadow: 'none', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '8px 0 10px', padding: 0 }
|
|
59
113
|
}, icon, wide ? React.createElement('span', { style: { whiteSpace: 'nowrap', overflow: 'hidden' } }, '运行看板') : null)
|
|
60
114
|
}
|
|
61
115
|
const taskBuckets = [
|
|
@@ -66,7 +120,7 @@ window.__ModuleLoader__.load({
|
|
|
66
120
|
]
|
|
67
121
|
const pages = [{ id: 'groups', label: '群聊会话' }, { id: 'tasks', label: '任务看板' }, { id: 'authorizations', label: '授权审批' }, { id: 'archive', label: '归档任务' }, { id: 'alerts', label: '告警' }]
|
|
68
122
|
function ObserverOverlay({ openSession }) {
|
|
69
|
-
const
|
|
123
|
+
const isOpen = useOpen()
|
|
70
124
|
const [activePage, setActivePage] = useState('groups')
|
|
71
125
|
const [data, setData] = useState()
|
|
72
126
|
const [error, setError] = useState()
|
|
@@ -74,25 +128,83 @@ window.__ModuleLoader__.load({
|
|
|
74
128
|
const [updatedAt, setUpdatedAt] = useState()
|
|
75
129
|
const [selectedGroupId, setSelectedGroupId] = useState('')
|
|
76
130
|
const [messagePage, setMessagePage] = useState(1)
|
|
131
|
+
const [outboxPage, setOutboxPage] = useState(1)
|
|
132
|
+
const [groupTableView, setGroupTableView] = useState('messages')
|
|
133
|
+
const [messageDeliveryFilter, setMessageDeliveryFilter] = useState('all')
|
|
134
|
+
const [outboxStatusFilter, setOutboxStatusFilter] = useState('all')
|
|
77
135
|
const [hoveredTaskId, setHoveredTaskId] = useState('')
|
|
78
136
|
const [copiedId, setCopiedId] = useState('')
|
|
79
137
|
const [navigatingSessionId, setNavigatingSessionId] = useState('')
|
|
80
138
|
const [alertType, setAlertType] = useState('all')
|
|
139
|
+
const [alertView, setAlertView] = useState('active')
|
|
81
140
|
const [resolvedAlertPage, setResolvedAlertPage] = useState(1)
|
|
82
141
|
const [authorizationComments, setAuthorizationComments] = useState({})
|
|
83
142
|
const [decidingAuthorizationId, setDecidingAuthorizationId] = useState('')
|
|
84
143
|
const [authorizationPage, setAuthorizationPage] = useState(1)
|
|
144
|
+
const [authorizationFilter, setAuthorizationFilter] = useState('all')
|
|
85
145
|
const [selectedAuthorizationId, setSelectedAuthorizationId] = useState('')
|
|
146
|
+
const [sidebarWidth, setSidebarWidth] = useState(280)
|
|
147
|
+
const overlayRef = useRef()
|
|
86
148
|
const refresh = useCallback(async () => {
|
|
87
149
|
try { setData(await load()); setUpdatedAt(new Date()); setError(undefined) }
|
|
88
150
|
catch (cause) { setError(cause instanceof Error ? cause.message : String(cause)) }
|
|
89
151
|
}, [])
|
|
90
|
-
|
|
152
|
+
useLayoutEffect(() => {
|
|
91
153
|
if (!isOpen) return undefined
|
|
92
154
|
refresh()
|
|
93
155
|
const timer = window.setInterval(refresh, 5000)
|
|
94
156
|
return () => window.clearInterval(timer)
|
|
95
157
|
}, [isOpen, refresh])
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
if (!isOpen) return undefined
|
|
160
|
+
const entry = document.querySelector('[aria-label="钉钉群聊运行看板"]')
|
|
161
|
+
let sidebar = entry?.parentElement
|
|
162
|
+
while (sidebar?.parentElement) {
|
|
163
|
+
const rect = sidebar.getBoundingClientRect()
|
|
164
|
+
if (rect.left === 0 && rect.width >= 40 && rect.width <= 400 && rect.height >= window.innerHeight * .8) break
|
|
165
|
+
sidebar = sidebar.parentElement
|
|
166
|
+
}
|
|
167
|
+
const update = () => {
|
|
168
|
+
const right = sidebar?.getBoundingClientRect().right
|
|
169
|
+
if (Number.isFinite(right) && right >= 40 && right <= 400) {
|
|
170
|
+
const roundedRight = Math.round(right)
|
|
171
|
+
if (overlayRef.current) overlayRef.current.style.paddingLeft = `${roundedRight}px`
|
|
172
|
+
setSidebarWidth((current) => current === roundedRight ? current : roundedRight)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
let animationFrame
|
|
176
|
+
let trackingUntil = 0
|
|
177
|
+
const trackTransition = () => {
|
|
178
|
+
trackingUntil = performance.now() + 700
|
|
179
|
+
if (animationFrame) return
|
|
180
|
+
const track = () => {
|
|
181
|
+
update()
|
|
182
|
+
if (performance.now() < trackingUntil) animationFrame = window.requestAnimationFrame(track)
|
|
183
|
+
else animationFrame = undefined
|
|
184
|
+
}
|
|
185
|
+
animationFrame = window.requestAnimationFrame(track)
|
|
186
|
+
}
|
|
187
|
+
update()
|
|
188
|
+
const observer = sidebar && typeof ResizeObserver === 'function' ? new ResizeObserver(update) : undefined
|
|
189
|
+
if (sidebar && observer) observer.observe(sidebar)
|
|
190
|
+
sidebar?.addEventListener('transitionrun', trackTransition)
|
|
191
|
+
document.addEventListener('click', trackTransition, true)
|
|
192
|
+
window.addEventListener('resize', update)
|
|
193
|
+
return () => {
|
|
194
|
+
observer?.disconnect()
|
|
195
|
+
sidebar?.removeEventListener('transitionrun', trackTransition)
|
|
196
|
+
document.removeEventListener('click', trackTransition, true)
|
|
197
|
+
window.removeEventListener('resize', update)
|
|
198
|
+
if (animationFrame) window.cancelAnimationFrame(animationFrame)
|
|
199
|
+
}
|
|
200
|
+
}, [isOpen])
|
|
201
|
+
useEffect(() => {
|
|
202
|
+
const closeForSession = (event) => {
|
|
203
|
+
if (event.target instanceof Element && event.target.closest('[role="treeitem"]')) setOpen(false)
|
|
204
|
+
}
|
|
205
|
+
document.addEventListener('click', closeForSession, true)
|
|
206
|
+
return () => document.removeEventListener('click', closeForSession, true)
|
|
207
|
+
}, [])
|
|
96
208
|
if (!isOpen) return null
|
|
97
209
|
const activities = new Map((data?.activities || []).map((item) => [item.taskId, item]))
|
|
98
210
|
const groupsById = new Map((data?.groups || []).map((group) => [group.groupId, group]))
|
|
@@ -121,32 +233,52 @@ window.__ModuleLoader__.load({
|
|
|
121
233
|
setCopiedId(value)
|
|
122
234
|
window.setTimeout(() => setCopiedId((current) => current === value ? '' : current), 1500)
|
|
123
235
|
}
|
|
124
|
-
const copyButton = (value, label) => React.createElement('
|
|
125
|
-
const groupCards = (data?.groups || []).map((group) => React.createElement('div', { key: group.groupId, style: { ...card, display: 'grid', gap: 7, minWidth: 280, padding: '12px 16px', boxShadow: '0 4px 16px rgba(15,23,42,.07)' } },
|
|
126
|
-
React.createElement('button', { type: 'button', onClick: () => navigate(group.residentSessionId), style: { border: 0, padding: 0, background: 'transparent', color: 'inherit', cursor: 'pointer', textAlign: 'left', display: 'grid', gap: 5, fontFamily: 'inherit' } },
|
|
127
|
-
React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 } }, React.createElement('strong', null, group.name || '未命名群'), React.createElement('span', { style: { flex: '0 0 auto', fontSize: 10.5, color: colors.muted } }, fmt(group.messages?.at?.(-1)?.occurredAt || group.updatedAt))),
|
|
128
|
-
React.createElement('div', { style: { fontSize: 11, color: colors.muted, overflowWrap: 'anywhere' } }, group.groupId)),
|
|
129
|
-
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, React.createElement('code', { title: group.residentSessionId || '', style: { flex: 1, minWidth: 0, fontSize: 12, overflow: 'hidden', textOverflow: 'ellipsis' } }, group.residentSessionId || '尚未建立'), copyButton(group.residentSessionId, '会话 ID'))))
|
|
236
|
+
const copyButton = (value, label) => React.createElement(Button, { variant: 'outline', size: 'sm', type: 'button', disabled: !value, onClick: (event) => { event.stopPropagation(); copyId(value).catch((cause) => setNavigationError(cause instanceof Error ? cause.message : String(cause))) }, style: { color: copiedId === value ? 'var(--dsw-alias-state-success-primary, #248a3d)' : undefined, cursor: value ? 'pointer' : 'default', fontSize: 10.5 } }, copiedId === value ? '已复制' : `复制${label}`)
|
|
130
237
|
const selectedGroup = groupsById.get(selectedGroupId) || (data?.groups || [])[0]
|
|
238
|
+
const selectedGroupSummary = selectedGroup ? React.createElement('div', { style: { minHeight: 64, boxSizing: 'border-box', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 20, padding: '10px 12px', borderBottom: `1px solid ${colors.border}`, background: colors.surface2 } },
|
|
239
|
+
React.createElement('div', { style: { minWidth: 0, display: 'grid', gap: 5 } }, React.createElement('div', { style: { display: 'flex', alignItems: 'baseline', gap: 10 } }, React.createElement('button', { type: 'button', onClick: () => navigate(selectedGroup.residentSessionId), style: { minWidth: 0, border: 0, padding: 0, background: 'transparent', color: 'inherit', cursor: 'pointer', textAlign: 'left', fontFamily: 'inherit', fontSize: 13, fontWeight: 600 } }, selectedGroup.name || '未命名群'), React.createElement('span', { style: { color: colors.muted, fontSize: 10.5 } }, `${selectedGroup.messages?.length || 0} 条消息`), React.createElement('span', { style: { color: colors.muted, fontSize: 10.5 } }, `更新于 ${fmt(selectedGroup.messages?.at?.(-1)?.occurredAt || selectedGroup.updatedAt)}`)), React.createElement('div', { style: { minWidth: 0, display: 'flex', alignItems: 'center', gap: 8, color: colors.muted, fontSize: 10.5 } }, React.createElement('span', null, '常驻 Session'), React.createElement('code', { title: selectedGroup.residentSessionId || '', style: { minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', color: 'inherit', fontSize: 10.5 } }, selectedGroup.residentSessionId || '尚未建立'))),
|
|
240
|
+
copyButton(selectedGroup.residentSessionId, '会话 ID')) : emptyState('暂无常驻群')
|
|
131
241
|
const selectedMessages = [...(selectedGroup?.messages || [])].sort((left, right) => Number(right.sequence || 0) - Number(left.sequence || 0))
|
|
242
|
+
const filteredMessages = selectedMessages.filter((message) => messageDeliveryFilter === 'all' || (message.agentDeliveryStatus || 'unknown') === messageDeliveryFilter)
|
|
132
243
|
const pageSize = 10
|
|
133
|
-
const pageCount = Math.max(1, Math.ceil(
|
|
244
|
+
const pageCount = Math.max(1, Math.ceil(filteredMessages.length / pageSize))
|
|
134
245
|
const currentMessagePage = Math.min(messagePage, pageCount)
|
|
135
|
-
const visibleMessages =
|
|
246
|
+
const visibleMessages = filteredMessages.slice((currentMessagePage - 1) * pageSize, currentMessagePage * pageSize)
|
|
136
247
|
const delivery = {
|
|
137
|
-
delivered: { label: '已投递',
|
|
138
|
-
failed: { label: '投递失败',
|
|
139
|
-
pending: { label: '投递中',
|
|
140
|
-
skipped: { label: '历史补拉·未投递',
|
|
141
|
-
unknown: { label: '历史状态未知',
|
|
248
|
+
delivered: { label: '已投递', state: 'done' },
|
|
249
|
+
failed: { label: '投递失败', state: 'error' },
|
|
250
|
+
pending: { label: '投递中', state: 'ongoing' },
|
|
251
|
+
skipped: { label: '历史补拉·未投递', state: 'warning' },
|
|
252
|
+
unknown: { label: '历史状态未知', state: 'warning' },
|
|
142
253
|
}
|
|
143
254
|
const messageRows = visibleMessages.map((message) => {
|
|
144
255
|
const status = delivery[message.agentDeliveryStatus] || delivery.unknown
|
|
145
256
|
return React.createElement('tr', { key: message.messageId },
|
|
146
|
-
React.createElement('td', { style: {
|
|
147
|
-
React.createElement('td', { title: message.text, style: {
|
|
148
|
-
React.createElement('td', { style: {
|
|
149
|
-
React.createElement('td', { style: {
|
|
257
|
+
React.createElement('td', { style: { ...tableBodyCell, width: 150 } }, React.createElement('strong', { style: { fontSize: 12 } }, message.senderName || message.senderOpenDingTalkId || '发送人未记录'), React.createElement('div', { style: { marginTop: 4, fontSize: 10.5, color: colors.muted } }, fmt(message.occurredAt))),
|
|
258
|
+
React.createElement('td', { title: message.text, style: { ...tableBodyCell, overflowWrap: 'anywhere' } }, message.text || '(空消息)'),
|
|
259
|
+
React.createElement('td', { style: { ...tableBodyCell, width: 150 }, title: message.agentDeliveryError || '' }, statusTag(status.label, status.state)),
|
|
260
|
+
React.createElement('td', { style: { ...tableBodyCell, width: 150 } }, React.createElement('code', { title: message.messageId, style: { fontSize: 10, color: colors.muted } }, `#${message.sequence ?? '—'} · ${short(message.messageId)}`)))
|
|
261
|
+
})
|
|
262
|
+
const selectedOutbox = [...(selectedGroup?.outbox || [])].reverse()
|
|
263
|
+
const filteredOutbox = selectedOutbox.filter((message) => outboxStatusFilter === 'all' || (outboxStatusFilter === 'waiting' ? message.status === 'pending' && message.readbackRequired === true : !(message.status === 'pending' && message.readbackRequired === true)))
|
|
264
|
+
const outboxPageSize = 10
|
|
265
|
+
const outboxPageCount = Math.max(1, Math.ceil(filteredOutbox.length / outboxPageSize))
|
|
266
|
+
const currentOutboxPage = Math.min(outboxPage, outboxPageCount)
|
|
267
|
+
const visibleOutbox = filteredOutbox.slice((currentOutboxPage - 1) * outboxPageSize, currentOutboxPage * outboxPageSize)
|
|
268
|
+
const outboundStatus = {
|
|
269
|
+
sent: { label: '已回读确认', state: 'done' },
|
|
270
|
+
pending: { label: '已回读确认', state: 'done' },
|
|
271
|
+
}
|
|
272
|
+
const outboxRows = visibleOutbox.map((message) => {
|
|
273
|
+
const status = message.status === 'pending' && message.readbackRequired === true ? { label: '待回读确认', state: 'warning' } : outboundStatus[message.status] || outboundStatus.sent
|
|
274
|
+
return React.createElement('tr', { key: message.outboundId },
|
|
275
|
+
React.createElement('td', { style: { ...tableBodyCell, width: 130 } }, statusTag(status.label, status.state)),
|
|
276
|
+
React.createElement('td', { title: message.text, style: { ...tableBodyCell, overflowWrap: 'anywhere' } }, message.text || '(空消息)'),
|
|
277
|
+
React.createElement('td', { style: { ...tableBodyCell, width: 180 } },
|
|
278
|
+
React.createElement('div', null, React.createElement('code', { title: message.sourceMessageId, style: { fontSize: 10, color: colors.muted } }, short(message.sourceMessageId))),
|
|
279
|
+
message.replyToMessageId ? React.createElement('div', { style: { marginTop: 5, fontSize: 10.5, color: colors.muted } }, '回复 ', React.createElement('code', { title: message.replyToMessageId }, short(message.replyToMessageId))) : null),
|
|
280
|
+
React.createElement('td', { style: { ...tableBodyCell, width: 170 } }, React.createElement('code', { title: message.deliveredMessageId || '', style: { fontSize: 10, color: colors.muted } }, message.deliveredMessageId ? short(message.deliveredMessageId) : '历史未记录')),
|
|
281
|
+
React.createElement('td', { style: { ...tableBodyCell, width: 150 } }, React.createElement('code', { title: message.outboundId, style: { fontSize: 10, color: colors.muted } }, short(message.outboundId))))
|
|
150
282
|
})
|
|
151
283
|
const renderTaskCard = (task) => {
|
|
152
284
|
const group = groupsById.get(task.groupId)
|
|
@@ -157,7 +289,7 @@ window.__ModuleLoader__.load({
|
|
|
157
289
|
const navigating = navigatingSessionId === task.childSessionId
|
|
158
290
|
return React.createElement('div', { key: task.taskId, onMouseEnter: () => setHoveredTaskId(task.taskId), onMouseLeave: () => setHoveredTaskId(''), style: { width: '100%', minWidth: 0, boxSizing: 'border-box', border: `1px solid ${colors.border}`, borderRadius: 12, background: colors.cardSurface, boxShadow: hovered ? '0 8px 20px rgba(15,23,42,.08), 0 2px 6px rgba(15,23,42,.05)' : 'var(--dsw-shadow-card, 0 1px 2px rgba(0,0,0,.08))', padding: '11px 13px', color: 'inherit', display: 'grid', gap: 6, opacity: queued ? 0.72 : 1, transition: 'box-shadow 180ms ease' } },
|
|
159
291
|
React.createElement('button', { type: 'button', disabled: queued || navigating, onClick: () => navigate(task.childSessionId, group?.residentSessionId), style: { width: '100%', minWidth: 0, boxSizing: 'border-box', border: 0, padding: 0, background: 'transparent', color: 'inherit', cursor: queued || navigating ? 'default' : 'pointer', textAlign: 'left', display: 'grid', gap: 6, fontFamily: 'inherit' } },
|
|
160
|
-
React.createElement('strong', { title: task.objective, style: { fontSize: 13, lineHeight: 1.45, display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden', overflowWrap: 'anywhere' } }, task.objective),
|
|
292
|
+
React.createElement('strong', { title: task.title || task.objective, style: { fontSize: 13, lineHeight: 1.45, display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden', overflowWrap: 'anywhere' } }, task.title || task.objective),
|
|
161
293
|
React.createElement('div', { style: { display: 'flex', alignItems: 'center', minWidth: 0 } }, React.createElement('span', { title: group?.name || task.groupId, style: { ...pill(colors.muted), maxWidth: '100%', padding: '2px 7px', fontSize: 10.5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, group?.name || task.groupId)),
|
|
162
294
|
summary ? React.createElement('div', { title: String(summary), style: { minWidth: 0, fontSize: 11, lineHeight: 1.55, color: task.waitingReason ? colors.warning : colors.muted, display: '-webkit-box', WebkitLineClamp: 4, WebkitBoxOrient: 'vertical', overflow: 'hidden', overflowWrap: 'anywhere', wordBreak: 'break-word' } }, summary) : null,
|
|
163
295
|
React.createElement('div', { style: { minWidth: 0, marginTop: 3, paddingTop: 9, borderTop: `1px solid ${colors.border}`, display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 } },
|
|
@@ -184,7 +316,7 @@ window.__ModuleLoader__.load({
|
|
|
184
316
|
const active = alert.status !== 'resolved'
|
|
185
317
|
const category = classifyAlert(alert)
|
|
186
318
|
return React.createElement('div', { key: alert.alertId || alert.fingerprint || index, style: { padding: '12px 0', borderTop: `1px solid ${colors.border}` } },
|
|
187
|
-
React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', gap: 12 } }, React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
319
|
+
React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', gap: 12 } }, React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, statusTag(active ? '当前异常' : '已恢复', active ? 'error' : 'done'), React.createElement(Pill, null, category.label)), React.createElement('span', { style: { fontSize: 12, color: colors.muted } }, fmt(active ? alert.lastSeenAt : alert.resolvedAt || alert.lastSeenAt))),
|
|
188
320
|
React.createElement('div', { style: { marginTop: 5, fontSize: 12 } }, alert.message || alert.detail || alert.reason || JSON.stringify(alert)),
|
|
189
321
|
React.createElement('code', { style: { display: 'block', marginTop: 6, fontSize: 10.5, color: colors.muted } }, `${alert.taskId} · ${alert.fingerprint} · ${alert.count || 1} 次`))
|
|
190
322
|
}
|
|
@@ -200,38 +332,53 @@ window.__ModuleLoader__.load({
|
|
|
200
332
|
const bucketColumns = taskBuckets.map((bucket) => {
|
|
201
333
|
const tasks = (data?.tasks || []).filter((task) => task.state === bucket.state && !task.archivedAt).sort((left, right) => String(right.updatedAt).localeCompare(String(left.updatedAt)))
|
|
202
334
|
const taskCards = tasks.length ? tasks.map(renderTaskCard) : [React.createElement('div', { key: 'empty', style: { border: `1px dashed ${colors.border}`, borderRadius: 10, minHeight: 90, display: 'grid', placeItems: 'center', color: colors.muted, fontSize: 12 } }, '暂无任务')]
|
|
203
|
-
return React.createElement('div', { key: bucket.state, style: { boxSizing: 'border-box', borderRadius: 14, background: bucket.background, boxShadow: '0 4px 16px rgba(15,23,42,.07)', padding: 10, display: 'flex', flexDirection: 'column', height: '
|
|
335
|
+
return React.createElement('div', { key: bucket.state, style: { boxSizing: 'border-box', borderRadius: 14, background: bucket.background, boxShadow: '0 4px 16px rgba(15,23,42,.07)', padding: 10, display: 'flex', flexDirection: 'column', height: 'calc(100dvh - 238px)', minHeight: 420, maxHeight: 'calc(100dvh - 238px)', overflow: 'hidden' } },
|
|
204
336
|
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, padding: '4px 6px 10px' } }, React.createElement('strong', { style: { display: 'inline-flex', alignItems: 'center', borderRadius: 999, padding: '3px 9px', fontSize: 11.5, fontWeight: 650, lineHeight: 1.4, color: 'var(--dsw-alias-label-primary-inverted, #fff)', background: bucket.tone } }, bucket.label), React.createElement('span', { style: { borderRadius: 999, background: colors.cardSurface, padding: '2px 7px', fontSize: 10.5, color: colors.muted } }, tasks.length)),
|
|
205
337
|
React.createElement('div', { style: { flex: 1, minHeight: 0, padding: '8px 12px 16px 8px', display: 'flex', flexDirection: 'column', gap: 10, overflowY: 'scroll', scrollbarGutter: 'stable' } }, ...taskCards))
|
|
206
338
|
})
|
|
207
|
-
const menu = React.createElement('nav', { 'aria-label': '运行看板视图', style: { minWidth: 0, display: 'flex',
|
|
208
|
-
...pages.map((page) => React.createElement('button', { key: page.id, type: 'button', onClick: () => setActivePage(page.id), 'aria-current': activePage === page.id ? 'page' : undefined, style: { border: 0, borderRadius:
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
339
|
+
const menu = React.createElement('nav', { 'aria-label': '运行看板视图', style: { minWidth: 0, height: 38, display: 'flex', alignItems: 'stretch', gap: 28, overflowX: 'auto', padding: '0 24px' } },
|
|
340
|
+
...pages.map((page) => React.createElement('button', { key: page.id, type: 'button', onClick: () => setActivePage(page.id), 'aria-current': activePage === page.id ? 'page' : undefined, style: { position: 'relative', border: 0, borderBottom: activePage === page.id ? '2px solid #4d6bfe' : '2px solid transparent', borderRadius: 0, padding: '0 0 8px', cursor: 'pointer', font: 'inherit', fontSize: 13, fontWeight: 400, color: activePage === page.id ? '#4d6bfe' : colors.muted, background: 'transparent', outline: 'none', whiteSpace: 'nowrap' } }, page.label)))
|
|
341
|
+
const refreshIcon = React.createElement('svg', { width: 14, height: 14, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 1.8, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': true }, React.createElement('path', { d: 'M20 11a8 8 0 1 0-2.34 5.66' }), React.createElement('path', { d: 'M20 4v7h-7' }))
|
|
342
|
+
const header = React.createElement('header', { style: { position: 'sticky', top: 0, zIndex: 2, borderBottom: `1px solid ${colors.border}`, background: colors.surface, pointerEvents: 'auto' } },
|
|
343
|
+
React.createElement('div', { style: { height: 52, boxSizing: 'border-box', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 24, padding: '0 24px' } },
|
|
344
|
+
React.createElement('div', { style: { minWidth: 0, display: 'flex', alignItems: 'center', gap: 14 } },
|
|
345
|
+
React.createElement('span', { style: { fontSize: 15, fontWeight: 400, whiteSpace: 'nowrap' } }, '运行看板'),
|
|
346
|
+
React.createElement('div', { 'aria-label': updatedAt ? `运行正常,最近更新 ${fmt(updatedAt)}` : '正在连接运行时', style: { display: 'inline-flex', alignItems: 'center', gap: 7, minWidth: 0, color: colors.muted, fontSize: 11.5, whiteSpace: 'nowrap' } },
|
|
347
|
+
React.createElement(StateDot, { state: updatedAt ? 'done' : 'ongoing', size: 7 }),
|
|
348
|
+
React.createElement('span', { style: { color: updatedAt ? statusTone.done : colors.muted } }, updatedAt ? '运行正常' : '正在连接'),
|
|
349
|
+
updatedAt ? React.createElement('span', { 'aria-hidden': true, style: { width: 1, height: 12, background: colors.border } }) : null,
|
|
350
|
+
updatedAt ? React.createElement('span', { title: fmt(updatedAt) }, `更新于 ${fmtTime(updatedAt)}`) : null)),
|
|
351
|
+
React.createElement(Button, { variant: 'outline', size: 'sm', type: 'button', onClick: refresh, style: { gap: 6 } }, '刷新', refreshIcon)),
|
|
352
|
+
menu)
|
|
353
|
+
const dataViewTabs = React.createElement('div', { role: 'tablist', 'aria-label': '群聊数据视图', style: { display: 'flex', alignSelf: 'stretch', gap: 20 } },
|
|
354
|
+
...[{ id: 'messages', label: `群消息 ${selectedMessages.length}` }, { id: 'outbox', label: `Outbox ${selectedOutbox.length}` }].map((item) => React.createElement('button', { key: item.id, role: 'tab', 'aria-selected': groupTableView === item.id, type: 'button', onClick: () => setGroupTableView(item.id), style: { border: 0, borderBottom: groupTableView === item.id ? `2px solid ${colors.accent}` : '2px solid transparent', background: 'transparent', color: groupTableView === item.id ? colors.accent : colors.muted, padding: '0 2px', fontFamily: 'inherit', fontSize: 12, cursor: 'pointer' } }, item.label)))
|
|
355
|
+
const groupTableToolbar = React.createElement('div', { style: toolbar },
|
|
356
|
+
dataViewTabs,
|
|
357
|
+
React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: 8, flexWrap: 'wrap' } },
|
|
358
|
+
(data?.groups || []).length > 1 ? React.createElement(SelectMenu, { label: '选择群聊会话', value: selectedGroup?.groupId || '', options: (data?.groups || []).map((group) => ({ id: group.groupId, label: group.name || group.groupId })), onChange: (value) => { setSelectedGroupId(value); setMessagePage(1); setOutboxPage(1) }, minWidth: 220 }) : null,
|
|
359
|
+
groupTableView === 'messages'
|
|
360
|
+
? React.createElement(SelectMenu, { label: '筛选投递状态', value: messageDeliveryFilter, options: [{ id: 'all', label: '全部投递状态' }, { id: 'delivered', label: '已投递' }, { id: 'failed', label: '投递失败' }, { id: 'pending', label: '投递中' }, { id: 'skipped', label: '历史未投递' }, { id: 'unknown', label: '状态未知' }], onChange: (value) => { setMessageDeliveryFilter(value); setMessagePage(1) } })
|
|
361
|
+
: React.createElement(SelectMenu, { label: '筛选发件状态', value: outboxStatusFilter, options: [{ id: 'all', label: '全部发件状态' }, { id: 'confirmed', label: '已回读确认' }, { id: 'waiting', label: '待回读确认' }], onChange: (value) => { setOutboxStatusFilter(value); setOutboxPage(1) } })))
|
|
362
|
+
const messagesTable = React.createElement(React.Fragment, null,
|
|
363
|
+
React.createElement('div', { style: { overflowX: 'auto' } }, React.createElement('table', { style: { width: '100%', minWidth: 820, borderCollapse: 'collapse', tableLayout: 'fixed' } },
|
|
364
|
+
React.createElement('thead', null, React.createElement('tr', { style: { background: colors.surface2, textAlign: 'left' } }, React.createElement('th', { style: { ...tableHeadCell, width: 150 } }, '发送人 / 时间'), React.createElement('th', { style: tableHeadCell }, '消息内容'), React.createElement('th', { style: { ...tableHeadCell, width: 150 } }, 'Agent 投递'), React.createElement('th', { style: { ...tableHeadCell, width: 150 } }, '消息'))),
|
|
365
|
+
React.createElement('tbody', null, ...(messageRows.length ? messageRows : [React.createElement('tr', { key: 'empty' }, React.createElement('td', { colSpan: 4, style: { ...tableBodyCell, padding: 36, textAlign: 'center', color: colors.muted } }, '暂无符合条件的群聊消息'))])))),
|
|
366
|
+
React.createElement('div', { style: tableFooter }, React.createElement('span', { style: { marginRight: 'auto', fontSize: 11, color: colors.muted } }, `${filteredMessages.length} 条 · 每页 ${pageSize} 条`), React.createElement(Button, { variant: 'outline', size: 'sm', type: 'button', disabled: currentMessagePage <= 1, onClick: () => setMessagePage((page) => Math.max(1, page - 1)) }, '上一页'), React.createElement('span', { style: { fontSize: 11, color: colors.muted } }, `${currentMessagePage} / ${pageCount}`), React.createElement(Button, { variant: 'outline', size: 'sm', type: 'button', disabled: currentMessagePage >= pageCount, onClick: () => setMessagePage((page) => Math.min(pageCount, page + 1)) }, '下一页')))
|
|
367
|
+
const outboxTable = React.createElement(React.Fragment, null,
|
|
368
|
+
React.createElement('div', { style: { overflowX: 'auto' } }, React.createElement('table', { style: { width: '100%', minWidth: 900, borderCollapse: 'collapse', tableLayout: 'fixed' } },
|
|
369
|
+
React.createElement('thead', null, React.createElement('tr', { style: { background: colors.surface2, textAlign: 'left' } }, React.createElement('th', { style: { ...tableHeadCell, width: 130 } }, '发送状态'), React.createElement('th', { style: tableHeadCell }, '消息内容'), React.createElement('th', { style: { ...tableHeadCell, width: 180 } }, '来源 / 回复目标'), React.createElement('th', { style: { ...tableHeadCell, width: 170 } }, '投递消息 ID'), React.createElement('th', { style: { ...tableHeadCell, width: 150 } }, 'Outbox ID'))),
|
|
370
|
+
React.createElement('tbody', null, ...(outboxRows.length ? outboxRows : [React.createElement('tr', { key: 'empty' }, React.createElement('td', { colSpan: 5, style: { ...tableBodyCell, padding: 36, textAlign: 'center', color: colors.muted } }, '暂无符合条件的 Agent 发件记录'))])))),
|
|
371
|
+
React.createElement('div', { style: tableFooter }, React.createElement('span', { style: { marginRight: 'auto', fontSize: 11, color: colors.muted } }, `${filteredOutbox.length} 条 · 每页 ${outboxPageSize} 条`), React.createElement(Button, { variant: 'outline', size: 'sm', type: 'button', disabled: currentOutboxPage <= 1, onClick: () => setOutboxPage((page) => Math.max(1, page - 1)) }, '上一页'), React.createElement('span', { style: { fontSize: 11, color: colors.muted } }, `${currentOutboxPage} / ${outboxPageCount}`), React.createElement(Button, { variant: 'outline', size: 'sm', type: 'button', disabled: currentOutboxPage >= outboxPageCount, onClick: () => setOutboxPage((page) => Math.min(outboxPageCount, page + 1)) }, '下一页')))
|
|
213
372
|
const groupsPage = React.createElement(React.Fragment, null,
|
|
214
|
-
|
|
215
|
-
React.createElement('section', null,
|
|
216
|
-
|
|
217
|
-
React.createElement('h2', { style: { margin: 0, fontSize: 16 } }, '群聊消息'),
|
|
218
|
-
React.createElement('select', { 'aria-label': '选择群聊会话', value: selectedGroup?.groupId || '', onChange: (event) => { setSelectedGroupId(event.target.value); setMessagePage(1) }, style: { minWidth: 240, border: `1px solid ${colors.border}`, borderRadius: 8, background: colors.cardSurface, color: 'inherit', padding: '7px 10px', fontFamily: 'inherit', fontSize: 12 } }, ...(data?.groups || []).map((group) => React.createElement('option', { key: group.groupId, value: group.groupId }, group.name || group.groupId))),
|
|
219
|
-
React.createElement('span', { style: { fontSize: 11, color: colors.muted } }, `${selectedMessages.length} 条 · 每页 ${pageSize} 条`)),
|
|
220
|
-
React.createElement('div', { style: { ...card, padding: 0, overflow: 'hidden', boxShadow: '0 4px 16px rgba(15,23,42,.07)' } },
|
|
221
|
-
React.createElement('div', { style: { overflowX: 'auto' } }, React.createElement('table', { style: { width: '100%', minWidth: 820, borderCollapse: 'collapse', tableLayout: 'fixed' } },
|
|
222
|
-
React.createElement('thead', null, React.createElement('tr', { style: { background: colors.surface2, textAlign: 'left', fontSize: 11, color: colors.muted } }, React.createElement('th', { style: { padding: '14px 12px', width: 150 } }, '发送人 / 时间'), React.createElement('th', { style: { padding: '14px 12px' } }, '消息内容'), React.createElement('th', { style: { padding: '14px 12px', width: 150 } }, 'Agent 投递'), React.createElement('th', { style: { padding: '14px 12px', width: 150 } }, '消息'))),
|
|
223
|
-
React.createElement('tbody', null, ...(messageRows.length ? messageRows : [React.createElement('tr', { key: 'empty' }, React.createElement('td', { colSpan: 4, style: { padding: 36, textAlign: 'center', color: colors.muted, fontSize: 12 } }, '暂无群聊消息'))])))),
|
|
224
|
-
React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', alignItems: 'center', gap: 8, padding: '10px 12px' } },
|
|
225
|
-
React.createElement('button', { type: 'button', disabled: currentMessagePage <= 1, onClick: () => setMessagePage((page) => Math.max(1, page - 1)), style: { border: `1px solid ${colors.border}`, borderRadius: 8, background: colors.cardSurface, color: 'inherit', padding: '5px 10px', cursor: currentMessagePage <= 1 ? 'default' : 'pointer', opacity: currentMessagePage <= 1 ? 0.45 : 1 } }, '上一页'),
|
|
226
|
-
React.createElement('span', { style: { fontSize: 11, color: colors.muted } }, `${currentMessagePage} / ${pageCount}`),
|
|
227
|
-
React.createElement('button', { type: 'button', disabled: currentMessagePage >= pageCount, onClick: () => setMessagePage((page) => Math.min(pageCount, page + 1)), style: { border: `1px solid ${colors.border}`, borderRadius: 8, background: colors.cardSurface, color: 'inherit', padding: '5px 10px', cursor: currentMessagePage >= pageCount ? 'default' : 'pointer', opacity: currentMessagePage >= pageCount ? 0.45 : 1 } }, '下一页')))))
|
|
228
|
-
const tasksPage = React.createElement('section', null, React.createElement('h2', { style: { margin: '0 0 12px', fontSize: 16 } }, '任务'), React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(4, minmax(320px, 1fr))', gap: 16, overflowX: 'auto', alignItems: 'start', paddingBottom: 8 } }, ...bucketColumns))
|
|
373
|
+
pageHeader('群聊会话', '查看常驻会话、群消息接收状态与 Agent 发件记录。', (data?.groups || []).length),
|
|
374
|
+
React.createElement('section', null, sectionHeader('消息记录', '群消息与 Agent 发件记录'), React.createElement('div', { style: tableFrame }, selectedGroupSummary, groupTableToolbar, groupTableView === 'messages' ? messagesTable : outboxTable)))
|
|
375
|
+
const tasksPage = React.createElement(React.Fragment, null, pageHeader('任务看板', '按执行阶段查看任务、叶子会话和目标进展。', (data?.tasks || []).filter((task) => !task.archivedAt).length), React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(4, minmax(320px, 1fr))', gap: 16, overflowX: 'auto', alignItems: 'start', paddingBottom: 8 } }, ...bucketColumns))
|
|
229
376
|
const authorizationStatus = {
|
|
230
|
-
'pending-send': { label: '
|
|
231
|
-
'waiting-reply': { label: '
|
|
232
|
-
answered: { label: '已处理',
|
|
377
|
+
'pending-send': { label: '待发送', state: 'warning' },
|
|
378
|
+
'waiting-reply': { label: '待批复', state: 'warning' },
|
|
379
|
+
answered: { label: '已处理', state: 'done' },
|
|
233
380
|
}
|
|
234
|
-
const authorizationDecision = { approved: { label: '已批准',
|
|
381
|
+
const authorizationDecision = { approved: { label: '已批准', state: 'done' }, rejected: { label: '已拒绝', state: 'error' } }
|
|
235
382
|
const decideAuthorization = async (requestId, decision) => {
|
|
236
383
|
try {
|
|
237
384
|
setNavigationError(undefined); setDecidingAuthorizationId(requestId)
|
|
@@ -244,10 +391,11 @@ window.__ModuleLoader__.load({
|
|
|
244
391
|
const leftPending = left.status === 'answered' ? 0 : 1; const rightPending = right.status === 'answered' ? 0 : 1
|
|
245
392
|
return rightPending - leftPending || String(right.createdAt || '').localeCompare(String(left.createdAt || ''))
|
|
246
393
|
})
|
|
394
|
+
const filteredAuthorizationItems = authorizationItems.filter((item) => authorizationFilter === 'all' || (authorizationFilter === 'pending' ? item.status !== 'answered' : item.decision === authorizationFilter))
|
|
247
395
|
const authorizationPageSize = 10
|
|
248
|
-
const authorizationPageCount = Math.max(1, Math.ceil(
|
|
396
|
+
const authorizationPageCount = Math.max(1, Math.ceil(filteredAuthorizationItems.length / authorizationPageSize))
|
|
249
397
|
const currentAuthorizationPage = Math.min(authorizationPage, authorizationPageCount)
|
|
250
|
-
const visibleAuthorizationItems =
|
|
398
|
+
const visibleAuthorizationItems = filteredAuthorizationItems.slice((currentAuthorizationPage - 1) * authorizationPageSize, currentAuthorizationPage * authorizationPageSize)
|
|
251
399
|
const selectedAuthorization = authorizationItems.find((item) => item.requestId === selectedAuthorizationId)
|
|
252
400
|
const authorizationDetailField = (label, value) => React.createElement('div', { style: { display: 'grid', gap: 5 } },
|
|
253
401
|
React.createElement('strong', { style: { fontSize: 11, color: colors.muted } }, label),
|
|
@@ -257,66 +405,66 @@ window.__ModuleLoader__.load({
|
|
|
257
405
|
const status = pending ? authorizationStatus[item.status] : authorizationDecision[item.decision] || authorizationStatus.answered
|
|
258
406
|
const group = groupsById.get(item.groupId)
|
|
259
407
|
return React.createElement('div', { key: item.requestId, style: { width: '100%', minWidth: 0, boxSizing: 'border-box', display: 'grid', gridTemplateColumns: '88px minmax(260px, 1.1fr) minmax(300px, 1.5fr) 130px 44px', alignItems: 'center', gap: 18, borderTop: `1px solid ${colors.border}`, background: 'transparent', color: 'inherit', padding: '15px 18px' } },
|
|
260
|
-
|
|
408
|
+
authorizationTag(status.label, status.state),
|
|
261
409
|
React.createElement('span', { style: { minWidth: 0 } }, React.createElement('strong', { title: item.objective, style: { display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden', fontSize: 12.5, lineHeight: 1.5 } }, item.objective), React.createElement('span', { style: { display: 'block', marginTop: 5, color: colors.muted, fontSize: 10.5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, group?.name || item.groupId)),
|
|
262
410
|
React.createElement('span', { style: { minWidth: 0 } }, React.createElement('span', { title: item.requestedAction, style: { display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden', fontSize: 11.5, lineHeight: 1.55, color: colors.muted } }, item.requestedAction), item.risk && item.risk !== '未单独说明' ? React.createElement('span', { style: { display: 'block', marginTop: 6, color: colors.danger, fontSize: 10.5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, `风险 · ${item.risk}`) : null),
|
|
263
411
|
React.createElement('span', { style: { color: colors.muted, fontSize: 10.5, lineHeight: 1.5 } }, fmt(item.createdAt)),
|
|
264
|
-
React.createElement('
|
|
412
|
+
React.createElement(Button, { variant: 'ghost', size: 'sm', type: 'button', onClick: () => setSelectedAuthorizationId(item.requestId) }, '查看'))
|
|
265
413
|
})
|
|
266
414
|
const authorizationDetail = selectedAuthorization ? React.createElement('div', { role: 'presentation', onMouseDown: (event) => { if (event.target === event.currentTarget) setSelectedAuthorizationId('') }, style: { position: 'fixed', inset: 0, zIndex: 1100, display: 'flex', justifyContent: 'flex-end', background: 'rgba(0,0,0,.28)', backdropFilter: 'blur(2px)' } },
|
|
267
415
|
React.createElement('section', { role: 'dialog', 'aria-modal': true, 'aria-label': '授权申请单详情', style: { width: 'min(640px, 100%)', height: '100%', display: 'flex', flexDirection: 'column', overflow: 'hidden', borderLeft: `1px solid ${colors.border}`, background: colors.surface, boxShadow: '-20px 0 60px rgba(0,0,0,.2)' } },
|
|
268
|
-
React.createElement('header', { style: { flex: '0 0 auto', display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap:
|
|
269
|
-
React.createElement('div', { style: { minWidth: 0 } }, React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap:
|
|
270
|
-
React.createElement('
|
|
271
|
-
React.createElement('div', { style: { flex: '1 1 auto', overflowY: 'auto', padding: '0 24px' } },
|
|
272
|
-
React.createElement('section', { style: { padding: '
|
|
273
|
-
React.createElement('section', { style: { padding: '
|
|
274
|
-
React.createElement('section', { style: { padding: '
|
|
416
|
+
React.createElement('header', { style: { flex: '0 0 auto', display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 24, padding: '24px 24px 20px', borderBottom: `1px solid ${colors.border}`, background: colors.surface } },
|
|
417
|
+
React.createElement('div', { style: { minWidth: 0 } }, React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 } }, authorizationTag(selectedAuthorization.status === 'answered' ? (authorizationDecision[selectedAuthorization.decision]?.label || '已处理') : (authorizationStatus[selectedAuthorization.status]?.label || '待批复'), selectedAuthorization.status === 'answered' ? (authorizationDecision[selectedAuthorization.decision]?.state || 'done') : 'warning'), React.createElement('span', { style: { color: colors.muted, fontSize: 11.5 } }, fmt(selectedAuthorization.createdAt))), React.createElement('h3', { style: { margin: 0, fontSize: 18, fontWeight: 600, lineHeight: 1.5 } }, selectedAuthorization.objective || '授权申请'), React.createElement('code', { style: { display: 'block', marginTop: 10, color: colors.muted, fontSize: 10.5 } }, selectedAuthorization.requestId)),
|
|
418
|
+
React.createElement(Button, { variant: 'ghost', size: 'sm', type: 'button', 'aria-label': '关闭申请单', onClick: () => setSelectedAuthorizationId(''), style: { flex: '0 0 auto', minWidth: 52, whiteSpace: 'nowrap' } }, '关闭')),
|
|
419
|
+
React.createElement('div', { style: { flex: '1 1 auto', overflowY: 'auto', padding: '0 24px 24px' } },
|
|
420
|
+
React.createElement('section', { style: { padding: '24px 0', borderBottom: `1px solid ${colors.border}` } }, authorizationDetailField('申请范围', selectedAuthorization.requestedAction)),
|
|
421
|
+
React.createElement('section', { style: { padding: '24px 0', borderBottom: `1px solid ${colors.border}` } }, authorizationDetailField('风险', selectedAuthorization.risk || '未单独说明')),
|
|
422
|
+
React.createElement('section', { style: { padding: '24px 0', borderBottom: `1px solid ${colors.border}` } }, authorizationDetailField('阻塞原因', selectedAuthorization.waitingReason)),
|
|
275
423
|
React.createElement('details', { open: true, style: { padding: '18px 0', borderBottom: `1px solid ${colors.border}` } }, React.createElement('summary', { style: { cursor: 'pointer', fontSize: 12, fontWeight: 600 } }, `现场证据 · ${(selectedAuthorization.evidence || []).length}`), React.createElement('div', { style: { marginTop: 14 } }, authorizationDetailField('', (selectedAuthorization.evidence || []).map((value, index) => `${index + 1}. ${value}`).join('\n')))),
|
|
276
424
|
React.createElement('details', { style: { padding: '18px 0', borderBottom: `1px solid ${colors.border}` } }, React.createElement('summary', { style: { cursor: 'pointer', fontSize: 12, fontWeight: 600 } }, `已尝试 · ${(selectedAuthorization.attemptedActions || []).length}`), React.createElement('div', { style: { marginTop: 14 } }, authorizationDetailField('', (selectedAuthorization.attemptedActions || []).map((value, index) => `${index + 1}. ${value}`).join('\n')))),
|
|
277
425
|
React.createElement('section', { style: { padding: '20px 0', borderBottom: `1px solid ${colors.border}` } }, authorizationDetailField('关联信息', `Task ID:${selectedAuthorization.taskId || '—'}\n群聊:${groupsById.get(selectedAuthorization.groupId)?.name || selectedAuthorization.groupId || '—'}`)),
|
|
278
426
|
selectedAuthorization.status === 'answered' ? React.createElement('section', { style: { padding: '20px 0' } }, authorizationDetailField('批复结果', `${selectedAuthorization.decision === 'approved' ? '批准' : selectedAuthorization.decision === 'rejected' ? '拒绝' : selectedAuthorization.decision || '已处理'}\n批复渠道:${selectedAuthorization.decisionSource === 'web' ? '运行看板' : selectedAuthorization.decisionSource === 'dingtalk' ? '钉钉私聊' : selectedAuthorization.decisionSource || '历史迁移'}\n批复时间:${fmt(selectedAuthorization.decidedAt)}\n批复内容:${selectedAuthorization.reply || '—'}`)) : null),
|
|
279
|
-
selectedAuthorization.status !== 'answered' ? React.createElement('footer', { style: { flex: '0 0 auto', display: 'grid', gridTemplateColumns: '1fr auto auto', gap: 10, alignItems: 'center', padding: '
|
|
427
|
+
selectedAuthorization.status !== 'answered' ? React.createElement('footer', { style: { flex: '0 0 auto', display: 'grid', gridTemplateColumns: '1fr auto auto', gap: 10, alignItems: 'center', padding: '16px 24px', borderTop: `1px solid ${colors.border}`, background: colors.surface2 } },
|
|
280
428
|
React.createElement('input', { 'aria-label': `审批意见 ${selectedAuthorization.requestId}`, value: authorizationComments[selectedAuthorization.requestId] || '', onChange: (event) => setAuthorizationComments((current) => ({ ...current, [selectedAuthorization.requestId]: event.target.value })), placeholder: '审批意见(可选)', style: { minWidth: 0, border: `1px solid ${colors.border}`, borderRadius: 9, background: colors.surface2, color: 'inherit', padding: '9px 11px', fontFamily: 'inherit', fontSize: 12 } }),
|
|
281
|
-
React.createElement('
|
|
282
|
-
React.createElement('
|
|
283
|
-
const authorizationsPage = React.createElement(React.Fragment, null, React.createElement('section', null,
|
|
284
|
-
|
|
285
|
-
React.createElement('div', { style:
|
|
286
|
-
React.createElement('div', { style:
|
|
429
|
+
React.createElement(Button, { variant: 'outline', size: 'sm', type: 'button', disabled: decidingAuthorizationId === selectedAuthorization.requestId, onClick: () => decideAuthorization(selectedAuthorization.requestId, 'rejected'), style: { color: colors.danger } }, '拒绝'),
|
|
430
|
+
React.createElement(Button, { variant: 'primary', size: 'sm', type: 'button', disabled: decidingAuthorizationId === selectedAuthorization.requestId, onClick: () => decideAuthorization(selectedAuthorization.requestId, 'approved') }, decidingAuthorizationId === selectedAuthorization.requestId ? '处理中…' : '批准')) : null)) : null
|
|
431
|
+
const authorizationsPage = React.createElement(React.Fragment, null, pageHeader('授权审批', '处理需要人工确认的操作范围,并保留风险、证据和批复记录。', authorizationItems.filter((item) => item.status !== 'answered').length), React.createElement('section', null,
|
|
432
|
+
sectionHeader('审批记录', `${authorizationItems.length} 条 · 每页 ${authorizationPageSize} 条`),
|
|
433
|
+
React.createElement('div', { style: tableFrame },
|
|
434
|
+
React.createElement('div', { style: toolbar }, React.createElement('span', { style: { fontSize: 12, color: colors.muted } }, `当前显示 ${filteredAuthorizationItems.length} 条`), React.createElement(SelectMenu, { label: '筛选审批状态', value: authorizationFilter, options: [{ id: 'all', label: '全部审批状态' }, { id: 'pending', label: '待批复' }, { id: 'approved', label: '已批准' }, { id: 'rejected', label: '已拒绝' }], onChange: (value) => { setAuthorizationFilter(value); setAuthorizationPage(1) } })),
|
|
435
|
+
React.createElement('div', { style: { height: 40, boxSizing: 'border-box', display: 'grid', gridTemplateColumns: '88px minmax(260px, 1.1fr) minmax(300px, 1.5fr) 130px 44px', alignItems: 'center', gap: 18, padding: '0 18px', background: colors.surface2, color: colors.muted, fontSize: 11, fontWeight: 500 } }, React.createElement('span', null, '状态'), React.createElement('span', null, '任务'), React.createElement('span', null, '申请摘要'), React.createElement('span', null, '申请时间'), React.createElement('span', null, '操作')),
|
|
287
436
|
React.createElement('div', null, ...(authorizationRows.length ? authorizationRows : [React.createElement('div', { key: 'empty', style: { padding: 36, textAlign: 'center', color: colors.muted, fontSize: 12 } }, '暂无授权申请')])),
|
|
288
|
-
React.createElement('div', { style:
|
|
289
|
-
React.createElement('
|
|
437
|
+
React.createElement('div', { style: tableFooter },
|
|
438
|
+
React.createElement(Button, { variant: 'outline', size: 'sm', type: 'button', disabled: currentAuthorizationPage <= 1, onClick: () => setAuthorizationPage((page) => Math.max(1, page - 1)) }, '上一页'),
|
|
290
439
|
React.createElement('span', { style: { fontSize: 11, color: colors.muted } }, `${currentAuthorizationPage} / ${authorizationPageCount}`),
|
|
291
|
-
React.createElement('
|
|
440
|
+
React.createElement(Button, { variant: 'outline', size: 'sm', type: 'button', disabled: currentAuthorizationPage >= authorizationPageCount, onClick: () => setAuthorizationPage((page) => Math.min(authorizationPageCount, page + 1)) }, '下一页')))
|
|
292
441
|
), authorizationDetail)
|
|
293
442
|
const archivedTasks = (data?.tasks || []).filter((task) => task.state === 'completed' && task.archivedAt).sort((left, right) => String(right.archivedAt).localeCompare(String(left.archivedAt)))
|
|
294
443
|
const archivePage = React.createElement('section', null,
|
|
295
|
-
|
|
296
|
-
React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: 12 } }, ...(archivedTasks.length ? archivedTasks.map(renderTaskCard) : [React.createElement(
|
|
444
|
+
pageHeader('归档任务', '归档只影响看板展示,Task、Session、Goal 与历史证据仍完整保留。', archivedTasks.length),
|
|
445
|
+
React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: 12 } }, ...(archivedTasks.length ? archivedTasks.map(renderTaskCard) : [React.createElement(React.Fragment, { key: 'empty' }, emptyState('暂无归档任务'))]))
|
|
297
446
|
)
|
|
298
447
|
const alertsPage = React.createElement(React.Fragment, null,
|
|
299
|
-
|
|
300
|
-
React.createElement('section',
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
React.createElement('div', { style: {
|
|
305
|
-
|
|
306
|
-
React.createElement('span', { style: { fontSize: 11, color: colors.muted } }, `${currentResolvedAlertPage} / ${resolvedAlertPageCount} · 每页 ${resolvedAlertPageSize} 条`),
|
|
307
|
-
React.createElement('button', { type: 'button', disabled: currentResolvedAlertPage >= resolvedAlertPageCount, onClick: () => setResolvedAlertPage((page) => Math.min(resolvedAlertPageCount, page + 1)), style: { border: `1px solid ${colors.border}`, borderRadius: 8, background: colors.cardSurface, color: 'inherit', padding: '5px 10px', cursor: currentResolvedAlertPage >= resolvedAlertPageCount ? 'default' : 'pointer', opacity: currentResolvedAlertPage >= resolvedAlertPageCount ? 0.45 : 1 } }, '下一页')))
|
|
448
|
+
pageHeader('告警', '集中查看消息通道、叶子会话、任务目标和 Runtime 的当前异常与恢复历史。', filteredActiveAlerts.length),
|
|
449
|
+
React.createElement('section', null, sectionHeader('告警记录', '按状态和类型筛选'), React.createElement('div', { style: tableFrame },
|
|
450
|
+
React.createElement('div', { style: toolbar },
|
|
451
|
+
React.createElement('div', { role: 'tablist', 'aria-label': '告警状态视图', style: { display: 'flex', alignSelf: 'stretch', gap: 20 } }, ...[{ id: 'active', label: `当前异常 ${filteredActiveAlerts.length}` }, { id: 'resolved', label: `恢复历史 ${filteredResolvedAlerts.length}` }].map((item) => React.createElement('button', { key: item.id, role: 'tab', 'aria-selected': alertView === item.id, type: 'button', onClick: () => setAlertView(item.id), style: { border: 0, borderBottom: alertView === item.id ? `2px solid ${colors.accent}` : '2px solid transparent', background: 'transparent', color: alertView === item.id ? colors.accent : colors.muted, padding: '0 2px', fontFamily: 'inherit', fontSize: 12, cursor: 'pointer' } }, item.label))),
|
|
452
|
+
React.createElement(SelectMenu, { label: '筛选告警类型', value: alertType, options: alertCategories.map((category) => ({ id: category.id, label: category.label })), onChange: (value) => { setAlertType(value); setResolvedAlertPage(1) } })),
|
|
453
|
+
React.createElement('div', { style: { padding: '0 16px' } }, ...(alertView === 'active' ? (filteredActiveAlerts.length ? filteredActiveAlerts.map(renderAlert) : [React.createElement('div', { key: 'empty', style: { padding: '36px 0', textAlign: 'center', color: colors.muted, fontSize: 12 } }, '当前没有此类型异常')]) : (visibleResolvedAlerts.length ? visibleResolvedAlerts.map(renderAlert) : [React.createElement('div', { key: 'empty', style: { padding: '36px 0', textAlign: 'center', color: colors.muted, fontSize: 12 } }, '暂无此类型恢复记录')]))),
|
|
454
|
+
alertView === 'resolved' ? React.createElement('div', { style: tableFooter }, React.createElement('span', { style: { marginRight: 'auto', fontSize: 11, color: colors.muted } }, `${filteredResolvedAlerts.length} 条 · 每页 ${resolvedAlertPageSize} 条`), React.createElement(Button, { variant: 'outline', size: 'sm', type: 'button', disabled: currentResolvedAlertPage <= 1, onClick: () => setResolvedAlertPage((page) => Math.max(1, page - 1)) }, '上一页'), React.createElement('span', { style: { fontSize: 11, color: colors.muted } }, `${currentResolvedAlertPage} / ${resolvedAlertPageCount}`), React.createElement(Button, { variant: 'outline', size: 'sm', type: 'button', disabled: currentResolvedAlertPage >= resolvedAlertPageCount, onClick: () => setResolvedAlertPage((page) => Math.min(resolvedAlertPageCount, page + 1)) }, '下一页')) : null))
|
|
308
455
|
)
|
|
309
456
|
const pageContent = activePage === 'tasks' ? tasksPage : activePage === 'authorizations' ? authorizationsPage : activePage === 'archive' ? archivePage : activePage === 'alerts' ? alertsPage : groupsPage
|
|
310
|
-
const
|
|
457
|
+
const pageViewport = React.createElement('div', { style: { minHeight: 'calc(100dvh - 138px)', boxSizing: 'border-box' } }, pageContent)
|
|
458
|
+
const main = React.createElement('main', { style: { maxWidth: activePage === 'tasks' ? 1480 : 1240, minHeight: 'calc(100dvh - 90px)', boxSizing: 'border-box', margin: '0 auto', padding: '24px 24px 32px', display: 'grid', alignContent: 'start', gap: 20, pointerEvents: 'auto' } },
|
|
311
459
|
error ? React.createElement('div', { style: { ...card, borderColor: colors.danger, color: colors.danger } }, `无法连接 resident 插件:${error}`) : null,
|
|
312
460
|
navigationError ? React.createElement('div', { style: { ...card, borderColor: colors.danger, color: colors.danger } }, `无法打开 DSH Session:${navigationError}`) : null,
|
|
313
|
-
|
|
461
|
+
pageViewport
|
|
314
462
|
)
|
|
315
|
-
return React.createElement('div', {
|
|
463
|
+
return React.createElement('div', { ref: overlayRef, style: { position: 'fixed', inset: 0, paddingLeft: sidebarWidth, boxSizing: 'border-box', zIndex: 900, minWidth: 0, background: colors.surface, color: 'inherit', overflow: 'auto', pointerEvents: 'none' } }, header, main)
|
|
316
464
|
}
|
|
317
465
|
function apply(ctx) {
|
|
318
|
-
ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register({ name: 'sidebar.footer.action', id: 'dingtalk-dsh-observer-entry', order: 5, inject: () => ({}) },
|
|
319
|
-
ctx.slots.inject('shell.overlay', () => ctx.slots.register({ name: 'shell.overlay', id: 'dingtalk-dsh-observer
|
|
466
|
+
ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register({ name: 'sidebar.footer.action', id: 'dingtalk-dsh-observer-entry', order: 5, inject: () => ({}) }, SidebarAction))
|
|
467
|
+
ctx.slots.inject('shell.overlay', () => ctx.slots.register({ name: 'shell.overlay', id: 'dingtalk-dsh-observer', order: 20, inject: () => ({ openSession: async (sessionId, parentSessionId) => {
|
|
320
468
|
if (parentSessionId) {
|
|
321
469
|
let address = ctx.sessions.subagentAddress(sessionId)
|
|
322
470
|
if (!address) { await ctx.sessions.refreshSubagents(parentSessionId); address = ctx.sessions.subagentAddress(sessionId) }
|