adtec-core-package 3.1.8 → 3.2.0

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 (62) hide show
  1. package/.idea/codeStyles/Project.xml +61 -0
  2. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  3. package/.idea/misc.xml +6 -0
  4. package/.idea/modules.xml +8 -0
  5. package/.idea/vcs.xml +6 -0
  6. package/.idea//345/211/215/347/253/257/346/240/270/345/277/203/345/214/205.iml +8 -0
  7. package/package.json +2 -1
  8. package/src/assets/style/index.less +30 -42
  9. package/src/assets/style/scrollbar-classic.less +105 -0
  10. package/src/assets/style/scrollbar-classic.scss +105 -0
  11. package/src/components/ElCardList.vue +7 -1
  12. package/src/components/Scrollbars/ElScrollbars.vue +10 -2
  13. package/src/components/editor-main/src/assets/styles/_variables.less +1 -1
  14. package/src/components/upload/DocxJsViewer.vue +2 -16
  15. package/src/components/upload/FileView.vue +7 -2
  16. package/src/components/upload/OfficePreview.vue +9 -1
  17. package/src/components/upload/PdfJsViewer.vue +2 -2
  18. package/src/config/VxeTableConfig.ts +2 -2
  19. package/src/css/elementUI/common/var.scss +4 -4
  20. package/src/css/elementUI/mixins/mixins.scss +4 -28
  21. package/src/css/elementUI/scrollbar.scss +20 -5
  22. package/src/css/vxeTableUI/components/table.scss +22 -29
  23. package/src/utils/excelPreviewUtil.ts +219 -45
  24. package/src/utils/officePreviewUtil.ts +6 -0
  25. package/.claude/skills/gitnexus/gitnexus-cli/SKILL.md +0 -83
  26. package/.claude/skills/gitnexus/gitnexus-debugging/SKILL.md +0 -89
  27. package/.claude/skills/gitnexus/gitnexus-exploring/SKILL.md +0 -78
  28. package/.claude/skills/gitnexus/gitnexus-guide/SKILL.md +0 -64
  29. package/.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md +0 -97
  30. package/.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md +0 -121
  31. package/.editorconfig +0 -6
  32. package/.prettierrc.json +0 -7
  33. package/.vscode/extensions.json +0 -8
  34. package/AGENTS.md +0 -43
  35. package/CLAUDE.md +0 -43
  36. package/adtec-core-package/adtec-core-package.css +0 -1
  37. package/adtec-core-package/adtec-core-package.js +0 -41216
  38. package/adtec-core-package/adtec-core-package.umd.cjs +0 -87
  39. package/adtec-core-package/favicon.ico +0 -0
  40. package/adtec-core-package-3.1.7.tgz +0 -0
  41. package/auto-imports.d.ts +0 -299
  42. package/env.d.ts +0 -1
  43. package/eslint.config.js +0 -56
  44. package/index.html +0 -13
  45. package/package/.editorconfig +0 -6
  46. package/package/adtec-core-package/adtec-core-package.css +0 -1
  47. package/package/adtec-core-package/adtec-core-package.js +0 -41216
  48. package/package/adtec-core-package/adtec-core-package.umd.cjs +0 -87
  49. package/package/adtec-core-package/favicon.ico +0 -0
  50. package/package/index.html +0 -13
  51. package/package/prebuilt/umo-editor/favicon.ico +0 -0
  52. package/package/prebuilt/umo-editor/umo-editor.css +0 -1
  53. package/package/public/favicon.ico +0 -0
  54. package/package/src/assets/base.css +0 -86
  55. package/package/src/assets/main.css +0 -35
  56. package/package/src/components/editor-main/src/extensions/bookmark.js +0 -110
  57. package/prebuilt/umo-editor/umo-editor.js.map +0 -1
  58. package/tsconfig.app.json +0 -12
  59. package/tsconfig.json +0 -11
  60. package/tsconfig.node.json +0 -18
  61. package/vite.config.ts +0 -46
  62. package/vite.config.umo.ts +0 -81
@@ -1,5 +1,6 @@
1
1
  @use 'function' as *;
2
2
  @use '../common/var' as *;
3
+ @use '../../../assets/style/scrollbar-classic.scss' as classic;
3
4
  // forward mixins
4
5
  @forward 'config';
5
6
  @forward 'function';
@@ -22,35 +23,10 @@
22
23
 
23
24
  // Scrollbar
