overtype 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.
package/src/styles.js ADDED
@@ -0,0 +1,486 @@
1
+ /**
2
+ * CSS styles for OverType editor
3
+ * Embedded in JavaScript to ensure single-file distribution
4
+ */
5
+
6
+ import { themeToCSSVars } from './themes.js';
7
+
8
+ /**
9
+ * Generate the complete CSS for the editor
10
+ * @param {Object} options - Configuration options
11
+ * @returns {string} Complete CSS string
12
+ */
13
+ export function generateStyles(options = {}) {
14
+ const {
15
+ fontSize = '14px',
16
+ lineHeight = 1.6,
17
+ fontFamily = "'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace",
18
+ padding = '20px',
19
+ theme = null,
20
+ mobile = {}
21
+ } = options;
22
+
23
+ // Generate mobile overrides
24
+ const mobileStyles = Object.keys(mobile).length > 0 ? `
25
+ @media (max-width: 640px) {
26
+ .overtype-wrapper .overtype-input,
27
+ .overtype-wrapper .overtype-preview {
28
+ ${Object.entries(mobile)
29
+ .map(([prop, val]) => {
30
+ const cssProp = prop.replace(/([A-Z])/g, '-$1').toLowerCase();
31
+ return `${cssProp}: ${val} !important;`;
32
+ })
33
+ .join('\n ')}
34
+ }
35
+ }
36
+ ` : '';
37
+
38
+ // Generate theme variables if provided
39
+ const themeVars = theme && theme.colors ? themeToCSSVars(theme.colors) : '';
40
+
41
+ return `
42
+ /* OverType Editor Styles */
43
+ .overtype-container {
44
+ position: relative !important;
45
+ width: 100% !important;
46
+ height: 100% !important;
47
+ ${themeVars ? `
48
+ /* Theme Variables */
49
+ ${themeVars}` : ''}
50
+ }
51
+
52
+ .overtype-wrapper {
53
+ position: relative !important;
54
+ width: 100% !important;
55
+ height: 100% !important;
56
+ overflow: hidden !important;
57
+ background: var(--bg-secondary, #ffffff) !important;
58
+ }
59
+
60
+ /* Critical alignment styles - must be identical for both layers */
61
+ .overtype-wrapper .overtype-input,
62
+ .overtype-wrapper .overtype-preview {
63
+ /* Positioning - must be identical */
64
+ position: absolute !important;
65
+ top: 0 !important;
66
+ left: 0 !important;
67
+ width: 100% !important;
68
+ height: 100% !important;
69
+
70
+ /* Font properties - any difference breaks alignment */
71
+ font-family: ${fontFamily} !important;
72
+ font-size: var(--instance-font-size, ${fontSize}) !important;
73
+ line-height: var(--instance-line-height, ${lineHeight}) !important;
74
+ font-weight: normal !important;
75
+ font-style: normal !important;
76
+ font-variant: normal !important;
77
+ font-stretch: normal !important;
78
+ font-kerning: none !important;
79
+ font-feature-settings: normal !important;
80
+
81
+ /* Box model - must match exactly */
82
+ padding: var(--instance-padding, ${padding}) !important;
83
+ margin: 0 !important;
84
+ border: none !important;
85
+ outline: none !important;
86
+ box-sizing: border-box !important;
87
+
88
+ /* Text layout - critical for character positioning */
89
+ white-space: pre-wrap !important;
90
+ word-wrap: break-word !important;
91
+ word-break: normal !important;
92
+ overflow-wrap: break-word !important;
93
+ tab-size: 2 !important;
94
+ -moz-tab-size: 2 !important;
95
+ text-align: left !important;
96
+ text-indent: 0 !important;
97
+ letter-spacing: normal !important;
98
+ word-spacing: normal !important;
99
+
100
+ /* Text rendering */
101
+ text-transform: none !important;
102
+ text-rendering: auto !important;
103
+ -webkit-font-smoothing: auto !important;
104
+ -webkit-text-size-adjust: 100% !important;
105
+
106
+ /* Direction and writing */
107
+ direction: ltr !important;
108
+ writing-mode: horizontal-tb !important;
109
+ unicode-bidi: normal !important;
110
+ text-orientation: mixed !important;
111
+
112
+ /* Visual effects that could shift perception */
113
+ text-shadow: none !important;
114
+ filter: none !important;
115
+ transform: none !important;
116
+ zoom: 1 !important;
117
+
118
+ /* Vertical alignment */
119
+ vertical-align: baseline !important;
120
+
121
+ /* Size constraints */
122
+ min-width: 0 !important;
123
+ min-height: 0 !important;
124
+ max-width: none !important;
125
+ max-height: none !important;
126
+
127
+ /* Overflow */
128
+ overflow-y: auto !important;
129
+ overflow-x: auto !important;
130
+ scrollbar-width: auto !important;
131
+ scrollbar-gutter: auto !important;
132
+
133
+ /* Animation/transition - disabled to prevent movement */
134
+ animation: none !important;
135
+ transition: none !important;
136
+ }
137
+
138
+ /* Input layer styles */
139
+ .overtype-wrapper .overtype-input {
140
+ /* Layer positioning */
141
+ z-index: 1 !important;
142
+
143
+ /* Text visibility */
144
+ color: transparent !important;
145
+ caret-color: var(--cursor, #f95738) !important;
146
+ background-color: transparent !important;
147
+
148
+ /* Textarea-specific */
149
+ resize: none !important;
150
+ appearance: none !important;
151
+ -webkit-appearance: none !important;
152
+ -moz-appearance: none !important;
153
+
154
+ /* Prevent mobile zoom on focus */
155
+ touch-action: manipulation !important;
156
+
157
+ /* Disable autofill and spellcheck */
158
+ autocomplete: off !important;
159
+ autocorrect: off !important;
160
+ autocapitalize: off !important;
161
+ spellcheck: false !important;
162
+ }
163
+
164
+ .overtype-wrapper .overtype-input::selection {
165
+ background-color: var(--selection, rgba(244, 211, 94, 0.4));
166
+ }
167
+
168
+ /* Preview layer styles */
169
+ .overtype-wrapper .overtype-preview {
170
+ /* Layer positioning */
171
+ z-index: 0 !important;
172
+ pointer-events: none !important;
173
+ color: var(--text, #0d3b66) !important;
174
+ background-color: transparent !important;
175
+
176
+ /* Prevent text selection */
177
+ user-select: none !important;
178
+ -webkit-user-select: none !important;
179
+ -moz-user-select: none !important;
180
+ -ms-user-select: none !important;
181
+ }
182
+
183
+ /* Defensive styles for preview child divs */
184
+ .overtype-wrapper .overtype-preview div {
185
+ /* Reset any inherited styles */
186
+ margin: 0 !important;
187
+ padding: 0 !important;
188
+ border: none !important;
189
+ text-align: left !important;
190
+ text-indent: 0 !important;
191
+ display: block !important;
192
+ position: static !important;
193
+ transform: none !important;
194
+ min-height: 0 !important;
195
+ max-height: none !important;
196
+ line-height: inherit !important;
197
+ font-size: inherit !important;
198
+ font-family: inherit !important;
199
+ }
200
+
201
+ /* Markdown element styling - NO SIZE CHANGES */
202
+ .overtype-wrapper .overtype-preview .header {
203
+ font-weight: bold !important;
204
+ }
205
+
206
+ /* Header colors */
207
+ .overtype-wrapper .overtype-preview .h1 {
208
+ color: var(--h1, #f95738) !important;
209
+ }
210
+ .overtype-wrapper .overtype-preview .h2 {
211
+ color: var(--h2, #ee964b) !important;
212
+ }
213
+ .overtype-wrapper .overtype-preview .h3 {
214
+ color: var(--h3, #3d8a51) !important;
215
+ }
216
+
217
+ /* Bold text */
218
+ .overtype-wrapper .overtype-preview strong {
219
+ color: var(--strong, #ee964b) !important;
220
+ font-weight: bold !important;
221
+ }
222
+
223
+ /* Italic text */
224
+ .overtype-wrapper .overtype-preview em {
225
+ color: var(--em, #f95738) !important;
226
+ text-decoration-color: var(--em, #f95738) !important;
227
+ text-decoration-thickness: 1px !important;
228
+ font-style: italic !important;
229
+ }
230
+
231
+ /* Inline code */
232
+ .overtype-wrapper .overtype-preview code {
233
+ background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
234
+ color: var(--code, #0d3b66) !important;
235
+ padding: 0 !important;
236
+ border-radius: 2px !important;
237
+ font-family: inherit !important;
238
+ font-weight: normal !important;
239
+ }
240
+
241
+ /* Code blocks */
242
+ .overtype-wrapper .overtype-preview pre {
243
+ background: #1e1e1e !important;
244
+ padding: 0 !important;
245
+ margin: 0 !important;
246
+ border-radius: 4px !important;
247
+ overflow-x: auto !important;
248
+ }
249
+
250
+ .overtype-wrapper .overtype-preview pre code {
251
+ background: none !important;
252
+ }
253
+
254
+ /* Blockquotes */
255
+ .overtype-wrapper .overtype-preview .blockquote {
256
+ color: var(--blockquote, #5a7a9b) !important;
257
+ padding: 0 !important;
258
+ margin: 0 !important;
259
+ border: none !important;
260
+ }
261
+
262
+ /* Links */
263
+ .overtype-wrapper .overtype-preview a {
264
+ color: var(--link, #0d3b66) !important;
265
+ text-decoration: underline !important;
266
+ font-weight: normal !important;
267
+ }
268
+
269
+ .overtype-wrapper .overtype-preview a:hover {
270
+ text-decoration: underline !important;
271
+ color: var(--link, #0d3b66) !important;
272
+ }
273
+
274
+ /* Lists - no list styling */
275
+ .overtype-wrapper .overtype-preview ul,
276
+ .overtype-wrapper .overtype-preview ol {
277
+ list-style: none !important;
278
+ margin: 0 !important;
279
+ padding: 0 !important;
280
+ }
281
+
282
+ .overtype-wrapper .overtype-preview li {
283
+ margin: 0 !important;
284
+ padding: 0 !important;
285
+ list-style: none !important;
286
+ }
287
+
288
+ /* Horizontal rules */
289
+ .overtype-wrapper .overtype-preview hr {
290
+ border: none !important;
291
+ color: var(--hr, #5a7a9b) !important;
292
+ margin: 0 !important;
293
+ padding: 0 !important;
294
+ }
295
+
296
+ .overtype-wrapper .overtype-preview .hr-marker {
297
+ color: var(--hr, #5a7a9b) !important;
298
+ opacity: 0.6 !important;
299
+ }
300
+
301
+ /* Code fence markers - with background when not in code block */
302
+ .overtype-wrapper .overtype-preview .code-fence {
303
+ color: var(--code, #0d3b66) !important;
304
+ background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
305
+ }
306
+
307
+ /* Code block lines - background for entire code block */
308
+ .overtype-wrapper .overtype-preview .code-block-line {
309
+ background: var(--code-bg, rgba(244, 211, 94, 0.4)) !important;
310
+ }
311
+
312
+ /* Remove background from code fence when inside code block line */
313
+ .overtype-wrapper .overtype-preview .code-block-line .code-fence {
314
+ background: transparent !important;
315
+ }
316
+
317
+ /* Raw markdown line */
318
+ .overtype-wrapper .overtype-preview .raw-line {
319
+ color: var(--raw-line, #5a7a9b) !important;
320
+ font-style: normal !important;
321
+ font-weight: normal !important;
322
+ }
323
+
324
+ /* Syntax markers */
325
+ .overtype-wrapper .overtype-preview .syntax-marker {
326
+ color: var(--syntax-marker, rgba(13, 59, 102, 0.52)) !important;
327
+ opacity: 0.7 !important;
328
+ }
329
+
330
+ /* List markers */
331
+ .overtype-wrapper .overtype-preview .list-marker {
332
+ color: var(--list-marker, #ee964b) !important;
333
+ }
334
+
335
+ /* Stats bar */
336
+ .overtype-wrapper.with-stats {
337
+ padding-bottom: 40px !important;
338
+ }
339
+
340
+ .overtype-wrapper .overtype-stats {
341
+ position: absolute !important;
342
+ bottom: 0 !important;
343
+ left: 0 !important;
344
+ right: 0 !important;
345
+ height: 40px !important;
346
+ padding: 0 20px !important;
347
+ background: #f8f9fa !important;
348
+ border-top: 1px solid #e0e0e0 !important;
349
+ display: flex !important;
350
+ justify-content: space-between !important;
351
+ align-items: center !important;
352
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
353
+ font-size: 0.85rem !important;
354
+ color: #666 !important;
355
+ z-index: 2 !important;
356
+ }
357
+
358
+ /* Dark theme stats bar */
359
+ .overtype-wrapper[data-theme="cave"] .overtype-stats {
360
+ background: var(--bg-secondary, #1D2D3E) !important;
361
+ border-top: 1px solid rgba(197, 221, 232, 0.1) !important;
362
+ color: var(--text, #c5dde8) !important;
363
+ }
364
+
365
+ .overtype-wrapper .overtype-stats .overtype-stat {
366
+ display: flex !important;
367
+ align-items: center !important;
368
+ gap: 5px !important;
369
+ white-space: nowrap !important;
370
+ }
371
+
372
+ .overtype-wrapper .overtype-stats .live-dot {
373
+ width: 8px !important;
374
+ height: 8px !important;
375
+ background: #4caf50 !important;
376
+ border-radius: 50% !important;
377
+ animation: pulse 2s infinite !important;
378
+ }
379
+
380
+ @keyframes pulse {
381
+ 0%, 100% { opacity: 1; transform: scale(1); }
382
+ 50% { opacity: 0.6; transform: scale(1.2); }
383
+ }
384
+
385
+ /* Adjust textarea and preview for stats bar */
386
+ .overtype-wrapper.with-stats .overtype-input,
387
+ .overtype-wrapper.with-stats .overtype-preview {
388
+ height: calc(100% - 40px) !important;
389
+ }
390
+
391
+ /* Toolbar Styles */
392
+ .overtype-toolbar {
393
+ display: flex;
394
+ align-items: center;
395
+ gap: 4px;
396
+ padding: 8px;
397
+ background: var(--toolbar-bg, var(--bg-primary, #f8f9fa));
398
+ border: 1px solid var(--toolbar-border, var(--border, #e0e0e0));
399
+ border-bottom: none;
400
+ border-radius: 8px 8px 0 0;
401
+ overflow-x: auto;
402
+ -webkit-overflow-scrolling: touch;
403
+ }
404
+
405
+ .overtype-toolbar-button {
406
+ display: flex;
407
+ align-items: center;
408
+ justify-content: center;
409
+ width: 32px;
410
+ height: 32px;
411
+ padding: 0;
412
+ border: none;
413
+ border-radius: 6px;
414
+ background: transparent;
415
+ color: var(--toolbar-icon, var(--text-secondary, #666));
416
+ cursor: pointer;
417
+ transition: all 0.2s ease;
418
+ flex-shrink: 0;
419
+ }
420
+
421
+ .overtype-toolbar-button svg {
422
+ width: 20px;
423
+ height: 20px;
424
+ fill: currentColor;
425
+ }
426
+
427
+ /* Special sizing for code block icon */
428
+ .overtype-toolbar-button[data-action="insertCodeBlock"] svg {
429
+ width: 22px;
430
+ height: 18px;
431
+ fill: transparent !important;
432
+ }
433
+
434
+ .overtype-toolbar-button:hover {
435
+ background: var(--toolbar-hover, var(--bg-secondary, #e9ecef));
436
+ color: var(--toolbar-icon, var(--text-primary, #333));
437
+ }
438
+
439
+ .overtype-toolbar-button:active {
440
+ transform: scale(0.95);
441
+ }
442
+
443
+ .overtype-toolbar-button.active {
444
+ background: var(--toolbar-active, var(--primary, #007bff));
445
+ color: var(--toolbar-icon, var(--text-primary, #333));
446
+ }
447
+
448
+ .overtype-toolbar-button:disabled {
449
+ opacity: 0.5;
450
+ cursor: not-allowed;
451
+ }
452
+
453
+ .overtype-toolbar-separator {
454
+ width: 1px;
455
+ height: 24px;
456
+ background: var(--border, #e0e0e0);
457
+ margin: 0 4px;
458
+ flex-shrink: 0;
459
+ }
460
+
461
+ /* Adjust wrapper when toolbar is present */
462
+ .overtype-container .overtype-toolbar + .overtype-wrapper {
463
+ border-radius: 0 0 8px 8px;
464
+ border-top: none;
465
+ }
466
+
467
+ /* Mobile toolbar adjustments */
468
+ @media (max-width: 640px) {
469
+ .overtype-toolbar {
470
+ padding: 6px;
471
+ gap: 2px;
472
+ }
473
+
474
+ .overtype-toolbar-button {
475
+ width: 36px;
476
+ height: 36px;
477
+ }
478
+
479
+ .overtype-toolbar-separator {
480
+ margin: 0 2px;
481
+ }
482
+ }
483
+
484
+ ${mobileStyles}
485
+ `;
486
+ }
package/src/themes.js ADDED
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Built-in themes for OverType editor
3
+ * Each theme provides a complete color palette for the editor
4
+ */
5
+
6
+ /**
7
+ * Solar theme - Light, warm and bright
8
+ */
9
+ export const solar = {
10
+ name: 'solar',
11
+ colors: {
12
+ bgPrimary: '#faf0ca', // Lemon Chiffon - main background
13
+ bgSecondary: '#ffffff', // White - editor background
14
+ text: '#0d3b66', // Yale Blue - main text
15
+ h1: '#f95738', // Tomato - h1 headers
16
+ h2: '#ee964b', // Sandy Brown - h2 headers
17
+ h3: '#3d8a51', // Forest green - h3 headers
18
+ strong: '#ee964b', // Sandy Brown - bold text
19
+ em: '#f95738', // Tomato - italic text
20
+ link: '#0d3b66', // Yale Blue - links
21
+ code: '#0d3b66', // Yale Blue - inline code
22
+ codeBg: 'rgba(244, 211, 94, 0.4)', // Naples Yellow with transparency
23
+ blockquote: '#5a7a9b', // Muted blue - blockquotes
24
+ hr: '#5a7a9b', // Muted blue - horizontal rules
25
+ syntaxMarker: 'rgba(13, 59, 102, 0.52)', // Yale Blue with transparency
26
+ cursor: '#f95738', // Tomato - cursor
27
+ selection: 'rgba(244, 211, 94, 0.4)', // Naples Yellow with transparency
28
+ listMarker: '#ee964b', // Sandy Brown - list markers
29
+ // Toolbar colors
30
+ toolbarBg: '#ffffff', // White - toolbar background
31
+ toolbarBorder: 'rgba(13, 59, 102, 0.15)', // Yale Blue border
32
+ toolbarIcon: '#0d3b66', // Yale Blue - icon color
33
+ toolbarHover: '#f5f5f5', // Light gray - hover background
34
+ toolbarActive: '#faf0ca', // Lemon Chiffon - active button background
35
+ }
36
+ };
37
+
38
+ /**
39
+ * Cave theme - Dark ocean depths
40
+ */
41
+ export const cave = {
42
+ name: 'cave',
43
+ colors: {
44
+ bgPrimary: '#141E26', // Deep ocean - main background
45
+ bgSecondary: '#1D2D3E', // Darker charcoal - editor background
46
+ text: '#c5dde8', // Light blue-gray - main text
47
+ h1: '#d4a5ff', // Rich lavender - h1 headers
48
+ h2: '#f6ae2d', // Hunyadi Yellow - h2 headers
49
+ h3: '#9fcfec', // Brighter blue - h3 headers
50
+ strong: '#f6ae2d', // Hunyadi Yellow - bold text
51
+ em: '#9fcfec', // Brighter blue - italic text
52
+ link: '#9fcfec', // Brighter blue - links
53
+ code: '#c5dde8', // Light blue-gray - inline code
54
+ codeBg: '#1a232b', // Very dark blue - code background
55
+ blockquote: '#9fcfec', // Brighter blue - same as italic
56
+ hr: '#c5dde8', // Light blue-gray - horizontal rules
57
+ syntaxMarker: 'rgba(159, 207, 236, 0.73)', // Brighter blue semi-transparent
58
+ cursor: '#f26419', // Orange Pantone - cursor
59
+ selection: 'rgba(51, 101, 138, 0.4)', // Lapis Lazuli with transparency
60
+ listMarker: '#f6ae2d', // Hunyadi Yellow - list markers
61
+ // Toolbar colors for dark theme
62
+ toolbarBg: '#1D2D3E', // Darker charcoal - toolbar background
63
+ toolbarBorder: 'rgba(197, 221, 232, 0.1)', // Light blue-gray border
64
+ toolbarIcon: '#c5dde8', // Light blue-gray - icon color
65
+ toolbarHover: '#243546', // Slightly lighter charcoal - hover background
66
+ toolbarActive: '#2a3f52', // Even lighter - active button background
67
+ }
68
+ };
69
+
70
+ /**
71
+ * Default themes registry
72
+ */
73
+ export const themes = {
74
+ solar,
75
+ cave,
76
+ // Aliases for backward compatibility
77
+ light: solar,
78
+ dark: cave
79
+ };
80
+
81
+ /**
82
+ * Get theme by name or return custom theme object
83
+ * @param {string|Object} theme - Theme name or custom theme object
84
+ * @returns {Object} Theme configuration
85
+ */
86
+ export function getTheme(theme) {
87
+ if (typeof theme === 'string') {
88
+ const themeObj = themes[theme] || themes.solar;
89
+ // Preserve the requested theme name (important for 'light' and 'dark' aliases)
90
+ return { ...themeObj, name: theme };
91
+ }
92
+ return theme;
93
+ }
94
+
95
+ /**
96
+ * Apply theme colors to CSS variables
97
+ * @param {Object} colors - Theme colors object
98
+ * @returns {string} CSS custom properties string
99
+ */
100
+ export function themeToCSSVars(colors) {
101
+ const vars = [];
102
+ for (const [key, value] of Object.entries(colors)) {
103
+ // Convert camelCase to kebab-case
104
+ const varName = key.replace(/([A-Z])/g, '-$1').toLowerCase();
105
+ vars.push(`--${varName}: ${value};`);
106
+ }
107
+ return vars.join('\n');
108
+ }
109
+
110
+ /**
111
+ * Merge custom colors with base theme
112
+ * @param {Object} baseTheme - Base theme object
113
+ * @param {Object} customColors - Custom color overrides
114
+ * @returns {Object} Merged theme object
115
+ */
116
+ export function mergeTheme(baseTheme, customColors = {}) {
117
+ return {
118
+ ...baseTheme,
119
+ colors: {
120
+ ...baseTheme.colors,
121
+ ...customColors
122
+ }
123
+ };
124
+ }