niuma-ui 1.0.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 (220) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +25 -0
  4. package/README.en.md +99 -0
  5. package/README.md +161 -0
  6. package/package.json +106 -0
  7. package/src/__tests__/RsBadge.spec.ts +40 -0
  8. package/src/__tests__/RsBreadcrumb.spec.ts +172 -0
  9. package/src/__tests__/RsButton.spec.ts +108 -0
  10. package/src/__tests__/RsCalendarGrid.spec.ts +82 -0
  11. package/src/__tests__/RsCard.spec.ts +97 -0
  12. package/src/__tests__/RsCheckbox.spec.ts +36 -0
  13. package/src/__tests__/RsCodeEditor.spec.ts +111 -0
  14. package/src/__tests__/RsConfirmDialog.spec.ts +158 -0
  15. package/src/__tests__/RsContainer.spec.ts +89 -0
  16. package/src/__tests__/RsContextMenu.spec.ts +118 -0
  17. package/src/__tests__/RsDatePicker.spec.ts +88 -0
  18. package/src/__tests__/RsDateTimePicker.spec.ts +69 -0
  19. package/src/__tests__/RsDialog.spec.ts +404 -0
  20. package/src/__tests__/RsDrawer.spec.ts +145 -0
  21. package/src/__tests__/RsDropdown.spec.ts +135 -0
  22. package/src/__tests__/RsEmpty.spec.ts +99 -0
  23. package/src/__tests__/RsForm.spec.ts +173 -0
  24. package/src/__tests__/RsIcon.spec.ts +244 -0
  25. package/src/__tests__/RsInput.spec.ts +269 -0
  26. package/src/__tests__/RsInputNumber.spec.ts +118 -0
  27. package/src/__tests__/RsLabel.spec.ts +106 -0
  28. package/src/__tests__/RsLink.spec.ts +49 -0
  29. package/src/__tests__/RsLoading.spec.ts +84 -0
  30. package/src/__tests__/RsMarkdown.spec.ts +143 -0
  31. package/src/__tests__/RsMenu.spec.ts +119 -0
  32. package/src/__tests__/RsNewComponents.spec.ts +17 -0
  33. package/src/__tests__/RsPagination.spec.ts +158 -0
  34. package/src/__tests__/RsPopover.spec.ts +126 -0
  35. package/src/__tests__/RsScrollbar.spec.ts +53 -0
  36. package/src/__tests__/RsSelect.spec.ts +291 -0
  37. package/src/__tests__/RsSidebar.spec.ts +119 -0
  38. package/src/__tests__/RsSplitPane.spec.ts +177 -0
  39. package/src/__tests__/RsStatCard.spec.ts +79 -0
  40. package/src/__tests__/RsSteps.spec.ts +125 -0
  41. package/src/__tests__/RsTable.perf.spec.ts +103 -0
  42. package/src/__tests__/RsTable.spec.ts +1135 -0
  43. package/src/__tests__/RsTabs.spec.ts +328 -0
  44. package/src/__tests__/RsTerminal.spec.ts +218 -0
  45. package/src/__tests__/RsTimePicker.spec.ts +77 -0
  46. package/src/__tests__/RsToaster.spec.ts +95 -0
  47. package/src/__tests__/RsToolbar.spec.ts +75 -0
  48. package/src/__tests__/RsTooltip.spec.ts +111 -0
  49. package/src/__tests__/RsTree.spec.ts +572 -0
  50. package/src/__tests__/RsUpload.spec.ts +160 -0
  51. package/src/__tests__/RsVirtualList.spec.ts +77 -0
  52. package/src/__tests__/date-picker-utils.spec.ts +70 -0
  53. package/src/__tests__/dialog-viewport.spec.ts +38 -0
  54. package/src/__tests__/iso-local-datetime.spec.ts +58 -0
  55. package/src/__tests__/rs-clipboard.spec.ts +142 -0
  56. package/src/__tests__/setup.ts +12 -0
  57. package/src/__tests__/split-pane-utils.spec.ts +136 -0
  58. package/src/__tests__/table-drag.spec.ts +35 -0
  59. package/src/__tests__/table-edit-utils.spec.ts +194 -0
  60. package/src/__tests__/table-utils.spec.ts +286 -0
  61. package/src/__tests__/theme.spec.ts +22 -0
  62. package/src/__tests__/tree-utils.spec.ts +234 -0
  63. package/src/__tests__/useTableCellTooltip.spec.ts +101 -0
  64. package/src/components/RsAvatar.vue +115 -0
  65. package/src/components/RsBadge.vue +59 -0
  66. package/src/components/RsBreadcrumb.vue +65 -0
  67. package/src/components/RsBreadcrumbItems.vue +36 -0
  68. package/src/components/RsButton.vue +393 -0
  69. package/src/components/RsCalendarGrid.vue +284 -0
  70. package/src/components/RsCard.vue +237 -0
  71. package/src/components/RsCheckbox.vue +189 -0
  72. package/src/components/RsCodeBlock.vue +278 -0
  73. package/src/components/RsCodeEditor.vue +597 -0
  74. package/src/components/RsConfigProvider.vue +92 -0
  75. package/src/components/RsConfirmDialog.vue +178 -0
  76. package/src/components/RsContainer.vue +235 -0
  77. package/src/components/RsContextMenu.vue +238 -0
  78. package/src/components/RsContextMenuItems.vue +77 -0
  79. package/src/components/RsDatePicker.vue +756 -0
  80. package/src/components/RsDateTimePicker.vue +42 -0
  81. package/src/components/RsDialog.vue +486 -0
  82. package/src/components/RsDrawer.vue +175 -0
  83. package/src/components/RsDropdown.vue +252 -0
  84. package/src/components/RsDropdownItems.vue +90 -0
  85. package/src/components/RsEmpty.vue +107 -0
  86. package/src/components/RsForm.vue +131 -0
  87. package/src/components/RsIcon.vue +104 -0
  88. package/src/components/RsInput.vue +1101 -0
  89. package/src/components/RsInputNumber.vue +532 -0
  90. package/src/components/RsLabel.vue +54 -0
  91. package/src/components/RsLink.vue +106 -0
  92. package/src/components/RsLoading.vue +171 -0
  93. package/src/components/RsMarkdown.vue +329 -0
  94. package/src/components/RsMenu.vue +256 -0
  95. package/src/components/RsMenuItems.vue +125 -0
  96. package/src/components/RsMonacoEditor.vue +541 -0
  97. package/src/components/RsPagination.vue +184 -0
  98. package/src/components/RsPopover.vue +77 -0
  99. package/src/components/RsProseEditor.vue +160 -0
  100. package/src/components/RsScrollbar.vue +171 -0
  101. package/src/components/RsSelect.vue +1045 -0
  102. package/src/components/RsSidebar.vue +121 -0
  103. package/src/components/RsSidebarGroup.vue +32 -0
  104. package/src/components/RsSidebarItem.vue +72 -0
  105. package/src/components/RsSplitPane.vue +619 -0
  106. package/src/components/RsStatCard.vue +85 -0
  107. package/src/components/RsSteps.vue +138 -0
  108. package/src/components/RsTable.vue +2664 -0
  109. package/src/components/RsTableColgroup.vue +17 -0
  110. package/src/components/RsTableHeaderRow.vue +30 -0
  111. package/src/components/RsTabs.vue +951 -0
  112. package/src/components/RsTerminal.vue +697 -0
  113. package/src/components/RsTimePicker.vue +554 -0
  114. package/src/components/RsTimePickerColumns.vue +226 -0
  115. package/src/components/RsToaster.vue +396 -0
  116. package/src/components/RsToolbar.vue +135 -0
  117. package/src/components/RsTooltip.vue +142 -0
  118. package/src/components/RsTooltipProvider.vue +20 -0
  119. package/src/components/RsTree.vue +1248 -0
  120. package/src/components/RsUpload.vue +279 -0
  121. package/src/components/RsVirtualList.vue +217 -0
  122. package/src/components/avatar-utils.ts +19 -0
  123. package/src/components/breadcrumb-utils.ts +19 -0
  124. package/src/components/class-utils.ts +29 -0
  125. package/src/components/code-editor-utils.ts +106 -0
  126. package/src/components/code-mirror-completion.ts +40 -0
  127. package/src/components/code-mirror-diagnostics.ts +52 -0
  128. package/src/components/code-mirror-ghost.ts +158 -0
  129. package/src/components/code-mirror-inline-edit.ts +55 -0
  130. package/src/components/code-mirror-intel.ts +81 -0
  131. package/src/components/code-mirror-lang.ts +128 -0
  132. package/src/components/context-menu-utils.ts +20 -0
  133. package/src/components/date-picker-utils.ts +284 -0
  134. package/src/components/date-range-validation.ts +29 -0
  135. package/src/components/date-validation.ts +38 -0
  136. package/src/components/dialog-viewport.ts +48 -0
  137. package/src/components/dialog-window.ts +253 -0
  138. package/src/components/dropdown-utils.ts +31 -0
  139. package/src/components/form-utils.ts +77 -0
  140. package/src/components/input-number-utils.ts +128 -0
  141. package/src/components/input-rules.ts +84 -0
  142. package/src/components/loading-utils.ts +8 -0
  143. package/src/components/markdown-utils.ts +126 -0
  144. package/src/components/menu-utils.ts +48 -0
  145. package/src/components/overlay-utils.ts +47 -0
  146. package/src/components/pagination-utils.ts +38 -0
  147. package/src/components/popover-utils.ts +8 -0
  148. package/src/components/reka.ts +106 -0
  149. package/src/components/resolve-radius.ts +22 -0
  150. package/src/components/resolve-size.ts +29 -0
  151. package/src/components/scrollbar-utils.ts +10 -0
  152. package/src/components/select-utils.ts +72 -0
  153. package/src/components/split-pane-utils.ts +187 -0
  154. package/src/components/steps-utils.ts +22 -0
  155. package/src/components/table/RsTableBodyRow.vue +287 -0
  156. package/src/components/table/RsTableCell.vue +239 -0
  157. package/src/components/table/RsTableCellContent.ts +18 -0
  158. package/src/components/table/RsTableCellEditor.vue +351 -0
  159. package/src/components/table/RsTableEditGutter.vue +54 -0
  160. package/src/components/table/RsTableHeaderFilter.vue +107 -0
  161. package/src/components/table/RsTableRowStatus.vue +69 -0
  162. package/src/components/table/rs-table.css +1141 -0
  163. package/src/components/table/table-context-menu.ts +53 -0
  164. package/src/components/table/table-edit-utils.ts +496 -0
  165. package/src/components/table/table-keys.ts +12 -0
  166. package/src/components/table-drag.ts +215 -0
  167. package/src/components/table-utils.ts +950 -0
  168. package/src/components/tabs-utils.ts +107 -0
  169. package/src/components/terminal-utils.ts +301 -0
  170. package/src/components/terminal-wheel.ts +49 -0
  171. package/src/components/time-picker-utils.ts +167 -0
  172. package/src/components/time-range-validation.ts +23 -0
  173. package/src/components/time-validation.ts +20 -0
  174. package/src/components/tree-utils.ts +518 -0
  175. package/src/components/upload-utils.ts +55 -0
  176. package/src/components/virtual-list-utils.ts +17 -0
  177. package/src/composables/useClickOutside.ts +22 -0
  178. package/src/composables/useRsConfig.ts +91 -0
  179. package/src/composables/useRsI18n.ts +22 -0
  180. package/src/composables/useRsTabsNav.ts +187 -0
  181. package/src/composables/useRsToast.ts +48 -0
  182. package/src/composables/useTableCellTooltip.ts +239 -0
  183. package/src/composables/useTableEdit.ts +271 -0
  184. package/src/composables/useTableRowHighlight.ts +50 -0
  185. package/src/dev/vite-codemirror-deps.ts +20 -0
  186. package/src/icons/custom/api.ts +54 -0
  187. package/src/icons/custom/clickhouse.ts +52 -0
  188. package/src/icons/custom/dameng.ts +83 -0
  189. package/src/icons/custom/ftp.ts +61 -0
  190. package/src/icons/custom/kingbase.ts +52 -0
  191. package/src/icons/custom/mongodb.ts +50 -0
  192. package/src/icons/custom/mysql.ts +51 -0
  193. package/src/icons/custom/oracle.ts +52 -0
  194. package/src/icons/custom/redis.ts +51 -0
  195. package/src/icons/custom/sqlite.ts +50 -0
  196. package/src/icons/custom/sqlserver.ts +52 -0
  197. package/src/icons/custom/vastbase.ts +57 -0
  198. package/src/icons/lucide.ts +78 -0
  199. package/src/icons/registry.ts +35 -0
  200. package/src/index.ts +425 -0
  201. package/src/lib/iso-local-datetime.ts +152 -0
  202. package/src/lib/rs-dayjs.ts +56 -0
  203. package/src/locale/messages.ts +362 -0
  204. package/src/locale/types.ts +5 -0
  205. package/src/monaco/debug-decorations.css +20 -0
  206. package/src/monaco/debug-decorations.ts +70 -0
  207. package/src/monaco/index.ts +22 -0
  208. package/src/monaco/languages/index.ts +21 -0
  209. package/src/monaco/languages/mongodb-shell.ts +125 -0
  210. package/src/monaco/languages/types.ts +55 -0
  211. package/src/monaco/setup.ts +101 -0
  212. package/src/styles.css +1040 -0
  213. package/src/theme/apply.ts +12 -0
  214. package/src/theme/brand.example.css +37 -0
  215. package/src/theme/presets.ts +69 -0
  216. package/src/theme/types.ts +81 -0
  217. package/src/utils/rs-clipboard.ts +272 -0
  218. package/src/vite-env.d.ts +7 -0
  219. package/vite-plugins/monaco-zh-nls.ts +101 -0
  220. package/vite-plugins/silence-antlr-parse-console.ts +29 -0
