dsh-file-activity 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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
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.1] - 2026-08-27
6
+
7
+ ### 变更
8
+
9
+ - feat(file-activity): 侧边栏页签选中态改用品牌蓝,三态一眼可分(issue #25)
10
+ - feat(file-activity): 文件活动列表按文件类型显示专属彩色图标(issue #24)
11
+
5
12
  ## [0.5.0] - 2026-08-26
6
13
 
7
14
  ### 变更
package/lib/client.js CHANGED
@@ -434,6 +434,102 @@ window.__ModuleLoader__.load({
434
434
  ], size),
435
435
  }
436
436
 
437
+ // Common-language / file-type badges (issue #24): brand fill + contrast
438
+ // ink, reading on both light and dark themes. Unmapped extensions keep the
439
+ // neutral currentColor file icon above. [bg, fg ink, short mark]
440
+ const FILE_BADGES = {
441
+ // JavaScript / TypeScript
442
+ js: ['#F7DF1E', '#323330', 'JS'], mjs: ['#F7DF1E', '#323330', 'JS'], cjs: ['#F7DF1E', '#323330', 'JS'],
443
+ ts: ['#3178C6', '#ffffff', 'TS'], mts: ['#3178C6', '#ffffff', 'TS'], cts: ['#3178C6', '#ffffff', 'TS'],
444
+ tsx: ['#3178C6', '#ffffff', 'TSX'], jsx: ['#3178C6', '#ffffff', 'JSX'],
445
+ // 后端语言
446
+ java: ['#007396', '#ffffff', 'JAVA'],
447
+ c: ['#A8B9CC', '#111111', 'C'],
448
+ cpp: ['#00599C', '#ffffff', 'C++'], cxx: ['#00599C', '#ffffff', 'C++'], cc: ['#00599C', '#ffffff', 'C++'], hpp: ['#00599C', '#ffffff', 'C++'],
449
+ h: ['#A8B9CC', '#111111', 'H'], hh: ['#A8B9CC', '#111111', 'H'],
450
+ cs: ['#68217A', '#ffffff', 'C#'], csharp: ['#68217A', '#ffffff', 'C#'],
451
+ go: ['#00ADD8', '#ffffff', 'GO'],
452
+ rs: ['#CE422B', '#ffffff', 'RS'],
453
+ rb: ['#B51624', '#ffffff', 'RB'],
454
+ php: ['#777BB4', '#ffffff', 'PHP'],
455
+ py: ['#3776AB', '#ffffff', 'PY'],
456
+ swift: ['#F05138', '#ffffff', 'SWIFT'],
457
+ kt: ['#7F52FF', '#ffffff', 'KT'], kotlin: ['#7F52FF', '#ffffff', 'KT'],
458
+ dart: ['#0175C2', '#ffffff', 'DART'],
459
+ scala: ['#DC322F', '#ffffff', 'SCALA'],
460
+ lua: ['#2C2C7C', '#ffffff', 'LUA'],
461
+ pl: ['#0298C3', '#ffffff', 'PERL'],
462
+ r: ['#336DC3', '#ffffff', 'R'],
463
+ m: ['#C1272D', '#ffffff', 'MAT'], mm: ['#C1272D', '#ffffff', 'MAT'],
464
+ // Web / 前端
465
+ html: ['#E34F26', '#ffffff', '</>'], htm: ['#E34F26', '#ffffff', '</>'],
466
+ css: ['#663399', '#ffffff', 'CSS'],
467
+ scss: ['#CD6799', '#ffffff', 'SCSS'], sass: ['#CD6799', '#ffffff', 'SCSS'],
468
+ vue: ['#42B883', '#ffffff', 'VUE'],
469
+ svelte: ['#FF3E00', '#ffffff', 'SVELTE'],
470
+ // 数据 / 结构化
471
+ json: ['#F7DF1E', '#323330', '{}'],
472
+ sql: ['#00758F', '#ffffff', 'SQL'],
473
+ csv: ['#2E7D32', '#ffffff', 'CSV'],
474
+ db: ['#0F62FE', '#ffffff', 'DB'], sqlite: ['#0F62FE', '#ffffff', 'DB'], sqlite3: ['#0F62FE', '#ffffff', 'DB'],
475
+ xml: ['#FF6F00', '#ffffff', 'XML'],
476
+ svg: ['#FF6F00', '#ffffff', 'SVG'],
477
+ // 文档
478
+ md: ['#42A5F5', '#ffffff', 'M↓'], markdown: ['#42A5F5', '#ffffff', 'M↓'],
479
+ txt: ['#90A4AE', '#ffffff', 'TXT'], text: ['#90A4AE', '#ffffff', 'TXT'], log: ['#90A4AE', '#ffffff', 'TXT'],
480
+ pdf: ['#E5202B', '#ffffff', 'PDF'],
481
+ doc: ['#2B579A', '#ffffff', 'DOC'], docx: ['#2B579A', '#ffffff', 'DOC'],
482
+ xls: ['#217346', '#ffffff', 'XLS'], xlsx: ['#217346', '#ffffff', 'XLS'],
483
+ ppt: ['#D24726', '#ffffff', 'PPT'], pptx: ['#D24726', '#ffffff', 'PPT'],
484
+ // 配置 / 构建
485
+ yml: ['#CB171E', '#ffffff', 'YML'], yaml: ['#CB171E', '#ffffff', 'YML'],
486
+ toml: ['#8D6E63', '#ffffff', 'TOML'],
487
+ ini: ['#546E7A', '#ffffff', 'CFG'], cfg: ['#546E7A', '#ffffff', 'CFG'], config: ['#546E7A', '#ffffff', 'CFG'],
488
+ env: ['#F9A825', '#323330', 'ENV'],
489
+ properties: ['#7B1FA2', '#ffffff', 'PROP'],
490
+ lock: ['#37474F', '#ffffff', 'LOCK'],
491
+ dockerfile: ['#2496ED', '#ffffff', 'DOCK'], docker: ['#2496ED', '#ffffff', 'DOCK'],
492
+ makefile: ['#607D8B', '#ffffff', 'MAKE'],
493
+ gradle: ['#02303A', '#ffffff', 'GRADLE'],
494
+ cmake: ['#265774', '#ffffff', 'CMAKE'],
495
+ ipynb: ['#F37726', '#ffffff', 'JNB'],
496
+ // 脚本 / Shell
497
+ sh: ['#89E051', '#111111', '>_'], bash: ['#89E051', '#111111', '>_'], zsh: ['#89E051', '#111111', '>_'],
498
+ ps1: ['#012456', '#ffffff', 'PS1'],
499
+ bat: ['#546E7A', '#ffffff', 'CMD'], cmd: ['#546E7A', '#ffffff', 'CMD'],
500
+ // 打包 / 二进制
501
+ zip: ['#FFA726', '#323330', 'ZIP'], tar: ['#FFA726', '#323330', 'ZIP'], gz: ['#FFA726', '#323330', 'ZIP'],
502
+ '7z': ['#FFA726', '#323330', 'ZIP'], rar: ['#FFA726', '#323330', 'ZIP'],
503
+ exe: ['#0078D4', '#ffffff', 'EXE'], msi: ['#0078D4', '#ffffff', 'EXE'],
504
+ wasm: ['#654FF0', '#ffffff', 'WASM'],
505
+ // 图片 / 媒体
506
+ png: ['#8E44AD', '#ffffff', 'IMG'], jpg: ['#8E44AD', '#ffffff', 'IMG'], jpeg: ['#8E44AD', '#ffffff', 'IMG'],
507
+ gif: ['#8E44AD', '#ffffff', 'IMG'], webp: ['#8E44AD', '#ffffff', 'IMG'], ico: ['#8E44AD', '#ffffff', 'IMG'], bmp: ['#8E44AD', '#ffffff', 'IMG'],
508
+ // 版本控制
509
+ gitignore: ['#F05032', '#ffffff', 'GIT'], gitattributes: ['#F05032', '#ffffff', 'GIT'],
510
+ }
511
+
512
+ /** One self-colored badge svg: rounded brand rect + short contrast mark.
513
+ * Mark font scales by length so 5-6 char marks (JAVA/SCALA/SWIFT) stay
514
+ * inside the 24×24 viewBox. */
515
+ const badgeIcon = ([bg, fg, mark], size) =>
516
+ createElement('svg', {
517
+ width: size, height: size, viewBox: '0 0 24 24', 'aria-hidden': 'true',
518
+ },
519
+ createElement('rect', { x: 1, y: 1, width: 22, height: 22, rx: 5, fill: bg }),
520
+ createElement('text', {
521
+ x: 12, y: 16, textAnchor: 'middle', fontSize: mark.length <= 2 ? 9 : mark.length <= 4 ? 7 : 5.5,
522
+ fontWeight: 700, fill: fg,
523
+ }, mark))
524
+
525
+ /** File-type icon dispatcher: branded badge for known extensions, the
526
+ * neutral file icon for everything else (case-insensitive, tolerates a
527
+ * leading dot like ".md"). */
528
+ const fileIconByExt = (ext, size = 14) => {
529
+ const spec = FILE_BADGES[String(ext ?? '').toLowerCase().replace(/^\./, '')]
530
+ return spec === undefined ? icon.file(size) : badgeIcon(spec, size)
531
+ }
532
+
437
533
  // ── themed stylesheet (injected once per activation) ──────────────────
438
534
  // Mirrors the better-sidebar explorer surface: tight 2px 6px 8px body,
439
535
  // 30px rows, box-sizing border-box indentation, folder rows use the
@@ -510,6 +606,22 @@ window.__ModuleLoader__.load({
510
606
  .dfa-pdf-download:hover { text-decoration:underline; }
511
607
  .dfa-pdf-frame { flex:1; min-height:0; width:100%; border:none; border-radius:6px; background:transparent; }
512
608
  @keyframes dfa-row-in { from { opacity:0; transform:translateY(1px); } to { opacity:1; transform:none; } }
609
+ /* ── sidebar tab selected-state overrides (issue #25) ─────────────────────
610
+ The host (dsh-better-sidebar) renders tabs with CSS-modules hashed class
611
+ names (e.g. tabActive_xxxxx) and no data attributes, so the selected tab
612
+ is targeted via the [class*="tabActive"] substring selector — the fallback
613
+ contract that keeps these rules working if the host ever renames its
614
+ classes. The double attribute selector [class*="tab"][class*="tabActive"]
615
+ (0,2,0) beats the host .tabActive (0,1,0) by specificity alone: no
616
+ !important, no reliance on injection order. Selected = theme-aware brand
617
+ fill + contrast ink (light #4176e6/#fff, dark #679efe/#0f1115); the :hover
618
+ variant (0,3,0) beats the host .tab:hover (0,2,0) so the selected state
619
+ never collapses into the host's grey hover. Unselected tabs stay untouched
620
+ (transparent + secondary label ink, host grey hover). */
621
+ [class*="tab"][class*="tabActive"] { background:var(--dsw-alias-state-business-primary); color:var(--dsw-alias-label-primary-foreground); }
622
+ [class*="tab"][class*="tabActive"]:hover { background:var(--dsw-alias-state-business-primary); color:var(--dsw-alias-label-primary-foreground); }
623
+ [class*="tab"][class*="tabActive"] [class*="tabClose"] { color:var(--dsw-alias-label-primary-foreground); }
624
+ [class*="tab"][class*="tabActive"] [class*="tabClose"]:hover { background:color-mix(in srgb, var(--dsw-alias-label-primary-foreground) 18%, transparent); color:var(--dsw-alias-label-primary-foreground); }
513
625
  `
514
626
 
515
627
  // ── row rendering helpers (recent list & stats tree) ──────────────────
@@ -536,6 +648,29 @@ window.__ModuleLoader__.load({
536
648
  return createElement('span', { className: 'dfa-counts', style: { paddingLeft: '6px' } }, ...pills)
537
649
  }
538
650
 
651
+ /** Extension of a file name (lowercase, no leading dot); '' when none.
652
+ * Dotfiles map to their whole name ('.gitignore' → 'gitignore') so the
653
+ * badge table can cover them; 'notes.' still yields ''. */
654
+ const extOf = (name) => {
655
+ const dot = name.lastIndexOf('.')
656
+ if (dot > 0) return name.slice(dot + 1).toLowerCase()
657
+ if (dot === 0) return name.slice(1).toLowerCase()
658
+ return ''
659
+ }
660
+
661
+ /** Extension-less but common build files → their badge key. */
662
+ const NAME_BADGES = {
663
+ makefile: 'makefile', dockerfile: 'dockerfile', 'cmakelists.txt': 'cmake',
664
+ }
665
+
666
+ /** Badge key for a file name: basename match first, then extension. */
667
+ const badgeKeyOf = (name) => {
668
+ const base = name.toLowerCase()
669
+ const named = NAME_BADGES[base]
670
+ if (named !== undefined) return named
671
+ return extOf(name)
672
+ }
673
+
539
674
  /** A stats-tree file row: icon + name + count pills + relative time. */
540
675
  const fileRow = (file, depth, onOpen) =>
541
676
  createElement(
@@ -547,7 +682,7 @@ window.__ModuleLoader__.load({
547
682
  style: { paddingLeft: 8 + depth * 20 },
548
683
  title: fileTitle(file.abs, file.firstSeen, file.lastSeen),
549
684
  },
550
- createElement('span', { className: 'dfa-row-icon dfa-icon-file' }, icon.file(14)),
685
+ createElement('span', { className: 'dfa-row-icon dfa-icon-file' }, fileIconByExt(badgeKeyOf(file.name))),
551
686
  createElement('span', { className: 'dfa-row-name dfa-name-file' }, file.name),
552
687
  countPills(file),
553
688
  file.lastSeen
@@ -46,3 +46,99 @@
46
46
  createElement('line', { x1: 6, y1: 6, x2: 18, y2: 18 }),
47
47
  ], size),
48
48
  }
49
+
50
+ // Common-language / file-type badges (issue #24): brand fill + contrast
51
+ // ink, reading on both light and dark themes. Unmapped extensions keep the
52
+ // neutral currentColor file icon above. [bg, fg ink, short mark]
53
+ const FILE_BADGES = {
54
+ // JavaScript / TypeScript
55
+ js: ['#F7DF1E', '#323330', 'JS'], mjs: ['#F7DF1E', '#323330', 'JS'], cjs: ['#F7DF1E', '#323330', 'JS'],
56
+ ts: ['#3178C6', '#ffffff', 'TS'], mts: ['#3178C6', '#ffffff', 'TS'], cts: ['#3178C6', '#ffffff', 'TS'],
57
+ tsx: ['#3178C6', '#ffffff', 'TSX'], jsx: ['#3178C6', '#ffffff', 'JSX'],
58
+ // 后端语言
59
+ java: ['#007396', '#ffffff', 'JAVA'],
60
+ c: ['#A8B9CC', '#111111', 'C'],
61
+ cpp: ['#00599C', '#ffffff', 'C++'], cxx: ['#00599C', '#ffffff', 'C++'], cc: ['#00599C', '#ffffff', 'C++'], hpp: ['#00599C', '#ffffff', 'C++'],
62
+ h: ['#A8B9CC', '#111111', 'H'], hh: ['#A8B9CC', '#111111', 'H'],
63
+ cs: ['#68217A', '#ffffff', 'C#'], csharp: ['#68217A', '#ffffff', 'C#'],
64
+ go: ['#00ADD8', '#ffffff', 'GO'],
65
+ rs: ['#CE422B', '#ffffff', 'RS'],
66
+ rb: ['#B51624', '#ffffff', 'RB'],
67
+ php: ['#777BB4', '#ffffff', 'PHP'],
68
+ py: ['#3776AB', '#ffffff', 'PY'],
69
+ swift: ['#F05138', '#ffffff', 'SWIFT'],
70
+ kt: ['#7F52FF', '#ffffff', 'KT'], kotlin: ['#7F52FF', '#ffffff', 'KT'],
71
+ dart: ['#0175C2', '#ffffff', 'DART'],
72
+ scala: ['#DC322F', '#ffffff', 'SCALA'],
73
+ lua: ['#2C2C7C', '#ffffff', 'LUA'],
74
+ pl: ['#0298C3', '#ffffff', 'PERL'],
75
+ r: ['#336DC3', '#ffffff', 'R'],
76
+ m: ['#C1272D', '#ffffff', 'MAT'], mm: ['#C1272D', '#ffffff', 'MAT'],
77
+ // Web / 前端
78
+ html: ['#E34F26', '#ffffff', '</>'], htm: ['#E34F26', '#ffffff', '</>'],
79
+ css: ['#663399', '#ffffff', 'CSS'],
80
+ scss: ['#CD6799', '#ffffff', 'SCSS'], sass: ['#CD6799', '#ffffff', 'SCSS'],
81
+ vue: ['#42B883', '#ffffff', 'VUE'],
82
+ svelte: ['#FF3E00', '#ffffff', 'SVELTE'],
83
+ // 数据 / 结构化
84
+ json: ['#F7DF1E', '#323330', '{}'],
85
+ sql: ['#00758F', '#ffffff', 'SQL'],
86
+ csv: ['#2E7D32', '#ffffff', 'CSV'],
87
+ db: ['#0F62FE', '#ffffff', 'DB'], sqlite: ['#0F62FE', '#ffffff', 'DB'], sqlite3: ['#0F62FE', '#ffffff', 'DB'],
88
+ xml: ['#FF6F00', '#ffffff', 'XML'],
89
+ svg: ['#FF6F00', '#ffffff', 'SVG'],
90
+ // 文档
91
+ md: ['#42A5F5', '#ffffff', 'M↓'], markdown: ['#42A5F5', '#ffffff', 'M↓'],
92
+ txt: ['#90A4AE', '#ffffff', 'TXT'], text: ['#90A4AE', '#ffffff', 'TXT'], log: ['#90A4AE', '#ffffff', 'TXT'],
93
+ pdf: ['#E5202B', '#ffffff', 'PDF'],
94
+ doc: ['#2B579A', '#ffffff', 'DOC'], docx: ['#2B579A', '#ffffff', 'DOC'],
95
+ xls: ['#217346', '#ffffff', 'XLS'], xlsx: ['#217346', '#ffffff', 'XLS'],
96
+ ppt: ['#D24726', '#ffffff', 'PPT'], pptx: ['#D24726', '#ffffff', 'PPT'],
97
+ // 配置 / 构建
98
+ yml: ['#CB171E', '#ffffff', 'YML'], yaml: ['#CB171E', '#ffffff', 'YML'],
99
+ toml: ['#8D6E63', '#ffffff', 'TOML'],
100
+ ini: ['#546E7A', '#ffffff', 'CFG'], cfg: ['#546E7A', '#ffffff', 'CFG'], config: ['#546E7A', '#ffffff', 'CFG'],
101
+ env: ['#F9A825', '#323330', 'ENV'],
102
+ properties: ['#7B1FA2', '#ffffff', 'PROP'],
103
+ lock: ['#37474F', '#ffffff', 'LOCK'],
104
+ dockerfile: ['#2496ED', '#ffffff', 'DOCK'], docker: ['#2496ED', '#ffffff', 'DOCK'],
105
+ makefile: ['#607D8B', '#ffffff', 'MAKE'],
106
+ gradle: ['#02303A', '#ffffff', 'GRADLE'],
107
+ cmake: ['#265774', '#ffffff', 'CMAKE'],
108
+ ipynb: ['#F37726', '#ffffff', 'JNB'],
109
+ // 脚本 / Shell
110
+ sh: ['#89E051', '#111111', '>_'], bash: ['#89E051', '#111111', '>_'], zsh: ['#89E051', '#111111', '>_'],
111
+ ps1: ['#012456', '#ffffff', 'PS1'],
112
+ bat: ['#546E7A', '#ffffff', 'CMD'], cmd: ['#546E7A', '#ffffff', 'CMD'],
113
+ // 打包 / 二进制
114
+ zip: ['#FFA726', '#323330', 'ZIP'], tar: ['#FFA726', '#323330', 'ZIP'], gz: ['#FFA726', '#323330', 'ZIP'],
115
+ '7z': ['#FFA726', '#323330', 'ZIP'], rar: ['#FFA726', '#323330', 'ZIP'],
116
+ exe: ['#0078D4', '#ffffff', 'EXE'], msi: ['#0078D4', '#ffffff', 'EXE'],
117
+ wasm: ['#654FF0', '#ffffff', 'WASM'],
118
+ // 图片 / 媒体
119
+ png: ['#8E44AD', '#ffffff', 'IMG'], jpg: ['#8E44AD', '#ffffff', 'IMG'], jpeg: ['#8E44AD', '#ffffff', 'IMG'],
120
+ gif: ['#8E44AD', '#ffffff', 'IMG'], webp: ['#8E44AD', '#ffffff', 'IMG'], ico: ['#8E44AD', '#ffffff', 'IMG'], bmp: ['#8E44AD', '#ffffff', 'IMG'],
121
+ // 版本控制
122
+ gitignore: ['#F05032', '#ffffff', 'GIT'], gitattributes: ['#F05032', '#ffffff', 'GIT'],
123
+ }
124
+
125
+ /** One self-colored badge svg: rounded brand rect + short contrast mark.
126
+ * Mark font scales by length so 5-6 char marks (JAVA/SCALA/SWIFT) stay
127
+ * inside the 24×24 viewBox. */
128
+ const badgeIcon = ([bg, fg, mark], size) =>
129
+ createElement('svg', {
130
+ width: size, height: size, viewBox: '0 0 24 24', 'aria-hidden': 'true',
131
+ },
132
+ createElement('rect', { x: 1, y: 1, width: 22, height: 22, rx: 5, fill: bg }),
133
+ createElement('text', {
134
+ x: 12, y: 16, textAnchor: 'middle', fontSize: mark.length <= 2 ? 9 : mark.length <= 4 ? 7 : 5.5,
135
+ fontWeight: 700, fill: fg,
136
+ }, mark))
137
+
138
+ /** File-type icon dispatcher: branded badge for known extensions, the
139
+ * neutral file icon for everything else (case-insensitive, tolerates a
140
+ * leading dot like ".md"). */
141
+ const fileIconByExt = (ext, size = 14) => {
142
+ const spec = FILE_BADGES[String(ext ?? '').toLowerCase().replace(/^\./, '')]
143
+ return spec === undefined ? icon.file(size) : badgeIcon(spec, size)
144
+ }
@@ -22,6 +22,29 @@
22
22
  return createElement('span', { className: 'dfa-counts', style: { paddingLeft: '6px' } }, ...pills)
23
23
  }
24
24
 
25
+ /** Extension of a file name (lowercase, no leading dot); '' when none.
26
+ * Dotfiles map to their whole name ('.gitignore' → 'gitignore') so the
27
+ * badge table can cover them; 'notes.' still yields ''. */
28
+ const extOf = (name) => {
29
+ const dot = name.lastIndexOf('.')
30
+ if (dot > 0) return name.slice(dot + 1).toLowerCase()
31
+ if (dot === 0) return name.slice(1).toLowerCase()
32
+ return ''
33
+ }
34
+
35
+ /** Extension-less but common build files → their badge key. */
36
+ const NAME_BADGES = {
37
+ makefile: 'makefile', dockerfile: 'dockerfile', 'cmakelists.txt': 'cmake',
38
+ }
39
+
40
+ /** Badge key for a file name: basename match first, then extension. */
41
+ const badgeKeyOf = (name) => {
42
+ const base = name.toLowerCase()
43
+ const named = NAME_BADGES[base]
44
+ if (named !== undefined) return named
45
+ return extOf(name)
46
+ }
47
+
25
48
  /** A stats-tree file row: icon + name + count pills + relative time. */
26
49
  const fileRow = (file, depth, onOpen) =>
27
50
  createElement(
@@ -33,7 +56,7 @@
33
56
  style: { paddingLeft: 8 + depth * 20 },
34
57
  title: fileTitle(file.abs, file.firstSeen, file.lastSeen),
35
58
  },
36
- createElement('span', { className: 'dfa-row-icon dfa-icon-file' }, icon.file(14)),
59
+ createElement('span', { className: 'dfa-row-icon dfa-icon-file' }, fileIconByExt(badgeKeyOf(file.name))),
37
60
  createElement('span', { className: 'dfa-row-name dfa-name-file' }, file.name),
38
61
  countPills(file),
39
62
  file.lastSeen
@@ -74,4 +74,20 @@
74
74
  .dfa-pdf-download:hover { text-decoration:underline; }
75
75
  .dfa-pdf-frame { flex:1; min-height:0; width:100%; border:none; border-radius:6px; background:transparent; }
76
76
  @keyframes dfa-row-in { from { opacity:0; transform:translateY(1px); } to { opacity:1; transform:none; } }
77
+ /* ── sidebar tab selected-state overrides (issue #25) ─────────────────────
78
+ The host (dsh-better-sidebar) renders tabs with CSS-modules hashed class
79
+ names (e.g. tabActive_xxxxx) and no data attributes, so the selected tab
80
+ is targeted via the [class*="tabActive"] substring selector — the fallback
81
+ contract that keeps these rules working if the host ever renames its
82
+ classes. The double attribute selector [class*="tab"][class*="tabActive"]
83
+ (0,2,0) beats the host .tabActive (0,1,0) by specificity alone: no
84
+ !important, no reliance on injection order. Selected = theme-aware brand
85
+ fill + contrast ink (light #4176e6/#fff, dark #679efe/#0f1115); the :hover
86
+ variant (0,3,0) beats the host .tab:hover (0,2,0) so the selected state
87
+ never collapses into the host's grey hover. Unselected tabs stay untouched
88
+ (transparent + secondary label ink, host grey hover). */
89
+ [class*="tab"][class*="tabActive"] { background:var(--dsw-alias-state-business-primary); color:var(--dsw-alias-label-primary-foreground); }
90
+ [class*="tab"][class*="tabActive"]:hover { background:var(--dsw-alias-state-business-primary); color:var(--dsw-alias-label-primary-foreground); }
91
+ [class*="tab"][class*="tabActive"] [class*="tabClose"] { color:var(--dsw-alias-label-primary-foreground); }
92
+ [class*="tab"][class*="tabActive"] [class*="tabClose"]:hover { background:color-mix(in srgb, var(--dsw-alias-label-primary-foreground) 18%, transparent); color:var(--dsw-alias-label-primary-foreground); }
77
93
  `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-file-activity",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
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",