@fresh-editor/fresh-editor 0.1.59 → 0.1.63

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/CHANGELOG.md CHANGED
@@ -1,5 +1,53 @@
1
1
  # Release Notes
2
2
 
3
+ ## 0.1.63
4
+
5
+ ### Features
6
+
7
+ * **Shell Command Prompt**: Pipe buffer or selection through shell commands (Alt+|).
8
+
9
+ * **On-Save Actions**: Run formatters/linters on save. Default formatters included for Rust (rustfmt), JavaScript/TypeScript (prettier), Python (ruff), C/C++ (clang-format), Go (gofmt).
10
+
11
+ * **Stdin Input**: Pipe content via stdin with background streaming (`echo "hello" | fresh -`).
12
+
13
+ * **Multi-File CLI**: Open multiple files from command line (#389).
14
+
15
+ * **Tab Indent Selection**: Tab indents selected lines, Shift+Tab dedents (#353).
16
+
17
+ * **Toggle Menu Bar**: Hide/show menu bar via command palette for extra screen space.
18
+
19
+ * **Global File Positions**: Cursor/scroll positions stored globally per file, not per project (#423).
20
+
21
+ * **Relative Line Numbers**: Show relative distances from cursor in gutter for easier vim-style navigation. Enable via `relative_line_numbers` config (#454).
22
+
23
+ ### Bug Fixes
24
+
25
+ * **On-Save Missing Tools**: Graceful handling when formatter/linter command not found.
26
+
27
+ * **Settings UI Nested Dialogs**: Fixed nested ObjectArray navigation and save not persisting (e.g., editing on_save inside language config).
28
+
29
+ * **Live Grep Working Directory**: Fixed search plugins using process cwd instead of project working directory.
30
+
31
+ * **Open File Path Resolution**: Fixed relative paths resolving incorrectly when editor launched from different directory.
32
+
33
+ ### Performance
34
+
35
+ * **Live Grep UI**: Fixed UI freezing for seconds during large codebase searches by making plugin event loop non-blocking.
36
+
37
+ ### Internal
38
+
39
+ * Embedded plugins in binary as fallback for cargo-binstall (#416).
40
+
41
+ * Removed duplicate theme JSON files (#438).
42
+
43
+ * Extracted modules from mod.rs (file_operations, split_actions, clipboard, etc.).
44
+
45
+ * Pinned Rust 1.92 via rust-toolchain.toml (#338).
46
+
47
+ * Windows build switched from MSVC to GNU target.
48
+
49
+ ---
50
+
3
51
  ## 0.1.59
4
52
 
5
53
  ### Features
package/binary-install.js CHANGED
@@ -10,18 +10,28 @@ const REPO = 'sinelaw/fresh';
10
10
  function download(url, dest) {
11
11
  return new Promise((resolve, reject) => {
12
12
  const file = fs.createWriteStream(dest);
13
+ file.on('error', (err) => {
14
+ file.close();
15
+ reject(err);
16
+ });
13
17
  https.get(url, (response) => {
14
18
  if (response.statusCode === 302 || response.statusCode === 301) {
15
- download(response.headers.location, dest).then(resolve).catch(reject);
19
+ file.close(() => {
20
+ download(response.headers.location, dest).then(resolve).catch(reject);
21
+ });
16
22
  return;
17
23
  }
18
24
  if (response.statusCode !== 200) {
25
+ file.close();
19
26
  reject(new Error(`Failed to download: ${response.statusCode}`));
20
27
  return;
21
28
  }
22
29
  response.pipe(file);
23
30
  file.on('finish', () => { file.close(); resolve(); });
24
- }).on('error', reject);
31
+ }).on('error', (err) => {
32
+ file.close();
33
+ reject(err);
34
+ });
25
35
  });
26
36
  }
27
37
 
@@ -31,10 +41,18 @@ async function install() {
31
41
  const url = `https://github.com/${REPO}/releases/download/v${VERSION}/${archiveName}`;
32
42
  const archivePath = path.join(__dirname, archiveName);
33
43
  const binDir = path.join(__dirname, 'bin');
44
+ const binaryPath = path.join(binDir, info.binaryName);
34
45
 
35
46
  console.log(`Downloading ${url}...`);
36
47
  await download(url, archivePath);
37
48
 
49
+ // Verify download succeeded
50
+ const stats = fs.statSync(archivePath);
51
+ if (stats.size === 0) {
52
+ throw new Error(`Downloaded file is empty: ${archivePath}`);
53
+ }
54
+ console.log(`Downloaded ${stats.size} bytes`);
55
+
38
56
  fs.mkdirSync(binDir, { recursive: true });
39
57
 
40
58
  if (info.ext === 'tar.xz') {
@@ -56,6 +74,12 @@ async function install() {
56
74
  }
57
75
 
58
76
  fs.unlinkSync(archivePath);
77
+
78
+ // Verify binary exists
79
+ if (!fs.existsSync(binaryPath)) {
80
+ throw new Error(`Installation failed: binary not found at ${binaryPath}`);
81
+ }
82
+
59
83
  console.log('fresh-editor installed successfully!');
60
84
  }
61
85
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fresh-editor/fresh-editor",
3
- "version": "0.1.59",
3
+ "version": "0.1.63",
4
4
  "description": "A modern terminal-based text editor with plugin support",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,6 +19,7 @@
19
19
  "install.js",
20
20
  "run-fresh.js",
21
21
  "plugins/**/*",
22
+ "themes/**/*",
22
23
  "README.md",
23
24
  "LICENSE",
24
25
  "CHANGELOG.md"
@@ -330,7 +330,8 @@
330
330
  },
331
331
  "required": [
332
332
  "action"
333
- ]
333
+ ],
334
+ "x-display-field": "/action"
334
335
  },
335
336
  "KeyPress": {
336
337
  "description": "A single key in a sequence",
@@ -372,7 +373,8 @@
372
373
  },
373
374
  "default": []
374
375
  }
375
- }
376
+ },
377
+ "x-display-field": "/inherits"
376
378
  },
