@bzenky/spoti 0.3.0 → 0.5.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.
Files changed (47) hide show
  1. package/README.md +68 -6
  2. package/dist/app.d.ts +10 -2
  3. package/dist/app.js +207 -82
  4. package/dist/app.js.map +1 -1
  5. package/dist/cli.js +4 -2
  6. package/dist/cli.js.map +1 -1
  7. package/dist/services/device.service.d.ts +3 -3
  8. package/dist/services/device.service.js +19 -11
  9. package/dist/services/device.service.js.map +1 -1
  10. package/dist/services/player.service.d.ts +4 -2
  11. package/dist/services/player.service.js +35 -5
  12. package/dist/services/player.service.js.map +1 -1
  13. package/dist/services/queue.service.js +11 -6
  14. package/dist/services/queue.service.js.map +1 -1
  15. package/dist/services/search.service.d.ts +4 -4
  16. package/dist/services/search.service.js +10 -9
  17. package/dist/services/search.service.js.map +1 -1
  18. package/dist/spotify/client.d.ts +2 -0
  19. package/dist/spotify/client.js +170 -46
  20. package/dist/spotify/client.js.map +1 -1
  21. package/dist/spotify/types.d.ts +2 -0
  22. package/dist/storage/config.d.ts +3 -0
  23. package/dist/storage/config.js +3 -0
  24. package/dist/storage/config.js.map +1 -1
  25. package/dist/ui/completions.d.ts +3 -0
  26. package/dist/ui/completions.js +237 -0
  27. package/dist/ui/completions.js.map +1 -0
  28. package/dist/ui/interactive-search.d.ts +36 -0
  29. package/dist/ui/interactive-search.js +337 -0
  30. package/dist/ui/interactive-search.js.map +1 -0
  31. package/dist/ui/output.d.ts +27 -11
  32. package/dist/ui/output.js +87 -45
  33. package/dist/ui/output.js.map +1 -1
  34. package/dist/ui/progress.d.ts +6 -0
  35. package/dist/ui/progress.js +25 -0
  36. package/dist/ui/progress.js.map +1 -0
  37. package/dist/ui/prompts.d.ts +11 -1
  38. package/dist/ui/prompts.js +65 -13
  39. package/dist/ui/prompts.js.map +1 -1
  40. package/dist/ui/watch.js +3 -2
  41. package/dist/ui/watch.js.map +1 -1
  42. package/dist/utils/errors.js +2 -1
  43. package/dist/utils/errors.js.map +1 -1
  44. package/dist/utils/text.d.ts +2 -0
  45. package/dist/utils/text.js +10 -0
  46. package/dist/utils/text.js.map +1 -0
  47. package/package.json +2 -1
@@ -0,0 +1,6 @@
1
+ export type ProgressRunner = <Result>(label: string, task: () => Promise<Result>) => Promise<Result>;
2
+ export interface ProgressTerminal {
3
+ readonly isTTY: boolean;
4
+ write(value: string): void;
5
+ }
6
+ export declare function withProgress<Result>(label: string, task: () => Promise<Result>, terminal?: ProgressTerminal): Promise<Result>;
@@ -0,0 +1,25 @@
1
+ import { stderr } from 'node:process';
2
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
3
+ const defaultTerminal = {
4
+ isTTY: Boolean(stderr.isTTY),
5
+ write: (value) => stderr.write(value),
6
+ };
7
+ export async function withProgress(label, task, terminal = defaultTerminal) {
8
+ if (!terminal.isTTY)
9
+ return task();
10
+ let frameIndex = 0;
11
+ terminal.write(`${frames[frameIndex]} ${label}`);
12
+ const timer = setInterval(() => {
13
+ frameIndex = (frameIndex + 1) % frames.length;
14
+ terminal.write(`\r${frames[frameIndex]} ${label}`);
15
+ }, 80);
16
+ timer.unref();
17
+ try {
18
+ return await task();
19
+ }
20
+ finally {
21
+ clearInterval(timer);
22
+ terminal.write('\r\u001B[2K');
23
+ }
24
+ }
25
+ //# sourceMappingURL=progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/ui/progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAYtC,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAU,CAAC;AAE3E,MAAM,eAAe,GAAqB;IACxC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;IAC5B,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;CACtC,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAa,EACb,IAA2B,EAC3B,WAA6B,eAAe;IAE5C,IAAI,CAAC,QAAQ,CAAC,KAAK;QAAE,OAAO,IAAI,EAAE,CAAC;IAEnC,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,QAAQ,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,UAAU,GAAG,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;QAC9C,QAAQ,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;IACrD,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,KAAK,CAAC,KAAK,EAAE,CAAC;IAEd,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,EAAE,CAAC;IACtB,CAAC;YAAS,CAAC;QACT,aAAa,CAAC,KAAK,CAAC,CAAC;QACrB,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAChC,CAAC;AACH,CAAC"}
@@ -1,8 +1,11 @@
1
- import type { Album, Artist, Playlist, Track } from '../services/models.js';
1
+ import type { Album, Artist, Playlist, RecentlyPlayedTrack, SavedTrack, Track } from '../services/models.js';
2
2
  export declare function selectTrack(tracks: Track[]): Promise<Track | null>;
