@pfmcodes/caret 0.1.1 → 0.1.2

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/esm/languages.js CHANGED
@@ -18,7 +18,9 @@ import bash from "../highlight.js/es/languages/bash.js";
18
18
  import plaintext from "../highlight.js/es/languages/plaintext.js";
19
19
  import hljs from "../highlight.js/es/core.js";
20
20
 
21
- export function init() {
21
+ let registeredLanguages = [];
22
+
23
+ function init() {
22
24
  // Register all languages
23
25
  hljs.registerLanguage("javascript", javascript);
24
26
  hljs.registerLanguage("xml", xml);
@@ -40,7 +42,56 @@ export function init() {
40
42
  hljs.registerLanguage("bash", bash);
41
43
  hljs.registerLanguage("shell", bash);
42
44
  hljs.registerLanguage("sh", bash);
43
- hljs.registerLanguage("plaintext", plaintext)
45
+ hljs.registerLanguage("plaintext", plaintext);
46
+ registeredLanguages = [
47
+ "javascript",
48
+ "js",
49
+ "xml",
50
+ "html",
51
+ "svg",
52
+ "python",
53
+ "java",
54
+ "csharp",
55
+ "cpp",
56
+ "ruby",
57
+ "php",
58
+ "go",
59
+ "c",
60
+ "rust",
61
+ "kotlin",
62
+ "swift",
63
+ "typescript",
64
+ "json",
65
+ "bash",
66
+ "shell",
67
+ "sh",
68
+ "plaintext"
69
+ ]
70
+ }
71
+
72
+ function registerLanguage(name, definition) {
73
+ hljs.registerLanguage(name, definition);
74
+ if (!registeredLanguages.includes(name)) {
75
+ registeredLanguages.push(name);
76
+ }
44
77
  }
45
78
 
46
- export default { init, registerLanguage: hljs.registerLanguage.bind(hljs), hljs };
79
+ const languages = {
80
+ init,
81
+ registeredLanguages,
82
+ registerLanguage,
83
+ hljs
84
+ }
85
+
86
+ export default languages;
87
+
88
+ /*
89
+
90
+ registeredLannguage: added for the editor.js can check if the langauge provided already is regsitered or not
91
+
92
+
93
+ init: just registers some languages and updates the registeredLangauges variable
94
+
95
+ registerLanguage: just registers a language
96
+
97
+ */
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@pfmcodes/caret",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "The official code editor engine for lexius",
5
5
  "type": "module",
6
6
  "main": "./index.cjs",
7
- "types": "./types/index.d.ts",
7
+ "types": "./types/types.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
10
  "import": "./esm/index.js",
package/types/editor.d.ts CHANGED
@@ -1,27 +1,167 @@
1
- export interface EditorInitData {
2
- /** Initial code value */
1
+ /**
2
+ * Caret Editor - TypeScript Type Definitions
3
+ * A lightweight code editor with syntax highlighting and custom caret rendering
4
+ */
5
+
6
+ /**
7
+ * Configuration options for creating a Caret editor instance
8
+ */
9
+ export interface CaretEditorConfig {
10
+ /**
11
+ * Initial code content to display in the editor
12
+ * @default ""
13
+ */
3
14
  value?: string;
4
15
 
5
- /** Language id registered in Highlight.js */
16
+ /**
17
+ * Programming language for syntax highlighting
18
+ * Must be a language supported by Highlight.js
19
+ * @example "javascript", "python", "html", "css"
20
+ */
6
21
  language: string;
7
22
 
8
- /** Highlight.js theme name (without .css) */
23
+ /**
24
+ * Highlight.js theme name for syntax highlighting
25
+ * Can be any theme available in the highlight.js/styles directory
26
+ * @example "hybrid", "monokai", "github", "atom-one-dark"
27
+ * @default "hybrid"
28
+ */
9
29
  theme?: string;
10
30
  }
11
31
 
12
- export interface CaretEditorElements {
13
- textarea: HTMLTextAreaElement;
14
- highlighted: HTMLPreElement;
15
- caret: HTMLDivElement;
32
+ /**
33
+ * Font metrics information returned by getFontMetrics
34
+ */
35
+ export interface FontMetrics {
36
+ /**
37
+ * Height of the ascender (portion of characters above baseline)
38
+ */
39
+ ascent: number;
40
+
41
+ /**
42
+ * Height of the descender (portion of characters below baseline)
43
+ */
44
+ descent: number;
45
+
46
+ /**
47
+ * Total height of the font (ascent + descent)
48
+ */
49
+ height: number;
50
+ }
51
+
52
+ /**
53
+ * Caret editor instance returned by createEditor
54
+ */
55
+ export interface CaretEditorInstance {
56
+ /**
57
+ * Get the current value/content of the editor
58
+ * @returns The current code content as a string
59
+ */
60
+ getValue(): string;
61
+
62
+ /**
63
+ * Set the value/content of the editor
64
+ * @param value - The new code content to set
65
+ */
66
+ setValue(value: string): void;
67
+
68
+ /**
69
+ * Focus the editor textarea
70
+ */
71
+ focus(): void;
72
+
73
+ /**
74
+ * Change the programming language for syntax highlighting
75
+ * @param language - The new language identifier (e.g., "javascript", "python")
76
+ */
77
+ setLanguage(language: string): void;
78
+
79
+ /**
80
+ * Destroy the editor instance and remove all event listeners
81
+ * Cleans up the DOM and prepares for garbage collection
82
+ */
83
+ destroy(): void;
16
84
  }
17
85
 
18
86
  /**
19
- * Creates a Caret editor inside the given container.
20
- *
21
- * @param editor DOM element that will contain the editor
22
- * @param data Initial editor configuration
87
+ * Language manager for registering and initializing Highlight.js languages
23
88
  */
24
- export function createEditor(
25
- editor: HTMLElement,
26
- data: EditorInitData
27
- ): Promise<void>;
89
+ export interface LanguageManager {
90
+ /**
91
+ * Initialize the language manager
92
+ */
93
+ init(): void;
94
+
95
+ /**
96
+ * Register a new language for syntax highlighting
97
+ * @param language - The language identifier to register
98
+ */
99
+ registerLanguage(language: string): void;
100
+
101
+ /**
102
+ * List of currently registered languages
103
+ */
104
+ registeredLanguages: string[];
105
+ }
106
+
107
+ /**
108
+ * Main editor object containing the createEditor function
109
+ */
110
+ export interface CaretEditor {
111
+ /**
112
+ * Create a new Caret editor instance
113
+ *
114
+ * @param container - The HTMLElement that will contain the editor
115
+ * @param config - Configuration options for the editor
116
+ * @returns A CaretEditorInstance with methods to control the editor
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * import editor from 'caret';
121
+ *
122
+ * const editorContainer = document.getElementById('editor');
123
+ * const instance = await editor.createEditor(editorContainer, {
124
+ * value: 'console.log("Hello, World!");',
125
+ * language: 'javascript',
126
+ * theme: 'monokai'
127
+ * });
128
+ *
129
+ * // Get current value
130
+ * const code = instance.getValue();
131
+ *
132
+ * // Update the content
133
+ * instance.setValue('const x = 42;');
134
+ *
135
+ * // Change language
136
+ * instance.setLanguage('typescript');
137
+ *
138
+ * // Clean up when done
139
+ * instance.destroy();
140
+ * ```
141
+ */
142
+ createEditor(
143
+ container: HTMLElement,
144
+ config: CaretEditorConfig
145
+ ): Promise<CaretEditorInstance>;
146
+ }
147
+
148
+ /**
149
+ * Default export - the main Caret editor object
150
+ */
151
+ declare const editor: CaretEditor;
152
+
153
+ export default editor;
154
+
155
+ /**
156
+ * Module augmentation for importing Highlight.js
157
+ */
158
+ declare module "../highlight.js/es/core.js" {
159
+ import type { HLJSApi } from "highlight.js";
160
+ const hljs: HLJSApi;
161
+ export default hljs;
162
+ }
163
+
164
+ declare module "./languages.js" {
165
+ const languages: LanguageManager;
166
+ export default languages;
167
+ }
@@ -0,0 +1,428 @@
1
+ /**
2
+ * Caret Editor - Language Utilities
3
+ * Helper types and utilities for working with language registration
4
+ */
5
+
6
+ import type { LanguageFn } from 'highlight.js';
7
+ import type { PreRegisteredLanguage, LanguagesModule } from './languages';
8
+
9
+ /**
10
+ * Language information object
11
+ */
12
+ export interface LanguageInfo {
13
+ /**
14
+ * Primary language identifier
15
+ */
16
+ name: string;
17
+
18
+ /**
19
+ * Display name for the language
20
+ */
21
+ displayName: string;
22
+
23
+ /**
24
+ * File extensions associated with this language
25
+ */
26
+ extensions: string[];
27
+
28
+ /**
29
+ * Common aliases for the language
30
+ */
31
+ aliases: string[];
32
+
33
+ /**
34
+ * Whether this language is pre-registered
35
+ */
36
+ isPreRegistered: boolean;
37
+ }
38
+
39
+ /**
40
+ * Map of language information for all pre-registered languages
41
+ */
42
+ export const LANGUAGE_INFO: Record<PreRegisteredLanguage, LanguageInfo> = {
43
+ javascript: {
44
+ name: 'javascript',
45
+ displayName: 'JavaScript',
46
+ extensions: ['.js', '.mjs', '.cjs'],
47
+ aliases: ['js'],
48
+ isPreRegistered: true
49
+ },
50
+ js: {
51
+ name: 'javascript',
52
+ displayName: 'JavaScript',
53
+ extensions: ['.js', '.mjs', '.cjs'],
54
+ aliases: ['javascript'],
55
+ isPreRegistered: true
56
+ },
57
+ typescript: {
58
+ name: 'typescript',
59
+ displayName: 'TypeScript',
60
+ extensions: ['.ts', '.tsx'],
61
+ aliases: ['ts'],
62
+ isPreRegistered: true
63
+ },
64
+ python: {
65
+ name: 'python',
66
+ displayName: 'Python',
67
+ extensions: ['.py', '.pyw'],
68
+ aliases: ['py'],
69
+ isPreRegistered: true
70
+ },
71
+ java: {
72
+ name: 'java',
73
+ displayName: 'Java',
74
+ extensions: ['.java'],
75
+ aliases: [],
76
+ isPreRegistered: true
77
+ },
78
+ csharp: {
79
+ name: 'csharp',
80
+ displayName: 'C#',
81
+ extensions: ['.cs'],
82
+ aliases: ['cs', 'c#'],
83
+ isPreRegistered: true
84
+ },
85
+ cpp: {
86
+ name: 'cpp',
87
+ displayName: 'C++',
88
+ extensions: ['.cpp', '.cc', '.cxx', '.hpp', '.h'],
89
+ aliases: ['c++'],
90
+ isPreRegistered: true
91
+ },
92
+ c: {
93
+ name: 'c',
94
+ displayName: 'C',
95
+ extensions: ['.c', '.h'],
96
+ aliases: [],
97
+ isPreRegistered: true
98
+ },
99
+ ruby: {
100
+ name: 'ruby',
101
+ displayName: 'Ruby',
102
+ extensions: ['.rb'],
103
+ aliases: ['rb'],
104
+ isPreRegistered: true
105
+ },
106
+ php: {
107
+ name: 'php',
108
+ displayName: 'PHP',
109
+ extensions: ['.php'],
110
+ aliases: [],
111
+ isPreRegistered: true
112
+ },
113
+ go: {
114
+ name: 'go',
115
+ displayName: 'Go',
116
+ extensions: ['.go'],
117
+ aliases: ['golang'],
118
+ isPreRegistered: true
119
+ },
120
+ rust: {
121
+ name: 'rust',
122
+ displayName: 'Rust',
123
+ extensions: ['.rs'],
124
+ aliases: ['rs'],
125
+ isPreRegistered: true
126
+ },
127
+ kotlin: {
128
+ name: 'kotlin',
129
+ displayName: 'Kotlin',
130
+ extensions: ['.kt', '.kts'],
131
+ aliases: ['kt'],
132
+ isPreRegistered: true
133
+ },
134
+ swift: {
135
+ name: 'swift',
136
+ displayName: 'Swift',
137
+ extensions: ['.swift'],
138
+ aliases: [],
139
+ isPreRegistered: true
140
+ },
141
+ xml: {
142
+ name: 'xml',
143
+ displayName: 'XML',
144
+ extensions: ['.xml'],
145
+ aliases: [],
146
+ isPreRegistered: true
147
+ },
148
+ html: {
149
+ name: 'html',
150
+ displayName: 'HTML',
151
+ extensions: ['.html', '.htm'],
152
+ aliases: [],
153
+ isPreRegistered: true
154
+ },
155
+ svg: {
156
+ name: 'svg',
157
+ displayName: 'SVG',
158
+ extensions: ['.svg'],
159
+ aliases: [],
160
+ isPreRegistered: true
161
+ },
162
+ css: {
163
+ name: 'css',
164
+ displayName: 'CSS',
165
+ extensions: ['.css'],
166
+ aliases: [],
167
+ isPreRegistered: true
168
+ },
169
+ json: {
170
+ name: 'json',
171
+ displayName: 'JSON',
172
+ extensions: ['.json'],
173
+ aliases: [],
174
+ isPreRegistered: true
175
+ },
176
+ bash: {
177
+ name: 'bash',
178
+ displayName: 'Bash',
179
+ extensions: ['.sh', '.bash'],
180
+ aliases: ['shell', 'sh'],
181
+ isPreRegistered: true
182
+ },
183
+ shell: {
184
+ name: 'shell',
185
+ displayName: 'Shell',
186
+ extensions: ['.sh'],
187
+ aliases: ['bash', 'sh'],
188
+ isPreRegistered: true
189
+ },
190
+ sh: {
191
+ name: 'sh',
192
+ displayName: 'Shell',
193
+ extensions: ['.sh'],
194
+ aliases: ['bash', 'shell'],
195
+ isPreRegistered: true
196
+ },
197
+ plaintext: {
198
+ name: 'plaintext',
199
+ displayName: 'Plain Text',
200
+ extensions: ['.txt'],
201
+ aliases: ['text', 'plain'],
202
+ isPreRegistered: true
203
+ }
204
+ };
205
+
206
+ /**
207
+ * Type guard to check if a language is pre-registered
208
+ */
209
+ export function isPreRegisteredLanguage(language: string): language is PreRegisteredLanguage {
210
+ const preRegistered: PreRegisteredLanguage[] = [
211
+ 'javascript', 'js', 'xml', 'html', 'svg', 'css',
212
+ 'python', 'java', 'csharp', 'cpp', 'ruby', 'php',
213
+ 'go', 'c', 'rust', 'kotlin', 'swift', 'typescript',
214
+ 'json', 'bash', 'shell', 'sh', 'plaintext'
215
+ ];
216
+ return preRegistered.includes(language as PreRegisteredLanguage);
217
+ }
218
+
219
+ /**
220
+ * Check if a language is currently registered
221
+ */
222
+ export function isLanguageRegistered(
223
+ languages: LanguagesModule,
224
+ language: string
225
+ ): boolean {
226
+ return languages.registeredLanguages.includes(language);
227
+ }
228
+
229
+ /**
230
+ * Get language information by name or alias
231
+ */
232
+ export function getLanguageInfo(language: string): LanguageInfo | null {
233
+ // Check if it's a direct match
234
+ if (isPreRegisteredLanguage(language)) {
235
+ return LANGUAGE_INFO[language];
236
+ }
237
+
238
+ // Check if it's an alias
239
+ for (const [key, info] of Object.entries(LANGUAGE_INFO)) {
240
+ if (info.aliases.includes(language)) {
241
+ return info;
242
+ }
243
+ }
244
+
245
+ return null;
246
+ }
247
+
248
+ /**
249
+ * Get language by file extension
250
+ */
251
+ export function getLanguageByExtension(extension: string): string | null {
252
+ const normalizedExt = extension.startsWith('.') ? extension : `.${extension}`;
253
+
254
+ for (const [key, info] of Object.entries(LANGUAGE_INFO)) {
255
+ if (info.extensions.includes(normalizedExt)) {
256
+ return info.name;
257
+ }
258
+ }
259
+
260
+ return null;
261
+ }
262
+
263
+ /**
264
+ * Get all available language names (primary names only)
265
+ */
266
+ export function getAllLanguageNames(): string[] {
267
+ const uniqueNames = new Set<string>();
268
+
269
+ for (const info of Object.values(LANGUAGE_INFO)) {
270
+ uniqueNames.add(info.name);
271
+ }
272
+
273
+ return Array.from(uniqueNames);
274
+ }
275
+
276
+ /**
277
+ * Language registration helper class
278
+ */
279
+ export class LanguageRegistry {
280
+ private languages: LanguagesModule;
281
+
282
+ constructor(languagesModule: LanguagesModule) {
283
+ this.languages = languagesModule;
284
+ }
285
+
286
+ /**
287
+ * Register a language if it's not already registered
288
+ */
289
+ registerIfNeeded(name: string, definition: LanguageFn): boolean {
290
+ if (this.isRegistered(name)) {
291
+ return false;
292
+ }
293
+
294
+ this.languages.registerLanguage(name, definition);
295
+ return true;
296
+ }
297
+
298
+ /**
299
+ * Check if a language is registered
300
+ */
301
+ isRegistered(name: string): boolean {
302
+ return isLanguageRegistered(this.languages, name);
303
+ }
304
+
305
+ /**
306
+ * Get all registered languages
307
+ */
308
+ getRegistered(): string[] {
309
+ return [...this.languages.registeredLanguages];
310
+ }
311
+
312
+ /**
313
+ * Get language info
314
+ */
315
+ getInfo(name: string): LanguageInfo | null {
316
+ return getLanguageInfo(name);
317
+ }
318
+
319
+ /**
320
+ * Register multiple languages at once
321
+ */
322
+ registerBulk(definitions: Record<string, LanguageFn>): void {
323
+ for (const [name, definition] of Object.entries(definitions)) {
324
+ this.registerIfNeeded(name, definition);
325
+ }
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Options for lazy language loading
331
+ */
332
+ export interface LazyLanguageOptions {
333
+ /**
334
+ * Base path for language modules
335
+ * @default "../highlight.js/es/languages/"
336
+ */
337
+ basePath?: string;
338
+
339
+ /**
340
+ * Whether to cache loaded languages
341
+ * @default true
342
+ */
343
+ cache?: boolean;
344
+ }
345
+
346
+ /**
347
+ * Lazy language loader for dynamic imports
348
+ */
349
+ export class LazyLanguageLoader {
350
+ private languages: LanguagesModule;
351
+ private basePath: string;
352
+ private cache: boolean;
353
+ private loadedLanguages: Set<string> = new Set();
354
+
355
+ constructor(
356
+ languagesModule: LanguagesModule,
357
+ options: LazyLanguageOptions = {}
358
+ ) {
359
+ this.languages = languagesModule;
360
+ this.basePath = options.basePath || '../highlight.js/es/languages/';
361
+ this.cache = options.cache !== false;
362
+ }
363
+
364
+ /**
365
+ * Load a language dynamically
366
+ */
367
+ async loadLanguage(name: string): Promise<void> {
368
+ // Check if already loaded (if caching is enabled)
369
+ if (this.cache && this.loadedLanguages.has(name)) {
370
+ return;
371
+ }
372
+
373
+ // Check if pre-registered
374
+ if (isLanguageRegistered(this.languages, name)) {
375
+ this.loadedLanguages.add(name);
376
+ return;
377
+ }
378
+
379
+ try {
380
+ const module = await import(
381
+ `${this.basePath}${name}.js`
382
+ );
383
+
384
+ const definition = module.default;
385
+ this.languages.registerLanguage(name, definition);
386
+
387
+ if (this.cache) {
388
+ this.loadedLanguages.add(name);
389
+ }
390
+ } catch (error) {
391
+ throw new Error(
392
+ `Failed to load language "${name}": ${(error as Error).message}`
393
+ );
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Load multiple languages
399
+ */
400
+ async loadLanguages(names: string[]): Promise<void> {
401
+ await Promise.all(names.map(name => this.loadLanguage(name)));
402
+ }
403
+
404
+ /**
405
+ * Check if a language has been loaded
406
+ */
407
+ isLoaded(name: string): boolean {
408
+ return this.loadedLanguages.has(name);
409
+ }
410
+
411
+ /**
412
+ * Clear the cache
413
+ */
414
+ clearCache(): void {
415
+ this.loadedLanguages.clear();
416
+ }
417
+ }
418
+
419
+ /**
420
+ * Export utility functions
421
+ */
422
+ export const LanguageUtils = {
423
+ isPreRegisteredLanguage,
424
+ isLanguageRegistered,
425
+ getLanguageInfo,
426
+ getLanguageByExtension,
427
+ getAllLanguageNames
428
+ };