24
25
  @mixin scroll-bar {
25
- $scrollbar-thumb-background: getCssVar('text-color', 'disabled');
26
- $scrollbar-track-background: getCssVar('fill-color', 'blank');
26
+ @include classic.adtec-scrollbar-hidden;
27
27
 
28
- &::-webkit-scrollbar {
29
- z-index: 11;
30
- width: 6px;
31
-
32
- &:horizontal {
33
- height: 6px;
34
- }
35
-
36
- &-thumb {
37
- border-radius: 5px;
38
- width: 6px;
39
- background: $scrollbar-thumb-background;
40
- }
41
-
42
- &-corner {
43
- background: $scrollbar-track-background;
44
- }
45
-
46
- &-track {
47
- background: $scrollbar-track-background;
48
-
49
- &-piece {
50
- background: $scrollbar-track-background;
51
- width: 6px;
52
- }
53
- }
28
+ &:hover {
29
+ @include classic.adtec-scrollbar-active;
54
30
  }
55
31
  }
56
32
 
@@ -3,6 +3,7 @@
3
3
  @use 'mixins/mixins' as *;
4
4
  @use 'mixins/var' as *;
5
5
  @use 'common/var' as *;
6
+ @use '../../assets/style/scrollbar-classic.scss' as classic;
6
7
 
7
8
  @include b(scrollbar) {
8
9
  @include set-component-css-var('scrollbar', $scrollbar);
@@ -16,6 +17,9 @@
16
17
  @include e(wrap) {
17
18
  overflow: auto;
18
19
  height: 100%;
20
+ box-sizing: border-box;
21
+ /* 预留滚动条槽位,避免 thumb 压在正文上 */
22
+ padding-right: calc(var(--el-scrollbar-size, 8px) + 4px);
19
23
 
20
24
  @include m(hidden-default) {
21
25
  scrollbar-width: none;
@@ -31,7 +35,7 @@
31
35
  width: 0;
32
36
  height: 0;
33
37
  cursor: pointer;
34
- border-radius: inherit;
38
+ border-radius: calc(var(--el-scrollbar-size, 8px) / 2);
35
39
  background-color: var(
36
40
  #{getCssVarName('scrollbar-bg-color')},
37
41
  map.get($scrollbar, 'bg-color')
@@ -56,14 +60,17 @@
56
60
 
57
61
  @include e(bar) {
58
62
  position: absolute;
59
- right: 2px;
63
+ right: 4px;
60
64
  bottom: 2px;
61
65
  z-index: 1;
62
- border-radius: 4px;
66
+ border-radius: calc(var(--el-scrollbar-size, 8px) / 2);
67
+ opacity: 0;
68
+ transition: opacity getCssVar('transition-duration') ease-out;
63
69
 
64
70
  @include when(vertical) {
65
- width: 6px;
71
+ width: var(--el-scrollbar-size, 8px);
66
72
  top: 2px;
73
+ background-color: rgba(0, 0, 0, 0.04);
67
74
 
68
75
  > div {
69
76
  width: 100%;
@@ -71,14 +78,22 @@
71
78
  }
72
79
 
73
80
  @include when(horizontal) {
74
- height: 6px;
81
+ height: var(--el-scrollbar-size, 8px);
75
82
  left: 2px;
83
+ background-color: rgba(0, 0, 0, 0.04);
76
84
 
77
85
  > div {
78
86
  height: 100%;
79
87
  }
80
88
  }
81
89
  }
90
+
91
+ &:hover,
92
+ &:active {
93
+ @include e(bar) {
94
+ opacity: 1;
95
+ }
96
+ }
82
97
  }
83
98
 
84
99
  .#{$namespace}-scrollbar-fade {
@@ -1,4 +1,5 @@
1
1
  @use '../helpers/baseMixin.scss';
2
+ @use '../../../assets/style/scrollbar-classic.scss' as classic;
2
3
  @use './icon.scss';
3
4
  @use './table-module/all.scss';
4
5
 
@@ -79,34 +80,16 @@
79
80
  overflow-x: scroll;
80
81
  }
81
82
 
82
- /* 仅显示外层滚动条:鼠标进入表格显示,移出隐藏 */
83
+ /* vxe 表格:hover 时显示现代圆角滚动条 */
83
84
  .vxe-table--scroll-x-handle,
84
85
  .vxe-table--scroll-y-handle {
85
- scrollbar-width: none;
86
- -ms-overflow-style: none;
87
- &::-webkit-scrollbar {
88
- width: 8px;
89
- height: 8px;
90
- background: transparent;
91
- }
92
- &::-webkit-scrollbar-thumb {
93
- background: transparent;
94
- border-radius: 4px;
95
- }
96
- &::-webkit-scrollbar-track {
97
- background: transparent;
98
- }
86
+ @include classic.adtec-scrollbar-hidden;
99
87
  }
100
88
 
101
89
  .vxe-table:hover {
102
90
  .vxe-table--scroll-x-handle,
103
91
  .vxe-table--scroll-y-handle {
104
- scrollbar-width: thin;
105
- scrollbar-color: #DDDEE0 transparent;
106
- }
107
- .vxe-table--scroll-x-handle::-webkit-scrollbar-thumb,
108
- .vxe-table--scroll-y-handle::-webkit-scrollbar-thumb {
109
- background: #DDDEE0;
92
+ @include classic.adtec-vxe-scrollbar-active;
110
93
  }
111
94
  }
112
95
 
@@ -604,10 +587,10 @@
604
587
  }
605
588
  }
606
589
  .vxe-table--scroll-x-virtual {
607
- height: 0 !important;
590
+ height: 0;
608
591
  }
609
592
  .vxe-table--scroll-y-virtual {
610
- width: 0px !important;
593
+ width: 0;
611
594
  }
612
595
  .vxe-table--scroll-x-virtual,
613
596
  .vxe-table--scroll-y-virtual {
@@ -635,10 +618,10 @@
635
618
  .vxe-table--scroll-x-handle {
636
619
  overflow-y: hidden;
637
620
  overflow-x: scroll;
638
- height: 8px;
621
+ height: var(--vxe-scrollbar-size, 8px);
639
622
  }
640
623
  .vxe-table--scroll-x-wrapper {
641
- height: 8px;
624
+ height: 100%;
642
625
  }
643
626
  .vxe-table--scroll-y-handle,
644
627
  .vxe-table--scroll-y-wrapper {
@@ -650,7 +633,7 @@
650
633
  .vxe-table--scroll-y-handle {
651
634
  overflow-y: scroll;
652
635
  overflow-x: hidden;
653
- width: 8px;
636
+ width: var(--vxe-scrollbar-size, 8px);
654
637
  height: 100%;
655
638
  }
656
639
  .vxe-table--scroll-x-space {
@@ -925,10 +908,20 @@
925
908
  border-collapse: separate;
926
909
  table-layout: fixed;
927
910
  }
928
- .vxe-table--render-wrapper{
929
- height:100%;
911
+ .vxe-table--render-wrapper {
912
+ height: 100%;
913
+ display: flex;
914
+ flex-direction: column;
915
+ overflow: hidden;
916
+
930
917
  .vxe-table--layout-wrapper {
931
- height:100%
918
+ flex: 1;
919
+ min-height: 0;
920
+ height: auto;
921
+ }
922
+
923
+ .vxe-table--scroll-x-virtual {
924
+ flex-shrink: 0;
932
925
  }
933
926
  }
934
927
  &:not(.is--empty) {
@@ -1,4 +1,5 @@
1
1
  import * as XLSX from 'xlsx-js-style'
2
+ import { unzipSync } from 'fflate'
2
3
 
3
4
  export interface ExcelPreviewCellStyle {
4
5
  backgroundColor?: string
@@ -43,7 +44,17 @@ export interface ExcelSearchMatch {
43
44
 
44
45
  type ColorStyle = { rgb?: string; theme?: number; tint?: number; indexed?: number }
45
46
 
46
- /** Excel 默认主题色(Office 2013+) */
47
+ type WorkbookStyles = {
48
+ Fonts?: Array<Record<string, unknown>>
49
+ Fills?: Array<Record<string, unknown>>
50
+ Borders?: Array<Record<string, unknown>>
51
+ CellXf?: Array<Record<string, unknown>>
52
+ }
53
+
54
+ /** xlsx-js-style 运行时附带 Styles,官方 WorkBook 类型未声明 */
55
+ type StyledWorkBook = XLSX.WorkBook & { Styles?: WorkbookStyles }
56
+
57
+ /** Excel 默认主题色(Office 2013+,含 accent / hyperlink) */
47
58
  const EXCEL_THEME_COLORS: Record<number, string> = {
48
59
  0: '#FFFFFF',
49
60
  1: '#000000',
@@ -55,6 +66,68 @@ const EXCEL_THEME_COLORS: Record<number, string> = {
55
66
  7: '#FFC000',
56
67
  8: '#5B9BD5',
57
68
  9: '#70AD47',
69
+ 10: '#0563C1',
70
+ 11: '#954F72',
71
+ }
72
+
73
+ /** Excel 标准 indexed 色板(常用前 16 色) */
74
+ const INDEXED_COLORS: Record<number, string> = {
75
+ 0: '#000000',
76
+ 1: '#FFFFFF',
77
+ 2: '#FF0000',
78
+ 3: '#00FF00',
79
+ 4: '#0000FF',
80
+ 5: '#FFFF00',
81
+ 6: '#FF00FF',
82
+ 7: '#00FFFF',
83
+ 8: '#000000',
84
+ 9: '#FFFFFF',
85
+ 10: '#FF0000',
86
+ 11: '#00FF00',
87
+ 12: '#0000FF',
88
+ 13: '#FFFF00',
89
+ 14: '#FF00FF',
90
+ 15: '#00FFFF',
91
+ 16: '#800000',
92
+ 17: '#008000',
93
+ 18: '#000080',
94
+ 19: '#808000',
95
+ 20: '#800080',
96
+ 21: '#008080',
97
+ 22: '#C0C0C0',
98
+ 23: '#808080',
99
+ 24: '#9999FF',
100
+ 25: '#993366',
101
+ 26: '#FFFFCC',
102
+ 27: '#CCFFFF',
103
+ 28: '#660066',
104
+ 29: '#FF8080',
105
+ 30: '#0066CC',
106
+ 31: '#CCCCFF',
107
+ 40: '#00CCFF',
108
+ 41: '#CCFFFF',
109
+ 42: '#CCFFCC',
110
+ 43: '#FFFF99',
111
+ 44: '#99CCFF',
112
+ 45: '#FF99CC',
113
+ 46: '#CC99FF',
114
+ 47: '#FFCC99',
115
+ 48: '#3366FF',
116
+ 49: '#33CCCC',
117
+ 50: '#99CC00',
118
+ 51: '#FFCC00',
119
+ 52: '#FF9900',
120
+ 53: '#FF6600',
121
+ 54: '#666699',
122
+ 55: '#969696',
123
+ 56: '#003366',
124
+ 57: '#339966',
125
+ 58: '#003300',
126
+ 59: '#333300',
127
+ 60: '#993300',
128
+ 61: '#993366',
129
+ 62: '#333399',
130
+ 63: '#333333',
58
131
  }
59
132
 
60
133
  const BORDER_WIDTH: Record<string, string> = {
@@ -69,6 +142,16 @@ const BORDER_WIDTH: Record<string, string> = {
69
142
 
70
143
  const DEFAULT_COL_WIDTH = 72
71
144
 
145
+ const isOleXls = (buffer: ArrayBuffer) => {
146
+ const sig = new Uint8Array(buffer, 0, 4)
147
+ return sig[0] === 0xd0 && sig[1] === 0xcf && sig[2] === 0x11 && sig[3] === 0xe0
148
+ }
149
+
150
+ const isZipXlsx = (buffer: ArrayBuffer) => {
151
+ const sig = new Uint8Array(buffer, 0, 2)
152
+ return sig[0] === 0x50 && sig[1] === 0x4b
153
+ }
154
+
72
155
  const hexToRgb = (hex: string) => {
73
156
  const normalized = hex.replace('#', '')
74
157
  const value =
@@ -106,8 +189,9 @@ const resolveColor = (color?: ColorStyle): string | undefined => {
106
189
  if (raw.length === 6) return `#${raw}`
107
190
  return undefined
108
191
  }
192
+ if (color.indexed != null) return INDEXED_COLORS[color.indexed]
109
193
  if (color.theme != null) {
110
- const base = EXCEL_THEME_COLORS[color.theme] ?? '#FFFFFF'
194
+ const base = EXCEL_THEME_COLORS[color.theme] ?? '#000000'
111
195
  return color.tint ? applyTint(base, color.tint) : base
112
196
  }
113
197
  return undefined
@@ -116,63 +200,64 @@ const resolveColor = (color?: ColorStyle): string | undefined => {
116
200
  const resolveBorderSide = (side?: { style?: string; color?: ColorStyle }) => {
117
201
  if (!side?.style || side.style === 'none') return undefined
118
202
  const width = BORDER_WIDTH[side.style] ?? '1px'
119
- const color = resolveColor(side.color) ?? '#d0d0d0'
203
+ const color = resolveColor(side.color) ?? '#000000'
120
204
  const lineStyle = side.style === 'dashed' ? 'dashed' : side.style === 'dotted' ? 'dotted' : 'solid'
121
205
  return `${width} ${lineStyle} ${color}`
122
206
  }
123
207
 
124
- const extractCellStyle = (cell?: XLSX.CellObject): ExcelPreviewCellStyle | undefined => {
125
- const rawStyle = cell?.s
126
- if (!rawStyle || typeof rawStyle === 'number') return undefined
127
- const style = rawStyle as Record<string, unknown>
208
+ const toNumber = (value: unknown, fallback = 0) => {
209
+ const num = Number(value)
210
+ return Number.isFinite(num) ? num : fallback
211
+ }
212
+
213
+ const isTruthy = (value: unknown) => value === true || value === 1 || value === '1'
214
+
215
+ const styleFromParts = (parts: {
216
+ font?: Record<string, unknown>
217
+ fill?: Record<string, unknown>
218
+ border?: Record<string, unknown>
219
+ alignment?: Record<string, unknown>
220
+ }): ExcelPreviewCellStyle | undefined => {
128
221
  const css: ExcelPreviewCellStyle = {}
129
222
 
130
- const fill = style.fill as { patternType?: string; fgColor?: ColorStyle; bgColor?: ColorStyle } | undefined
223
+ const fill = parts.fill
131
224
  if (fill && fill.patternType !== 'none') {
132
- const bg = resolveColor(fill.fgColor) || resolveColor(fill.bgColor)
133
- if (bg) css.backgroundColor = bg
225
+ const fg = fill.fgColor as ColorStyle | undefined
226
+ const bg = fill.bgColor as ColorStyle | undefined
227
+ const background = resolveColor(fg) || resolveColor(bg)
228
+ if (background) css.backgroundColor = background
134
229
  }
135
230
 
136
- const font = style.font as {
137
- bold?: boolean
138
- italic?: boolean
139
- underline?: boolean | string
140
- sz?: number | string
141
- color?: ColorStyle
142
- } | undefined
231
+ const font = parts.font
143
232
  if (font) {
144
- if (font.bold) css.fontWeight = 'bold'
145
- if (font.italic) css.fontStyle = 'italic'
233
+ if (isTruthy(font.bold)) css.fontWeight = 'bold'
234
+ if (isTruthy(font.italic)) css.fontStyle = 'italic'
146
235
  if (font.underline) css.textDecoration = 'underline'
147
236
  if (font.sz) css.fontSize = `${font.sz}pt`
148
- const color = resolveColor(font.color)
237
+ const color = resolveColor(font.color as ColorStyle | undefined)
149
238
  if (color) css.color = color
150
239
  }
151
240
 
152
- const alignment = style.alignment as {
153
- horizontal?: string
154
- vertical?: string
155
- wrapText?: boolean
156
- } | undefined
241
+ const alignment = parts.alignment
157
242
  if (alignment) {
158
- if (alignment.horizontal && alignment.horizontal !== 'general') {
159
- css.textAlign = alignment.horizontal as ExcelPreviewCellStyle['textAlign']
243
+ const horizontal = alignment.horizontal as string | undefined
244
+ if (horizontal && horizontal !== 'general') {
245
+ css.textAlign = horizontal as ExcelPreviewCellStyle['textAlign']
160
246
  }
161
- if (alignment.vertical) {
247
+ const vertical = alignment.vertical as string | undefined
248
+ if (vertical) {
162
249
  css.verticalAlign =
163
- alignment.vertical === 'center'
164
- ? 'middle'
165
- : (alignment.vertical as ExcelPreviewCellStyle['verticalAlign'])
250
+ vertical === 'center' ? 'middle' : (vertical as ExcelPreviewCellStyle['verticalAlign'])
166
251
  }
167
- if (alignment.wrapText) css.whiteSpace = 'pre-wrap'
252
+ if (isTruthy(alignment.wrapText)) css.whiteSpace = 'pre-wrap'
168
253
  }
169
254
 
170
- const border = style.border as Record<string, { style?: string; color?: ColorStyle }> | undefined
255
+ const border = parts.border
171
256
  if (border) {
172
- const top = resolveBorderSide(border.top)
173
- const right = resolveBorderSide(border.right)
174
- const bottom = resolveBorderSide(border.bottom)
175
- const left = resolveBorderSide(border.left)
257
+ const top = resolveBorderSide(border.top as { style?: string; color?: ColorStyle })
258
+ const right = resolveBorderSide(border.right as { style?: string; color?: ColorStyle })
259
+ const bottom = resolveBorderSide(border.bottom as { style?: string; color?: ColorStyle })
260
+ const left = resolveBorderSide(border.left as { style?: string; color?: ColorStyle })
176
261
  if (top) css.borderTop = top
177
262
  if (right) css.borderRight = right
178
263
  if (bottom) css.borderBottom = bottom
@@ -182,6 +267,83 @@ const extractCellStyle = (cell?: XLSX.CellObject): ExcelPreviewCellStyle | undef
182
267
  return Object.keys(css).length > 0 ? css : undefined
183
268
  }
184
269
 
270
+ const resolveXfStyle = (
271
+ styles: WorkbookStyles | undefined,
272
+ xfIndex: number,
273
+ ): ExcelPreviewCellStyle | undefined => {
274
+ if (!styles?.CellXf?.length || xfIndex < 0) return undefined
275
+ const xf = styles.CellXf[xfIndex]
276
+ if (!xf) return undefined
277
+
278
+ const fontId = toNumber(xf.fontId ?? xf.fontid, 0)
279
+ const fillId = toNumber(xf.fillId ?? xf.fillid, 0)
280
+ const borderId = toNumber(xf.borderId ?? xf.borderid, 0)
281
+
282
+ return styleFromParts({
283
+ font: styles.Fonts?.[fontId] as Record<string, unknown> | undefined,
284
+ fill: styles.Fills?.[fillId] as Record<string, unknown> | undefined,
285
+ border: styles.Borders?.[borderId] as Record<string, unknown> | undefined,
286
+ alignment: xf.alignment as Record<string, unknown> | undefined,
287
+ })
288
+ }
289
+
290
+ /** xlsx-js-style 读入后 cell.s 往往只有 fill,字体/边框需用 XML 中的 s 索引关联 CellXf */
291
+ const parseXlsxStyleIndexMaps = (buffer: ArrayBuffer, sheetCount: number) => {
292
+ if (!isZipXlsx(buffer) || sheetCount <= 0) return [] as Map<string, number>[]
293
+
294
+ try {
295
+ const files = unzipSync(new Uint8Array(buffer))
296
+ const decoder = new TextDecoder('utf-8')
297
+ const maps: Map<string, number>[] = []
298
+
299
+ for (let i = 0; i < sheetCount; i++) {
300
+ const path = `xl/worksheets/sheet${i + 1}.xml`
301
+ const xmlBytes = files[path]
302
+ const map = new Map<string, number>()
303
+ if (xmlBytes) {
304
+ const xml = decoder.decode(xmlBytes)
305
+ const cellTagRe = /<c\b([^>/]*)(?:\/>|>)/g
306
+ let match: RegExpExecArray | null
307
+ while ((match = cellTagRe.exec(xml))) {
308
+ const attrs = match[1]
309
+ const addr = attrs.match(/\br="([^"]+)"/)?.[1]
310
+ const styleIndex = attrs.match(/\bs="(\d+)"/)?.[1]
311
+ if (addr && styleIndex != null) {
312
+ map.set(addr, Number(styleIndex))
313
+ }
314
+ }
315
+ }
316
+ maps.push(map)
317
+ }
318
+ return maps
319
+ } catch {
320
+ return Array.from({ length: sheetCount }, () => new Map<string, number>())
321
+ }
322
+ }
323
+
324
+ const extractCellStyle = (
325
+ cell?: XLSX.CellObject,
326
+ styles?: WorkbookStyles,
327
+ xfIndex?: number,
328
+ ): ExcelPreviewCellStyle | undefined => {
329
+ if (styles && xfIndex != null && xfIndex >= 0) {
330
+ const resolved = resolveXfStyle(styles, xfIndex)
331
+ if (resolved) return resolved
332
+ }
333
+
334
+ if (typeof cell?.s === 'number' && styles) {
335
+ return resolveXfStyle(styles, cell.s)
336
+ }
337
+
338
+ const rawStyle = cell?.s
339
+ if (!rawStyle || typeof rawStyle !== 'object') return undefined
340
+
341
+ // cell.s 对象通常仅含 fill,作为兜底
342
+ return styleFromParts({
343
+ fill: rawStyle as Record<string, unknown>,
344
+ })
345
+ }
346
+
185
347
  export const cellStyleToCss = (style?: ExcelPreviewCellStyle): Record<string, string> => {
186
348
  if (!style) return {}
187
349
  const css: Record<string, string> = {}
@@ -228,13 +390,23 @@ const parseColWidths = (sheet: XLSX.WorkSheet, startCol: number, endCol: number)
228
390
 
229
391
  export const parseExcelWorkbook = async (src: Blob | ArrayBuffer): Promise<ExcelPreviewSheet[]> => {
230
392
  const buffer = src instanceof Blob ? await src.arrayBuffer() : src
231
- const workbook = XLSX.read(buffer, { type: 'array', cellDates: true, cellStyles: true })
232
- return workbook.SheetNames.map((name) => {
393
+ const workbook = XLSX.read(buffer, {
394
+ type: 'array',
395
+ cellDates: true,
396
+ cellStyles: true,
397
+ ...(isOleXls(buffer) ? {} : {}),
398
+ }) as StyledWorkBook
399
+ const styles = workbook.Styles
400
+ const styleIndexMaps = isZipXlsx(buffer)
401
+ ? parseXlsxStyleIndexMaps(buffer, workbook.SheetNames.length)
402
+ : []
403
+
404
+ return workbook.SheetNames.map((name, sheetIndex) => {
233
405
  const sheet = workbook.Sheets[name]
234
406
  const range = sheet?.['!ref'] ? XLSX.utils.decode_range(sheet['!ref']) : null
235
407
  return {
236
408
  name,
237
- rows: buildSheetRows(sheet),
409
+ rows: buildSheetRows(sheet, styles, styleIndexMaps[sheetIndex]),
238
410
  startCol: range?.s.c ?? 0,
239
411
  endCol: range?.e.c ?? 0,
240
412
  colWidths: range ? parseColWidths(sheet, range.s.c, range.e.c) : {},
@@ -242,7 +414,11 @@ export const parseExcelWorkbook = async (src: Blob | ArrayBuffer): Promise<Excel
242
414
  })
243
415
  }
244
416
 
245
- const buildSheetRows = (sheet?: XLSX.WorkSheet): ExcelPreviewCell[][] => {
417
+ const buildSheetRows = (
418
+ sheet?: XLSX.WorkSheet,
419
+ styles?: WorkbookStyles,
420
+ styleIndexMap?: Map<string, number>,
421
+ ): ExcelPreviewCell[][] => {
246
422
  if (!sheet || !sheet['!ref']) return []
247
423
  const range = XLSX.utils.decode_range(sheet['!ref'])
248
424
  const colWidths = parseColWidths(sheet, range.s.c, range.e.c)
@@ -268,13 +444,14 @@ const buildSheetRows = (sheet?: XLSX.WorkSheet): ExcelPreviewCell[][] => {
268
444
  const addr = XLSX.utils.encode_cell({ r, c })
269
445
  const cellObj = sheet[addr]
270
446
  const merge = mergeStarts.get(`${r},${c}`)
447
+ const xfIndex = styleIndexMap?.get(addr)
271
448
  row.push({
272
449
  value: formatCellValue(cellObj),
273
450
  row: r,
274
451
  col: c,
275
452
  rowSpan: merge?.rowSpan,
276
453
  colSpan: merge?.colSpan,
277
- style: extractCellStyle(cellObj),
454
+ style: extractCellStyle(cellObj, styles, xfIndex),
278
455
  width: colWidths[c],
279
456
  })
280
457
  }
@@ -302,6 +479,3 @@ export const searchExcelSheets = (
302
479
  })
303
480
  return matches
304
481
  }
305
-
306
- export const cellKey = (sheetIndex: number, row: number, col: number) =>
307
- `${sheetIndex}:${row}:${col}`
@@ -120,3 +120,9 @@ export const isLegacyDocFile = (type?: string, name?: string, fileUrl?: string)
120
120
  /** 弹层等场景:使用 OfficePreview 内置标题栏(PDF / Word / Excel) */
121
121
  export const isHeaderPreviewFile = (type?: string, name?: string, fileUrl?: string) =>
122
122
  HEADER_PREVIEW_TYPES.has(toOfficePreviewType(type, name, fileUrl))
123
+
124
+ /** pdf.js 加载参数:依赖 PDF 内嵌字体与浏览器系统字体 */
125
+ export const buildPdfJsLoadOptions = (data: ArrayBuffer) => ({
126
+ data,
127
+ useSystemFonts: true,
128
+ })
@@ -1,83 +0,0 @@
1
- ---
2
- name: gitnexus-cli
3
- description: "Use when the user needs to run GitNexus CLI commands like analyze/index a repo, check status, clean the index, generate a wiki, or list indexed repos. Examples: \"Index this repo\", \"Reanalyze the codebase\", \"Generate a wiki\""
4
- ---
5
-
6
- # GitNexus CLI Commands
7
-
8
- All commands work via `npx` — no global install required.
9
-
10
- ## Commands
11
-
12
- ### analyze — Build or refresh the index
13
-
14
- ```bash
15
- npx gitnexus analyze
16
- ```
17
-
18
- Run from the project root. This parses all source files, builds the knowledge graph, writes it to `.gitnexus/`, and generates CLAUDE.md / AGENTS.md context files.
19
-
20
- | Flag | Effect |
21
- | -------------- | ---------------------------------------------------------------- |
22
- | `--force` | Force full re-index even if up to date |
23
- | `--embeddings` | Enable embedding generation for semantic search (off by default) |
24
- | `--drop-embeddings` | Drop existing embeddings on rebuild. By default, an `analyze` without `--embeddings` preserves them. |
25
-
26
- **When to run:** First time in a project, after major code changes, or when `gitnexus://repo/{name}/context` reports the index is stale. In Claude Code, a PostToolUse hook detects staleness after `git commit` and `git merge` and notifies the agent to run `analyze` — the hook does not run analyze itself, to avoid blocking the agent for up to 120s and risking KuzuDB corruption on timeout.
27
-
28
- ### status — Check index freshness
29
-
30
- ```bash
31
- npx gitnexus status
32
- ```
33
-
34
- Shows whether the current repo has a GitNexus index, when it was last updated, and symbol/relationship counts. Use this to check if re-indexing is needed.
35
-
36
- ### clean — Delete the index
37
-
38
- ```bash
39
- npx gitnexus clean
40
- ```
41
-
42
- Deletes the `.gitnexus/` directory and unregisters the repo from the global registry. Use before re-indexing if the index is corrupt or after removing GitNexus from a project.
43
-
44
- | Flag | Effect |
45
- | --------- | ------------------------------------------------- |
46
- | `--force` | Skip confirmation prompt |
47
- | `--all` | Clean all indexed repos, not just the current one |
48
-
49
- ### wiki — Generate documentation from the graph
50
-
51
- ```bash
52
- npx gitnexus wiki
53
- ```
54
-
55
- Generates repository documentation from the knowledge graph using an LLM. Requires an API key (saved to `~/.gitnexus/config.json` on first use).
56
-
57
- | Flag | Effect |
58
- | ------------------- | ----------------------------------------- |
59
- | `--force` | Force full regeneration |
60
- | `--model <model>` | LLM model (default: minimax/minimax-m2.5) |
61
- | `--base-url <url>` | LLM API base URL |
62
- | `--api-key <key>` | LLM API key |
63
- | `--concurrency <n>` | Parallel LLM calls (default: 3) |
64
- | `--gist` | Publish wiki as a public GitHub Gist |
65
-
66
- ### list — Show all indexed repos
67
-
68
- ```bash
69
- npx gitnexus list
70
- ```
71
-
72
- Lists all repositories registered in `~/.gitnexus/registry.json`. The MCP `list_repos` tool provides the same information.
73
-
74
- ## After Indexing
75
-
76
- 1. **Read `gitnexus://repo/{name}/context`** to verify the index loaded
77
- 2. Use the other GitNexus skills (`exploring`, `debugging`, `impact-analysis`, `refactoring`) for your task
78
-
79
- ## Troubleshooting
80
-
81
- - **"Not inside a git repository"**: Run from a directory inside a git repo
82
- - **Index is stale after re-analyzing**: Restart Claude Code to reload the MCP server
83
- - **Embeddings slow**: Omit `--embeddings` (it's off by default) or set `OPENAI_API_KEY` for faster API-based embedding