3
3
  export declare function selectAlbum(albums: Album[]): Promise<Album | null>;
4
4
  export declare function selectArtist(artists: Artist[]): Promise<Artist | null>;
5
5
  export declare function selectPlaylist(playlists: Playlist[]): Promise<Playlist | null>;
6
+ export declare function selectRecentTrack(items: RecentlyPlayedTrack[]): Promise<Track | null>;
7
+ export declare function selectLikedTrack(items: SavedTrack[]): Promise<Track | null>;
8
+ export declare function selectListedPlaylist(playlists: Playlist[]): Promise<Playlist | null>;
6
9
  export type AlbumAction = 'play-album' | 'play-track';
7
10
  export type ArtistAction = 'play-artist' | 'select-album';
8
11
  export type PlaylistAction = 'play-playlist';
@@ -11,3 +14,10 @@ export declare function selectArtistAction(): Promise<ArtistAction | null>;
11
14
  export declare function selectPlaylistAction(): Promise<PlaylistAction | null>;
12
15
  export declare function promptSpotifyClientId(): Promise<string | null>;
13
16
  export declare function confirmUpdate(currentVersion: string, latestVersion: string): Promise<boolean>;
17
+ export type SelectionInput = {
18
+ status: 'cancelled' | 'invalid';
19
+ } | {
20
+ status: 'selected';
21
+ index: number;
22
+ };
23
+ export declare function parseSelectionInput(value: string, itemCount: number): SelectionInput;
@@ -1,17 +1,39 @@
1
1
  import { createInterface } from 'node:readline/promises';
2
2
  import { stdin, stdout } from 'node:process';
3
- import { formatAlbum, formatArtist, formatPlaylist, formatTrack } from './output.js';
3
+ import { createOutputStyles, formatAlbum, formatArtist, formatPlaylist, formatTrack, } from './output.js';
4
4
  export async function selectTrack(tracks) {
5
- return selectItem(tracks, formatTrack);
5
+ const styles = createOutputStyles(Boolean(stdout.isTTY), process.env);
6
+ return selectItem(tracks, (track, index) => formatTrack(track, index, styles));
6
7
  }
7
8
  export async function selectAlbum(albums) {
8
- return selectItem(albums, formatAlbum);
9
+ const styles = createOutputStyles(Boolean(stdout.isTTY), process.env);
10
+ return selectItem(albums, (album, index) => formatAlbum(album, index, styles));
9
11
  }
10
12
  export async function selectArtist(artists) {
11
- return selectItem(artists, formatArtist);
13
+ const styles = createOutputStyles(Boolean(stdout.isTTY), process.env);
14
+ return selectItem(artists, (artist, index) => formatArtist(artist, index, styles));
12
15
  }
13
16
  export async function selectPlaylist(playlists) {
14
- return selectItem(playlists, formatPlaylist);
17
+ const styles = createOutputStyles(Boolean(stdout.isTTY), process.env);
18
+ return selectItem(playlists, (playlist, index) => formatPlaylist(playlist, index, styles));
19
+ }
20
+ export async function selectRecentTrack(items) {
21
+ return selectDisplayedTrack(items.map((item) => item.track), 'recent');
22
+ }
23
+ export async function selectLikedTrack(items) {
24
+ return selectDisplayedTrack(items.map((item) => item.track), 'liked');
25
+ }
26
+ export async function selectListedPlaylist(playlists) {
27
+ if (playlists.length === 0 || !stdin.isTTY || !stdout.isTTY)
28
+ return null;
29
+ const prompt = createInterface({ input: stdin, output: stdout });
30
+ try {
31
+ const index = await promptForSelection(prompt, `\nPlay a playlist [1-${playlists.length}] (Enter to keep current playback): `, playlists.length);
32
+ return index === null ? null : (playlists[index] ?? null);
33
+ }
34
+ finally {
35
+ prompt.close();
36
+ }
15
37
  }