377
379
  "KeybindingMapOptions": {
378
380
  "description": "Available keybinding maps",
@@ -453,8 +455,17 @@
453
455
  "format": "uint",
454
456
  "minimum": 0,
455
457
  "default": null
458
+ },
459
+ "on_save": {
460
+ "description": "Actions to run when a file of this language is saved\nActions are run in order; if any fails (non-zero exit), subsequent actions don't run",
461
+ "type": "array",
462
+ "items": {
463
+ "$ref": "#/$defs/OnSaveAction"
464
+ },
465
+ "default": []
456
466
  }
457
- }
467
+ },
468
+ "x-display-field": "/grammar"
458
469
  },
459
470
  "HighlighterPreference": {
460
471
  "description": "Preference for which syntax highlighting backend to use",
@@ -476,6 +487,63 @@
476
487
  }
477
488
  ]
478
489
  },
490
+ "OnSaveAction": {
491
+ "description": "Action to run when a file is saved",
492
+ "type": "object",
493
+ "properties": {
494
+ "command": {
495
+ "description": "The shell command to run\nThe file path is available as $FILE or as an argument",
496
+ "type": "string"
497
+ },
498
+ "args": {
499
+ "description": "Arguments to pass to the command\nUse \"$FILE\" to include the file path",
500
+ "type": "array",
501
+ "items": {
502
+ "type": "string"
503
+ },
504
+ "default": []
505
+ },
506
+ "working_dir": {
507
+ "description": "Working directory for the command (defaults to project root)",
508
+ "type": [
509
+ "string",
510
+ "null"
511
+ ],
512
+ "default": null
513
+ },
514
+ "stdin": {
515
+ "description": "Whether to use the buffer content as stdin",
516
+ "type": "boolean",
517
+ "default": false
518
+ },
519
+ "replace_buffer": {
520
+ "description": "Whether to replace the buffer with the command's stdout\nUseful for formatters",
521
+ "type": "boolean",
522
+ "default": false
523
+ },
524
+ "timeout_ms": {
525
+ "description": "Timeout in milliseconds (default: 10000)",
526
+ "type": "integer",
527
+ "format": "uint64",
528
+ "minimum": 0,
529
+ "default": 10000
530
+ },
531
+ "optional": {
532
+ "description": "Whether this action is optional (won't error if command not found)\nUseful for default formatters that may not be installed\nWhen true, shows a status message instead of an error if command is missing",
533
+ "type": "boolean",
534
+ "default": false
535
+ },
536
+ "enabled": {
537
+ "description": "Whether this action is enabled (default: true)\nSet to false to disable an action without removing it from config",
538
+ "type": "boolean",
539
+ "default": true
540
+ }
541
+ },
542
+ "required": [
543
+ "command"
544
+ ],
545
+ "x-display-field": "/command"
546
+ },
479
547
  "LspServerConfig": {
480
548
  "description": "LSP server configuration",
481
549
  "type": "object",
@@ -518,7 +586,8 @@
518
586
  },
