@pfmcodes/caret 0.1.5 → 0.2.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/README.md CHANGED
@@ -1,25 +1,25 @@
1
1
  # Caret
2
2
 
3
3
  [![License: MIT](https://img.shields.io/badge/License-MIT-4000ff.svg)](https://opensource.org/licenses/MIT)
4
- [![JavaScript](https://img.shields.io/badge/JavaScript-64.0%25-f6fa03)](https://github.com/PFMCODES/lexius-edior)
5
- [![CSS](https://img.shields.io/badge/CSS-18.0%25-2e7ad1)](https://github.com/pfmcodes/lexius-editor)
6
- [![SCSS](https://img.shields.io/badge/SCSS-18.0%25-f8899d)](https://github.com/pfmcodes/lexius-editor)
4
+ [![JavaScript](https://img.shields.io/badge/JavaScript-63.7%25-f6fa03)](https://github.com/PFMCODES/lexius-edior)
5
+ [![JavaScript](https://img.shields.io/badge/TypeScript-32.3%25-0244f7)](https://github.com/PFMCODES/lexius-edior)
6
+ [![CSS](https://img.shields.io/badge/CSS-4.0%25-2e7ad1)](https://github.com/pfmcodes/lexius-editor)
7
7
 
8
8
  A lightweight, feature-rich code editor with real-time syntax highlighting and custom caret rendering. Built with vanilla JavaScript and powered by Highlight.js, caret delivers a smooth coding experience with professional-grade features.
9
9
 
10
- ## Features
10
+ ## Features
11
11
 
12
- - 🎨 **Live Syntax Highlighting** - Real-time code highlighting powered by Highlight.js
13
- - 🖱️ **Custom Caret** - Smooth, pixel-perfect Caret positioning and rendering
14
- - 🔢 **Line Numbers** - Built-in line counter with dynamic updates
15
- - ⌨️ **Smart Indentation** - Tab/Shift+Tab support for indenting/unindenting code blocks
16
- - 🎭 **Theme Support** - Multiple syntax highlighting themes (light/dark modes)
17
- - 📜 **Smooth Scrolling** - Synchronized scrolling for code, highlights, and line numbers
18
- - 📦 **ES Modules** - Modern ESM architecture for easy integration
19
- - 🎯 **TypeScript Ready** - Full TypeScript definitions included
20
- - **Lightweight** - Pure JavaScript, no heavy frameworks required
12
+ - **Live Syntax Highlighting** - Real-time code highlighting powered by Highlight.js
13
+ - **Custom Caret** - Smooth, pixel-perfect Caret positioning and rendering
14
+ - **Line Numbers** - Built-in line counter with dynamic updates
15
+ - **Smart Indentation** - Tab/Shift+Tab support for indenting/unindenting code blocks
16
+ - **Theme Support** - Multiple syntax highlighting themes (light/dark modes)
17
+ - **Smooth Scrolling** - Synchronized scrolling for code, highlights, and line numbers
18
+ - **ES Modules** - Modern ESM architecture for easy integration
19
+ - **TypeScript Ready** - Full TypeScript definitions included
20
+ - **Lightweight** - Pure JavaScript, no heavy frameworks required
21
21
 
22
- ## 📋 Table of Contents
22
+ ## Table of Contents
23
23
 
24
24
  - [What's new](#What's-New?)
25
25
  - [Installation](#installation)
@@ -29,12 +29,14 @@ A lightweight, feature-rich code editor with real-time syntax highlighting and c
29
29
  - [Customization](#customization)
30
30
  - [Contributing](#contributing)
31
31
  - [License](#license)
32
+ - [Perfomance Notes](#performance-notes)
32
33
 
33
34
  ## What's-New?
34
35
 
35
- ### New fresh package directory, now the heavy work is done in the cloud while minimizing package size to just a few Kilo Bytes.
36
+ ### The editor has been optimized like crazy, the editor used to lag at 500 lines not it does not lag until 10k+ lines(x20 performance increase)
37
+ ### for more info about the perfomace check out [this](#performance-notes) and [this](#performance).
36
38
 
37
- ## 🚀 Installation
39
+ ## Installation
38
40
 
39
41
  ### NPM
40
42
 
@@ -54,7 +56,7 @@ yarn add @pfmcodes/caret
54
56
  pnpm add @pfmcodes/caret
55
57
  ```
56
58
 
57
- ## 🏁 Quick Start
59
+ ## Quick Start
58
60
 
59
61
  ### Basic Usage
60
62
 
@@ -89,7 +91,7 @@ pnpm add @pfmcodes/caret
89
91
  ### ES Module Import
90
92
 
91
93
  ```javascript
92
- import editor from '@pfmcodes/caret';
94
+ import editor from './node_modules/@pfmcodes/caret/esm/index.js';
93
95
 
94
96
  // Create editor instance
95
97
  const editorInstance = await editor.editor.createEditor(
@@ -102,7 +104,7 @@ const editorInstance = await editor.editor.createEditor(
102
104
  );
103
105
  ```
104
106
 
105
- ## 📁 Project Structure
107
+ ## Project Structure
106
108
 
107
109
  ```
108
110
  caret/
@@ -116,12 +118,12 @@ caret/
116
118
  └── LICENSE # MIT License
117
119
  ```
118
120
 
119
- ## 💡 Usage
121
+ ## Usage
120
122
 
121
123
  ### JavaScript Editor
122
124
 
123
125
  ```javascript
124
- import editor from '@pfmcodes/caret'; // auto link to commonjs version
126
+ import editor from './node_modules/@pfmcodes/caret/esm/index.js'; // auto link to commonjs version
125
127
 
126
128
  const jsEditor = await editor.editor.createEditor(
127
129
  document.getElementById('js-editor'),
@@ -168,7 +170,7 @@ const emptyEditor = await editor.createEditor(
168
170
  );
169
171
  ```
170
172
 
171
- ## 🎨 Customization
173
+ ## Customization
172
174
 
173
175
  ### Custom Styling
174
176
 
@@ -205,7 +207,7 @@ The editor comes with default styles that you can override:
205
207
  }
206
208
  ```
207
209
 
208
- ## 🎯 Advanced Features
210
+ ## Advanced Features
209
211
 
210
212
  ### Multi-Language Support
211
213
 
@@ -290,7 +292,7 @@ Caret supports all Highlight.js themes. Popular options include:
290
292
  - `stackoverflow-light`
291
293
  - `xcode`
292
294
 
293
- ## 🛠️ API Reference
295
+ ## API Reference
294
296
 
295
297
  ### createEditor(container, options)
296
298
 
@@ -339,7 +341,7 @@ The editor features a custom-rendered Caret that adapts to your theme (light/dar
339
341
  #### Synchronized Scrolling
340
342
  All editor components (code, highlights, line numbers) scroll together smoothly.
341
343
 
342
- ## 📖 Complete Example
344
+ ## Complete Example
343
345
 
344
346
  Here's a complete working example:
345
347
 
@@ -457,7 +459,7 @@ for (let i = 0; i < 10; i++) {
457
459
  </html>
458
460
  ```
459
461
 
460
- ## ⚙️ Technical Details
462
+ ## Technical Details
461
463
 
462
464
  ### How It Works
463
465
 
@@ -478,6 +480,7 @@ The editor synchronizes all layers during:
478
480
  - **Real-time Highlighting**: Uses Highlight.js for fast, accurate syntax highlighting
479
481
  - **Canvas Measurement**: Employs HTML5 Canvas API for precise text width calculations
480
482
  - **Event Optimization**: Efficiently updates only what's necessary on each interaction
483
+ - **Heavy Optimization**: v0.1.5 used to handle 500 lines before lagging, with new caret@0.2.0 and onwards, there's almost 20 times performance increase now it can handle 10K+ lines smoothly before lagging(better results in optimized browsers like firefox)
481
484
 
482
485
  ### Browser Support
483
486
 
@@ -485,7 +488,7 @@ The editor synchronizes all layers during:
485
488
  - Chrome, Firefox, Safari, Edge (latest versions)
486
489
  - Requires JavaScript modules support
487
490
 
488
- ## 🤝 Contributing
491
+ ## Contributing
489
492
 
490
493
  Contributions are welcome! Please follow these steps:
491
494
 
@@ -514,26 +517,31 @@ npm run dev
514
517
  npm run build
515
518
  ```
516
519
 
517
- ## 📝 License
520
+ ## License
518
521
 
519
522
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
520
523
 
521
- ## 🙏 Acknowledgments
524
+ ## Performance Notes
525
+ - Handles 10k+ lines smoothly in all browsers
526
+ - Firefox DevTools can inspect up to 3M lines without breaking a sweat
527
+ - Chrome DevTools politely requests you don't inspect past 300k lines
528
+
529
+ ## Acknowledgments
522
530
 
523
531
  - Built with modern JavaScript/TypeScript
524
532
  - Syntax highlighting powered by Highlight.js
525
533
  - Inspired by various text editor projects in the JavaScript ecosystem
526
534
 
527
- ## 📞 Support
535
+ ## Support
528
536
 
529
537
  - **Issues**: [GitHub Issues](https://github.com/pfmcodes/lexius-editor/issues)
530
538
  - **Discussions**: [GitHub Discussions](https://github.com/pfmcodes/lexius-editor/discussions)
531
539
 
532
- ## 🔗 Links
540
+ ## Links
533
541
 
534
542
  - [GitHub Repository](https://github.com/pfmcodes/lexius-editor)
535
543
  - [NPM Package](https://www.npmjs.com/package/@pfmcodes/caret) *(if published)*
536
544
 
537
545
  ---
538
546
 
539
- Made with ❤️ by [PFMCODES](https://github.com/PFMCODES)
547
+ Made with ❤️ by [PFMCODES](https://github.com/PFMCODES)
@@ -59,12 +59,13 @@ async function createEditor(editor, data) {
59
59
  editor1.autocomplete = "off";
60
60
  editor1.autocorrect = "off";
61
61
  editor.style = "position: relative; width: 600px; height: 300px; overflow: hidden; /* 👈 CRITICAL */ font-size: 14px;"
62
- if (code) {
63
- editor1.value = code;
62
+ if (code && editor && editor1 && language && highlighted) {
64
63
  editor1.style.paddingTop = "-9px";
65
- highlighted.innerHTML = hljs.highlight(code, { language: language }).value;
64
+ console.log(data.value + " data.value");
65
+ editor1.value = data.value;
66
+ highlighted.innerHTML = await _render(data.value, language, editor1);
66
67
  }
67
- const keyDown = (e) => {
68
+ const keyDown = async (e) => {
68
69
  if (e.key !== "Tab") return;
69
70
 
70
71
  e.preventDefault();
@@ -117,7 +118,7 @@ async function createEditor(editor, data) {
117
118
  editor1.selectionEnd =
118
119
  end + delta * newLines.length;
119
120
 
120
- highlighted.innerHTML = hljs.highlight(editor1.value, { language }).value;
121
+ highlighted.innerHTML = await _render(editor1.value, language, editor1);
121
122
  updateLineNumbers();
122
123
  updateCaret();
123
124
  }
@@ -194,17 +195,17 @@ async function createEditor(editor, data) {
194
195
 
195
196
  caret.style.height = `${lineHeight - 5}px`;
196
197
  }
197
- const input = () => {
198
+ const input = async () => {
198
199
  caret.style.opacity = "1";
199
- highlighted.innerHTML = hljs.highlight(editor1.value, { language: language }).value;
200
+ highlighted.innerHTML = await _render(editor1.value, language, editor1);
200
201
  updateLineNumbers();
201
202
  updateCaret();
202
203
  };
203
204
  editor1.addEventListener("input", input);
204
- const scroll = () => {
205
+ const scroll = async () => {
205
206
  const x = -editor1.scrollLeft;
206
207
  const y = -editor1.scrollTop;
207
-
208
+ highlighted.innerHTML = await _render(editor1.value, language, editor1);
208
209
  highlighted.style.transform = `translate(${x}px, ${y}px)`;
209
210
  caret.style.transform = `translate(${x}px, ${y}px)`;
210
211
  lineCounter.style.transform = `translateY(${y}px)`;
@@ -230,8 +231,8 @@ async function createEditor(editor, data) {
230
231
  editor1.removeEventListener('keydown', keyDown);
231
232
  editor.innerHTML = "";
232
233
  }
233
- function refresh() {
234
- highlighted.innerHTML = hljs.highlight(editor1.value, { language }).value;
234
+ async function refresh() {
235
+ highlighted.innerHTML = await _render(editor1.value, language, editor1);
235
236
  updateLineNumbers();
236
237
  updateCaret();
237
238
  }
@@ -263,6 +264,52 @@ async function createEditor(editor, data) {
263
264
  };
264
265
  }
265
266
 
267
+ function escapeHtml(str) {
268
+ return str
269
+ .replace(/&/g, "&amp;")
270
+ .replace(/</g, "&lt;")
271
+ .replace(/>/g, "&gt;");
272
+ }
273
+
274
+ async function _render(code, language, editor) {
275
+ // If no editor context provided, just highlight everything (initial load)
276
+ if (!editor) {
277
+ return hljs.highlight(code, { language }).value;
278
+ }
279
+
280
+ const scrollTop = editor.scrollTop;
281
+ const scrollBottom = scrollTop + editor.clientHeight;
282
+ const style = getComputedStyle(editor);
283
+ const lineHeight = parseFloat(style.lineHeight);
284
+
285
+ // Calculate visible line range
286
+ const startLine = Math.floor(scrollTop / lineHeight);
287
+ const endLine = Math.ceil(scrollBottom / lineHeight);
288
+
289
+ const lines = code.split("\n");
290
+
291
+ // Add buffer (render extra lines above/below for smooth scrolling)
292
+ const bufferLines = 10;
293
+ const visibleStart = Math.max(0, startLine - bufferLines) || 0;
294
+ const visibleEnd = Math.min(lines.length, endLine + bufferLines) || 0;
295
+
296
+ // Split into three sections
297
+ const beforeLines = lines.slice(0, visibleStart);
298
+ const visibleLines = lines.slice(visibleStart, visibleEnd);
299
+ const afterLines = lines.slice(visibleEnd);
300
+
301
+ // Only highlight visible portion
302
+
303
+ const highlightedVisible = hljs.highlight(visibleLines.join("\n"), { language }).value;
304
+ // Plain text for non-visible areas (no highlighting = faster)
305
+ if (highlightedVisible.trim() === "") {
306
+ return hljs.highlight(escapeHtml(code), { language }).value;
307
+ }
308
+ const beforeHTML = "\n".repeat(beforeLines.length);
309
+ const afterHTML = "\n".repeat(afterLines.length);
310
+ return beforeHTML + highlightedVisible + afterHTML;
311
+ }
312
+
266
313
  const editor = {
267
314
  createEditor
268
315
  };
package/esm/editor.js CHANGED
@@ -59,12 +59,13 @@ async function createEditor(editor, data) {
59
59
  editor1.autocomplete = "off";
60
60
  editor1.autocorrect = "off";
61
61
  editor.style = "position: relative; width: 600px; height: 300px; overflow: hidden; /* 👈 CRITICAL */ font-size: 14px;"
62
- if (code) {
63
- editor1.value = code;
62
+ if (code && editor && editor1 && language && highlighted) {
64
63
  editor1.style.paddingTop = "-9px";
65
- highlighted.innerHTML = hljs.highlight(code, { language: language }).value;
64
+ console.log(data.value + " data.value");
65
+ editor1.value = data.value;
66
+ highlighted.innerHTML = await _render(data.value, language, editor1);
66
67
  }
67
- const keyDown = (e) => {
68
+ const keyDown = async (e) => {
68
69
  if (e.key !== "Tab") return;
69
70
 
70
71
  e.preventDefault();
@@ -117,7 +118,7 @@ async function createEditor(editor, data) {
117
118
  editor1.selectionEnd =
118
119
  end + delta * newLines.length;
119
120
 
120
- highlighted.innerHTML = hljs.highlight(editor1.value, { language }).value;
121
+ highlighted.innerHTML = await _render(editor1.value, language, editor1);
121
122
  updateLineNumbers();
122
123
  updateCaret();
123
124
  }
@@ -194,17 +195,17 @@ async function createEditor(editor, data) {
194
195
 
195
196
  caret.style.height = `${lineHeight - 5}px`;
196
197
  }
197
- const input = () => {
198
+ const input = async () => {
198
199
  caret.style.opacity = "1";
199
- highlighted.innerHTML = hljs.highlight(editor1.value, { language: language }).value;
200
+ highlighted.innerHTML = await _render(editor1.value, language, editor1);
200
201
  updateLineNumbers();
201
202
  updateCaret();
202
203
  };
203
204
  editor1.addEventListener("input", input);
204
- const scroll = () => {
205
+ const scroll = async () => {
205
206
  const x = -editor1.scrollLeft;
206
207
  const y = -editor1.scrollTop;
207
-
208
+ highlighted.innerHTML = await _render(editor1.value, language, editor1);
208
209
  highlighted.style.transform = `translate(${x}px, ${y}px)`;
209
210
  caret.style.transform = `translate(${x}px, ${y}px)`;
210
211
  lineCounter.style.transform = `translateY(${y}px)`;
@@ -230,8 +231,8 @@ async function createEditor(editor, data) {
230
231
  editor1.removeEventListener('keydown', keyDown);
231
232
  editor.innerHTML = "";
232
233
  }
233
- function refresh() {
234
- highlighted.innerHTML = hljs.highlight(editor1.value, { language }).value;
234
+ async function refresh() {
235
+ highlighted.innerHTML = await _render(editor1.value, language, editor1);
235
236
  updateLineNumbers();
236
237
  updateCaret();
237
238
  }
@@ -263,6 +264,52 @@ async function createEditor(editor, data) {
263
264
  };
264
265
  }
265
266
 
267
+ function escapeHtml(str) {
268
+ return str
269
+ .replace(/&/g, "&amp;")
270
+ .replace(/</g, "&lt;")
271
+ .replace(/>/g, "&gt;");
272
+ }
273
+
274
+ async function _render(code, language, editor) {
275
+ // If no editor context provided, just highlight everything (initial load)
276
+ if (!editor) {
277
+ return hljs.highlight(code, { language }).value;
278
+ }
279
+
280
+ const scrollTop = editor.scrollTop;
281
+ const scrollBottom = scrollTop + editor.clientHeight;
282
+ const style = getComputedStyle(editor);
283
+ const lineHeight = parseFloat(style.lineHeight);
284
+
285
+ // Calculate visible line range
286
+ const startLine = Math.floor(scrollTop / lineHeight);
287
+ const endLine = Math.ceil(scrollBottom / lineHeight);
288
+
289
+ const lines = code.split("\n");
290
+
291
+ // Add buffer (render extra lines above/below for smooth scrolling)
292
+ const bufferLines = 10;
293
+ const visibleStart = Math.max(0, startLine - bufferLines) || 0;
294
+ const visibleEnd = Math.min(lines.length, endLine + bufferLines) || 0;
295
+
296
+ // Split into three sections
297
+ const beforeLines = lines.slice(0, visibleStart);
298
+ const visibleLines = lines.slice(visibleStart, visibleEnd);
299
+ const afterLines = lines.slice(visibleEnd);
300
+
301
+ // Only highlight visible portion
302
+
303
+ const highlightedVisible = hljs.highlight(visibleLines.join("\n"), { language }).value;
304
+ // Plain text for non-visible areas (no highlighting = faster)
305
+ if (highlightedVisible.trim() === "") {
306
+ return hljs.highlight(escapeHtml(code), { language }).value;
307
+ }
308
+ const beforeHTML = "\n".repeat(beforeLines.length);
309
+ const afterHTML = "\n".repeat(afterLines.length);
310
+ return beforeHTML + highlightedVisible + afterHTML;
311
+ }
312
+
266
313
  const editor = {
267
314
  createEditor
268
315
  };
package/index.css CHANGED
@@ -2,16 +2,21 @@
2
2
  position: absolute;
3
3
  border: none;
4
4
  inset: 0;
5
- padding: 10px;
5
+ padding: 10px 10px 10px 0px;
6
6
  font-family: monospace;
7
7
  background: transparent;
8
8
  color: transparent;
9
9
  caret-color: #fff;
10
10
  border: 1px solid #ccc;
11
- overflow: scroll; /* 👈 ONLY THIS SCROLLS */
11
+ overflow-x: scroll; /* 👈 ONLY THIS SCROLLS */
12
12
  z-index: 1;
13
13
  width: 100%;
14
14
  }
15
+
16
+ #Caret-texarea::-webkit-scrollbar {
17
+ z-index: 3;
18
+ }
19
+
15
20
  #Caret-textarea:focus {
16
21
  outline: none;
17
22
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pfmcodes/caret",
3
- "version": "0.1.5",
3
+ "version": "0.2.1",
4
4
  "description": "The official code editor engine for lexius",
5
5
  "type": "module",
6
6
  "main": "./esm/index.js",
package/types/editor.ts CHANGED
@@ -3,15 +3,18 @@ import languages from "./languages.ts";
3
3
 
4
4
  languages.init();
5
5
 
6
- async function createEditor(editor: HTMLElement, data: any) {
6
+ async function createEditor(editor: HTMLElement, data: { value?: string, language: string, theme?: string }) {
7
7
  const editor1: HTMLTextAreaElement = document.createElement("textarea");
8
8
  const highlighted: HTMLPreElement = document.createElement("pre");
9
9
  const caret: HTMLDivElement = document.createElement("div");
10
10
  const measureCanvas: HTMLCanvasElement = document.createElement("canvas");
11
- const measureCtx: CanvasRenderingContext2D = measureCanvas.getContext("2d")!;
12
- const isDark: boolean = data.theme && (data.theme.includes("dark") || data.theme.includes("night"));
13
- const caretColor: string = isDark ? "#fff" : "#7116d8";
14
- const lineColor: string = isDark ? "#fff" : "#000";
11
+ const measureCtx: CanvasRenderingContext2D = measureCanvas.getContext("2d") as CanvasRenderingContext2D;
12
+ if (!measureCtx) {
13
+ throw new Error("Failed to get 2D context from canvas");
14
+ }
15
+ const isDark = data.theme && (data.theme.includes("dark") || data.theme.includes("night"));
16
+ const caretColor = isDark ? "#fff" : "#7116d8";
17
+ const lineColor = isDark ? "#fff" : "#000";
15
18
  const lineCounter: HTMLDivElement = document.createElement("div");
16
19
 
17
20
  editor1.id = "Caret-textarea";
@@ -23,32 +26,32 @@ async function createEditor(editor: HTMLElement, data: any) {
23
26
  caret.className = 'dark';
24
27
  lineCounter.className = 'dark';
25
28
  editor1.style.backgroundColor = isDark ? "#222" : "#fff";
26
- let code: string = data.value || "";
27
- let language: string = data.language;
28
- let theme: string = data.theme;
29
+ let code = data.value || "";
30
+ let language = data.language;
31
+ let theme = data.theme;
29
32
  if (!languages.registeredLanguages.includes(language)) {
30
33
  const mod = await import(`https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/${language}.js`);
31
34
  languages.registerLanguage(language, mod.default);
32
35
  languages.registeredLanguages.push(language);
33
36
  }
34
37
  if (theme) {
35
- let themeLink = document.getElementById("Caret-theme")
38
+ let themeLink: HTMLLinkElement | null = document.getElementById("Caret-theme") as HTMLLinkElement;
36
39
  if (!themeLink) {
37
40
  const link = document.createElement("link");
38
41
  link.rel = "stylesheet";
39
42
  link.id = "Caret-theme";
40
- link.href = `./highlight.js/styles/${theme}.css`;
43
+ link.href = `https://esm.sh/@pfmcodes/highlight.js@1.0.0/styles/${theme}.css`;
41
44
  document.head.appendChild(link);
42
45
  } else {
43
- themeLink.href = `./highlight.js/styles/${theme}.css`;
46
+ themeLink.href = `https://esm.sh/@pfmcodes/highlight.js@1.0.0/styles/${theme}.css`;
44
47
  }
45
48
  } else {
46
- let themeLink = document.getElementById("Caret-theme");
49
+ let themeLink: HTMLLinkElement | null = document.getElementById("Caret-theme") as HTMLLinkElement;
47
50
  if (!themeLink) {
48
51
  const link = document.createElement("link");
49
52
  link.rel = "stylesheet";
50
53
  link.id = "Caret-theme";
51
- link.href = `./highlight.js/styles/hybrid.css`;
54
+ link.href = `https://esm.sh/@pfmcodes/highlight.js@1.0.0/styles/hybrid.css`;
52
55
  document.head.appendChild(link);
53
56
  } else {
54
57
  themeLink.href = `./highlight.js/styles/hybrid.css`;
@@ -57,14 +60,15 @@ async function createEditor(editor: HTMLElement, data: any) {
57
60
  editor1.spellcheck = false;
58
61
  editor1.autocapitalize = "off";
59
62
  editor1.autocomplete = "off";
60
- (editor1 as any).autocorrect = false;
63
+ editor1.autocorrect = "off" as any;
61
64
  editor.style = "position: relative; width: 600px; height: 300px; overflow: hidden; /* 👈 CRITICAL */ font-size: 14px;"
62
- if (code) {
63
- editor1.value = code;
65
+ if (code && editor && editor1 && language && highlighted) {
64
66
  editor1.style.paddingTop = "-9px";
65
- highlighted.innerHTML = hljs.highlight(code, { language: language }).value;
67
+ console.log(data.value + " data.value");
68
+ editor1.value = data.value as string;
69
+ highlighted.innerHTML = await _render(code, language, editor1);
66
70
  }
67
- const keyDown = (e: any) => {
71
+ const keyDown = async (e: KeyboardEvent) => {
68
72
  if (e.key !== "Tab") return;
69
73
 
70
74
  e.preventDefault();
@@ -83,8 +87,8 @@ async function createEditor(editor: HTMLElement, data: any) {
83
87
  const block = value.slice(lineStart, finalLineEnd);
84
88
  const lines = block.split("\n");
85
89
 
86
- let newLines: any;
87
- let delta: number = 0;
90
+ let newLines;
91
+ let delta = 0;
88
92
 
89
93
  if (e.shiftKey) {
90
94
  // UNINDENT
@@ -117,7 +121,7 @@ async function createEditor(editor: HTMLElement, data: any) {
117
121
  editor1.selectionEnd =
118
122
  end + delta * newLines.length;
119
123
 
120
- highlighted.innerHTML = hljs.highlight(editor1.value, { language }).value;
124
+ highlighted.innerHTML = await _render(editor1.value, language, editor1);
121
125
  updateLineNumbers();
122
126
  updateCaret();
123
127
  }
@@ -128,15 +132,15 @@ async function createEditor(editor: HTMLElement, data: any) {
128
132
  editor.appendChild(caret);
129
133
 
130
134
  function updateFontMetrics() {
131
- const style: any = getComputedStyle(editor1);
135
+ const style = getComputedStyle(editor1);
132
136
  measureCtx.font = `${style.fontSize} ${style.fontFamily}`;
133
137
  }
134
138
 
135
139
  function updateLineNumbers() {
136
- const lineCount: number = editor1.value.split("\n").length;
140
+ const lineCount = editor1.value.split("\n").length;
137
141
 
138
- let html: string = "";
139
- for (let i: number = 1; i <= lineCount; i++) {
142
+ let html = "";
143
+ for (let i = 1; i <= lineCount; i++) {
140
144
  html += `<div class="Caret-lineCounter-number" style="color: ${lineColor}">${i}</div>`;
141
145
  }
142
146
 
@@ -194,17 +198,17 @@ async function createEditor(editor: HTMLElement, data: any) {
194
198
 
195
199
  caret.style.height = `${lineHeight - 5}px`;
196
200
  }
197
- const input = () => {
201
+ const input = async () => {
198
202
  caret.style.opacity = "1";
199
- highlighted.innerHTML = hljs.highlight(editor1.value, { language: language }).value;
203
+ highlighted.innerHTML = await _render(editor1.value, language, editor1);
200
204
  updateLineNumbers();
201
205
  updateCaret();
202
206
  };
203
207
  editor1.addEventListener("input", input);
204
- const scroll = () => {
208
+ const scroll = async () => {
205
209
  const x = -editor1.scrollLeft;
206
210
  const y = -editor1.scrollTop;
207
-
211
+ highlighted.innerHTML = await _render(editor1.value, language, editor1);
208
212
  highlighted.style.transform = `translate(${x}px, ${y}px)`;
209
213
  caret.style.transform = `translate(${x}px, ${y}px)`;
210
214
  lineCounter.style.transform = `translateY(${y}px)`;
@@ -230,8 +234,8 @@ async function createEditor(editor: HTMLElement, data: any) {
230
234
  editor1.removeEventListener('keydown', keyDown);
231
235
  editor.innerHTML = "";
232
236
  }
233
- function refresh() {
234
- highlighted.innerHTML = hljs.highlight(editor1.value, { language }).value;
237
+ async function refresh() {
238
+ highlighted.innerHTML = await _render(editor1.value, language, editor1);
235
239
  updateLineNumbers();
236
240
  updateCaret();
237
241
  }
@@ -263,6 +267,52 @@ async function createEditor(editor: HTMLElement, data: any) {
263
267
  };
264
268
  }
265
269
 
270
+ function escapeHtml(str: string) {
271
+ return str
272
+ .replace(/&/g, "&amp;")
273
+ .replace(/</g, "&lt;")
274
+ .replace(/>/g, "&gt;");
275
+ }
276
+
277
+ async function _render(code: string, language: string, editor: HTMLElement) {
278
+ // If no editor context provided, just highlight everything (initial load)
279
+ if (!editor) {
280
+ return hljs.highlight(code, { language }).value;
281
+ }
282
+
283
+ const scrollTop = editor.scrollTop;
284
+ const scrollBottom = scrollTop + editor.clientHeight;
285
+ const style = getComputedStyle(editor);
286
+ const lineHeight = parseFloat(style.lineHeight);
287
+
288
+ // Calculate visible line range
289
+ const startLine = Math.floor(scrollTop / lineHeight);
290
+ const endLine = Math.ceil(scrollBottom / lineHeight);
291
+
292
+ const lines = code.split("\n");
293
+
294
+ // Add buffer (render extra lines above/below for smooth scrolling)
295
+ const bufferLines = 10;
296
+ const visibleStart = Math.max(0, startLine - bufferLines) || 0;
297
+ const visibleEnd = Math.min(lines.length, endLine + bufferLines) || 0;
298
+
299
+ // Split into three sections
300
+ const beforeLines = lines.slice(0, visibleStart);
301
+ const visibleLines = lines.slice(visibleStart, visibleEnd);
302
+ const afterLines = lines.slice(visibleEnd);
303
+
304
+ // Only highlight visible portion
305
+
306
+ const highlightedVisible = hljs.highlight(visibleLines.join("\n"), { language }).value;
307
+ // Plain text for non-visible areas (no highlighting = faster)
308
+ if (highlightedVisible.trim() === "") {
309
+ return hljs.highlight(escapeHtml(code), { language }).value;
310
+ }
311
+ const beforeHTML = "\n".repeat(beforeLines.length);
312
+ const afterHTML = "\n".repeat(afterLines.length);
313
+ return beforeHTML + highlightedVisible + afterHTML;
314
+ }
315
+
266
316
  const editor = {
267
317
  createEditor
268
318
  };
@@ -1,21 +1,40 @@
1
+ // @ts-ignore
1
2
  import javascript from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/javascript.js";
3
+ // @ts-ignore
2
4
  import xml from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/xml.js";
5
+ // @ts-ignore
3
6
  import css from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/css.js";
7
+ // @ts-ignore
4
8
  import python from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/python.js";
9
+ // @ts-ignore
5
10
  import java from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/java.js";
11
+ // @ts-ignore
6
12
  import csharp from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/csharp.js";
13
+ // @ts-ignore
7
14
  import cpp from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/cpp.js";
15
+ // @ts-ignore
8
16
  import ruby from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/ruby.js";
17
+ // @ts-ignore
9
18
  import php from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/php.js";
19
+ // @ts-ignore
10
20
  import go from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/go.js";
21
+ // @ts-ignore
11
22
  import c from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/c.js";
23
+ // @ts-ignore
12
24
  import rust from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/rust.js";
25
+ // @ts-ignore
13
26
  import kotlin from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/kotlin.js";
27
+ // @ts-ignore
14
28
  import swift from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/swift.js";
29
+ // @ts-ignore
15
30
  import typescript from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/typescript.js";
31
+ // @ts-ignore
16
32
  import json from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/json.js";
33
+ // @ts-ignore
17
34
  import bash from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/bash.js";
35
+ // @ts-ignore
18
36
  import plaintext from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/languages/plaintext.js";
37
+ // @ts-ignore
19
38
  import hljs from "https://esm.sh/@pfmcodes/highlight.js@1.0.0/es/core.js";
20
39
 
21
40
  let registeredLanguages: Array<T> = [];