@lobehub/editor 1.17.2 → 1.18.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.
@@ -4,3 +4,4 @@ export { default as ReactMarkdownPlugin } from './react';
4
4
  export type { MARKDOWN_READER_LEVEL } from './service/shortcut';
5
5
  export { IMarkdownShortCutService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX, } from './service/shortcut';
6
6
  export { isPunctuationChar } from './utils';
7
+ export { detectCodeLanguage, detectLanguage } from './utils/detectLanguage';
@@ -2,4 +2,5 @@ export { INSERT_MARKDOWN_COMMAND } from "./command";
2
2
  export { MarkdownPlugin } from "./plugin";
3
3
  export { default as ReactMarkdownPlugin } from "./react";
4
4
  export { IMarkdownShortCutService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX } from "./service/shortcut";
5
- export { isPunctuationChar } from "./utils";
5
+ export { isPunctuationChar } from "./utils";
6
+ export { detectCodeLanguage, detectLanguage } from "./utils/detectLanguage";
@@ -13,7 +13,7 @@ function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.g
13
13
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
14
14
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
15
15
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
16
- import { $isCodeNode } from '@lexical/code';
16
+ import { $createCodeNode, $isCodeNode } from '@lexical/code';
17
17
  import { $getNodeByKey, $getSelection, $isRangeSelection, $isTextNode, COLLABORATION_TAG, COMMAND_PRIORITY_CRITICAL, HISTORIC_TAG, KEY_ENTER_COMMAND, PASTE_COMMAND } from 'lexical';
18
18
  import { KernelPlugin } from "../../../editor-kernel/plugin";
19
19
  import { createDebugLogger } from "../../../utils/debug";
@@ -21,6 +21,7 @@ import { registerMarkdownCommand } from "../command";
21
21
  import MarkdownDataSource from "../data-source/markdown-data-source";
22
22
  import { IMarkdownShortCutService, MarkdownShortCutService } from "../service/shortcut";
23
23
  import { canContainTransformableMarkdown } from "../utils";
24
+ import { detectCodeLanguage, detectLanguage } from "../utils/detectLanguage";
24
25
  export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
25
26
  _inherits(MarkdownPlugin, _KernelPlugin);
26
27
  var _super = _createSuper(MarkdownPlugin);
@@ -136,43 +137,56 @@ export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
136
137
  htmlLength: (html === null || html === void 0 ? void 0 : html.length) || 0,
137
138
  textLength: text.length
138
139
  });
140
+ var enablePasteMarkdown = (_this2$config$enableP = (_this2$config = _this2.config) === null || _this2$config === void 0 ? void 0 : _this2$config.enablePasteMarkdown) !== null && _this2$config$enableP !== void 0 ? _this2$config$enableP : true;
141
+
142
+ // If markdown formatting is disabled, we're done
143
+ if (!enablePasteMarkdown) {
144
+ // Force plain text paste for external content
145
+ event.preventDefault();
146
+ event.stopPropagation();
147
+ editor.update(function () {
148
+ var selection = $getSelection();
149
+ if (!$isRangeSelection(selection)) return;
150
+
151
+ // Insert plain text
152
+ selection.insertText(text);
153
+ });
154
+ _this2.logger.debug('markdown formatting disabled, plain text inserted');
155
+ return true;
156
+ }
139
157
 
140
158
  // Check if this is likely a rich-text paste from the editor or other rich editor
141
159
  // Rich text pastes typically have HTML that's more complex than just wrapping text