519
587
  "required": [
520
588
  "command"
521
- ]
589
+ ],
590
+ "x-display-field": "/command"
522
591
  },
523
592
  "ProcessLimits": {
524
593
  "description": "Configuration for process resource limits",
@@ -89,7 +89,8 @@ globalThis.onGitGrepPromptChanged = function(args: {
89
89
  }
90
90
 
91
91
  // Spawn git grep asynchronously
92
- editor.spawnProcess("git", ["grep", "-n", "--column", "-I", "--", query])
92
+ const cwd = editor.getCwd();
93
+ editor.spawnProcess("git", ["grep", "-n", "--column", "-I", "--", query], cwd)
93
94
  .then((result) => {
94
95
  if (result.exit_code === 0) {
95
96
  // Parse results and update suggestions
@@ -81,6 +81,16 @@ interface LayoutHints {
81
81
  column_guides?: number[] | null;
82
82
  }
83
83
 
84
+ /** Handle for a cancellable process spawned with spawnProcess */
85
+ interface ProcessHandle extends PromiseLike<SpawnResult> {
86
+ /** Promise that resolves to the process ID */
87
+ readonly processId: Promise<number>;
88
+ /** Promise that resolves to the result when the process completes */
89
+ readonly result: Promise<SpawnResult>;
90
+ /** Kill the process. Returns true if killed, false if already completed */
91
+ kill(): Promise<boolean>;
92
+ }
93
+
84
94
  /** Result from spawnProcess */
85
95
  interface SpawnResult {
86
96
  /** Complete stdout as string. Newlines preserved; trailing newline included. */
@@ -626,15 +636,31 @@ interface EditorAPI {
626
636
  */
627
637
  spawnBackgroundProcess(command: string, args: string[], cwd?: string | null): Promise<BackgroundProcessResult>;
628
638
  /**
629
- * Kill a background process by ID
639
+ * Kill a background or cancellable process by ID
630
640
  *
631
641
  * Sends SIGTERM to gracefully terminate the process.
632
642
  * Returns true if the process was found and killed, false if not found.
633
643
  *
634
- * @param process_id - ID returned from spawnBackgroundProcess
644
+ * @param process_id - ID returned from spawnBackgroundProcess or spawnProcessStart
635
645
  * @returns true if process was killed, false if not found
636
646
  */
637
647
  killProcess(#[bigint] process_id: number): Promise<boolean>;
648
+ /**
649
+ * Wait for a cancellable process to complete and get its result
650
+ *
651
+ * @param process_id - ID returned from spawnProcessStart
652
+ * @returns SpawnResult with stdout, stderr, and exit_code
653
+ */
654
+ spawnProcessWait(#[bigint] process_id: number): Promise<SpawnResult>;
655
+ /**
656
+ * Delay execution for a specified number of milliseconds
657
+ *
658
+ * Useful for debouncing user input or adding delays between operations.
659
+ * @param ms - Number of milliseconds to delay
660
+ * @example
661
+ * await editor.delay(100); // Wait 100ms
662
+ */
663
+ delay(#[bigint] ms: number): Promise<[]>;
638
664
  /**
639
665
  * Start a prompt with pre-filled initial value
640
666
  * @param label - Label to display (e.g., "Git grep: ")
@@ -672,24 +698,24 @@ interface EditorAPI {
672
698
  */
673
699
  setBufferCursor(buffer_id: number, position: number): boolean;
674
700
 
675
- // === Async Operations ===
676
701
  /**
677
- * Run an external command and capture its output
702
+ * Spawn an external process and return a cancellable handle
678
703
  *
679
- * Waits for process to complete before returning. For long-running processes,
680
- * consider if this will block your plugin. Output is captured completely;
681
- * very large outputs may use significant memory.
704
+ * Returns a ProcessHandle that can be awaited for the result or killed early.
705
+ * The handle is also a PromiseLike, so `await spawnProcess(...)` works directly.
682
706
  * @param command - Program name (searched in PATH) or absolute path
683
707
  * @param args - Command arguments (each array element is one argument)
684
708
  * @param cwd - Working directory; null uses editor's cwd
685
709
  * @example
686
- * const result = await editor.spawnProcess("git", ["log", "--oneline", "-5"]);
687
- * if (result.exit_code !== 0) {
688
- * editor.setStatus(`git failed: ${result.stderr}`);
689
- * }
710
+ * // Simple usage (backward compatible)
711
+ * const result = await editor.spawnProcess("git", ["status"]);
712
+ *
713
+ * // Cancellable usage
714
+ * const search = editor.spawnProcess("rg", ["pattern"]);
715
+ * // ... later, if user types new query:
716
+ * search.kill(); // Cancel the search
690
717
  */
691
- spawnProcess(command: string, args: string[], cwd?: string | null): Promise<SpawnResult>;
692
-
718
+ spawnProcess(command: string, args?: string[], cwd?: string | null): ProcessHandle;
693
719
  // === Overlay Operations ===
694
720
  /**
695
721
  * Add a colored highlight overlay to text without modifying content
@@ -22,8 +22,12 @@ let previewBufferId: number | null = null;
22
22
  let previewSplitId: number | null = null;
23
23
  let originalSplitId: number | null = null;
24
24
  let lastQuery: string = "";
25
- let searchDebounceTimer: number | null = null;
26
25
  let previewCreated: boolean = false;
26
+ let currentSearch: ProcessHandle | null = null;
27
+ let pendingKill: Promise<boolean> | null = null; // Track pending kill globally
28
+ let searchVersion = 0; // Incremented on each input change for debouncing
29
+
30
+ const DEBOUNCE_MS = 150; // Wait 150ms after last keystroke before searching
27
31
 
28
32
  // Parse ripgrep output line
29
33
  // Format: file:line:column:content
@@ -169,22 +173,62 @@ function closePreview(): void {
169
173
  }
170
174
  }
171
175
 
172
- // Run ripgrep search
176
+ // Run ripgrep search with debouncing
173
177
  async function runSearch(query: string): Promise<void> {
178
+ // Increment version to invalidate any pending debounced search
179
+ const thisVersion = ++searchVersion;
180
+ editor.debug(`[live_grep] runSearch called: query="${query}", version=${thisVersion}`);
181
+
182
+ // Kill any existing search immediately (don't wait) to stop wasting CPU
183
+ // Store the kill promise globally so ALL pending searches wait for it
184
+ if (currentSearch) {
185
+ editor.debug(`[live_grep] killing existing search immediately`);
186
+ pendingKill = currentSearch.kill();
187
+ currentSearch = null;
188
+ }
189
+
174
190
  if (!query || query.trim().length < 2) {
191
+ // Wait for any pending kill to complete before returning
192
+ if (pendingKill) {
193
+ await pendingKill;
194
+ pendingKill = null;
195
+ }
196
+ editor.debug(`[live_grep] query too short, clearing`);
175
197
  editor.setPromptSuggestions([]);
176
198
  grepResults = [];
177
199
  return;
178
200
  }
179
201
 
202
+ // Debounce: wait a bit to see if user is still typing
203
+ editor.debug(`[live_grep] debouncing for ${DEBOUNCE_MS}ms...`);
204
+ await editor.delay(DEBOUNCE_MS);
205
+
206
+ // Always await any pending kill before continuing - ensures old process is dead
207
+ if (pendingKill) {
208
+ editor.debug(`[live_grep] waiting for previous search to terminate`);
209
+ await pendingKill;
210
+ pendingKill = null;
211
+ editor.debug(`[live_grep] previous search terminated`);
212
+ }
213
+
214
+ // If version changed during delay, a newer search was triggered - abort this one
215
+ if (searchVersion !== thisVersion) {
216
+ editor.debug(`[live_grep] version mismatch after debounce (${thisVersion} vs ${searchVersion}), aborting`);
217
+ return;
218
+ }
219
+
180
220
  // Avoid duplicate searches
181
221
  if (query === lastQuery) {
222
+ editor.debug(`[live_grep] duplicate query, skipping`);
182
223
  return;
183
224
  }
184
225
  lastQuery = query;
185
226
 
186
227
  try {
187
- const result = await editor.spawnProcess("rg", [
228
+ const cwd = editor.getCwd();
229
+ editor.debug(`[live_grep] spawning rg for query="${query}" in cwd="${cwd}"`);
230
+ const searchStartTime = Date.now();
231
+ const search = editor.spawnProcess("rg", [
188
232
  "--line-number",
189
233
  "--column",
190
234
  "--no-heading",
@@ -197,10 +241,25 @@ async function runSearch(query: string): Promise<void> {
197
241
  "-g", "!*.lock",
198
242
  "--",
199
243
  query,
200
- ]);
244
+ ], cwd);
245
+
246
+ currentSearch = search;
247
+ editor.debug(`[live_grep] awaiting search result...`);
248
+ const result = await search;
249
+ const searchDuration = Date.now() - searchStartTime;
250
+ editor.debug(`[live_grep] rg completed in ${searchDuration}ms, exit_code=${result.exit_code}, stdout_len=${result.stdout.length}`);
251
+
252
+ // Check if this search was cancelled (a new search started)
253
+ if (currentSearch !== search) {
254
+ editor.debug(`[live_grep] search was superseded, discarding results`);
255
+ return; // Discard stale results
256
+ }
257
+ currentSearch = null;
201
258
 
202
259
  if (result.exit_code === 0) {
260
+ const parseStart = Date.now();
203
261
  const { results, suggestions } = parseRipgrepOutput(result.stdout);
262
+ editor.debug(`[live_grep] parsed ${results.length} results in ${Date.now() - parseStart}ms`);
204
263
  grepResults = results;
205
264
  editor.setPromptSuggestions(suggestions);
206
265
 
@@ -213,14 +272,24 @@ async function runSearch(query: string): Promise<void> {
213
272
  }
214
273
  } else if (result.exit_code === 1) {
215
274
  // No matches
275
+ editor.debug(`[live_grep] no matches (exit_code=1)`);
216
276
  grepResults = [];
217
277
  editor.setPromptSuggestions([]);
218
278
  editor.setStatus("No matches found");
279
+ } else if (result.exit_code === -1) {
280
+ // Process was killed, ignore
281
+ editor.debug(`[live_grep] process was killed`);
219
282
  } else {
283
+ editor.debug(`[live_grep] search error: ${result.stderr}`);
220
284
  editor.setStatus(`Search error: ${result.stderr}`);
221
285
  }
222
286
  } catch (e) {
223
- editor.setStatus(`Search error: ${e}`);
287
+ // Ignore errors from killed processes
288
+ const errorMsg = String(e);
289
+ editor.debug(`[live_grep] caught error: ${errorMsg}`);
290
+ if (!errorMsg.includes("killed") && !errorMsg.includes("not found")) {
291
+ editor.setStatus(`Search error: ${e}`);
292
+ }
224
293
  }
225
294
  }
226
295
 
@@ -248,12 +317,9 @@ globalThis.onLiveGrepPromptChanged = function (args: {
248
317
  return true;
249
318
  }
250
319
 
251
- // Debounce search to avoid too many requests while typing
252
- if (searchDebounceTimer !== null) {
253
- // Can't actually cancel in this runtime, but we track it
254
- }
320
+ editor.debug(`[live_grep] onPromptChanged: input="${args.input}"`);
255
321
 
256
- // Run search (with small delay effect via async)
322
+ // runSearch handles debouncing internally
257
323
  runSearch(args.input);
258
324
 
259
325
  return true;
@@ -286,6 +352,12 @@ globalThis.onLiveGrepPromptConfirmed = function (args: {
286
352
  return true;
287
353
  }
288
354
 
355
+ // Kill any running search
356
+ if (currentSearch) {
357
+ currentSearch.kill();
358
+ currentSearch = null;
359
+ }
360
+
289
361
  // Close preview first
290
362
  closePreview();
291
363
 
@@ -314,6 +386,12 @@ globalThis.onLiveGrepPromptCancelled = function (args: {
314
386
  return true;
315
387
  }
316
388
 
389
+ // Kill any running search
390
+ if (currentSearch) {
391
+ currentSearch.kill();
392
+ currentSearch = null;
393
+ }
394
+
317
395
  // Close preview and cleanup
318
396
  closePreview();
319
397
  grepResults = [];
@@ -185,7 +185,8 @@ async function performSearch(pattern: string, replace: string, isRegex: boolean)
185
185
  args.push("--", pattern);
186
186
 
187
187
  try {
188
- const result = await editor.spawnProcess("git", args);
188
+ const cwd = editor.getCwd();
189
+ const result = await editor.spawnProcess("git", args, cwd);
189
190
 
190
191
  searchResults = [];
191
192
 
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "dracula",
3
+ "editor": {
4
+ "bg": [40, 42, 54],
5
+ "fg": [248, 248, 242],
6
+ "cursor": [255, 121, 198],
7
+ "selection_bg": [68, 71, 90],
8
+ "current_line_bg": [68, 71, 90],
9
+ "line_number_fg": [98, 114, 164],
10
+ "line_number_bg": [40, 42, 54]
11
+ },
12
+ "ui": {
13
+ "tab_active_fg": [248, 248, 242],
14
+ "tab_active_bg": [189, 147, 249],
15
+ "tab_inactive_fg": [248, 248, 242],
16
+ "tab_inactive_bg": [68, 71, 90],
17
+ "tab_separator_bg": [40, 42, 54],
18
+ "status_bar_fg": [40, 42, 54],
19
+ "status_bar_bg": [189, 147, 249],
20
+ "prompt_fg": [40, 42, 54],
21
+ "prompt_bg": [80, 250, 123],
22
+ "prompt_selection_fg": [248, 248, 242],
23
+ "prompt_selection_bg": [189, 147, 249],
24
+ "popup_border_fg": [98, 114, 164],
25
+ "popup_bg": [68, 71, 90],
26
+ "popup_selection_bg": [189, 147, 249],
27
+ "popup_text_fg": [248, 248, 242],
28
+ "suggestion_bg": [68, 71, 90],
29
+ "suggestion_selected_bg": [189, 147, 249],
30
+ "help_bg": [40, 42, 54],
31
+ "help_fg": [248, 248, 242],
32
+ "help_key_fg": [139, 233, 253],
33
+ "help_separator_fg": [98, 114, 164],
34
+ "help_indicator_fg": [255, 85, 85],
35
+ "help_indicator_bg": [40, 42, 54],
36
+ "split_separator_fg": [98, 114, 164]
37
+ },
38
+ "search": {
39
+ "match_bg": [241, 250, 140],
40
+ "match_fg": [40, 42, 54]
41
+ },
42
+ "diagnostic": {
43
+ "error_fg": [255, 85, 85],
44
+ "error_bg": [64, 42, 54],
45
+ "warning_fg": [241, 250, 140],
46
+ "warning_bg": [64, 60, 42],
47
+ "info_fg": [139, 233, 253],
48
+ "info_bg": [40, 56, 70],
49
+ "hint_fg": [98, 114, 164],
50
+ "hint_bg": [40, 42, 54]
51
+ },
52
+ "syntax": {
53
+ "keyword": [255, 121, 198],
54
+ "string": [241, 250, 140],
55
+ "comment": [98, 114, 164],
56
+ "function": [80, 250, 123],
57
+ "type": [139, 233, 253],
58
+ "variable": [248, 248, 242],
59
+ "constant": [189, 147, 249],
60
+ "operator": [255, 121, 198]
61
+ }
62
+ }
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "nord",
3
+ "editor": {
4
+ "bg": [46, 52, 64],
5
+ "fg": [216, 222, 233],
6
+ "cursor": [136, 192, 208],
7
+ "selection_bg": [67, 76, 94],
8
+ "current_line_bg": [59, 66, 82],
9
+ "line_number_fg": [76, 86, 106],
10
+ "line_number_bg": [46, 52, 64]
11
+ },
12
+ "ui": {
13
+ "tab_active_fg": [236, 239, 244],
14
+ "tab_active_bg": [67, 76, 94],
15
+ "tab_inactive_fg": [216, 222, 233],
16
+ "tab_inactive_bg": [59, 66, 82],
17
+ "tab_separator_bg": [46, 52, 64],
18
+ "status_bar_fg": [46, 52, 64],
19
+ "status_bar_bg": [136, 192, 208],
20
+ "prompt_fg": [46, 52, 64],
21
+ "prompt_bg": [163, 190, 140],
22
+ "prompt_selection_fg": [236, 239, 244],
23
+ "prompt_selection_bg": [94, 129, 172],
24
+ "popup_border_fg": [76, 86, 106],
25
+ "popup_bg": [59, 66, 82],
26
+ "popup_selection_bg": [94, 129, 172],
27
+ "popup_text_fg": [216, 222, 233],
28
+ "suggestion_bg": [59, 66, 82],
29
+ "suggestion_selected_bg": [94, 129, 172],
30
+ "help_bg": [46, 52, 64],
31
+ "help_fg": [216, 222, 233],
32
+ "help_key_fg": [136, 192, 208],
33
+ "help_separator_fg": [76, 86, 106],
34
+ "help_indicator_fg": [191, 97, 106],
35
+ "help_indicator_bg": [46, 52, 64],
36
+ "split_separator_fg": [76, 86, 106]
37
+ },
38
+ "search": {
39
+ "match_bg": [235, 203, 139],
40
+ "match_fg": [46, 52, 64]
41
+ },
42
+ "diagnostic": {
43
+ "error_fg": [191, 97, 106],
44
+ "error_bg": [59, 46, 50],
45
+ "warning_fg": [235, 203, 139],
46
+ "warning_bg": [59, 56, 46],
47
+ "info_fg": [129, 161, 193],
48
+ "info_bg": [46, 52, 64],
49
+ "hint_fg": [76, 86, 106],
50
+ "hint_bg": [46, 52, 64]
51
+ },
52
+ "syntax": {
53
+ "keyword": [129, 161, 193],
54
+ "string": [163, 190, 140],
55
+ "comment": [76, 86, 106],
56
+ "function": [136, 192, 208],
57
+ "type": [143, 188, 187],
58
+ "variable": [216, 222, 233],
59
+ "constant": [180, 142, 173],
60
+ "operator": [129, 161, 193]
61
+ }
62
+ }
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "solarized-dark",
3
+ "editor": {
4
+ "bg": [0, 43, 54],
5
+ "fg": [131, 148, 150],
6
+ "cursor": [38, 139, 210],
7
+ "selection_bg": [7, 54, 66],
8
+ "current_line_bg": [7, 54, 66],
9
+ "line_number_fg": [88, 110, 117],
10
+ "line_number_bg": [0, 43, 54]
11
+ },
12
+ "ui": {
13
+ "tab_active_fg": [253, 246, 227],
14
+ "tab_active_bg": [38, 139, 210],
15
+ "tab_inactive_fg": [131, 148, 150],
16
+ "tab_inactive_bg": [7, 54, 66],
17
+ "tab_separator_bg": [0, 43, 54],
18
+ "status_bar_fg": [0, 43, 54],
19
+ "status_bar_bg": [147, 161, 161],
20
+ "prompt_fg": [0, 43, 54],
21
+ "prompt_bg": [181, 137, 0],
22
+ "prompt_selection_fg": [253, 246, 227],
23
+ "prompt_selection_bg": [38, 139, 210],
24
+ "popup_border_fg": [88, 110, 117],
25
+ "popup_bg": [7, 54, 66],
26
+ "popup_selection_bg": [38, 139, 210],
27
+ "popup_text_fg": [131, 148, 150],
28
+ "suggestion_bg": [7, 54, 66],
29
+ "suggestion_selected_bg": [38, 139, 210],
30
+ "help_bg": [0, 43, 54],
31
+ "help_fg": [131, 148, 150],
32
+ "help_key_fg": [42, 161, 152],
33
+ "help_separator_fg": [88, 110, 117],
34
+ "help_indicator_fg": [220, 50, 47],
35
+ "help_indicator_bg": [0, 43, 54],
36
+ "split_separator_fg": [88, 110, 117]
37
+ },
38
+ "search": {
39
+ "match_bg": [181, 137, 0],
40
+ "match_fg": [253, 246, 227]
41
+ },
42
+ "diagnostic": {
43
+ "error_fg": [220, 50, 47],
44
+ "error_bg": [42, 43, 54],
45
+ "warning_fg": [181, 137, 0],
46
+ "warning_bg": [30, 54, 54],
47
+ "info_fg": [38, 139, 210],
48
+ "info_bg": [0, 50, 66],
49
+ "hint_fg": [88, 110, 117],
50
+ "hint_bg": [0, 43, 54]
51
+ },
52
+ "syntax": {
53
+ "keyword": [133, 153, 0],
54
+ "string": [42, 161, 152],
55
+ "comment": [88, 110, 117],
56
+ "function": [38, 139, 210],
57
+ "type": [181, 137, 0],
58
+ "variable": [131, 148, 150],
59
+ "constant": [203, 75, 22],
60
+ "operator": [131, 148, 150]
61
+ }
62
+ }