@zzusp/dingtalk-dsh-observer 0.5.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/web-client.js +36 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zzusp/dingtalk-dsh-observer",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "DingTalk task board client for DeepSeek Harness",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
package/web-client.js CHANGED
@@ -4,7 +4,7 @@ window.__ModuleLoader__.load({
4
4
  const module = { exports: {} }
5
5
  const React = require('react')
6
6
  const { Button, Menu, Pill, StateDot } = require('@deepseek-ai/dsh-client-ui-primitives')
7
- const { useCallback, useEffect, useState } = React
7
+ const { useCallback, useEffect, useLayoutEffect, useRef, useState } = React
8
8
  const ENDPOINT = 'http://127.0.0.1:18998'
9
9
  const colors = {
10
10
  surface: 'var(--dsw-alias-bg-base, #fff)',
@@ -69,7 +69,7 @@ window.__ModuleLoader__.load({
69
69
  }
70
70
  const authorizationTag = (label, state) => {
71
71
  const tone = statusTone[state] || colors.muted
72
- return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6, minWidth: 68, minHeight: 26, boxSizing: 'border-box', border: `1px solid color-mix(in srgb, ${tone} 24%, transparent)`, borderRadius: 999, background: `color-mix(in srgb, ${tone} 9%, transparent)`, color: tone, padding: '3px 9px', fontSize: 11, fontWeight: 550, lineHeight: 1.4, whiteSpace: 'nowrap' } }, React.createElement(StateDot, { state, size: 7 }), label)
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
73
  }
74
74
  function SelectMenu({ label, value, options, onChange, minWidth = 150 }) {
75
75
  const [open, setOpen] = useState(false)
@@ -144,11 +144,12 @@ window.__ModuleLoader__.load({
144
144
  const [authorizationFilter, setAuthorizationFilter] = useState('all')
145
145
  const [selectedAuthorizationId, setSelectedAuthorizationId] = useState('')
146
146
  const [sidebarWidth, setSidebarWidth] = useState(280)
147
+ const overlayRef = useRef()
147
148
  const refresh = useCallback(async () => {
148
149
  try { setData(await load()); setUpdatedAt(new Date()); setError(undefined) }
149
150
  catch (cause) { setError(cause instanceof Error ? cause.message : String(cause)) }
150
151
  }, [])
151
- useEffect(() => {
152
+ useLayoutEffect(() => {
152
153
  if (!isOpen) return undefined
153
154
  refresh()
154
155
  const timer = window.setInterval(refresh, 5000)
@@ -164,14 +165,38 @@ window.__ModuleLoader__.load({
164
165
  sidebar = sidebar.parentElement
165
166
  }
166
167
  const update = () => {
167
- const width = sidebar?.getBoundingClientRect().width
168
- if (Number.isFinite(width) && width >= 40 && width <= 400) setSidebarWidth(Math.round(width))
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)
169
186
  }
170
187
  update()
171
188
  const observer = sidebar && typeof ResizeObserver === 'function' ? new ResizeObserver(update) : undefined
172
189
  if (sidebar && observer) observer.observe(sidebar)
190
+ sidebar?.addEventListener('transitionrun', trackTransition)
191
+ document.addEventListener('click', trackTransition, true)
173
192
  window.addEventListener('resize', update)
174
- return () => { observer?.disconnect(); window.removeEventListener('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
+ }
175
200
  }, [isOpen])
176
201
  useEffect(() => {
177
202
  const closeForSession = (event) => {
@@ -313,7 +338,8 @@ window.__ModuleLoader__.load({
313
338
  })
314
339
  const menu = React.createElement('nav', { 'aria-label': '运行看板视图', style: { minWidth: 0, height: 38, display: 'flex', alignItems: 'stretch', gap: 28, overflowX: 'auto', padding: '0 24px' } },
315
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)))
316
- const header = React.createElement('header', { style: { position: 'sticky', top: 0, zIndex: 2, borderBottom: `1px solid ${colors.border}`, background: colors.surface } },
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' } },
317
343
  React.createElement('div', { style: { height: 52, boxSizing: 'border-box', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 24, padding: '0 24px' } },
318
344
  React.createElement('div', { style: { minWidth: 0, display: 'flex', alignItems: 'center', gap: 14 } },
319
345
  React.createElement('span', { style: { fontSize: 15, fontWeight: 400, whiteSpace: 'nowrap' } }, '运行看板'),
@@ -322,7 +348,7 @@ window.__ModuleLoader__.load({
322
348
  React.createElement('span', { style: { color: updatedAt ? statusTone.done : colors.muted } }, updatedAt ? '运行正常' : '正在连接'),
323
349
  updatedAt ? React.createElement('span', { 'aria-hidden': true, style: { width: 1, height: 12, background: colors.border } }) : null,
324
350
  updatedAt ? React.createElement('span', { title: fmt(updatedAt) }, `更新于 ${fmtTime(updatedAt)}`) : null)),
325
- React.createElement(Button, { variant: 'outline', size: 'sm', type: 'button', onClick: refresh }, '刷新')),
351
+ React.createElement(Button, { variant: 'outline', size: 'sm', type: 'button', onClick: refresh, style: { gap: 6 } }, '刷新', refreshIcon)),
326
352
  menu)
327
353
  const dataViewTabs = React.createElement('div', { role: 'tablist', 'aria-label': '群聊数据视图', style: { display: 'flex', alignSelf: 'stretch', gap: 20 } },
328
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)))
@@ -429,12 +455,12 @@ window.__ModuleLoader__.load({
429
455
  )
430
456
  const pageContent = activePage === 'tasks' ? tasksPage : activePage === 'authorizations' ? authorizationsPage : activePage === 'archive' ? archivePage : activePage === 'alerts' ? alertsPage : groupsPage
431
457
  const pageViewport = React.createElement('div', { style: { minHeight: 'calc(100dvh - 138px)', boxSizing: 'border-box' } }, pageContent)
432
- 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 } },
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' } },
433
459
  error ? React.createElement('div', { style: { ...card, borderColor: colors.danger, color: colors.danger } }, `无法连接 resident 插件:${error}`) : null,
434
460
  navigationError ? React.createElement('div', { style: { ...card, borderColor: colors.danger, color: colors.danger } }, `无法打开 DSH Session:${navigationError}`) : null,
435
461
  pageViewport
436
462
  )
437
- return React.createElement('div', { style: { position: 'fixed', top: 0, right: 0, bottom: 0, left: sidebarWidth, zIndex: 900, minWidth: 0, background: colors.surface, color: 'inherit', overflow: 'auto' } }, header, main)
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)
438
464
  }
439
465
  function apply(ctx) {
440
466
  ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register({ name: 'sidebar.footer.action', id: 'dingtalk-dsh-observer-entry', order: 5, inject: () => ({}) }, SidebarAction))