142
- if (html && html.trim()) {
143
- var htmlDoc = new DOMParser().parseFromString(html, 'text/html');
144
- var hasRichContent =
145
- // Has block elements (p, div, h1-h6, etc.)
146
- htmlDoc.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, ul, ol, li, table').length > 0 ||
147
- // Has inline formatting (strong, em, code, etc.)
148
- htmlDoc.querySelectorAll('strong, em, b, i, u, code, span[style]').length > 0 ||
149
- // Has data attributes (often used by editors to store metadata)
150
- htmlDoc.querySelectorAll('[data-lexical-text], [data-lexical-decorator]').length > 0;
151
- if (hasRichContent) {
152
- // This looks like rich content from an editor - let Lexical handle it
153
- _this2.logger.debug('rich content detected, letting Lexical handle paste');
154
- return false;
155
- }
160
+ if (clipboardData.types.includes('application/x-lexical-editor')) {
161
+ _this2.logger.debug('rich content detected, letting Lexical handle paste');
162
+ return false;
156
163
  }
157
164
 
158
165
  // Check if markdown paste formatting is enabled (default: true)
159
- var enablePasteMarkdown = (_this2$config$enableP = (_this2$config = _this2.config) === null || _this2$config === void 0 ? void 0 : _this2$config.enablePasteMarkdown) !== null && _this2$config$enableP !== void 0 ? _this2$config$enableP : true;
160
166
 
161
- // Force plain text paste for external content
162
- event.preventDefault();
163
- event.stopPropagation();
164
- editor.update(function () {
165
- var selection = $getSelection();
166
- if (!$isRangeSelection(selection)) return;
167
+ // Check if content is code (JSON, SQL, etc.) and should be inserted as code block
168
+ var codeInfo = _this2.detectCodeContent(text);
169
+ if (codeInfo) {
170
+ // Code detected - insert as code block
171
+ _this2.logger.debug("code detected (".concat(codeInfo.language, "), inserting as code block"));
172
+ event.preventDefault();
173
+ event.stopPropagation();
174
+ editor.update(function () {
175
+ var selection = $getSelection();
176
+ if (!$isRangeSelection(selection)) return;
167
177
 
168
- // Insert plain text
169
- selection.insertText(text);
170
- });
178
+ // Create code block node with detected language
179
+ var codeNode = $createCodeNode(codeInfo.language);
180
+ selection.insertNodes([codeNode]);
171
181
 
172
- // If markdown formatting is disabled, we're done
173
- if (!enablePasteMarkdown) {
174
- _this2.logger.debug('markdown formatting disabled, plain text inserted');
175
- return true;
182
+ // Insert the code text into the code block
183
+ codeNode.select();
184
+ var codeSelection = $getSelection();
185
+ if ($isRangeSelection(codeSelection)) {
186
+ codeSelection.insertText(text);
187
+ }
188
+ });
189
+ return true; // Command handled
176
190
  }
177
191
 
178
192
  // Check if the pasted plain text contains markdown patterns
@@ -190,52 +204,87 @@ export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
190
204
  // No markdown detected - plain text is already inserted
191
205
  _this2.logger.debug('no markdown patterns detected, keeping as plain text');
192
206
  }
207
+ if (clipboardData.types.includes('text/html') && clipboardData.types.includes('text/rtf')) {
208
+ // Code detected - insert as code block
209
+ _this2.logger.debug("code like, inserting as code block");
210
+ event.preventDefault();
211
+ event.stopPropagation();
212
+ editor.update(function () {
213
+ var selection = $getSelection();
214
+ if (!$isRangeSelection(selection)) return;
215
+
216
+ // Create code block node with detected language
217
+ var codeNode = $createCodeNode('plaintext');
218
+ selection.insertNodes([codeNode]);
219
+
220
+ // Insert the code text into the code block
221
+ codeNode.select();
222
+ var codeSelection = $getSelection();
223
+ if ($isRangeSelection(codeSelection)) {
224
+ codeSelection.insertText(text);
225
+ }
226
+ });
227
+ return true; // Command handled
228
+ }
229
+
230
+ // Force plain text paste for external content
231
+ event.preventDefault();
232
+ event.stopPropagation();
233
+ editor.update(function () {
234
+ var selection = $getSelection();
235
+ if (!$isRangeSelection(selection)) return;
236
+
237
+ // Insert plain text
238
+ selection.insertText(text);
239
+ });
193
240
  return true; // Command handled
194
241
  }, COMMAND_PRIORITY_CRITICAL));
