Skip to content

Commit cbc4c8a

Browse files
Merge branch 'raycast:main' into ext/beeminder
2 parents d7e88f0 + d952187 commit cbc4c8a

29 files changed

+420
-34
lines changed

.github/CODEOWNERS

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,7 +1556,7 @@
15561556
/extensions/sportssync @daniyalmaster693
15571557
/extensions/spotify-beta @mattisssa @peduarte @sxn @dillionverma @andreaselia @stuart @tonka3000 @dancannon
15581558
/extensions/spotify-controls @thomaspaulmann @altrdev @ron-myers @sandypockets @Kenan7 @dmacdermott @pernielsentikaer @yansq @amureki
1559-
/extensions/spotify-player @mattisssa @peduarte @sxn @dillionverma @andreaselia @stuart @tonka3000 @dancannon @pernielsentikaer @stevensd2m @erics118 @hjoelh @hobhouse @jatindotdev @the-devbear @rfaccio @badta5te @andyburris @thomaslombart @rhesamu @mpatel283 @LitoMore @EnneEmme
1559+
/extensions/spotify-player @mattisssa @peduarte @sxn @dillionverma @andreaselia @stuart @tonka3000 @dancannon @pernielsentikaer @stevensd2m @erics118 @hjoelh @hobhouse @jatindotdev @the-devbear @rfaccio @badta5te @andyburris @thomaslombart @rhesamu @mpatel283 @LitoMore @EnneEmme @lineville @tsibog @viethung0823
15601560
/extensions/spring-initializr @danvega
15611561
/extensions/spryker-docs @DinisEsteves
15621562
/extensions/sql-format @tonngw @jfkisafk
@@ -1692,8 +1692,8 @@
16921692
/extensions/tny @stayallive @stayallive
16931693
/extensions/to-streamshare @0PandaDEV @bkuzmanoski
16941694
/extensions/todo-list @maggie-j-liu @joshkorol @madza91 @bkeys818 @pernielsentikaer @telmen @ridemountainpig
1695-
/extensions/todoist @thomaslombart @AnishDe12020 @kud @casassg @Princeyadav05 @jfkisafk @ridemountainpig @kcole93 @RobErskine
1696-
/extensions/toggl-track @franzwilhelm @bkeys818 @michaelfaisst @teziovsky @lukebars @tangerine1202
1695+
/extensions/todoist @thomaslombart @AnishDe12020 @kud @casassg @Princeyadav05 @jfkisafk @ridemountainpig @kcole93 @RobErskine @jagadeesh-k-2802
1696+
/extensions/toggl-track @franzwilhelm @bkeys818 @michaelfaisst @teziovsky @lukebars @tangerine1202 @AlexIsMaking
16971697
/extensions/toggle-desktop-visibility @Haojen @VatsalSy
16981698
/extensions/toggle-fn @elonwoo @pernielsentikaer
16991699
/extensions/toggle-grayscale @zcsabbagh

.github/raycast2github.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1838,5 +1838,9 @@
18381838
"dziad": "tborychowski",
18391839
"mattstone": "nandastone",
18401840
"britown": "bkuzmanoski",
1841-
"joeynotjoe_2": "joeynotjoe"
1841+
"joeynotjoe_2": "joeynotjoe",
1842+
"lineville": "lineville",
1843+
"jagadeesh_k": "jagadeesh-k-2802",
1844+
"tsibog": "tsibog",
1845+
"viethung0823": "viethung0823"
18421846
}

docs/.config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"version": "1.91.2"
2+
"version": "1.92.1"
33
}

docs/examples/doppler.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@ function ShareSecretAction() {
8484

8585
await Clipboard.copy((body as any).authenticated_url);
8686

87-
toast.style = Feedback.Toast.Style.Success;
87+
toast.style = Toast.Style.Success;
8888
toast.title = "Shared secret";
8989
toast.message = "Copied link to clipboard";
9090
} catch (error) {
91-
toast.style = Feedback.Toast.Style.Failure;
91+
toast.style = Toast.Style.Failure;
9292
toast.title = "Failed sharing secret";
9393
toast.message = String(error);
9494
}

extensions/asana/CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Asana Changelog
22