16
38
  export async function selectAlbumAction() {
17
39
  return selectAction([
@@ -60,9 +82,20 @@ async function selectAction(actions) {
60
82
  .join('\n')}\n${actions.length + 1}. Nothing\n\n`);
61
83
  const prompt = createInterface({ input: stdin, output: stdout });
62
84
  try {
63
- const answer = await prompt.question(`Select [1-${actions.length + 1}]: `);
64
- const index = Number.parseInt(answer, 10) - 1;
65
- return actions[index]?.value ?? null;
85
+ const index = await promptForSelection(prompt, `Select [1-${actions.length + 1}]: `, actions.length + 1);
86
+ return index === null ? null : (actions[index]?.value ?? null);
87
+ }
88
+ finally {
89
+ prompt.close();
90
+ }
91
+ }
92
+ async function selectDisplayedTrack(tracks, source) {
93
+ if (tracks.length === 0 || !stdin.isTTY || !stdout.isTTY)
94
+ return null;
95
+ const prompt = createInterface({ input: stdin, output: stdout });
96
+ try {
97
+ const index = await promptForSelection(prompt, `\nPlay a ${source} track [1-${tracks.length}] (Enter to keep current playback): `, tracks.length);
98
+ return index === null ? null : (tracks[index] ?? null);
66
99
  }
67
100
  finally {
68
101
  prompt.close();
@@ -76,14 +109,33 @@ async function selectItem(items, format) {
76
109
  stdout.write(`${items.map((item, index) => format(item, index)).join('\n')}\n\n`);
77
110
  const prompt = createInterface({ input: stdin, output: stdout });
78
111
  try {
79
- const answer = await prompt.question(`Select [1-${items.length}] (Enter to cancel): `);
80
- if (!answer.trim())
81
- return null;
82
- const index = Number.parseInt(answer, 10) - 1;
83
- return items[index] ?? null;
112
+ const index = await promptForSelection(prompt, `Select [1-${items.length}] (Enter to cancel): `, items.length);
113
+ return index === null ? null : (items[index] ?? null);
84
114
  }
85
115
  finally {
86
116
  prompt.close();
87
117
  }
88
118
  }
119
+ export function parseSelectionInput(value, itemCount) {
120
+ const normalized = value.trim();
121
+ if (!normalized)
122
+ return { status: 'cancelled' };
123
+ if (!/^\d+$/.test(normalized))
124
+ return { status: 'invalid' };
125
+ const index = Number(normalized) - 1;
126
+ if (!Number.isSafeInteger(index) || index < 0 || index >= itemCount) {
127
+ return { status: 'invalid' };
128
+ }
129
+ return { status: 'selected', index };
130
+ }
131
+ async function promptForSelection(prompt, question, itemCount) {
132
+ while (true) {
133
+ const result = parseSelectionInput(await prompt.question(question), itemCount);
134
+ if (result.status === 'cancelled')
135
+ return null;
136
+ if (result.status === 'selected')
137
+ return result.index;
138
+ stdout.write(`Selection must be a number between 1 and ${itemCount}.\n`);
139
+ }
140
+ }
89
141
  //# sourceMappingURL=prompts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/ui/prompts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/ui/prompts.ts"],"names":[],"mappings":"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"}
package/dist/ui/watch.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { stdin, stdout } from 'node:process';
2
2
  import { AppError } from '../utils/errors.js';
3
- import { formatPlayback } from './output.js';
3
+ import { createOutputStyles, formatPlayback } from './output.js';
4
4
  export const watchPlayback = async ({ player, refreshIntervalMs }) => {
5
5
  if (!stdin.isTTY || !stdout.isTTY) {
6
6
  throw new AppError('Watch mode requires an interactive terminal.');
7
7
  }
8
+ const styles = createOutputStyles(true, process.env);
8
9
  let stopped = false;
9
10
  let resolveStop = () => undefined;
10
11
  const stopPromise = new Promise((resolve) => {
@@ -20,7 +21,7 @@ export const watchPlayback = async ({ player, refreshIntervalMs }) => {
20
21
  while (!stopped) {
21
22
  const playback = await player.getCurrentPlayback();
22
23
  const content = playback
23
- ? formatPlayback(playback)
24
+ ? formatPlayback(playback, styles)
24
25
  : 'Nothing is currently playing.';
25
26
  stdout.write(`\u001B[2J\u001B[H${content}\n\nPress Ctrl+C to exit`);
26
27
  if (!stopped)
@@ -1 +1 @@
1
- {"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/ui/watch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG7C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAS7C,MAAM,CAAC,MAAM,aAAa,GAAoB,KAAK,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,EAAE,EAAE;IACpF,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClC,MAAM,IAAI,QAAQ,CAAC,8CAA8C,CAAC,CAAC;IACrE,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,WAAW,GAAe,GAAG,EAAE,CAAC,SAAS,CAAC;IAC9C,MAAM,WAAW,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAChD,WAAW,GAAG,OAAO,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,GAAS,EAAE;QACtB,OAAO,GAAG,IAAI,CAAC;QACf,WAAW,EAAE,CAAC;IAChB,CAAC,CAAC;IAEF,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC7B,MAAM,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAEzC,IAAI,CAAC;QACH,OAAO,CAAC,OAAO,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,kBAAkB,EAAE,CAAC;YACnD,MAAM,OAAO,GAAG,QAAQ;gBACtB,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC;gBAC1B,CAAC,CAAC,+BAA+B,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,oBAAoB,OAAO,0BAA0B,CAAC,CAAC;YACpE,IAAI,CAAC,OAAO;gBAAE,MAAM,cAAc,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC,CAAC;AAEF,KAAK,UAAU,cAAc,CAAC,YAAoB,EAAE,WAA0B;IAC5E,IAAI,KAAiC,CAAC;IACtC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACjD,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC;IAChD,IAAI,KAAK;QAAE,YAAY,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC"}
1
+ {"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/ui/watch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG7C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AASjE,MAAM,CAAC,MAAM,aAAa,GAAoB,KAAK,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,EAAE,EAAE;IACpF,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClC,MAAM,IAAI,QAAQ,CAAC,8CAA8C,CAAC,CAAC;IACrE,CAAC;IAED,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IACrD,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,WAAW,GAAe,GAAG,EAAE,CAAC,SAAS,CAAC;IAC9C,MAAM,WAAW,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAChD,WAAW,GAAG,OAAO,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,GAAS,EAAE;QACtB,OAAO,GAAG,IAAI,CAAC;QACf,WAAW,EAAE,CAAC;IAChB,CAAC,CAAC;IAEF,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC7B,MAAM,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAEzC,IAAI,CAAC;QACH,OAAO,CAAC,OAAO,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,kBAAkB,EAAE,CAAC;YACnD,MAAM,OAAO,GAAG,QAAQ;gBACtB,CAAC,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAClC,CAAC,CAAC,+BAA+B,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,oBAAoB,OAAO,0BAA0B,CAAC,CAAC;YACpE,IAAI,CAAC,OAAO;gBAAE,MAAM,cAAc,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC,CAAC;AAEF,KAAK,UAAU,cAAc,CAAC,YAAoB,EAAE,WAA0B;IAC5E,IAAI,KAAiC,CAAC;IACtC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACjD,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC;IAChD,IAAI,KAAK;QAAE,YAAY,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC"}
@@ -28,7 +28,8 @@ export class PremiumRequiredError extends AppError {
28
28
  export class RateLimitedError extends AppError {
29
29
  retryAfterSeconds;
30
30
  constructor(retryAfterSeconds) {
31
- super(`Spotify rate limit reached. Try again in ${retryAfterSeconds} seconds.`);
31
+ const unit = retryAfterSeconds === 1 ? 'second' : 'seconds';
32
+ super(`Spotify rate limit reached. Try again in ${retryAfterSeconds} ${unit}.`);
32
33
  this.retryAfterSeconds = retryAfterSeconds;
33
34
  }
34
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IAGf;IAFlB,YACE,OAAe,EACC,WAAW,CAAC;QAE5B,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,aAAQ,GAAR,QAAQ,CAAI;QAG5B,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,OAAO,kBAAmB,SAAQ,QAAQ;CAAG;AAEnD,MAAM,OAAO,2BAA4B,SAAQ,QAAQ;IACvD,YAAY,OAAO,GAAG,4CAA4C;QAChE,KAAK,CAAC,OAAO,CAAC,CAAC;IACjB,CAAC;CACF;AAED,MAAM,OAAO,wBAAyB,SAAQ,QAAQ;CAAG;AAEzD,MAAM,OAAO,mBAAoB,SAAQ,QAAQ;IAC/C;QACE,KAAK,CACH,uFAAuF,CACxF,CAAC;IACJ,CAAC;CACF;AAED,MAAM,OAAO,oBAAqB,SAAQ,QAAQ;IAChD;QACE,KAAK,CAAC,mDAAmD,CAAC,CAAC;IAC7D,CAAC;CACF;AAED,MAAM,OAAO,gBAAiB,SAAQ,QAAQ;IAChB;IAA5B,YAA4B,iBAAyB;QACnD,KAAK,CAAC,4CAA4C,iBAAiB,WAAW,CAAC,CAAC;QADtD,sBAAiB,GAAjB,iBAAiB,CAAQ;IAErD,CAAC;CACF;AAED,MAAM,OAAO,eAAgB,SAAQ,QAAQ;IAGzB;IAFlB,YACE,OAAe,EACC,MAAc;QAE9B,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,WAAM,GAAN,MAAM,CAAQ;IAGhC,CAAC;CACF;AAED,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AACnE,CAAC"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IAGf;IAFlB,YACE,OAAe,EACC,WAAW,CAAC;QAE5B,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,aAAQ,GAAR,QAAQ,CAAI;QAG5B,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,OAAO,kBAAmB,SAAQ,QAAQ;CAAG;AAEnD,MAAM,OAAO,2BAA4B,SAAQ,QAAQ;IACvD,YAAY,OAAO,GAAG,4CAA4C;QAChE,KAAK,CAAC,OAAO,CAAC,CAAC;IACjB,CAAC;CACF;AAED,MAAM,OAAO,wBAAyB,SAAQ,QAAQ;CAAG;AAEzD,MAAM,OAAO,mBAAoB,SAAQ,QAAQ;IAC/C;QACE,KAAK,CACH,uFAAuF,CACxF,CAAC;IACJ,CAAC;CACF;AAED,MAAM,OAAO,oBAAqB,SAAQ,QAAQ;IAChD;QACE,KAAK,CAAC,mDAAmD,CAAC,CAAC;IAC7D,CAAC;CACF;AAED,MAAM,OAAO,gBAAiB,SAAQ,QAAQ;IAChB;IAA5B,YAA4B,iBAAyB;QACnD,MAAM,IAAI,GAAG,iBAAiB,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5D,KAAK,CAAC,4CAA4C,iBAAiB,IAAI,IAAI,GAAG,CAAC,CAAC;QAFtD,sBAAiB,GAAjB,iBAAiB,CAAQ;IAGrD,CAAC;CACF;AAED,MAAM,OAAO,eAAgB,SAAQ,QAAQ;IAGzB;IAFlB,YACE,OAAe,EACC,MAAc;QAE9B,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,WAAM,GAAN,MAAM,CAAQ;IAGhC,CAAC;CACF;AAED,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AACnE,CAAC"}
@@ -0,0 +1,2 @@
1
+ /** Normalize external or user-provided text before placing it on one terminal line. */
2
+ export declare function sanitizeOneLineText(value: string): string;
@@ -0,0 +1,10 @@
1
+ import { stripVTControlCharacters } from 'node:util';
2
+ /** Normalize external or user-provided text before placing it on one terminal line. */
3
+ export function sanitizeOneLineText(value) {
4
+ const withoutControls = Array.from(stripVTControlCharacters(value), (character) => {
5
+ const codePoint = character.codePointAt(0) ?? 0;
6
+ return codePoint <= 31 || (codePoint >= 127 && codePoint <= 159) ? ' ' : character;
7
+ }).join('');
8
+ return withoutControls.replace(/\s+/g, ' ').trim();
9
+ }
10
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/utils/text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAErD,uFAAuF;AACvF,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,MAAM,eAAe,GAAG,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,EAAE;QAChF,MAAM,SAAS,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAChD,OAAO,SAAS,IAAI,EAAE,IAAI,CAAC,SAAS,IAAI,GAAG,IAAI,SAAS,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IACrF,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACZ,OAAO,eAAe,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACrD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bzenky/spoti",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "A local-first command-line client for controlling Spotify",
5
5
  "type": "module",
6
6
  "bin": {
@@ -46,6 +46,7 @@
46
46
  "dependencies": {
47
47
  "commander": "^15.0.0",
48
48
  "open": "^11.0.2",
49
+ "picocolors": "^1.1.1",
49
50
  "zod": "^4.5.4"
50
51
  },
51
52
  "devDependencies": {