neiki-editor 2.10.1 → 3.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/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  <img src="https://img.shields.io/badge/css-%23663399.svg?style=for-the-badge&logo=css&logoColor=white" alt="CSS">
12
12
  <br>
13
13
  <img src="https://img.shields.io/badge/License-AGPL--3.0-2563EB?style=for-the-badge&logo=open-source-initiative&logoColor=white&labelColor=000F15&logoWidth=20" alt="License">
14
- <img src="https://img.shields.io/badge/Version-2.10.1-2563EB?style=for-the-badge&logo=semantic-release&logoColor=white&labelColor=000F15&logoWidth=20" alt="Version">
14
+ <img src="https://img.shields.io/badge/Version-3.0.0-2563EB?style=for-the-badge&logo=semantic-release&logoColor=white&labelColor=000F15&logoWidth=20" alt="Version">
15
15
  </p>
16
16
 
17
17
  <p align="center">
@@ -62,7 +62,7 @@ Add this single line — CSS is included automatically, always the **latest vers
62
62
  #### Pin a specific version
63
63
 
64
64
  ```html
65
- <script src="https://cdn.neikiri.dev/neiki-editor/2.10.1/neiki-editor.min.js"></script>
65
+ <script src="https://cdn.neikiri.dev/neiki-editor/3.0.0/neiki-editor.min.js"></script>
66
66
  ```
67
67
 
68
68
  #### Load CSS and JS separately
@@ -73,8 +73,8 @@ Add this single line — CSS is included automatically, always the **latest vers
73
73
  <script src="https://cdn.neikiri.dev/neiki-editor/neiki-editor.js"></script>
74
74
 
75
75
  <!-- Or pinned -->
76
- <link rel="stylesheet" href="https://cdn.neikiri.dev/neiki-editor/2.10.1/neiki-editor.css">
77
- <script src="https://cdn.neikiri.dev/neiki-editor/2.10.1/neiki-editor.js"></script>
76
+ <link rel="stylesheet" href="https://cdn.neikiri.dev/neiki-editor/3.0.0/neiki-editor.css">
77
+ <script src="https://cdn.neikiri.dev/neiki-editor/3.0.0/neiki-editor.js"></script>
78
78
  ```
79
79
 
80
80
  #### Alternative CDN — jsDelivr
@@ -84,15 +84,15 @@ Add this single line — CSS is included automatically, always the **latest vers
84
84
  <script src="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@latest/dist/neiki-editor.min.js"></script>
85
85
 
86
86
  <!-- Pinned -->
87
- <script src="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@2.10.1/dist/neiki-editor.min.js"></script>
87
+ <script src="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@3.0.0/dist/neiki-editor.min.js"></script>
88
88
 
89
89
  <!-- Separate files (latest) -->
90
90
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@latest/dist/neiki-editor.css">
91
91
  <script src="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@latest/dist/neiki-editor.js"></script>
92
92
 
93
93
  <!-- Separate files (pinned) -->
94
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@2.10.1/dist/neiki-editor.css">
95
- <script src="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@2.10.1/dist/neiki-editor.js"></script>
94
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@3.0.0/dist/neiki-editor.css">
95
+ <script src="https://cdn.jsdelivr.net/gh/neikiri/neiki-editor@3.0.0/dist/neiki-editor.js"></script>
96
96
  ```
97
97
 
98
98
  #### Package Manager
