dsh-file-activity 0.5.4 → 0.5.5

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/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  本文件记录 dsh-file-activity 的所有版本变更。格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [0.5.5] - 2026-09-02
6
+
7
+ ### 变更
8
+
9
+ - fix(scripts): #72 插件依赖未随安装自动安装(dsh-shared 未发布 npm) (#96)
10
+ - fix(file-activity): 浮窗预览失焦自动关闭(issue #76) (#95)
11
+ - fix(file-activity): #72 补充 dsh-better-sidebar 级联安装——dependencies 声明 + 缺失提示
12
+ - style: prettier 全量格式修复(CI 格式门禁)
13
+
14
+ ## [Unreleased]
15
+
16
+ ### 变更
17
+
18
+ - fix(ui): 浮窗预览失焦自动关闭——点击外部任意处/切换页签即关,预览失败 2.5s 自动关闭或点击任意处关闭,头部「点击外部关闭」提示(issue #76)
19
+
5
20
  ## [0.5.4] - 2026-09-01
6
21
 
7
22
  ### 变更
package/README.md CHANGED
@@ -36,9 +36,7 @@
36
36
 
37
37
  ## 安装
38
38
 
39
- > 💡 **npm 安装(普通用户推荐)**:`dsh plugin --profile web add dsh-file-activity`——无需克隆本仓库;以下 link 方式供本仓库开发者使用。
40
-
41
- 前置:已安装 `dsh-better-sidebar`(v0.12+,推荐 v0.14)。
39
+ > 💡 **npm 安装(普通用户推荐)**:`dsh plugin --profile web add dsh-file-activity`——无需克隆本仓库;依赖自动级联安装:`dsh-better-sidebar`(宿主,提供侧边栏扩展点)与 `dsh-shared`(server 端共享工具包)均声明为 dependencies,安装时自动安装并加入 profile bundles,无需手动单独处理。
42
40
 
43
41
  ```sh
44
42
  # 方式一:dsh plugin(推荐)
package/lib/client.js CHANGED
@@ -90,6 +90,8 @@ const strings = {
90
90
  fileOutside: () =>
91
91
  isZh() ? '文件位于工作区外,暂无法读取内容' : 'The file is outside the workspace and cannot be read',
92
92
  downloadToView: () => (isZh() ? '下载查看' : 'download to view'),
93
+ clickOutsideToClose: () => (isZh() ? '点击外部关闭' : 'Click outside to close'),
94
+ autoCloseHint: () => (isZh() ? '预览失败,即将自动关闭' : 'Preview failed — closing automatically'),
93
95
  }
94
96
 
95
97
  // ── path / time formatting helpers ────────────────────────────────────
@@ -776,6 +778,7 @@ const STYLES = `
776
778
  display:flex; flex-direction:column; overflow:hidden; }
777
779
  .dfa-fp-head { display:flex; align-items:center; gap:6px; padding:6px 8px; border-bottom:1px solid var(--dsw-alias-border-l1); flex:none; }
778
780
  .dfa-fp-title { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font:var(--dsw-font-s-strong-14); color:var(--dsw-alias-label-primary); }
781
+ .dfa-fp-hint { flex:none; font:var(--dsw-font-xxs-12); color:var(--dsw-alias-label-tertiary); opacity:0.8; }
779
782
  .dfa-fp-actions { display:flex; align-items:center; gap:2px; flex:none; }
780
783
  .dfa-fp-body { flex:1; overflow:auto; padding:10px 12px; min-height:0; }
781
784
  .dfa-fp-note { color:var(--dsw-alias-label-tertiary); font:var(--dsw-font-xxs-12); }
@@ -1124,6 +1127,11 @@ function FileActivityView({ ctx, store, scope, visible, dataStore }) {
1124
1127
  useEffect(() => {
1125
1128
  dataStore.set({ preview: null })
1126
1129
  }, [sessionId, dataStore])
1130
+ // Switching tabs hides this view: close any floating preview so it never
1131
+ // lingers over the main UI (issue #76).
1132
+ useEffect(() => {
1133
+ closePreviewOnHidden(visible, dataStore)
1134
+ }, [visible, dataStore])
1127
1135
  const toggleDir = (path) => setCollapsedDirs((prev) => toggleInSet(prev, path))
1128
1136
  const openPreview = (path) => dataStore.set({ preview: { abs: path, name: basenameOf(path) } })
1129
1137
  const closePreview = () => dataStore.set({ preview: null })
@@ -1176,6 +1184,34 @@ function fsReadError(json, viewer) {
1176
1184
  return { status: 'error', viewer, message }
1177
1185
  }
1178
1186
 
1187
+ /** Milliseconds after which an error-state preview closes itself (issue #76):
1188
+ * a shell that failed to load any content is useless, so it must not linger
1189
+ * over the main UI until the user finds the × button. */
1190
+ const AUTO_CLOSE_MS = 2500
1191
+
1192
+ /** Whether a pointerdown target lies inside the floating window. The window
1193
+ * surface carries the `.dfa-fp` class; anything else counts as "outside"
1194
+ * and dismisses the preview (issue #76 — click anywhere outside closes). */
1195
+ function isInsideFloating(target) {
1196
+ if (!target || typeof target.closest !== 'function') return false
1197
+ return target.closest('.dfa-fp') !== null
1198
+ }
1199
+
1200
+ /** Click behavior for the window surface: in the error state ANY click
1201
+ * closes the shell (there is no content to interact with), otherwise the
1202
+ * click is swallowed so the viewer's own interactions keep working. */
1203
+ function previewClickAction(load, event) {
1204
+ if (load.status === 'error') return 'close'
1205
+ if (event && event.stopPropagation) event.stopPropagation()
1206
+ return 'stop'
1207
+ }
1208
+
1209
+ /** Close the floating preview when the tab goes hidden (switching tabs /
1210
+ * operating the main UI), so it never lingers over the interface. */
1211
+ function closePreviewOnHidden(visible, dataStore) {
1212
+ if (!visible) dataStore.set({ preview: null })
1213
+ }
1214
+
1179
1215
  /**
1180
1216
  * Load fsRead content through the sidebar API and resolve the viewer's
1181
1217
  * load state (ready with text, or error with the API message). When the
@@ -1275,6 +1311,7 @@ function renderPreviewBody(load, ctx, store, scope, path, title, sessionId) {
1275
1311
  load.message
1276
1312
  ? createElement('div', { style: { marginTop: '6px', fontSize: '11px', opacity: 0.85 } }, load.message)
1277
1313
  : null,
1314
+ createElement('div', { style: { marginTop: '6px', fontSize: '11px', opacity: 0.7 } }, strings.autoCloseHint()),
1278
1315
  )
1279
1316
  }
1280
1317
  if (load.viewer.id === 'pdf') {
@@ -1310,15 +1347,25 @@ function renderPreviewBody(load, ctx, store, scope, path, title, sessionId) {
1310
1347
  * built-in viewer that fetches its own URL internally (it ignores the
1311
1348
  * `mediaUrl` prop), so it gets a small iframe preview instead.
1312
1349
  */
1313
- function FloatingPreview({ ctx, store, scope, preview, onClose }) {
1314
- const sessionId = scope?.sessionId ?? ''
1315
- const path = preview.abs
1316
- const title = preview.name
1317
- const service = ctx.betterSidebar
1318
- const load = usePreviewLoader(service, path, sessionId, scope)
1350
+ /**
1351
+ * Dismissal affordances (issue #76 — the preview must never linger):
1352
+ * 1. pointerdown anywhere OUTSIDE the window closes it (capture phase, so
1353
+ * it fires even if another element sits above the scrim);
1354
+ * 2. the scrim's own onClick (kept as a fallback);
1355
+ * 3. Escape;
1356
+ * 4. the × button;
1357
+ * 5. an error state closes itself after AUTO_CLOSE_MS (no content to show).
1358
+ */
1359
+ function usePreviewDismiss(load, onClose) {
1360
+ useEffect(() => {
1361
+ if (typeof document === 'undefined' || typeof document.addEventListener !== 'function') return () => {}
1362
+ const handler = (event) => {
1363
+ if (!isInsideFloating(event?.target)) onClose()
1364
+ }
1365
+ document.addEventListener('pointerdown', handler, true)
1366
+ return () => document.removeEventListener('pointerdown', handler, true)
1367
+ }, [onClose])
1319
1368
 
1320
- // Clicking outside is the primary dismiss (the overlay's onClick);
1321
- // Escape is a keyboard affordance. Both call onClose.
1322
1369
  useEffect(() => {
1323
1370
  if (typeof document === 'undefined' || typeof document.addEventListener !== 'function') return () => {}
1324
1371
  const handler = (event) => {
@@ -1328,6 +1375,16 @@ function FloatingPreview({ ctx, store, scope, preview, onClose }) {
1328
1375
  return () => document.removeEventListener('keydown', handler)
1329
1376
  }, [onClose])
1330
1377
 
1378
+ useEffect(() => {
1379
+ if (load.status !== 'error') return undefined
1380
+ if (typeof window === 'undefined' || typeof window.setTimeout !== 'function') return undefined
1381
+ const timer = window.setTimeout(onClose, AUTO_CLOSE_MS)
1382
+ return () => window.clearTimeout(timer)
1383
+ }, [load.status, onClose])
1384
+ }
1385
+
1386
+ /** The floating window element: head (title + hint + close) and body. */
1387
+ function renderFloatingWindow(load, ctx, store, scope, path, title, sessionId, onClose) {
1331
1388
  return createElement(
1332
1389
  'div',
1333
1390
  { className: 'dfa-fp-overlay', onClick: onClose },
@@ -1336,13 +1393,14 @@ function FloatingPreview({ ctx, store, scope, preview, onClose }) {
1336
1393
  {
1337
1394
  className: 'dfa-fp',
1338
1395
  onClick: (event) => {
1339
- if (event && event.stopPropagation) event.stopPropagation()
1396
+ if (previewClickAction(load, event) === 'close') onClose()
1340
1397
  },
1341
1398
  },
1342
1399
  createElement(
1343
1400
  'div',
1344
1401
  { className: 'dfa-fp-head' },
1345
1402
  createElement('span', { className: 'dfa-fp-title' }, title),
1403
+ createElement('span', { className: 'dfa-fp-hint' }, strings.clickOutsideToClose()),
1346
1404
  createElement(
1347
1405
  'span',
1348
1406
  { className: 'dfa-fp-actions' },
@@ -1367,6 +1425,16 @@ function FloatingPreview({ ctx, store, scope, preview, onClose }) {
1367
1425
  )
1368
1426
  }
1369
1427
 
1428
+ function FloatingPreview({ ctx, store, scope, preview, onClose }) {
1429
+ const sessionId = scope?.sessionId ?? ''
1430
+ const path = preview.abs
1431
+ const title = preview.name
1432
+ const service = ctx.betterSidebar
1433
+ const load = usePreviewLoader(service, path, sessionId, scope)
1434
+ usePreviewDismiss(load, onClose)
1435
+ return renderFloatingWindow(load, ctx, store, scope, path, title, sessionId, onClose)
1436
+ }
1437
+
1370
1438
  /**
1371
1439
  * Lightweight PDF preview. better-sidebar's built-in PdfView fetches
1372
1440
  * `/sidebar/file` internally (it ignores any injected `mediaUrl` prop),
@@ -1467,7 +1535,17 @@ exports.apply = function apply(ctx) {
1467
1535
  // Stylesheet first, unconditionally (HMR pitfall — see injectStyles).
1468
1536
  injectStyles(ctx)
1469
1537
  const service = ctx.betterSidebar
1470
- if (service === undefined) return
1538
+ if (service === undefined) {
1539
+ // 依赖缺失提示(issue #72 同类问题):dsh-file-activity 的 client 端
1540
+ // 依赖 dsh-better-sidebar 提供侧边栏扩展点,未安装时静默返回会让用户
1541
+ // 以为插件坏了——明确提示安装方式。
1542
+ if (typeof console !== 'undefined' && typeof console.warn === 'function') {
1543
+ console.warn(
1544
+ '[dsh-file-activity] dsh-better-sidebar 未安装:文件活动页签无法挂载。请安装宿主插件:dsh plugin --profile web add dsh-better-sidebar dsh-file-activity',
1545
+ )
1546
+ }
1547
+ return
1548
+ }
1471
1549
 
1472
1550
  // Per-session data store: { bySession: { [sessionId]: { recent, counts, loading } }, preview }
1473
1551
  // Each conversation reads/writes only its own bucket, so switching
@@ -1485,7 +1563,18 @@ exports.apply = function apply(ctx) {
1485
1563
 
1486
1564
  // Internal functions exposed for the render-path test suite only; inert in
1487
1565
  // the browser bundle (plain properties on the exports object).
1488
- exports.__test = { loadFsReadContent, fsReadError, fetchTextContent, textUrlOf, strings }
1566
+ exports.__test = {
1567
+ loadFsReadContent,
1568
+ fsReadError,
1569
+ fetchTextContent,
1570
+ textUrlOf,
1571
+ strings,
1572
+ renderPreviewBody,
1573
+ previewClickAction,
1574
+ isInsideFloating,
1575
+ closePreviewOnHidden,
1576
+ AUTO_CLOSE_MS,
1577
+ }
1489
1578
 
1490
1579
 
1491
1580
  return module.exports
@@ -84,8 +84,7 @@ async function serveRecordedFile(request, response, ctx, store) {
84
84
  const sessionId = url.searchParams.get('sessionId')
85
85
  const raw = url.searchParams.get('path')
86
86
  assertMediaParams(sessionId, raw)
87
- if (!isRecordedPath(store.state, sessionId, raw))
88
- throw mediaError(403, "path is not in this session's file activity")
87
+ if (!isRecordedPath(store.state, sessionId, raw)) throw mediaError(403, "path is not in this session's file activity")
89
88
  const abs = isAbsolute(raw) ? raw : join(sessionCwdOf(ctx, sessionId), raw)
90
89
  if (url.searchParams.get('as') === 'text') {
91
90
  await serveText(response, abs)
@@ -68,7 +68,17 @@ exports.apply = function apply(ctx) {
68
68
  // Stylesheet first, unconditionally (HMR pitfall — see injectStyles).
69
69
  injectStyles(ctx)
70
70
  const service = ctx.betterSidebar
71
- if (service === undefined) return
71
+ if (service === undefined) {
72
+ // 依赖缺失提示(issue #72 同类问题):dsh-file-activity 的 client 端
73
+ // 依赖 dsh-better-sidebar 提供侧边栏扩展点,未安装时静默返回会让用户
74
+ // 以为插件坏了——明确提示安装方式。
75
+ if (typeof console !== 'undefined' && typeof console.warn === 'function') {
76
+ console.warn(
77
+ '[dsh-file-activity] dsh-better-sidebar 未安装:文件活动页签无法挂载。请安装宿主插件:dsh plugin --profile web add dsh-better-sidebar dsh-file-activity',
78
+ )
79
+ }
80
+ return
81
+ }
72
82
 
73
83
  // Per-session data store: { bySession: { [sessionId]: { recent, counts, loading } }, preview }
74
84
  // Each conversation reads/writes only its own bucket, so switching
@@ -86,4 +96,15 @@ exports.apply = function apply(ctx) {
86
96
 
87
97
  // Internal functions exposed for the render-path test suite only; inert in
88
98
  // the browser bundle (plain properties on the exports object).
89
- exports.__test = { loadFsReadContent, fsReadError, fetchTextContent, textUrlOf, strings }
99
+ exports.__test = {
100
+ loadFsReadContent,
101
+ fsReadError,
102
+ fetchTextContent,
103
+ textUrlOf,
104
+ strings,
105
+ renderPreviewBody,
106
+ previewClickAction,
107
+ isInsideFloating,
108
+ closePreviewOnHidden,
109
+ AUTO_CLOSE_MS,
110
+ }
@@ -42,4 +42,6 @@ const strings = {
42
42
  fileOutside: () =>
43
43
  isZh() ? '文件位于工作区外,暂无法读取内容' : 'The file is outside the workspace and cannot be read',
44
44
  downloadToView: () => (isZh() ? '下载查看' : 'download to view'),
45
+ clickOutsideToClose: () => (isZh() ? '点击外部关闭' : 'Click outside to close'),
46
+ autoCloseHint: () => (isZh() ? '预览失败,即将自动关闭' : 'Preview failed — closing automatically'),
45
47
  }
@@ -26,6 +26,34 @@ function fsReadError(json, viewer) {
26
26
  return { status: 'error', viewer, message }
27
27
  }
28
28
 
29
+ /** Milliseconds after which an error-state preview closes itself (issue #76):
30
+ * a shell that failed to load any content is useless, so it must not linger
31
+ * over the main UI until the user finds the × button. */
32
+ const AUTO_CLOSE_MS = 2500
33
+
34
+ /** Whether a pointerdown target lies inside the floating window. The window
35
+ * surface carries the `.dfa-fp` class; anything else counts as "outside"
36
+ * and dismisses the preview (issue #76 — click anywhere outside closes). */
37
+ function isInsideFloating(target) {
38
+ if (!target || typeof target.closest !== 'function') return false
39
+ return target.closest('.dfa-fp') !== null
40
+ }
41
+
42
+ /** Click behavior for the window surface: in the error state ANY click
43
+ * closes the shell (there is no content to interact with), otherwise the
44
+ * click is swallowed so the viewer's own interactions keep working. */
45
+ function previewClickAction(load, event) {
46
+ if (load.status === 'error') return 'close'
47
+ if (event && event.stopPropagation) event.stopPropagation()
48
+ return 'stop'
49
+ }
50
+
51
+ /** Close the floating preview when the tab goes hidden (switching tabs /
52
+ * operating the main UI), so it never lingers over the interface. */
53
+ function closePreviewOnHidden(visible, dataStore) {
54
+ if (!visible) dataStore.set({ preview: null })
55
+ }
56
+
29
57
  /**
30
58
  * Load fsRead content through the sidebar API and resolve the viewer's
31
59
  * load state (ready with text, or error with the API message). When the
@@ -125,6 +153,7 @@ function renderPreviewBody(load, ctx, store, scope, path, title, sessionId) {
125
153
  load.message
126
154
  ? createElement('div', { style: { marginTop: '6px', fontSize: '11px', opacity: 0.85 } }, load.message)
127
155
  : null,
156
+ createElement('div', { style: { marginTop: '6px', fontSize: '11px', opacity: 0.7 } }, strings.autoCloseHint()),
128
157
  )
129
158
  }
130
159
  if (load.viewer.id === 'pdf') {
@@ -160,15 +189,25 @@ function renderPreviewBody(load, ctx, store, scope, path, title, sessionId) {
160
189
  * built-in viewer that fetches its own URL internally (it ignores the
161
190
  * `mediaUrl` prop), so it gets a small iframe preview instead.
162
191
  */
163
- function FloatingPreview({ ctx, store, scope, preview, onClose }) {
164
- const sessionId = scope?.sessionId ?? ''
165
- const path = preview.abs
166
- const title = preview.name
167
- const service = ctx.betterSidebar
168
- const load = usePreviewLoader(service, path, sessionId, scope)
192
+ /**
193
+ * Dismissal affordances (issue #76 — the preview must never linger):
194
+ * 1. pointerdown anywhere OUTSIDE the window closes it (capture phase, so
195
+ * it fires even if another element sits above the scrim);
196
+ * 2. the scrim's own onClick (kept as a fallback);
197
+ * 3. Escape;
198
+ * 4. the × button;
199
+ * 5. an error state closes itself after AUTO_CLOSE_MS (no content to show).
200
+ */
201
+ function usePreviewDismiss(load, onClose) {
202
+ useEffect(() => {
203
+ if (typeof document === 'undefined' || typeof document.addEventListener !== 'function') return () => {}
204
+ const handler = (event) => {
205
+ if (!isInsideFloating(event?.target)) onClose()
206
+ }
207
+ document.addEventListener('pointerdown', handler, true)
208
+ return () => document.removeEventListener('pointerdown', handler, true)
209
+ }, [onClose])
169
210
 
170
- // Clicking outside is the primary dismiss (the overlay's onClick);
171
- // Escape is a keyboard affordance. Both call onClose.
172
211
  useEffect(() => {
173
212
  if (typeof document === 'undefined' || typeof document.addEventListener !== 'function') return () => {}
174
213
  const handler = (event) => {
@@ -178,6 +217,16 @@ function FloatingPreview({ ctx, store, scope, preview, onClose }) {
178
217
  return () => document.removeEventListener('keydown', handler)
179
218
  }, [onClose])
180
219
 
220
+ useEffect(() => {
221
+ if (load.status !== 'error') return undefined
222
+ if (typeof window === 'undefined' || typeof window.setTimeout !== 'function') return undefined
223
+ const timer = window.setTimeout(onClose, AUTO_CLOSE_MS)
224
+ return () => window.clearTimeout(timer)
225
+ }, [load.status, onClose])
226
+ }
227
+
228
+ /** The floating window element: head (title + hint + close) and body. */
229
+ function renderFloatingWindow(load, ctx, store, scope, path, title, sessionId, onClose) {
181
230
  return createElement(
182
231
  'div',
183
232
  { className: 'dfa-fp-overlay', onClick: onClose },
@@ -186,13 +235,14 @@ function FloatingPreview({ ctx, store, scope, preview, onClose }) {
186
235
  {
187
236
  className: 'dfa-fp',
188
237
  onClick: (event) => {
189
- if (event && event.stopPropagation) event.stopPropagation()
238
+ if (previewClickAction(load, event) === 'close') onClose()
190
239
  },
191
240
  },
192
241
  createElement(
193
242
  'div',
194
243
  { className: 'dfa-fp-head' },
195
244
  createElement('span', { className: 'dfa-fp-title' }, title),
245
+ createElement('span', { className: 'dfa-fp-hint' }, strings.clickOutsideToClose()),
196
246
  createElement(
197
247
  'span',
198
248
  { className: 'dfa-fp-actions' },
@@ -217,6 +267,16 @@ function FloatingPreview({ ctx, store, scope, preview, onClose }) {
217
267
  )
218
268
  }
219
269
 
270
+ function FloatingPreview({ ctx, store, scope, preview, onClose }) {
271
+ const sessionId = scope?.sessionId ?? ''
272
+ const path = preview.abs
273
+ const title = preview.name
274
+ const service = ctx.betterSidebar
275
+ const load = usePreviewLoader(service, path, sessionId, scope)
276
+ usePreviewDismiss(load, onClose)
277
+ return renderFloatingWindow(load, ctx, store, scope, path, title, sessionId, onClose)
278
+ }
279
+
220
280
  /**
221
281
  * Lightweight PDF preview. better-sidebar's built-in PdfView fetches
222
282
  * `/sidebar/file` internally (it ignores any injected `mediaUrl` prop),
@@ -62,6 +62,7 @@ const STYLES = `
62
62
  display:flex; flex-direction:column; overflow:hidden; }
63
63
  .dfa-fp-head { display:flex; align-items:center; gap:6px; padding:6px 8px; border-bottom:1px solid var(--dsw-alias-border-l1); flex:none; }
64
64
  .dfa-fp-title { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font:var(--dsw-font-s-strong-14); color:var(--dsw-alias-label-primary); }
65
+ .dfa-fp-hint { flex:none; font:var(--dsw-font-xxs-12); color:var(--dsw-alias-label-tertiary); opacity:0.8; }
65
66
  .dfa-fp-actions { display:flex; align-items:center; gap:2px; flex:none; }
66
67
  .dfa-fp-body { flex:1; overflow:auto; padding:10px 12px; min-height:0; }
67
68
  .dfa-fp-note { color:var(--dsw-alias-label-tertiary); font:var(--dsw-font-xxs-12); }
@@ -158,6 +158,11 @@ function FileActivityView({ ctx, store, scope, visible, dataStore }) {
158
158
  useEffect(() => {
159
159
  dataStore.set({ preview: null })
160
160
  }, [sessionId, dataStore])
161
+ // Switching tabs hides this view: close any floating preview so it never
162
+ // lingers over the main UI (issue #76).
163
+ useEffect(() => {
164
+ closePreviewOnHidden(visible, dataStore)
165
+ }, [visible, dataStore])
161
166
  const toggleDir = (path) => setCollapsedDirs((prev) => toggleInSet(prev, path))
162
167
  const openPreview = (path) => dataStore.set({ preview: { abs: path, name: basenameOf(path) } })
163
168
  const closePreview = () => dataStore.set({ preview: null })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-file-activity",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "DSH 侧边栏文件活动插件:按 LRU 记录文件读取/新增/修改事件(最近访问),按绝对路径树形统计(文件统计),点击文件浮窗预览(代码高亮/Markdown/图片/PDF)。DSH web plugin: file activity tracker — LRU recent-access list plus per-file read/create/modify counts in a folder tree, with floating preview.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -43,16 +43,15 @@
43
43
  ]
44
44
  }
45
45
  },
46
+ "dependencies": {
47
+ "dsh-better-sidebar": ">=0.14.0 <0.18.0",
48
+ "dsh-shared": "^0.1.0"
49
+ },
46
50
  "peerDependencies": {
47
51
  "cordis": "^4.0.0-rc.8",
48
- "dsh-better-sidebar": "^0.14.0",
49
- "react": "^18.2.0 || ^19.2.0",
50
- "dsh-shared": "^0.1.0"
52
+ "react": "^18.2.0 || ^19.2.0"
51
53
  },
52
54
  "peerDependenciesMeta": {
53
- "dsh-better-sidebar": {
54
- "optional": true
55
- },
56
55
  "cordis": {
57
56
  "optional": true
58
57
  }