zedx 0.9.0 → 0.10.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
@@ -1,6 +1,10 @@
1
- # zedx
1
+ <div align="center">
2
2
 
3
- Scaffold [Zed Editor](https://zed.dev/) extensions and sync your settings across machines.
3
+ <img src="./assets/zedx-logo.png" width="300" alt="ZedX Logo" />
4
+
5
+ CLI toolkit for Scaffolding [Zed Editor](https://zed.dev/) extensions and syncing settings across machines.
6
+
7
+ </div>
4
8
 
5
9
  ![screenshot](./assets/screenshot1.png)
6
10
 
@@ -13,7 +17,9 @@ npm install -g zedx
13
17
  brew install tahayvr/tap/zedx
14
18
  ```
15
19
 
16
- ### Usage
20
+ ## Usage
21
+
22
+ ### Scaffolding an extension
17
23
 
18
24
  ```bash
19
25
  # Create a new extension
@@ -22,28 +28,77 @@ zedx create
22
28
  # Add a theme or language to an existing extension
23
29
  zedx add theme "Midnight Blue"
24
30
  zedx add language rust
31
+ ```
32
+
33
+ ### Supported extension types:
34
+
35
+ 1. **Themes** - Color schemes for the editor
36
+ 2. **Languages** - Syntax highlighting, indentation, and optional LSP support
37
+
38
+ You can choose to include theme, language, or both when creating an extension.
39
+
40
+ ### Validation
25
41
 
42
+ ```bash
26
43
  # Validate extension config and show what is missing or incomplete
27
44
  zedx check
45
+ ```
46
+
47
+ ### Sync
48
+
49
+ Sync your Zed config across machines using a private GitHub repo as the source of truth.
50
+
51
+ **1. Link a repo (one-time setup)**
52
+
53
+ ```bash
54
+ zedx sync init
55
+ ```
56
+
57
+ Prompts for a GitHub repo URL (SSH or HTTPS) and a branch name (defaults to `main`). The repo is saved to `~/.config/zedx/config.json`. No files are synced yet.
58
+
59
+ > [!NOTE]
60
+ > `settings.json` and `keymap.json` are tracked. Extension sync is handled via the `auto_install_extensions` field within `settings.json`, which Zed uses to automatically download and install extensions.
61
+
62
+ **2. Run a sync**
28
63
 
29
- # Bump extension version
64
+ ```bash
65
+ zedx sync # Sync local ↔ remote, prompts when both sides changed
66
+ zedx sync --local # Always keep local on conflict (no prompt)
67
+ zedx sync --remote # Always use remote on conflict (no prompt)
68
+ ```
69
+
70
+ **3. Check sync state**
71
+
72
+ ```bash
73
+ zedx sync status
74
+ ```
75
+
76
+ **4. Auto-sync with an OS daemon**
77
+
78
+ ```bash
79
+ zedx sync install # Install and enable the daemon
80
+ zedx sync uninstall # Disable and remove the daemon
81
+ ```
82
+
83
+ Installs a file-watcher that triggers `zedx sync` automatically whenever config files are saved. Supported platforms:
84
+
85
+ | Platform | Mechanism | Logs |
86
+ | -------- | ------------------------------------------------------ | ---------------------------------------- |
87
+ | macOS | launchd (`~/Library/LaunchAgents/dev.zedx.sync.plist`) | `~/Library/Logs/zedx-sync.log` |
88
+ | Linux | systemd user units (`~/.config/systemd/user/`) | `journalctl --user -u zedx-sync.service` |
89
+
90
+ The daemon enforces a 30-second throttle on macOS to avoid rapid re-triggers. When a conflict is detected in daemon mode (no TTY), local always wins and a warning is logged.
91
+
92
+ ### Versioning
93
+
94
+ Bump the extension version:
95
+
96
+ ```bash
30
97
  zedx version patch # 1.2.3 → 1.2.4
31
98
  zedx version minor # 1.2.3 → 1.3.0
32
99
  zedx version major # 1.2.3 → 2.0.0
33
-
34
- # Sync Zed settings and extensions via a GitHub repo
35
- zedx sync init # Link a GitHub repo as the sync target (run once)
36
- zedx sync # Sync local and remote config (prompts on conflict)
37
- zedx sync --local # Sync, always keeping local on conflict
38
- zedx sync --remote # Sync, always using remote on conflict
39
- zedx sync status # Show sync state between local config and the remote repo
40
- zedx sync install # Install an OS daemon to auto-sync when Zed config changes
41
- zedx sync uninstall # Remove the OS daemon
42
100
  ```
43
101
 
44
- ### Supported extension types:
102
+ ### License
45
103
 
46
- 1. **Themes** - Color schemes for the editor
47
- 2. **Languages** - Syntax highlighting, indentation, and optional LSP support
48
-
49
- You can choose to include theme, language, or both when creating an extension.
104
+ License is Apache-2.0. See [LICENSE](./LICENSE) for details.
package/dist/daemon.js CHANGED
@@ -157,7 +157,7 @@ export async function syncInstall() {
157
157
  if (platform !== 'darwin' && platform !== 'linux')
158
158
  unsupportedPlatform();
159
159
  const zedPaths = resolveZedPaths();
160
- const watchPaths = [zedPaths.settings, zedPaths.extensions];
160
+ const watchPaths = [zedPaths.settings, zedPaths.keymap];
161
161
  const zedxBin = resolveZedxBinary();
162
162
  p.log.info(`Binary: ${color.dim(zedxBin)}`);
163
163
  p.log.info(`Watching:`);
package/dist/sync.js CHANGED
@@ -37,76 +37,66 @@ async function withTempDir(fn) {
37
37
  await fs.remove(tmp);
38
38
  }
39
39
  }
40
- // Prepare settings.json for pushing to the repo by stripping auto_install_extensions.
41
- // That field is derived from extensions/index.json on pull, so storing it in the
42
- // remote would create stale/conflicting data across machines.
43
- async function prepareSettingsForPush(localSettingsPath, repoSettingsPath) {
44
- const raw = await fs.readFile(localSettingsPath, 'utf-8');
45
- const stripped = raw.replace(/\/\/[^\n]*/g, '');
46
- let settingsObj = {};
47
- try {
48
- settingsObj = JSON.parse(stripped);
49
- }
50
- catch {
51
- // If we can't parse it (e.g. complex comments), push as-is
52
- await fs.ensureDir(path.dirname(repoSettingsPath));
53
- await fs.copy(localSettingsPath, repoSettingsPath, { overwrite: true });
40
+ // Before every push, reconcile auto_install_extensions in settings.json against
41
+ // the live extensions/index.json so that:
42
+ // - newly installed extensions (present in index, missing from the list) are added as true
43
+ // - uninstalled extensions (absent from index, set to true in the list) are removed
44
+ // - entries explicitly set to false by the user are always preserved (user intent)
45
+ async function reconcileAutoInstallExtensions(localSettingsPath, localExtensionsIndexPath, silent = false) {
46
+ if (!(await fs.pathExists(localExtensionsIndexPath)))
54
47
  return;
55
- }
56
- delete settingsObj['auto_install_extensions'];
57
- await fs.ensureDir(path.dirname(repoSettingsPath));
58
- await fs.writeFile(repoSettingsPath, JSON.stringify(settingsObj, null, 4), 'utf-8');
59
- }
60
- // Extension merge helper
61
- async function applyRemoteSettings(repoSettings, repoExtensions, localSettingsPath, silent = false) {
62
- // Backup existing settings
48
+ let settingsObj = {};
63
49
  if (await fs.pathExists(localSettingsPath)) {
64
- await fs.copy(localSettingsPath, localSettingsPath + '.bak', { overwrite: true });
65
- if (!silent)
66
- p.log.info(`Backed up settings to ${color.dim(localSettingsPath + '.bak')}`);
67
- }
68
- let settingsJson = await fs.readFile(repoSettings, 'utf-8');
69
- // Merge auto_install_extensions from index.json into settings
70
- if (await fs.pathExists(repoExtensions)) {
71
50
  try {
72
- const indexJson = (await fs.readJson(repoExtensions));
73
- const extensionIds = Object.keys(indexJson.extensions ?? {}).filter(id => !indexJson.extensions[id]?.dev);
74
- if (extensionIds.length > 0) {
75
- const stripped = settingsJson.replace(/\/\/[^\n]*/g, '');
76
- let settingsObj = {};
77
- try {
78
- settingsObj = JSON.parse(stripped);
79
- }
80
- catch {
81
- if (!silent)
82
- p.log.warn(color.yellow('Could not parse settings.json — skipping extension merge.'));
83
- }
84
- // Preserve any existing entries (e.g. false entries for "never install"),
85
- // then add true for every extension recorded in index.json.
86
- const existing = typeof settingsObj['auto_install_extensions'] === 'object' &&
87
- settingsObj['auto_install_extensions'] !== null
88
- ? settingsObj['auto_install_extensions']
89
- : {};
90
- const autoInstall = { ...existing };
91
- for (const id of extensionIds) {
92
- // Only set to true if there is no explicit user preference already
93
- if (!(id in autoInstall)) {
94
- autoInstall[id] = true;
95
- }
96
- }
97
- settingsObj['auto_install_extensions'] = autoInstall;
98
- settingsJson = JSON.stringify(settingsObj, null, 4);
99
- if (!silent)
100
- p.log.info(`Injected ${color.cyan(String(extensionIds.length))} extension(s) into ${color.dim('auto_install_extensions')}`);
101
- }
51
+ const raw = await fs.readFile(localSettingsPath, 'utf-8');
52
+ settingsObj = JSON.parse(raw.replace(/\/\/[^\n]*/g, ''));
102
53
  }
103
54
  catch {
104
55
  if (!silent)
105
- p.log.warn(color.yellow('Could not parse extensions/index.json — skipping extension merge.'));
56
+ p.log.warn(color.yellow('Could not parse settings.json — skipping extension reconciliation.'));
57
+ return;
106
58
  }
107
59
  }
60
+ let installedIds = [];
61
+ try {
62
+ const indexJson = (await fs.readJson(localExtensionsIndexPath));
63
+ installedIds = Object.keys(indexJson.extensions ?? {}).filter(id => !indexJson.extensions[id]?.dev);
64
+ }
65
+ catch {
66
+ if (!silent)
67
+ p.log.warn(color.yellow('Could not parse extensions/index.json — skipping extension reconciliation.'));
68
+ return;
69
+ }
70
+ const existing = typeof settingsObj['auto_install_extensions'] === 'object' &&
71
+ settingsObj['auto_install_extensions'] !== null
72
+ ? settingsObj['auto_install_extensions']
73
+ : {};
74
+ const installedSet = new Set(installedIds);
75
+ const reconciled = {};
76
+ // Keep all explicit false entries (user said "never install this")
77
+ for (const [id, val] of Object.entries(existing)) {
78
+ if (val === false)
79
+ reconciled[id] = false;
80
+ }
81
+ // Add every currently installed extension as true
82
+ for (const id of installedIds) {
83
+ reconciled[id] = true;
84
+ }
85
+ // Drop true entries for extensions no longer installed
86
+ // (already handled — we only re-add what's in installedSet above)
87
+ const added = installedIds.filter(id => !(id in existing));
88
+ const removed = Object.keys(existing).filter(id => existing[id] === true && !installedSet.has(id));
89
+ if (added.length === 0 && removed.length === 0)
90
+ return;
91
+ settingsObj['auto_install_extensions'] = reconciled;
108
92
  await fs.ensureDir(path.dirname(localSettingsPath));
109
- await fs.writeFile(localSettingsPath, settingsJson, 'utf-8');
93
+ await fs.writeFile(localSettingsPath, JSON.stringify(settingsObj, null, 4), 'utf-8');
94
+ if (!silent) {
95
+ if (added.length > 0)
96
+ p.log.info(`Added ${color.cyan(String(added.length))} new extension(s) to ${color.dim('auto_install_extensions')}: ${added.join(', ')}`);
97
+ if (removed.length > 0)
98
+ p.log.info(`Removed ${color.cyan(String(removed.length))} uninstalled extension(s) from ${color.dim('auto_install_extensions')}: ${removed.join(', ')}`);
99
+ }
110
100
  }
111
101
  // zedx sync status
112
102
  export async function syncStatus() {
@@ -138,14 +128,18 @@ export async function syncStatus() {
138
128
  {
139
129
  repoPath: path.join(tmp, 'settings.json'),
140
130
  localPath: zedPaths.settings,
141
- label: 'settings.json',
131
+ label: 'Settings',
142
132
  },
143
133
  {
144
- repoPath: path.join(tmp, 'extensions', 'index.json'),
145
- localPath: zedPaths.extensions,
146
- label: 'extensions/index.json',
134
+ repoPath: path.join(tmp, 'keymap.json'),
135
+ localPath: zedPaths.keymap,
136
+ label: 'Key bindings',
147
137
  },
148
138
  ];
139
+ // Track per-file actions needed for the outro message
140
+ const toPush = [];
141
+ const toPull = [];
142
+ const toResolve = [];
149
143
  for (const file of files) {
150
144
  const localExists = await fs.pathExists(file.localPath);
151
145
  const remoteFileExists = remoteExists && (await fs.pathExists(file.repoPath));
@@ -155,10 +149,12 @@ export async function syncStatus() {
155
149
  }
156
150
  if (localExists && !remoteFileExists) {
157
151
  p.log.warn(`${color.bold(file.label)}: ${color.green('local only')} — not pushed yet`);
152
+ toPush.push(file.label);
158
153
  continue;
159
154
  }
160
155
  if (!localExists && remoteFileExists) {
161
156
  p.log.warn(`${color.bold(file.label)}: ${color.cyan('remote only')} — not pulled yet`);
157
+ toPull.push(file.label);
162
158
  continue;
163
159
  }
164
160
  const localContent = await fs.readFile(file.localPath, 'utf-8');
@@ -174,16 +170,34 @@ export async function syncStatus() {
174
170
  const remoteChanged = !lastSync || remoteMtime > lastSync;
175
171
  if (localChanged && !remoteChanged) {
176
172
  p.log.warn(`${color.bold(file.label)}: ${color.green('local ahead')} — modified ${color.dim(localMtime.toLocaleString())}`);
173
+ toPush.push(file.label);
177
174
  }
178
175
  else if (remoteChanged && !localChanged) {
179
176
  p.log.warn(`${color.bold(file.label)}: ${color.cyan('remote ahead')} — modified ${color.dim(remoteMtime.toLocaleString())}`);
177
+ toPull.push(file.label);
180
178
  }
181
179
  else {
182
180
  p.log.warn(`${color.bold(file.label)}: ${color.yellow('conflict')} — both changed since last sync`);
181
+ toResolve.push(file.label);
182
+ }
183
+ }
184
+ const singleAction = [toPush, toPull, toResolve].filter(a => a.length > 0).length === 1;
185
+ const needsSync = toPush.length > 0 || toPull.length > 0 || toResolve.length > 0;
186
+ if (needsSync) {
187
+ if (singleAction && toPush.length > 0) {
188
+ p.outro(`Run ${color.cyan('zedx sync')} to push ${toPush.map(l => color.bold(l)).join(', ')} to remote.`);
183
189
  }
190
+ else if (singleAction && toPull.length > 0) {
191
+ p.outro(`Run ${color.cyan('zedx sync')} to pull ${toPull.map(l => color.bold(l)).join(', ')} from remote.`);
192
+ }
193
+ else {
194
+ p.outro(`Run ${color.cyan('zedx sync')} to resolve.`);
195
+ }
196
+ }
197
+ else {
198
+ p.outro('Everything is in sync.');
184
199
  }
185
200
  });
186
- p.outro(`Run ${color.cyan('zedx sync')} to resolve.`);
187
201
  }
188
202
  // zedx sync init
189
203
  export async function syncInit() {
@@ -239,13 +253,13 @@ export async function syncSelect() {
239
253
  const allFiles = [
240
254
  {
241
255
  value: 'settings',
242
- label: 'settings.json',
243
- hint: 'Zed editor settings',
256
+ label: 'Settings',
257
+ hint: 'settings.json',
244
258
  },
245
259
  {
246
- value: 'extensions',
247
- label: 'extensions/index.json',
248
- hint: 'Installed extensions list',
260
+ value: 'keymap',
261
+ label: 'Key bindings',
262
+ hint: 'keymap.json',
249
263
  },
250
264
  ];
251
265
  const selected = await p.multiselect({
@@ -283,7 +297,7 @@ export async function runSync(opts = {}) {
283
297
  };
284
298
  if (!silent) {
285
299
  console.log('');
286
- p.intro(`${color.bgBlue(color.bold(' zedx sync '))} ${color.blue('Syncing Zed settings and extensions…')}`);
300
+ p.intro(`${color.bgBlue(color.bold(' zedx sync '))} ${color.blue('Syncing Zed config…')}`);
287
301
  }
288
302
  const config = await requireSyncConfig();
289
303
  const zedPaths = resolveZedPaths();
@@ -316,13 +330,13 @@ export async function runSync(opts = {}) {
316
330
  key: 'settings',
317
331
  repoPath: path.join(tmp, 'settings.json'),
318
332
  localPath: zedPaths.settings,
319
- label: 'settings.json',
333
+ label: 'Settings',
320
334
  },
321
335
  {
322
- key: 'extensions',
323
- repoPath: path.join(tmp, 'extensions', 'index.json'),
324
- localPath: zedPaths.extensions,
325
- label: 'extensions/index.json',
336
+ key: 'keymap',
337
+ repoPath: path.join(tmp, 'keymap.json'),
338
+ localPath: zedPaths.keymap,
339
+ label: 'Key bindings',
326
340
  },
327
341
  ];
328
342
  const files = selectedFiles
@@ -337,29 +351,27 @@ export async function runSync(opts = {}) {
337
351
  log.warn(`${file.label}: not found locally or remotely — skipping.`);
338
352
  continue;
339
353
  }
340
- // Remote doesn't have it yet — push local
354
+ // Remote doesn't have it yet — first push.
355
+ // Bootstrap auto_install_extensions from local extensions/index.json so
356
+ // the synced settings.json is self-contained on a fresh machine.
341
357
  if (localExists && !remoteFileExists) {
342
358
  log.info(`${file.label}: ${color.green('pushing')} (not in remote yet)`);
343
- if (file.label === 'settings.json') {
344
- await prepareSettingsForPush(file.localPath, file.repoPath);
345
- }
346
- else {
347
- await fs.ensureDir(path.dirname(file.repoPath));
348
- await fs.copy(file.localPath, file.repoPath, { overwrite: true });
349
- }
359
+ await reconcileAutoInstallExtensions(file.localPath, zedPaths.extensionsIndex, silent);
360
+ await fs.ensureDir(path.dirname(file.repoPath));
361
+ await fs.copy(file.localPath, file.repoPath, { overwrite: true });
350
362
  anyChanges = true;
351
363
  continue;
352
364
  }
353
365
  // Local doesn't have it — pull remote
354
366
  if (!localExists && remoteFileExists) {
355
367
  log.info(`${file.label}: ${color.cyan('pulling')} (not found locally)`);
356
- if (file.label === 'settings.json') {
357
- await applyRemoteSettings(file.repoPath, path.join(tmp, 'extensions', 'index.json'), file.localPath, silent);
358
- }
359
- else {
360
- await fs.ensureDir(path.dirname(file.localPath));
361
- await fs.copy(file.repoPath, file.localPath, { overwrite: true });
368
+ if (await fs.pathExists(file.localPath)) {
369
+ await fs.copy(file.localPath, file.localPath + '.bak', { overwrite: true });
370
+ if (!silent)
371
+ p.log.info(`Backed up settings to ${color.dim(file.localPath + '.bak')}`);
362
372
  }
373
+ await fs.ensureDir(path.dirname(file.localPath));
374
+ await fs.copy(file.repoPath, file.localPath, { overwrite: true });
363
375
  continue;
364
376
  }
365
377
  // Both exist — compare content
@@ -377,27 +389,20 @@ export async function runSync(opts = {}) {
377
389
  const localChanged = !lastSync || localMtime > lastSync;
378
390
  const remoteChanged = !lastSync || remoteMtime > lastSync;
379
391
  if (localChanged && !remoteChanged) {
380
- // Only local changed → push
392
+ // Only local changed → reconcile extensions then push
381
393
  log.info(`${file.label}: ${color.green('pushing')} (local is newer)`);
382
- if (file.label === 'settings.json') {
383
- await prepareSettingsForPush(file.localPath, file.repoPath);
384
- }
385
- else {
386
- await fs.ensureDir(path.dirname(file.repoPath));
387
- await fs.copy(file.localPath, file.repoPath, { overwrite: true });
388
- }
394
+ await reconcileAutoInstallExtensions(file.localPath, zedPaths.extensionsIndex, silent);
395
+ await fs.ensureDir(path.dirname(file.repoPath));
396
+ await fs.copy(file.localPath, file.repoPath, { overwrite: true });
389
397
  anyChanges = true;
390
398
  }
391
399
  else if (remoteChanged && !localChanged) {
392
400
  // Only remote changed → pull
393
401
  log.info(`${file.label}: ${color.cyan('pulling')} (remote is newer)`);
394
- if (file.label === 'settings.json') {
395
- await applyRemoteSettings(file.repoPath, path.join(tmp, 'extensions', 'index.json'), file.localPath, silent);
396
- }
397
- else {
398
- await fs.ensureDir(path.dirname(file.localPath));
399
- await fs.copy(file.repoPath, file.localPath, { overwrite: true });
400
- }
402
+ await fs.copy(file.localPath, file.localPath + '.bak', { overwrite: true });
403
+ if (!silent)
404
+ p.log.info(`Backed up settings to ${color.dim(file.localPath + '.bak')}`);
405
+ await fs.copy(file.repoPath, file.localPath, { overwrite: true });
401
406
  }
402
407
  else {
403
408
  // Both changed — resolve based on strategy
@@ -441,32 +446,25 @@ export async function runSync(opts = {}) {
441
446
  if (resolution === 'local') {
442
447
  if (!silent && conflict === 'prompt')
443
448
  p.log.info(`${file.label}: ${color.green('keeping local, will push')}`);
444
- if (file.label === 'settings.json') {
445
- await prepareSettingsForPush(file.localPath, file.repoPath);
446
- }
447
- else {
448
- await fs.ensureDir(path.dirname(file.repoPath));
449
- await fs.copy(file.localPath, file.repoPath, { overwrite: true });
450
- }
449
+ await reconcileAutoInstallExtensions(file.localPath, zedPaths.extensionsIndex, silent);
450
+ await fs.ensureDir(path.dirname(file.repoPath));
451
+ await fs.copy(file.localPath, file.repoPath, { overwrite: true });
451
452
  anyChanges = true;
452
453
  }
453
454
  else {
454
455
  if (!silent && conflict === 'prompt')
455
456
  p.log.info(`${file.label}: ${color.cyan('applying remote')}`);
456
- if (file.label === 'settings.json') {
457
- await applyRemoteSettings(file.repoPath, path.join(tmp, 'extensions', 'index.json'), file.localPath, silent);
458
- }
459
- else {
460
- await fs.ensureDir(path.dirname(file.localPath));
461
- await fs.copy(file.repoPath, file.localPath, { overwrite: true });
462
- }
457
+ await fs.copy(file.localPath, file.localPath + '.bak', { overwrite: true });
458
+ if (!silent)
459
+ p.log.info(`Backed up settings to ${color.dim(file.localPath + '.bak')}`);
460
+ await fs.copy(file.repoPath, file.localPath, { overwrite: true });
463
461
  }
464
462
  }
465
463
  }
466
464
  // 3. Commit + push if any local files were written to the repo
467
465
  if (anyChanges) {
468
466
  spinner.start('Pushing changes to remote...');
469
- await git.add(['settings.json', path.join('extensions', 'index.json')]);
467
+ await git.add(files.map(f => path.basename(f.repoPath)));
470
468
  const status = await git.status();
471
469
  if (status.staged.length > 0) {
472
470
  const timestamp = new Date().toISOString();
@@ -1,7 +1,8 @@
1
1
  export type ExtensionType = 'theme' | 'language';
2
2
  export interface ZedPaths {
3
3
  settings: string;
4
- extensions: string;
4
+ keymap: string;
5
+ extensionsIndex: string;
5
6
  }
6
7
  export interface SyncConfig {
7
8
  syncRepo: string;
package/dist/zed-paths.js CHANGED
@@ -4,26 +4,32 @@ export function resolveZedPaths() {
4
4
  const home = os.homedir();
5
5
  const platform = process.platform;
6
6
  if (platform === 'darwin') {
7
+ const configDir = path.join(home, '.config', 'zed');
7
8
  return {
8
- settings: path.join(home, '.config', 'zed', 'settings.json'),
9
- extensions: path.join(home, 'Library', 'Application Support', 'Zed', 'extensions', 'index.json'),
9
+ settings: path.join(configDir, 'settings.json'),
10
+ keymap: path.join(configDir, 'keymap.json'),
11
+ extensionsIndex: path.join(home, 'Library', 'Application Support', 'Zed', 'extensions', 'index.json'),
10
12
  };
11
13
  }
12
14
  if (platform === 'linux') {
13
15
  const xdgData = process.env.FLATPAK_XDG_DATA_HOME ||
14
16
  process.env.XDG_DATA_HOME ||
15
17
  path.join(home, '.local', 'share');
18
+ const configDir = path.join(home, '.config', 'zed');
16
19
  return {
17
- settings: path.join(home, '.config', 'zed', 'settings.json'),
18
- extensions: path.join(xdgData, 'zed', 'extensions', 'index.json'),
20
+ settings: path.join(configDir, 'settings.json'),
21
+ keymap: path.join(configDir, 'keymap.json'),
22
+ extensionsIndex: path.join(xdgData, 'zed', 'extensions', 'index.json'),
19
23
  };
20
24
  }
21
25
  if (platform === 'win32') {
22
26
  const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
23
27
  const localAppData = process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local');
28
+ const configDir = path.join(appData, 'Zed');
24
29
  return {
25
- settings: path.join(appData, 'Zed', 'settings.json'),
26
- extensions: path.join(localAppData, 'Zed', 'extensions', 'index.json'),
30
+ settings: path.join(configDir, 'settings.json'),
31
+ keymap: path.join(configDir, 'keymap.json'),
32
+ extensionsIndex: path.join(localAppData, 'Zed', 'extensions', 'index.json'),
27
33
  };
28
34
  }
29
35
  throw new Error(`Unsupported platform: ${platform}`);
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "zedx",
3
- "version": "0.9.0",
4
- "description": "Scaffold Zed Editor extensions and sync your settings across machines.",
3
+ "version": "0.10.0",
4
+ "description": "The CLI toolkit for Zed Editor.",
5
5
  "keywords": [
6
6
  "boilerplate",
7
7
  "extension",
8
8
  "scaffold",
9
+ "settings",
10
+ "sync",
9
11
  "zed",
10
12
  "zed-editor"
11
13
  ],