@@ -0,0 +1,52 @@
1
+ import { Compartment, RangeSetBuilder, StateEffect, StateField } from '@codemirror/state'
2
+ import { Decoration, EditorView } from '@codemirror/view'
3
+ import type { RsCodeEditorDiagnostic } from './code-editor-utils'
4
+
5
+ const setDiagnosticsEffect = StateEffect.define<RsCodeEditorDiagnostic[]>()
6
+
7
+ function buildLineDecorations(doc: { lines: number; line: (n: number) => { from: number } }, diags: RsCodeEditorDiagnostic[]) {
8
+ const builder = new RangeSetBuilder<Decoration>()
9
+ for (const d of diags) {
10
+ const lineNo = d.line ?? 1
11
+ if (lineNo < 1 || lineNo > doc.lines) continue
12
+ const line = doc.line(lineNo)
13
+ const cls =
14
+ d.severity === 'warning'
15
+ ? 'cm-lintRange cm-lintRange-warning'
16
+ : d.severity === 'info'
17
+ ? 'cm-lintRange cm-lintRange-info'
18
+ : 'cm-lintRange cm-lintRange-error'
19
+ builder.add(line.from, line.from, Decoration.line({ class: cls }))
20
+ }
21
+ return builder.finish()
22
+ }
23
+
24
+ const diagnosticField = StateField.define({
25
+ create() {
26
+ return Decoration.none
27
+ },
28
+ update(deco, tr) {
29
+ for (const e of tr.effects) {
30
+ if (e.is(setDiagnosticsEffect)) {
31
+ return buildLineDecorations(tr.state.doc, e.value)
32
+ }
33
+ }
34
+ if (tr.docChanged) {
35
+ return deco.map(tr.changes)
36
+ }
37
+ return deco
38
+ },
39
+ provide: (f) => EditorView.decorations.from(f),
40
+ })
41
+
42
+ export const diagnosticCompartment = new Compartment()
43
+
44
+ export function diagnosticExtensions() {
45
+ return diagnosticCompartment.of([diagnosticField])
46
+ }
47
+
48
+ export function setEditorDiagnostics(view: EditorView, diags: RsCodeEditorDiagnostic[]) {
49
+ view.dispatch({
50
+ effects: setDiagnosticsEffect.of(diags),
51
+ })
52
+ }
@@ -0,0 +1,158 @@
1
+ import { Prec, StateEffect, StateField, type Extension } from '@codemirror/state'
2
+ import { Decoration, EditorView, keymap, WidgetType } from '@codemirror/view'
3
+ import { posToLineColumn } from './code-mirror-intel'
4
+
5
+ export const setGhostCompletion = StateEffect.define<string | null>()
6
+
7
+ interface GhostMeta {
8
+ text: string
9
+ pos: number
10
+ }
11
+
12
+ class GhostWidget extends WidgetType {
13
+ constructor(readonly text: string) {
14
+ super()
15
+ }
16
+
17
+ eq(other: GhostWidget) {
18
+ return other.text === this.text
19
+ }
20
+
21
+ toDOM() {
22
+ const span = document.createElement('span')
23
+ span.className = 'rs-code-editor__ghost'
24
+ span.textContent = this.text
25
+ return span
26
+ }
27
+
28
+ ignoreEvent() {
29
+ return true
30
+ }
31
+ }
32
+
33
+ const ghostMetaField = StateField.define<GhostMeta | null>({
34
+ create() {
35
+ return null
36
+ },
37
+ update(value, tr) {
38
+ for (const effect of tr.effects) {
39
+ if (effect.is(setGhostCompletion)) {
40
+ if (!effect.value) return null
41
+ return { text: effect.value, pos: tr.state.selection.main.head }
42
+ }
43
+ }
44
+ if (tr.docChanged) return null
45
+ if (tr.selection && !tr.effects.some((e) => e.is(setGhostCompletion))) {
46
+ return null
47
+ }
48
+ return value
49
+ },
50
+ })
51
+
52
+ const ghostField = StateField.define<ReturnType<typeof Decoration.set>>({
53
+ create() {
54
+ return Decoration.none
55
+ },
56
+ update(deco, tr) {
57
+ deco = deco.map(tr.changes)
58
+ for (const effect of tr.effects) {
59
+ if (effect.is(setGhostCompletion)) {
60
+ if (!effect.value) return Decoration.none
61
+ const pos = tr.state.selection.main.head
62
+ return Decoration.set([
63
+ Decoration.widget({
64
+ widget: new GhostWidget(effect.value),
65
+ side: 1,
66
+ }).range(pos),
67
+ ])
68
+ }
69
+ }
70
+ if (tr.docChanged || tr.selection) {
71
+ const hasSet = tr.effects.some((e) => e.is(setGhostCompletion))
72
+ if (!hasSet) return Decoration.none
73
+ }
74
+ return deco
75
+ },
76
+ provide: (field) => EditorView.decorations.from(field),
77
+ })
78
+
79
+ export interface CodeMirrorGhostHandlers {
80
+ completionRequest?: (prefix: string, suffix: string, line: number, column: number) => Promise<string | null>
81
+ }
82
+
83
+ function readGhostAtCursor(view: EditorView): GhostMeta | null {
84
+ const meta = view.state.field(ghostMetaField, false)
85
+ if (!meta) return null
86
+ if (meta.pos !== view.state.selection.main.head) return null
87
+ return meta
88
+ }
89
+
90
+ export function codeMirrorGhostExtensions(
91
+ handlers: () => CodeMirrorGhostHandlers,
92
+ scheduleDebounced: (fn: () => void) => void,
93
+ cancelScheduled?: () => void,
94
+ ): Extension[] {
95
+ let seq = 0
96
+ return [
97
+ ghostMetaField,
98
+ ghostField,
99
+ EditorView.updateListener.of((update) => {
100
+ if (!update.docChanged) return
101
+ const h = handlers()
102
+ if (!h.completionRequest) return
103
+ cancelScheduled?.()
104
+ const id = ++seq
105
+ scheduleDebounced(() => {
106
+ const view = update.view
107
+ const pos = view.state.selection.main.head
108
+ const prefix = view.state.doc.sliceString(0, pos)
109
+ const suffix = view.state.doc.sliceString(pos)
110
+ const { line, column } = posToLineColumn(view, pos)
111
+ void h.completionRequest!(prefix, suffix, line, column).then((text) => {
112
+ if (id !== seq) return
113
+ if (!text?.trim()) {
114
+ view.dispatch({ effects: setGhostCompletion.of(null) })
115
+ return
116
+ }
117
+ const current = view.state.selection.main.head
118
+ if (view.state.doc.sliceString(current) !== suffix) return
119
+ view.dispatch({ effects: setGhostCompletion.of(text) })
120
+ })
121
+ })
122
+ }),
123
+ Prec.highest(
124
+ keymap.of([
125
+ {
126
+ key: 'Tab',
127
+ run: (view) => {
128
+ const ghost = readGhostAtCursor(view)
129
+ if (!ghost) return false
130
+ cancelScheduled?.()
131
+ seq += 1
132
+ const pos = view.state.selection.main.head
133
+ view.dispatch({
134
+ changes: { from: pos, to: pos, insert: ghost.text },
135
+ selection: { anchor: pos + ghost.text.length },
136
+ effects: setGhostCompletion.of(null),
137
+ })
138
+ return true
139
+ },
140
+ },
141
+ {
142
+ key: 'Escape',
143
+ run: (view) => {
144
+ if (!readGhostAtCursor(view)) return false
145
+ cancelScheduled?.()
146
+ seq += 1
147
+ view.dispatch({ effects: setGhostCompletion.of(null) })
148
+ return true
149
+ },
150
+ },
151
+ ]),
152
+ ),
153
+ ]
154
+ }
155
+
156
+ export function clearGhostCompletion(view: EditorView) {
157
+ view.dispatch({ effects: setGhostCompletion.of(null) })
158
+ }
@@ -0,0 +1,55 @@
1
+ import { Prec, type Extension } from '@codemirror/state'
2
+ import { EditorView, keymap } from '@codemirror/view'
3
+
4
+ export interface InlineEditTrigger {
5
+ selection: string
6
+ from: number
7
+ to: number
8
+ }
9
+
10
+ /** 提取选区上下各若干行,供 Inline Edit 理解作用域(不含重复包裹选区)。 */
11
+ export function extractSurroundingContext(doc: string, from: number, to: number, lineRadius = 20): string {
12
+ if (!doc) return ''
13
+ const lines = doc.split('\n')
14
+ let pos = 0
15
+ let startLine = 0
16
+ let endLine = lines.length - 1
17
+ for (let i = 0; i < lines.length; i++) {
18
+ const lineStart = pos
19
+ const lineEnd = pos + lines[i].length
20
+ if (from >= lineStart && from <= lineEnd + (i < lines.length - 1 ? 1 : 0)) startLine = i
21
+ if (to >= lineStart && to <= lineEnd + (i < lines.length - 1 ? 1 : 0)) endLine = i
22
+ pos = lineEnd + 1
23
+ }
24
+ const contextStart = Math.max(0, startLine - lineRadius)
25
+ const contextEnd = Math.min(lines.length - 1, endLine + lineRadius)
26
+ return lines.slice(contextStart, contextEnd + 1).join('\n')
27
+ }
28
+
29
+ export function codeMirrorInlineEditExtension(
30
+ onTrigger: (view: EditorView, info: InlineEditTrigger) => void,
31
+ ): Extension {
32
+ return Prec.highest(
33
+ keymap.of([
34
+ {
35
+ key: 'Mod-k',
36
+ run: (view) => {
37
+ const { from, to } = view.state.selection.main
38
+ if (from === to) return false
39
+ const selection = view.state.sliceDoc(from, to)
40
+ if (!selection.trim()) return false
41
+ onTrigger(view, { selection, from, to })
42
+ return true
43
+ },
44
+ },
45
+ ]),
46
+ )
47
+ }
48
+
49
+ export function replaceEditorRange(view: EditorView, from: number, to: number, insert: string) {
50
+ view.dispatch({
51
+ changes: { from, to, insert },
52
+ selection: { anchor: from + insert.length },
53
+ })
54
+ view.focus()
55
+ }
@@ -0,0 +1,81 @@
1
+ import { EditorView, hoverTooltip } from '@codemirror/view'
2
+ import type { Extension } from '@codemirror/state'
3
+
4
+ export interface CodeMirrorIntelHandlers {
5
+ hoverRequest?: (line: number, column: number) => Promise<string | null>
6
+ definitionRequest?: (line: number, column: number) => Promise<{
7
+ file: string
8
+ line: number
9
+ column?: number
10
+ } | null>
11
+ currentFilePath?: string
12
+ onGotoDefinition?: (loc: { file: string; line: number; column?: number }) => void
13
+ }
14
+
15
+ export function posToLineColumn(view: EditorView, pos: number) {
16
+ const line = view.state.doc.lineAt(pos)
17
+ return {
18
+ line: line.number,
19
+ column: pos - line.from + 1,
20
+ }
21
+ }
22
+
23
+ export function goToPosition(view: EditorView, line: number, column = 1) {
24
+ const doc = view.state.doc
25
+ const ln = Math.min(Math.max(1, line), doc.lines)
26
+ const lineObj = doc.line(ln)
27
+ const pos = lineObj.from + Math.max(0, column - 1)
28
+ view.dispatch({
29
+ selection: { anchor: pos },
30
+ scrollIntoView: true,
31
+ })
32
+ view.focus()
33
+ }
34
+
35
+ export function codeMirrorIntelExtensions(handlers: () => CodeMirrorIntelHandlers): Extension[] {
36
+ const hoverExt = hoverTooltip(
37
+ async (view, pos) => {
38
+ const h = handlers()
39
+ if (!h.hoverRequest) return null
40
+ const { line, column } = posToLineColumn(view, pos)
41
+ const text = await h.hoverRequest(line, column)
42
+ if (!text?.trim()) return null
43
+ return {
44
+ pos,
45
+ above: true,
46
+ create() {
47
+ const dom = document.createElement('div')
48
+ dom.className = 'rs-code-editor__hover'
49
+ dom.textContent = text
50
+ return { dom }
51
+ },
52
+ }
53
+ },
54
+ { hoverTime: 450 },
55
+ )
56
+
57
+ const clickExt = EditorView.domEventHandlers({
58
+ mousedown(event, view) {
59
+ if (!event.ctrlKey && !event.metaKey) return false
60
+ const h = handlers()
61
+ if (!h.definitionRequest) return false
62
+ const pos = view.posAtCoords({ x: event.clientX, y: event.clientY })
63
+ if (pos == null) return false
64
+ const { line, column } = posToLineColumn(view, pos)
65
+ void h.definitionRequest(line, column).then((loc) => {
66
+ if (!loc?.line) return
67
+ const current = (h.currentFilePath ?? '').replace(/\\/g, '/')
68
+ const target = (loc.file ?? '').replace(/\\/g, '/')
69
+ if (target && current && target !== current) {
70
+ h.onGotoDefinition?.(loc)
71
+ return
72
+ }
73
+ goToPosition(view, loc.line, loc.column ?? 1)
74
+ })
75
+ event.preventDefault()
76
+ return true
77
+ },
78
+ })
79
+
80
+ return [hoverExt, clickExt]
81
+ }
@@ -0,0 +1,128 @@
1
+ import type { Extension } from '@codemirror/state'
2
+ import type { RsCodeEditorSqlConfig } from './code-editor-utils'
3
+
4
+ export interface ResolveCodeMirrorLanguageOptions {
5
+ sql?: RsCodeEditorSqlConfig
6
+ }
7
+
8
+ /** 按语言标识动态加载 CodeMirror 语言扩展 */
9
+ export async function resolveCodeMirrorLanguage(
10
+ lang: string,
11
+ options?: ResolveCodeMirrorLanguageOptions,
12
+ ): Promise<Extension[]> {
13
+ const l = lang.toLowerCase().trim()
14
+ try {
15
+ switch (l) {
16
+ case 'js':
17
+ case 'javascript':
18
+ case 'jsx': {
19
+ const { javascript } = await import('@codemirror/lang-javascript')
20
+ return [javascript()]
21
+ }
22
+ case 'ts':
23
+ case 'typescript':
24
+ case 'tsx': {
25
+ const { javascript } = await import('@codemirror/lang-javascript')
26
+ return [javascript({ typescript: true })]
27
+ }
28
+ case 'py':
29
+ case 'python': {
30
+ const { python } = await import('@codemirror/lang-python')
31
+ return [python()]
32
+ }
33
+ case 'go': {
34
+ const { go } = await import('@codemirror/lang-go')
35
+ return [go()]
36
+ }
37
+ case 'html':
38
+ case 'htm': {
39
+ const { html } = await import('@codemirror/lang-html')
40
+ return [html()]
41
+ }
42
+ case 'css':
43
+ case 'scss':
44
+ case 'less': {
45
+ const { css } = await import('@codemirror/lang-css')
46
+ return [css()]
47
+ }
48
+ case 'json':
49
+ case 'jsonc': {
50
+ const { json } = await import('@codemirror/lang-json')
51
+ return [json()]
52
+ }
53
+ case 'sql': {
54
+ const { sql, PostgreSQL, StandardSQL, MySQL } = await import('@codemirror/lang-sql')
55
+ const dialectName = options?.sql?.dialect ?? 'postgresql'
56
+ let dialect = PostgreSQL
57
+ if (dialectName === 'mysql') dialect = MySQL
58
+ else if (dialectName === 'standard') dialect = StandardSQL
59
+ return [
60
+ sql({
61
+ dialect,
62
+ schema: options?.sql?.schema as never,
63
+ defaultTable: options?.sql?.defaultTable,
64
+ defaultSchema: options?.sql?.defaultSchema,
65
+ }),
66
+ ]
67
+ }
68
+ case 'rust':
69
+ case 'rs': {
70
+ const { rust } = await import('@codemirror/lang-rust')
71
+ return [rust()]
72
+ }
73
+ case 'cpp':
74
+ case 'cc':
75
+ case 'cxx':
76
+ case 'c': {
77
+ const { cpp } = await import('@codemirror/lang-cpp')
78
+ return [cpp()]
79
+ }
80
+ case 'java': {
81
+ const { java } = await import('@codemirror/lang-java')
82
+ return [java()]
83
+ }
84
+ case 'md':
85
+ case 'markdown': {
86
+ const { markdown } = await import('@codemirror/lang-markdown')
87
+ return [markdown()]
88
+ }
89
+ case 'xml':
90
+ case 'svg': {
91
+ const { xml } = await import('@codemirror/lang-xml')
92
+ return [xml()]
93
+ }
94
+ case 'yaml':
95
+ case 'yml': {
96
+ const { yaml } = await import('@codemirror/lang-yaml')
97
+ return [yaml()]
98
+ }
99
+ case 'vue': {
100
+ const { html } = await import('@codemirror/lang-html')
101
+ return [html()]
102
+ }
103
+ case 'sh':
104
+ case 'bash':
105
+ case 'shell':
106
+ case 'zsh': {
107
+ const [{ StreamLanguage }, { shell }] = await Promise.all([
108
+ import('@codemirror/language'),
109
+ import('@codemirror/legacy-modes/mode/shell'),
110
+ ])
111
+ return [StreamLanguage.define(shell)]
112
+ }
113
+ default:
114
+ return []
115
+ }
116
+ } catch {
117
+ return []
118
+ }
119
+ }
120
+
121
+ export function isCodeMirrorLightTheme(): boolean {
122
+ return typeof document !== 'undefined' && document.documentElement.dataset.rsTheme === 'light'
123
+ }
124
+
125
+ /** 预加载 CodeMirror 核心与目标语言包(供辅助窗口 reveal 前调用) */
126
+ export async function prewarmCodeMirrorEditor(language: string): Promise<void> {
127
+ await Promise.all([import('codemirror'), resolveCodeMirrorLanguage(language)])
128
+ }
@@ -0,0 +1,20 @@
1
+ /** 右键菜单图标尺寸(与 Dropdown / Menu 一致) */
2
+ export const RS_CONTEXT_MENU_ICON_SIZE = 16
3
+ export const RS_CONTEXT_MENU_ARROW_SIZE = 14
4
+
5
+ /** 右键菜单项定义 */
6
+ export interface RsContextMenuItem {
7
+ /** 唯一标识,选中时回传 */
8
+ key: string
9
+ label: string
10
+ icon?: string
11
+ disabled?: boolean
12
+ /** 危险操作样式 */
13
+ danger?: boolean
14
+ /** 为 true 时渲染分隔线 */
15
+ separator?: boolean
16
+ /** 子菜单项 */
17
+ children?: RsContextMenuItem[]
18
+ /** 键盘快捷键提示(仅展示,不绑定事件),如 "⌘K"、"⌃C" */
19
+ shortcut?: string
20
+ }