@@ -143,7 +143,7 @@ const editor = new NeikiEditor('#editor', {
143
143
  autofocus: false,
144
144
  spellcheck: true,
145
145
  readonly: false,
146
- theme: 'light', // 'light' or 'dark'
146
+ theme: 'light', // 'light', 'dark', 'blue', or 'dark-blue'
147
147
  language: 'en', // 'en', 'cs', or custom via addTranslation()
148
148
  translations: null, // custom translation keys (merged with built-in)
149
149
  autosaveKey: null, // optional custom localStorage scope for autosave
@@ -181,7 +181,7 @@ const editor = new NeikiEditor('#editor', {
181
181
  | `autofocus` | `boolean` | `false` | Focus editor on initialization |
182
182
  | `spellcheck` | `boolean` | `true` | Enable browser spellcheck |
183
183
  | `readonly` | `boolean` | `false` | Make editor read-only |
184
- | `theme` | `string` | `'light'` | `'light'` or `'dark'` |
184
+ | `theme` | `string` | `'light'` | `'light'`, `'dark'`, `'blue'`, or `'dark-blue'` |
185
185
  | `language` | `string` | `'en'` | UI language — `en`, `cs`, `zh`, `es`, `de`, `fr`, `pt`, `ja` |
186
186
  | `translations` | `object\|null` | `null` | Custom translation keys (merged with built-in) |
187
187
  | `autosaveKey` | `string\|null` | `null` | Custom `localStorage` scope for autosave content and enabled state |
@@ -193,6 +193,7 @@ const editor = new NeikiEditor('#editor', {
193
193
  | `onReady` | `function\|null` | `null` | Callback when editor is ready |
194
194
  | `showHelp` | `boolean` | `true` | Show Help button in More menu (⋯) |
195
195
  | `imageUploadHandler` | `function\|null` | `null` | Async callback `(file) => Promise<url>` for uploading images to a server/CDN instead of base64 |
196
+ | `videoUploadHandler` | `function\|null` | `null` | Async callback `(file) => Promise<url>` for uploading videos to a server/CDN instead of base64 |
196
197
  | `customClass` | `string\|null` | `null` | Custom CSS class appended to the editor content area (`neiki-content`) |
197
198
 
198
199
  ---
@@ -247,11 +248,12 @@ The `insertDropdown` toolbar item renders a single **Insert** button that opens
247
248
  |------|-------------|
248
249
  | **Link** | Insert/edit hyperlink (**Ctrl+K**) |
249
250
  | **Image** | Insert image (URL or file upload → base64) |
251
+ | **Video** | Insert video (URL or file upload → base64, or via `videoUploadHandler`) |
250
252
  | **Table** | Insert table with custom rows/columns |
251
253
  | **Emoji** | Emoji picker (100+ emojis) |
252
254
  | **Symbol** | Special characters (©, ®, €, π, Ω, arrows, etc.) |
253
255
 
254
- You can still use `link`, `image`, `table`, `emoji`, `specialChars` as standalone toolbar buttons if preferred.
256
+ You can still use `link`, `image`, `video`, `table`, `emoji`, `specialChars` as standalone toolbar buttons if preferred.
255
257
 
256
258
  ### More Menu
257
259
 
@@ -265,7 +267,7 @@ The `moreMenu` toolbar item renders a **⋯** button (pushed to the right) that
265
267
  | **Print** | Print editor content |
266
268
  | **Autosave** | Toggle autosave to localStorage |
267
269
  | **Clear all** | Clear all editor content |
268
- | **Toggle Theme** | Switch between light/dark theme |
270
+ | **Change theme** | Choose light, dark, blue, or dark-blue from a select |
269
271
  | **Fullscreen** | Toggle fullscreen mode |
270
272
  | **Help** | Show help modal with author, version, and links (configurable via `showHelp`) |
271
273
 
@@ -276,7 +278,7 @@ The `moreMenu` toolbar item renders a **⋯** button (pushed to the right) that
276
278
  | `undo` | Undo (**Ctrl+Z**) |
277
279
  | `redo` | Redo (**Ctrl+Y** / **Ctrl+Shift+Z**) |
278
280
  | `findReplace` | Find & Replace with regex support |
279
- | `viewCode` | Toggle HTML source editor |
281
+ | `viewCode` | Toggle formatted HTML source editor with syntax highlighting |
280
282
  | `blockquote` | Block quote (toggles on/off) |
281
283
  | `horizontalRule` | Horizontal line |
282
284
 
@@ -284,7 +286,7 @@ The `moreMenu` toolbar item renders a **⋯** button (pushed to the right) that
284
286
 
285
287
  ## 🎨 Themes
286
288
 
287
- Neiki's Editor ships with **Light** and **Dark** themes.
289
+ Neiki's Editor ships with **Light**, **Dark**, **Blue**, and **Dark Blue** themes.
288
290
 
289
291
  ### Set theme on init:
290
292
 
@@ -294,14 +296,16 @@ const editor = new NeikiEditor('#editor', {
294
296
  });
295
297
  ```
296
298
 
297
- ### Toggle theme at runtime:
299
+ ### Change theme at runtime:
298
300
 
299
- Use the **Toggle Theme** item in the More menu (⋯), or toggle programmatically:
301
+ Use the **Change theme** select in the More menu (⋯), or change themes programmatically:
300
302
 
301
303
  ```javascript
302
304
  editor.toggleTheme();
303
305
  // or set a specific theme:
304
306
  editor.setTheme('dark');
307
+ editor.setTheme('blue');
308
+ editor.setTheme('dark-blue');
305
309
  ```
306
310
 
307
311
  The selected theme persists across page reloads via `localStorage`.
@@ -410,8 +414,8 @@ editor.enable(); // Enable editing
410
414
  editor.disable(); // Disable editing (read-only)
411
415
  editor.destroy(); // Remove editor, restore original element
412
416
  editor.toggleFullscreen(); // Toggle fullscreen mode
413
- editor.toggleTheme(); // Toggle light/dark theme
414
- editor.setTheme('dark'); // Set a specific theme
417
+ editor.toggleTheme(); // Cycle through built-in themes
418
+ editor.setTheme('dark'); // Set a specific theme ('light', 'dark', 'blue', 'dark-blue')
415
419
  editor.triggerSave(); // Trigger onSave callback
416
420
  editor.previewContent(); // Open preview modal
417
421
  editor.downloadContent(); // Download content as HTML file
@@ -514,6 +518,8 @@ The Image dialog includes a file upload input. Selected images are converted to
514
518
 
515
519
  Drag image files directly into the editor content area. Images are automatically converted to base64 and inserted at the drop position.
516
520
 
521
+ Selected text can also be dragged to a new position inside the editor. Text, image, and video drags show the same insertion indicator so the drop position is visible before release.
522
+
517
523
  ### Resize Images
518
524
 
519
525
  Click any image in the editor to select it — **resize handles** appear on all four corners. Drag any handle to resize while maintaining aspect ratio. A live **size label** (width × height) is displayed below the image during resizing.
@@ -530,6 +536,26 @@ The floating text-formatting toolbar is automatically hidden when an image is se
530
536
 
531
537
  ---
532
538
 
539
+ ## 🎬 Video Support
540
+
541
+ Use **Insert → Video** to add a video by URL, by selecting a local video file, or by dragging a video file into the editor. Without `videoUploadHandler`, selected videos are embedded as base64 data URIs. With `videoUploadHandler`, the editor uploads the file through your callback and inserts the returned URL.
542
+
543
+ ```javascript
544
+ const editor = new NeikiEditor('#editor', {
545
+ videoUploadHandler: async (file) => {
546
+ const formData = new FormData();
547
+ formData.append('video', file);
548
+ const response = await fetch('/upload-video', { method: 'POST', body: formData });
549
+ const data = await response.json();
550
+ return data.url;
551
+ }
552
+ });
553
+ ```
554
+
555
+ Inserted videos render as `<video controls>`, are preserved by the built-in sanitizer, and can be resized or repositioned like images.
556
+
557
+ ---
558
+
533
559
  ## 🔍 Find & Replace
534
560
 
535
561
  Open with the toolbar button or implement via the modal API.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * NeikiEditor - Production-Ready WYSIWYG Rich Text Editor
3
3
  * CSS Stylesheet
4
- * Version: 2.10.1
4
+ * Version: 3.0.0
5
5
  */
6
6
 
7
7
  /* ============================================
@@ -38,7 +38,8 @@
38
38
  }
39
39
 
40
40
  /* Dark Theme */
41
- .neiki-editor.neiki-dark {
41
+ .neiki-editor.neiki-dark,
42
+ .neiki-modal-overlay.neiki-dark {
42
43
  --neiki-bg-primary: #1e1e1e;
43
44
  --neiki-bg-secondary: #252526;
44
45
  --neiki-bg-tertiary: #2d2d30;
@@ -67,6 +68,68 @@
67
68
  --neiki-table-border: #3e3e42;
68
69
  }
69
70
 
71
+ /* Blue Theme */
72
+ .neiki-editor.neiki-theme-blue,
73
+ .neiki-modal-overlay.neiki-theme-blue {
74
+ --neiki-bg-primary: #f8fbff;
75
+ --neiki-bg-secondary: #eef6ff;
76
+ --neiki-bg-tertiary: #dbeafe;
77
+ --neiki-bg-hover: #bfdbfe;
78
+ --neiki-bg-active: #93c5fd;
79
+ --neiki-border-color: #bfdbfe;
80
+ --neiki-border-color-dark: #60a5fa;
81
+ --neiki-text-primary: #0f172a;
82
+ --neiki-text-secondary: #1e3a8a;
83
+ --neiki-text-muted: #64748b;
84
+ --neiki-accent-color: #2563eb;
85
+ --neiki-accent-hover: #1d4ed8;
86
+ --neiki-danger-color: #dc3545;
87
+ --neiki-success-color: #16803d;
88
+ --neiki-shadow-sm: 0 1px 2px rgba(37, 99, 235, 0.08);
89
+ --neiki-shadow-md: 0 4px 10px rgba(37, 99, 235, 0.12);
90
+ --neiki-shadow-lg: 0 14px 28px rgba(37, 99, 235, 0.16);
91
+ --neiki-editor-bg: #ffffff;
92
+ --neiki-toolbar-bg: #eaf4ff;
93
+ --neiki-statusbar-bg: #eef6ff;
94
+ --neiki-modal-overlay: rgba(15, 23, 42, 0.45);
95
+ --neiki-scrollbar-thumb: #93c5fd;
96
+ --neiki-scrollbar-track: #eff6ff;
97
+ --neiki-selection-bg: #bfdbfe;
98
+ --neiki-highlight-color: #fef08a;
99
+ --neiki-table-border: #bfdbfe;
100
+ }
101
+
102
+ /* Dark Blue Theme */
103
+ .neiki-editor.neiki-theme-dark-blue,
104
+ .neiki-modal-overlay.neiki-theme-dark-blue {
105
+ --neiki-bg-primary: #07111f;
106
+ --neiki-bg-secondary: #0b1d33;
107
+ --neiki-bg-tertiary: #102a46;
108
+ --neiki-bg-hover: #173b61;
109
+ --neiki-bg-active: #1d4f82;
110
+ --neiki-border-color: #1f3f63;
111
+ --neiki-border-color-dark: #2f6da5;
112
+ --neiki-text-primary: #e6f1ff;
113
+ --neiki-text-secondary: #b9d7f8;
114
+ --neiki-text-muted: #7ea6cc;
115
+ --neiki-accent-color: #38bdf8;
116
+ --neiki-accent-hover: #0ea5e9;
117
+ --neiki-danger-color: #fb7185;
118
+ --neiki-success-color: #34d399;
119
+ --neiki-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.24);
120
+ --neiki-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.34);
121
+ --neiki-shadow-lg: 0 18px 36px rgba(0, 0, 0, 0.46);
122
+ --neiki-editor-bg: #081827;
123
+ --neiki-toolbar-bg: #0b1d33;
124
+ --neiki-statusbar-bg: #0b1d33;
125
+ --neiki-modal-overlay: rgba(0, 0, 0, 0.72);
126
+ --neiki-scrollbar-thumb: #1d4f82;
127
+ --neiki-scrollbar-track: #07111f;
128
+ --neiki-selection-bg: #164e63;
129
+ --neiki-highlight-color: #854d0e;
130
+ --neiki-table-border: #1f3f63;
131
+ }
132
+
70
133
  /* ============================================
71
134
  Base Editor Container
72
135
  ============================================ */
@@ -229,6 +292,34 @@
229
292
  box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.15);
230
293
  }
231
294
 
295
+ .neiki-theme-select-wrapper {
296
+ display: inline-flex;
297
+ align-items: center;
298
+ gap: 6px;
299
+ height: 32px;
300
+ }
301
+
302
+ .neiki-theme-select-icon {
303
+ display: inline-flex;
304
+ align-items: center;
305
+ justify-content: center;
306
+ color: var(--neiki-text-secondary);
307
+ width: 18px;
308
+ height: 18px;
309
+ flex-shrink: 0;
310
+ }
311
+
312
+ .neiki-theme-select-icon svg {
313
+ width: 16px;
314
+ height: 16px;
315
+ fill: currentColor;
316
+ }
317
+
318
+ .neiki-theme-select {
319
+ min-width: 116px;
320
+ max-width: 150px;
321
+ }
322
+
232
323
  /* Color Picker Button */
233
324
  .neiki-color-btn {
234
325
  position: relative;
@@ -307,7 +398,7 @@
307
398
  }
308
399
 
309
400
  .neiki-content.neiki-drag-over::after {
310
- content: 'Drop images here';
401
+ content: 'Drop media here';
311
402
  position: absolute;
312
403
  top: 50%;
313
404
  left: 50%;
@@ -319,6 +410,44 @@
319
410
  z-index: 10;
320
411
  }
321
412
 
413
+ .neiki-drop-indicator {
414
+ position: absolute;
415
+ width: 3px;
416
+ min-height: 22px;
417
+ background: var(--neiki-accent-color);
418
+ border-radius: 999px;
419
+ box-shadow: 0 0 0 3px rgba(13, 110, 253, 0.18);
420
+ pointer-events: none;
421
+ opacity: 0;
422
+ transform: translateX(-1px);
423
+ transition: opacity 0.08s ease;
424
+ z-index: 80;
425
+ }
426
+
427
+ .neiki-drop-indicator::before,
428
+ .neiki-drop-indicator::after {
429
+ content: '';
430
+ position: absolute;
431
+ left: 50%;
432
+ width: 9px;
433
+ height: 9px;
434
+ background: var(--neiki-accent-color);
435
+ border-radius: 50%;
436
+ transform: translateX(-50%);
437
+ }
438
+
439
+ .neiki-drop-indicator::before {
440
+ top: -4px;
441
+ }
442
+
443
+ .neiki-drop-indicator::after {
444
+ bottom: -4px;
445
+ }
446
+
447
+ .neiki-drop-indicator.show {
448
+ opacity: 1;
449
+ }
450
+
322
451
  /* Focus state for editor container - disabled */
323
452
  /* .neiki-editor.neiki-focused removed */
324
453
 
@@ -445,6 +574,15 @@
445
574
  outline-offset: 2px;
446
575
  }
447
576
 
577
+ .neiki-content video {
578
+ display: block;
579
+ max-width: 100%;
580
+ height: auto;
581
+ margin: 0 0 1em 0;
582
+ border-radius: 6px;
583
+ background: #000;
584
+ }
585
+
448
586
  /* Search Highlight */
449
587
  .neiki-highlight {
450
588
  background: var(--neiki-highlight-color);
@@ -1492,23 +1630,89 @@
1492
1630
  color: var(--neiki-text-primary);
1493
1631
  }
1494
1632
 
1495
- .neiki-code-view-textarea {
1633
+ .neiki-code-view-editor {
1634
+ position: relative;
1496
1635
  flex: 1;
1497
1636
  width: 100%;
1637
+ min-height: 0;
1638
+ background: var(--neiki-bg-primary);
1639
+ }
1640
+
1641
+ .neiki-code-view-highlight,
1642
+ .neiki-code-view-textarea {
1643
+ position: absolute;
1644
+ inset: 0;
1645
+ width: 100%;
1646
+ height: 100%;
1498
1647
  padding: 16px;
1499
1648
  border: none;
1500
- background: var(--neiki-bg-primary);
1501
- color: var(--neiki-text-primary);
1502
1649
  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
1503
1650
  font-size: 13px;
1504
1651
  line-height: 1.5;
1652
+ tab-size: 2;
1653
+ white-space: pre-wrap;
1654
+ word-break: break-word;
1655
+ overflow: auto;
1656
+ }
1657
+
1658
+ .neiki-code-view-highlight {
1659
+ margin: 0;
1660
+ color: var(--neiki-text-primary);
1661
+ pointer-events: none;
1662
+ user-select: none;
1663
+ }
1664
+
1665
+ .neiki-code-view-textarea {
1666
+ background: transparent;
1667
+ color: transparent;
1668
+ caret-color: var(--neiki-text-primary);
1505
1669
  resize: none;
1670
+ z-index: 1;
1506
1671
  }
1507
1672
 
1508
1673
  .neiki-code-view-textarea:focus {
1509
1674
  outline: none;
1510
1675
  }
1511
1676
 
1677
+ .neiki-code-view-textarea::selection {
1678
+ background: var(--neiki-selection-bg);
1679
+ color: transparent;
1680
+ }
1681
+
1682
+ .neiki-html-punct {
1683
+ color: var(--neiki-text-muted);
1684
+ }
1685
+
1686
+ .neiki-html-tag {
1687
+ color: #0f74d8;
1688
+ font-weight: 600;
1689
+ }
1690
+
1691
+ .neiki-html-attr {
1692
+ color: #9a5b00;
1693
+ }
1694
+
1695
+ .neiki-html-string {
1696
+ color: #16803d;
1697
+ }
1698
+
1699
+ .neiki-html-comment {
1700
+ color: var(--neiki-text-muted);
1701
+ font-style: italic;
1702
+ }
1703
+
1704
+ .neiki-dark .neiki-html-tag {
1705
+ color: #7dd3fc;
1706
+ }
1707
+
1708
+ .neiki-dark .neiki-html-attr {
1709
+ color: #fbbf24;
1710
+ }
1711
+
1712
+ .neiki-dark .neiki-html-string {
1713
+ color: #86efac;
1714
+ }
1715
+
1512
1716
  .neiki-code-view-apply {
1513
1717
  width: auto !important;
1514
1718
  padding: 0 12px !important;
@@ -1722,7 +1926,7 @@
1722
1926
  }
1723
1927
 
1724
1928
  /* ============================================
1725
- Image Resize Handles
1929
+ Media Resize Handles
1726
1930
  ============================================ */
1727
1931
  .neiki-img-resizable {
1728
1932
  display: inline-block;
@@ -1731,11 +1935,16 @@
1731
1935
  outline-offset: 2px;
1732
1936
  border-radius: 4px;
1733
1937
  line-height: 0;
1938
+ user-select: none;
1939
+ -webkit-user-select: none;
1734
1940
  }
1735
1941
 
1736
- .neiki-img-resizable img {
1942
+ .neiki-img-resizable img,
1943
+ .neiki-img-resizable video {
1737
1944
  display: block;
1738
1945
  max-width: 100%;
1946
+ user-select: none;
1947
+ -webkit-user-select: none;
1739
1948
  }
1740
1949
 
1741
1950
  .neiki-img-resize-handle {
@@ -1785,7 +1994,7 @@
1785
1994
  }
1786
1995
 
1787
1996
  /* ============================================
1788
- Image Toolbar (replace, delete, drag)
1997
+ Media Toolbar (replace, delete, drag)
1789
1998
  ============================================ */
1790
1999
  .neiki-img-toolbar {
1791
2000
  position: absolute;
@@ -1801,6 +2010,8 @@
1801
2010
  box-shadow: var(--neiki-shadow-md);
1802
2011
  z-index: 20;
1803
2012
  white-space: nowrap;
2013
+ user-select: none;
2014
+ -webkit-user-select: none;
1804
2015
  }
1805
2016
 
1806
2017
  .neiki-img-toolbar-btn {
@@ -1891,7 +2102,7 @@
1891
2102
  user-select: none;
1892
2103
  }
1893
2104
 
1894
- .neiki-content:hover .neiki-block-grip {
2105
+ .neiki-content-wrapper:hover .neiki-block-grip {
1895
2106
  opacity: 0.5;
1896
2107
  }
1897
2108
 
@@ -2321,8 +2532,6 @@
2321
2532
  top: 100%;
2322
2533
  right: 0;
2323
2534
  z-index: 1000;
2324
- min-width: 140px;
2325
- max-width: 170px;
2326
2535
  background: var(--neiki-bg-primary);
2327
2536
  border: 1px solid var(--neiki-border-color);
2328
2537
  border-radius: 6px;
@@ -2365,6 +2574,28 @@
2365
2574
  background: rgba(220, 53, 69, 0.1);
2366
2575
  }
2367
2576
 
2577
+ .neiki-theme-menu-item {
2578
+ cursor: default;
2579
+ }
2580
+
2581
+ .neiki-theme-menu-select {
2582
+ min-width: 105px;
2583
+ height: 28px;
2584
+ padding: 0 24px 0 8px;
2585
+ border: 1px solid var(--neiki-border-color);
2586
+ border-radius: 4px;
2587
+ background: var(--neiki-bg-primary);
2588
+ color: var(--neiki-text-primary);
2589
+ font-size: 12px;
2590
+ cursor: pointer;
2591
+ }
2592
+
2593
+ .neiki-theme-menu-select:focus {
2594
+ outline: none;
2595
+ border-color: var(--neiki-accent-color);
2596
+ box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.15);
2597
+ }
2598
+
2368
2599
  /* Autosave badge in More menu */
2369
2600
  .neiki-autosave-badge {
2370
2601
  font-size: 12px;