3-
## [Added Copy Task Formatted URL command] - 2024-03-27
3+
## [Add option to hide the "Start Date" field] - 2025-02-12
4+
5+
- Add a preference to allow the user to hide the `Start Date` field in the `Create Task` command
6+
7+
## [Add "Copy Task Formatted URL" action] - 2024-03-27
48

59
- Added a new task command - "Copy Task Formatted URL"
610

@@ -18,7 +22,7 @@
1822

1923
- Remove Raycast signature preference from the `Create Task` command
2024

21-
## [Bugfix] - 2022-11-24
25+
## [Fix a bug] - 2022-11-24
2226

2327
Fixed an issue where the Asana extension would timeout when querying for projects in workspaces with a lot of projects.
2428

extensions/asana/package.json

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,26 @@
1111
],
1212
"owner": "raycast",
1313
"access": "public",
14-
"categories": ["Productivity"],
14+
"categories": [
15+
"Productivity"
16+
],
1517
"license": "MIT",
1618
"commands": [
1719
{
1820
"name": "create-task",
1921
"title": "Create Task",
2022
"description": "Create and assign new tasks.",
21-
"mode": "view"
23+
"mode": "view",
24+
"preferences": [
25+
{
26+
"name": "showStartDate",
27+
"type": "checkbox",
28+
"label": "Show the \"Start Date\" field",
29+
"description": "When enabled, the \"Start Date\" field will be shown in the \"Create Task\" command.",
30+
"required": false,
31+
"default": true
32+
}
33+
]
2234
},
2335
{
2436
"name": "my-tasks",

extensions/asana/src/components/CreateTaskForm.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
import { Action, ActionPanel, Clipboard, Form, Icon, useNavigation, Toast, showToast } from "@raycast/api";
1+
import {
2+
Action,
3+
ActionPanel,
4+
Clipboard,
5+
Form,
6+
Icon,
7+
useNavigation,
8+
Toast,
9+
showToast,
10+
getPreferenceValues,
11+
} from "@raycast/api";
212
import { format } from "date-fns";
313
import { FormValidation, getAvatarIcon, useCachedState, useForm } from "@raycast/utils";
414
import { useMemo, useEffect } from "react";
@@ -122,6 +132,7 @@ export default function CreateTaskForm(props: {
122132

123133
const hasCustomFields = customFields && customFields.length > 0;
124134
const selectedWorkspace = workspaces?.find((workspace) => values.workspace === workspace.gid);
135+
const { showStartDate } = getPreferenceValues<Preferences.CreateTask>();
125136

126137
return (
127138
<Form
@@ -172,7 +183,7 @@ export default function CreateTaskForm(props: {
172183
);
173184
})}
174185
</Form.Dropdown>
175-
{selectedWorkspace?.is_organization ? (
186+
{selectedWorkspace?.is_organization && showStartDate ? (
176187
<Form.DatePicker title="Start Date" type={Form.DatePicker.Type.Date} {...itemProps.start_date} />
177188
) : null}
178189
<Form.DatePicker title="Due Date" type={Form.DatePicker.Type.Date} {...itemProps.due_date} />

extensions/spotify-player/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Spotify Player Changelog
22

3+
## [Fix Missing Playlists in Add Playing Song to Playlist command] - 2025-02-20
4+
5+
- Fixed an issue where some playlists were not appearing when users attempted to add a currently playing song to a playlist.
6+
7+
## [Add Copy Embed Code Command] - 2025-02-20
8+
9+
- Added a new command to copy the iframe embed code for the currently playing song.
10+
11+
## [Add "Skip 15 Seconds" and "Back 15 Seconds" commands] - 2025-02-18
12+
13+
- Added the ability to skip forward or back 15 seconds in the current episode. This adds two new commands as well as two new menu bar items which only show when an 'episode' is playing.
14+
315
## [Artist Name Visibility Option] - 2025-02-07
416

517
- Added the option to hide the artist's name in the Menu Bar Player.

extensions/spotify-player/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,14 @@ Use this to quickly start playing a song based on your query.
106106

107107
Use this to get a playlist generated by Raycast AI based on a prompt. You can then add this playlist to Spotify or queue all the songs directly.
108108

109+
### Skip 15 Seconds
110+
111+
Use this to skip ahead 15 seconds.
112+
113+
### Back 15 Seconds
114+
115+
Use this to go back 15 seconds.
116+
109117
---
110118

111119
In order to use this extension, you'll need to authenticate with Spotify. This extension requires the following permissions [scopes](https://developer.spotify.com/documentation/web-api/concepts/scopes):

extensions/spotify-player/package.json

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@
3131
"rhesamu",
3232
"themitpatel",
3333
"litomore",
34-
"enneemme"
34+
"enneemme",
35+
"lineville",
36+
"tsibog",
37+
"viethung0823"
3538
],
3639
"pastContributors": [
3740
"bkeys818"
@@ -443,6 +446,30 @@
443446
"description": "Start the DJ.",
444447
"mode": "no-view",
445448
"disabledByDefault": true
449+
},
450+
{
451+
"name": "copyEmbed",
452+
"title": "Copy Embed Code",
453+
"subtitle": "Spotify",
454+
"description": "Copy the iframe embed code for the currently playing song or podcast episode.",
455+
"mode": "no-view",
456+
"disabledByDefault": true
457+
},
458+
{
459+
"name": "skip15",
460+
"title": "Skip 15 Seconds",
461+
"subtitle": "Spotify",
462+
"description": "Skip 15 seconds forward in an episode.",
463+
"mode": "no-view",
464+
"disabledByDefault": true
465+
},
466+
{
467+
"name": "back15",
468+
"title": "Back 15 Seconds",
469+
"subtitle": "Spotify",
470+
"description": "Back 15 seconds in an episode.",
471+
"mode": "no-view",
472+
"disabledByDefault": true
446473
}
447474
],
448475
"dependencies": {

extensions/spotify-player/src/api/getMyPlaylists.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
11
import { getErrorMessage } from "../helpers/getError";
22
import { getSpotifyClient } from "../helpers/withSpotifyClient";
33

4-
type GetMySavedAlbumsProps = { limit?: number };
4+
type GetUserPlaylistsProps = { limit?: number };
55

6-
export async function getMyPlaylists({ limit = 50 }: GetMySavedAlbumsProps = {}) {
6+
export async function getMyPlaylists({ limit = 50 }: GetUserPlaylistsProps = {}) {
77
const { spotifyClient } = getSpotifyClient();
8+
let response = null;
9+
let nextUrl = null;
810

911
try {
10-
const response = await spotifyClient.getMePlaylists({ limit });
12+
response = await spotifyClient.getMePlaylists({ limit });
13+
nextUrl = response?.next;
14+
15+
while (nextUrl) {
16+
const nextResponse = await spotifyClient.getNext(nextUrl);
17+
response = {
18+
...response,
19+
...nextResponse,
20+
items: [...(response?.items ?? []), ...(nextResponse.items ?? [])],
21+
};
22+
nextUrl = nextResponse?.next;
23+
}
24+
1125
return response;
1226
} catch (err) {
1327
const error = getErrorMessage(err);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { showHUD } from "@raycast/api";
2+
import { setSpotifyClient } from "./helpers/withSpotifyClient";
3+
import { getCurrentlyPlaying } from "./api/getCurrentlyPlaying";
4+
import { safeLaunchCommandInBackground } from "./helpers/safeCommandLauncher";
5+
import { seek } from "./api/seek";
6+
7+
export default async function Command() {
8+
await setSpotifyClient();
9+
10+
const currentlyPlayingData = await getCurrentlyPlaying();
11+
const nothingIsPlaying = !currentlyPlayingData || !currentlyPlayingData.item;
12+
13+
if (nothingIsPlaying) {
14+
return await showHUD("Nothing is currently playing");
15+
}
16+
17+
try {
18+
const currentPositionSeconds = (currentlyPlayingData?.progress_ms || 0) / 1000;
19+
await seek(Math.max(currentPositionSeconds - 15, 0));
20+
await showHUD("Skipped back 15 seconds");
21+
await safeLaunchCommandInBackground("current-track");
22+
} catch (error) {
23+
await showHUD("Nothing is currently playing");
24+
}
25+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Clipboard, showHUD } from "@raycast/api";
2+
import { setSpotifyClient } from "./helpers/withSpotifyClient";
3+
import { getCurrentlyPlaying } from "./api/getCurrentlyPlaying";
4+
5+
export default async function Command() {
6+
await setSpotifyClient();
7+
8+
const currentlyPlayingData = await getCurrentlyPlaying();
9+
const nothingIsPlaying = !currentlyPlayingData || !currentlyPlayingData.item;
10+
11+
if (nothingIsPlaying) {
12+
return await showHUD("Nothing is currently playing");
13+
}
14+
15+
const external_urls = currentlyPlayingData.item.external_urls;
16+
const spotifyUrl = external_urls?.spotify;
17+
18+
const embedUrl = spotifyUrl?.replace("open.spotify.com/", "open.spotify.com/embed/");
19+
20+
const embedCode = `<iframe style="border-radius:12px" src="${embedUrl}?utm_source=generator" width="100%" height="352" frameBorder="0" allowfullscreen="" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>`;
21+
22+
await Clipboard.copy(embedCode);
23+
return showHUD("Copied embed code to clipboard");
24+
}

extensions/spotify-player/src/helpers/spotify.api.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5082,3 +5082,41 @@ export function getMeTopTracks(
50825082
),
50835083
);
50845084
}
5085+
/**
5086+
* Get next page
5087+
*
5088+
*/
5089+
export function getNext(
5090+
nextUrl: string,
5091+
opts?: Oazapfts.RequestOpts,
5092+
) {
5093+
const url = nextUrl.replace(servers.server1, "");
5094+
return oazapfts.ok(
5095+
oazapfts.fetchJson<
5096+
| {
5097+
status: 200;
5098+
data: PagingPlaylistObject;
5099+
}
5100+
| {
5101+
status: 401;
5102+
data: {
5103+
error: ErrorObject;
5104+
};
5105+
}
5106+
| {
5107+
status: 403;
5108+
data: {
5109+
error: ErrorObject;
5110+
};
5111+
}
5112+
| {
5113+
status: 429;
5114+
data: {
5115+
error: ErrorObject;
5116+
};
5117+
}
5118+
>(url, {
5119+
...opts,
5120+
}),
5121+
);
5122+
}

extensions/spotify-player/src/nowPlayingMenuBar.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { formatTitle } from "./helpers/formatTitle";
3535
import { getErrorMessage } from "./helpers/getError";
3636

3737
import { useSpotifyAppData } from "./hooks/useSpotifyAppData";
38+
import { seek } from "./api/seek";
3839

3940
function NowPlayingMenuBarCommand({ launchType }: LaunchProps) {
4041
const { hideArtistName, maxTextLength, iconType } = getPreferenceValues<Preferences.NowPlayingMenuBar>();
@@ -190,6 +191,39 @@ function NowPlayingMenuBarCommand({ launchType }: LaunchProps) {
190191
const showName = show.name;
191192
title = formatTitle({ name, artistName: showName, hideArtistName, maxTextLength });
192193
coverImageUrl = show.images.slice(-1)[0]?.url || "";
194+
195+
menuItems = (
196+
<>
197+
<MenuBarExtra.Item
198+
icon={Icon.RotateClockwise}
199+
title="Skip 15 seconds"
200+
onAction={async () => {
201+
try {
202+
const currentPositionSeconds = (currentlyPlayingData?.progress_ms || 0) / 1000;
203+
await seek(currentPositionSeconds + 15);
204+
await currentlyPlayingRevalidate();
205+
} catch (err) {
206+
const error = getErrorMessage(err);
207+
showHUD(error);
208+
}
209+
}}
210+
/>
211+
<MenuBarExtra.Item
212+
icon={Icon.RotateAntiClockwise}
213+
title="Back 15 seconds"
214+
onAction={async () => {
215+
try {
216+
const currentPositionSeconds = (currentlyPlayingData?.progress_ms || 0) / 1000;
217+
await seek(currentPositionSeconds - 15);
218+
await currentlyPlayingRevalidate();
219+
} catch (err) {
220+
const error = getErrorMessage(err);
221+
showHUD(error);
222+
}
223+
}}
224+
/>
225+
</>
226+
);
193227
}
194228

195229
return (

0 commit comments

Comments
 (0)