195
242
  this.register(registerMarkdownCommand(editor, this.service));
196
243
  }
197
244
 
198
245
  /**
199
- * Detect if text contains markdown patterns
200
- * Returns false if content is likely code (JSON, HTML, SQL, etc.)
246
+ * Detect if content is code and should be inserted as code block
247
+ * Uses advanced language detection with pattern matching
248
+ * Excludes markdown as it should be handled by markdown formatting dialog
201
249
  */
202
250
  }, {
203
- key: "detectMarkdownContent",
204
- value: function detectMarkdownContent(text) {
205
- var trimmed = text.trim();
206
-
207
- // Check if content is JSON
208
- if (trimmed.startsWith('{') && trimmed.endsWith('}') || trimmed.startsWith('[') && trimmed.endsWith(']')) {
209
- try {
210
- JSON.parse(trimmed);
211
- this.logger.debug('content is valid JSON, not treating as markdown');
212
- return false;
213
- } catch (_unused) {
214
- // Not valid JSON, continue checking
251
+ key: "detectCodeContent",
252
+ value: function detectCodeContent(text) {
253
+ // Use the advanced language detector
254
+ var detected = detectLanguage(text);
255
+ if (detected && detected.confidence > 50) {
256
+ // Don't insert markdown as code block - it should trigger the formatting dialog
257
+ if (detected.language === 'markdown') {
258
+ return null;
215
259
  }
260
+ this.logger.debug('language detected:', detected);
261
+ return detected;
216
262
  }
217
263
 
218
- // Check if content has significant HTML structure
219
- var htmlTagPattern = /<[a-z][\S\s]*?>/gi;
220
- var htmlMatches = text.match(htmlTagPattern);
221
- if (htmlMatches && htmlMatches.length > 5) {
222
- // More than 5 HTML tags suggests this is HTML content, not markdown
223
- this.logger.debug('content has significant HTML structure, not treating as markdown');
224
- return false;
264
+ // Fallback to fast detection for common formats
265
+ var fastDetected = detectCodeLanguage(text);
266
+ if (fastDetected) {
267
+ // Don't insert markdown as code block
268
+ if (fastDetected === 'markdown') {
269
+ return null;
270
+ }
271
+ return {
272
+ confidence: 80,
273
+ language: fastDetected
274
+ };
225
275
  }
276
+ return null;
277
+ }
226
278
 
227
- // Check if content looks like code (SQL, XML, etc.)
228
- // Common patterns: SQL keywords, XML declarations, file paths
229
- var codePatterns = [/^\s*(select|insert|update|delete|create|alter|drop)\s+/im,
230
- // SQL
231
- /^\s*<\?xml/i,
232
- // XML declaration
233
- /^[a-z]:\\|^\/[a-z]/im // File paths (Windows/Unix)
234
- ];
235
- if (codePatterns.some(function (pattern) {
236
- return pattern.test(text);
237
- })) {
238
- this.logger.debug('content looks like code, not treating as markdown');
279
+ /**
280
+ * Detect if text contains markdown patterns
281
+ * Returns false if content is likely code (will be handled by detectCodeContent)
282
+ */
283
+ }, {
284
+ key: "detectMarkdownContent",
285
+ value: function detectMarkdownContent(text) {
286
+ // If code is detected, don't treat as markdown
287
+ if (this.detectCodeContent(text)) {
239
288
  return false;
240
289
  }
241
290
  var markdownPatterns = [
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Language detection utility for code snippets
3
+ * Uses pattern matching and heuristics to identify programming languages
4
+ */
5
+ interface LanguageDetectionResult {
6
+ confidence: number;
7
+ language: string;
8
+ }
9
+ /**
10
+ * Detect the programming language of a code snippet
11
+ * @param code - The code snippet to analyze
12
+ * @returns Language detection result with confidence score
13
+ */
14
+ export declare function detectLanguage(code: string): LanguageDetectionResult | null;
15
+ /**
16
+ * Simple detection for common formats with high confidence
17
+ * Falls back to detectLanguage for more complex detection
18
+ */
19
+ export declare function detectCodeLanguage(code: string): string | null;
20
+ export {};
@@ -0,0 +1,267 @@
1
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
2
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
3
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
4
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
5
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
6
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
7
+ /**
8
+ * Language detection utility for code snippets
9
+ * Uses pattern matching and heuristics to identify programming languages
10
+ */
11
+
12
+ var LANGUAGE_PATTERNS = {
13
+ // Shell/Config
14
+ bash: {
15
+ optional: [/\|\||&&/, /echo\s+/, /if\s+\[/],
16
+ required: [/^#!\/bin\/(ba)?sh/, /\${?\w+}?/],
17
+ weight: 7
18
+ },
19
+ csharp: {
20
+ optional: [/\[.*]/, /async\s+Task/, /var\s+\w+\s*=/],
21
+ required: [/(public|private|protected)\s+(class|interface|namespace)/, /using\s+\w+/],
22
+ weight: 8
23
+ },
24
+ css: {
25
+ optional: [/@media|@import|@keyframes/, /!important/],
26
+ required: [/[#.]?[\w-]+\s*{/, /:\s*[\w#%-]+;/],
27
+ weight: 8
28
+ },
29
+ dockerfile: {
30
+ required: [/^FROM\s+/, /^(RUN|CMD|COPY|ADD|WORKDIR|ENV|EXPOSE)\s+/m],
31
+ weight: 10
32
+ },
33
+ go: {
34
+ optional: [/import\s+\(/, /:=/, /defer|goroutine/],
35
+ required: [/package\s+\w+/, /func\s+\w+/],
36
+ weight: 8
37
+ },
38
+ graphql: {
39
+ optional: [/fragment\s+/, /on\s+\w+/],
40
+ required: [/(query|mutation|subscription)\s+/, /{[\S\s]*}/],
41
+ weight: 8
42
+ },
43
+ // Web languages
44
+ html: {
45
+ optional: [/<\/\w+>/, /class=|id=/],
46
+ required: [/<(html|head|body|div|span|p|a|img|script|style|link|meta)/i],
47
+ weight: 9
48
+ },
49
+ ini: {
50
+ exclude: [/^\s*</, /{.*}/],
51
+ required: [/^\[.*]$/m, /\w+\s*=\s*/],
52
+ weight: 6
53
+ },
54
+ java: {
55
+ optional: [/@Override|@Autowired/, /extends|implements/, /System\.out/],
56
+ required: [/(public|private|protected)\s+(class|interface|static)/, /\w+\s+\w+\s*\([^)]*\)\s*{/],
57
+ weight: 8
58
+ },
59
+ // JavaScript family
60
+ javascript: {
61
+ exclude: [/^\s*</, /interface\s+\w+/, /:\s*\w+\s*[;=]/],
62
+ optional: [/=>\s*{/, /console\.(log|error|warn)/, /\.then\(/, /require\(/],
63
+ required: [/(function|const|let|var|class|import|export|async|await)\s/],
64
+ weight: 7
65
+ },
66
+ // Data formats
67
+ json: {
68
+ optional: [/"[^"]*"\s*:/, /:\s*["'[{]/],
69
+ required: [/^\s*[[{]/, /[\]}]\s*$/],
70
+ weight: 10
71
+ },
72
+ jsx: {
73
+ exclude: [/^\s*<!DOCTYPE/, /^\s*<html/],
74
+ optional: [/className=/, /{.*}/, /import.*from/],
75
+ required: [/<\w+[^>]*>/, /(const|function|class)\s+\w+/],
76
+ weight: 8
77
+ },
78
+ makefile: {
79
+ optional: [/\$\(.*\)/, /\.PHONY/],
80
+ required: [/^[\w-]+:\s*/, /^\t/m],
81
+ weight: 7
82
+ },
83
+ // Other common languages
84
+ markdown: {
85
+ optional: [/```/, /\*\*.*\*\*/, /^\s*[*+-]\s+/m],
86
+ required: [/^#{1,6}\s+/, /\[.*]\(.*\)/],
87
+ weight: 6
88
+ },
89
+ php: {
90
+ optional: [/function\s+\w+/, /class\s+\w+/, /echo|print/],
91
+ required: [/<\?php/, /\$\w+/],
92
+ weight: 10
93
+ },
94
+ powershell: {
95
+ optional: [/\|\s*Where/, /\|\s*Select/, /param\(/],
96
+ required: [/\$\w+/, /(Get|Set|New|Remove)-\w+/],
97
+ weight: 8
98
+ },
99
+ // Backend languages
100
+ python: {
101
+ optional: [/if __name__|print\(/, /self\.|lambda/, /@\w+\s*$/m],
102
+ required: [/(def|class|import|from)\s+\w+/, /:\s*$/m],
103
+ weight: 8
104
+ },
105
+ ruby: {
106
+ optional: [/do\s*\|.*\|/, /puts|require/, /:\w+\s*=>/],
107
+ required: [/(def|class|module|end)\s/, /@\w+/],
108
+ weight: 7
109
+ },
110
+ rust: {
111
+ optional: [/::\w+/, /&mut|&str/, /#\[derive\(/],
112
+ required: [/(fn|struct|impl|trait|use)\s+/, /let\s+(mut\s+)?\w+/],
113
+ weight: 8
114
+ },
115
+ scss: {
116
+ optional: [/@mixin|@include|@extend/, /#{.*}/],
117
+ required: [/\$[\w-]+\s*:/, /[&@]\w+/],
118
+ weight: 8
119
+ },
120
+ // Database
121
+ sql: {
122
+ optional: [/from|where|join|group by|order by/i, /\*/],
123
+ required: [/(select|insert|update|delete|create|alter|drop)\s+/i],
124
+ weight: 9
125
+ },
126
+ // Config formats
127
+ toml: {
128
+ optional: [/\[\[.*]]/, /"""[\S\s]*"""/],
129
+ required: [/^\[.*]$/m, /\w+\s*=\s*[\w"']/],
130
+ weight: 8
131
+ },
132
+ tsx: {
133
+ optional: [/:\s*React\./, /:\s*FC</, /useState|useEffect/],
134
+ required: [/<\w+[^>]*>/, /interface|type\s+\w+/],
135
+ weight: 9
136
+ },
137
+ typescript: {
138
+ optional: [/<\w+>/, /as\s+\w+/, /implements|extends/],
139
+ required: [/(interface|type|enum)\s+\w+/, /:\s*\w+(\[]|<.+>)?\s*[);=]/],
140
+ weight: 8
141
+ },
142
+ xml: {
143
+ optional: [/<\/\w+>/, /xmlns/],
144
+ required: [/<\?xml|<\w+[^>]*>/],
145
+ weight: 9
146
+ },
147
+ yaml: {
148
+ exclude: [/^\s*[[\]{]/],
149
+ required: [/^[\s-]*\w+:\s*/, /^[\s-]*-\s+/m],
150
+ weight: 8
151
+ }
152
+ };
153
+
154
+ /**
155
+ * Detect the programming language of a code snippet
156
+ * @param code - The code snippet to analyze
157
+ * @returns Language detection result with confidence score
158
+ */
159
+ export function detectLanguage(code) {
160
+ if (!code || code.trim().length === 0) {
161
+ return null;
162
+ }
163
+ var trimmed = code.trim();
164
+ var scores = {};
165
+
166
+ // Test each language pattern
167
+ for (var _i = 0, _Object$entries = Object.entries(LANGUAGE_PATTERNS); _i < _Object$entries.length; _i++) {
168
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
169
+ language = _Object$entries$_i[0],
170
+ pattern = _Object$entries$_i[1];
171
+ var score = 0;
172
+ var baseWeight = pattern.weight || 5;
173
+
174
+ // Check exclude patterns first
175
+ if (pattern.exclude) {
176
+ var hasExclude = pattern.exclude.some(function (regex) {
177
+ return regex.test(trimmed);
178
+ });
179
+ if (hasExclude) {
180
+ continue; // Skip this language
181
+ }
182
+ }
183
+
184
+ // Check required patterns
185
+ if (pattern.required) {
186
+ var allRequired = pattern.required.every(function (regex) {
187
+ return regex.test(trimmed);
188
+ });
189
+ if (!allRequired) {
190
+ continue; // All required patterns must match
191
+ }
192
+ score += baseWeight * 2; // Strong indicator
193
+ }
194
+
195
+ // Check optional patterns
196
+ if (pattern.optional) {
197
+ var matchedOptional = pattern.optional.filter(function (regex) {
198
+ return regex.test(trimmed);
199
+ }).length;
200
+ score += matchedOptional * baseWeight;
201
+ }
202
+ if (score > 0) {
203
+ scores[language] = score;
204
+ }
205
+ }
206
+
207
+ // Find language with highest score
208
+ var entries = Object.entries(scores);
209
+ if (entries.length === 0) {
210
+ return null;
211
+ }
212
+ entries.sort(function (a, b) {
213
+ return b[1] - a[1];
214
+ });
215
+ var _entries$ = _slicedToArray(entries[0], 2),
216
+ topLanguage = _entries$[0],
217
+ topScore = _entries$[1];
218
+
219
+ // Calculate confidence (0-100)
220
+ var maxPossibleScore = (LANGUAGE_PATTERNS[topLanguage].weight || 5) * 5;
221
+ var confidence = Math.min(100, Math.round(topScore / maxPossibleScore * 100));
222
+
223
+ // Only return if confidence is high enough
224
+ if (confidence < 30) {
225
+ return null;
226
+ }
227
+ return {
228
+ confidence: confidence,
229
+ language: topLanguage
230
+ };
231
+ }
232
+
233
+ /**
234
+ * Simple detection for common formats with high confidence
235
+ * Falls back to detectLanguage for more complex detection
236
+ */
237
+ export function detectCodeLanguage(code) {
238
+ var trimmed = code.trim();
239
+
240
+ // Fast path for JSON
241
+ if (trimmed.startsWith('{') && trimmed.endsWith('}') || trimmed.startsWith('[') && trimmed.endsWith(']')) {
242
+ try {
243
+ JSON.parse(trimmed);
244
+ return 'json';
245
+ } catch (_unused) {
246
+ // Not valid JSON
247
+ }
248
+ }
249
+
250
+ // Fast path for common patterns
251
+ if (/^\s*(select|insert|update|delete|create|alter|drop)\s+/i.test(code)) {
252
+ return 'sql';
253
+ }
254
+ if (/^<\?xml/i.test(code)) {
255
+ return 'xml';
256
+ }
257
+ if (/^FROM\s+|^RUN\s+|^CMD\s+/m.test(code)) {
258
+ return 'dockerfile';
259
+ }
260
+ if (/<\?php/.test(code)) {
261
+ return 'php';
262
+ }
263
+
264
+ // Use pattern matching for complex detection
265
+ var result = detectLanguage(code);
266
+ return result && result.confidence > 50 ? result.language : null;
267
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/editor",
3
- "version": "1.17.2",
3
+ "version": "1.18.0",
4
4
  "description": "A powerful and extensible rich text editor built on Meta's Lexical framework, providing a modern editing experience with React integration.",
5
5
  "keywords": [
6
6
  "lobehub",