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 +7 -0
- package/lib/client.js +136 -1
- package/lib/parts/icons.part.js +96 -0
- package/lib/parts/rows.part.js +24 -1
- package/lib/parts/styles.part.js +16 -0
- package/package.json +1 -1
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' },
|
|
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
|
package/lib/parts/icons.part.js
CHANGED
|
@@ -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
|
+
}
|
package/lib/parts/rows.part.js
CHANGED
|
@@ -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' },
|
|
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
|
package/lib/parts/styles.part.js
CHANGED
|
@@ -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.
|
|
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",
|