chore(App): bump bs-platform + use rescript (#421)

This commit is contained in:
DEMARET Arnaut
2022-05-27 11:46:03 +02:00
committed by GitHub
parent f306b2f8ff
commit 750e6accff
40 changed files with 917 additions and 1010 deletions
+1 -1
View File
@@ -5,6 +5,6 @@
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"jaredly.reason-vscode"
"chenglou92.rescript-vscode"
]
}
+6 -3
View File
@@ -17,6 +17,9 @@
],
"suffix": ".bs.js",
"namespace": true,
"bs-dependencies": ["reason-react", "bs-css"],
"refmt": 3
}
"bs-dependencies": [
"@rescript/react",
"bs-css",
"bs-css-emotion"
]
}
+7 -6
View File
@@ -18,9 +18,9 @@
"lint:fix:prettier": "prettier --write '**/*.{json,jsx,tsx,mjs,js,ts}'",
"lint:es": "eslint '**/*.{js,jsx,mjs,ts,tsx}'",
"lint:fix:es": "eslint --fix '**/*.{js,jsx,mjs,ts,tsx}'",
"bsb": "bsb -make-world",
"bsb:watch": "bsb -make-world -w",
"bsb:clean": "bsb -clean-world",
"res": "rescript build",
"res:watch": "rescript build -w",
"res:clean": "rescript clean",
"dist": "yarn install && yarn build && electron-builder build --win --linux --mac --ia32 --x64",
"dist:win": "yarn install && yarn build && electron-builder build --win --x64",
"dist:linux": "yarn install && yarn build && electron-builder build --linux --x64",
@@ -131,6 +131,7 @@
"devDependencies": {
"@babel/core": "7.13.13",
"@babel/preset-env": "^7.13.12",
"@rescript/react": "0.10.3",
"@svgr/webpack": "5.5.0",
"acorn-dynamic-import": "^4.0.0",
"babel-eslint": "10.1.0",
@@ -138,8 +139,8 @@
"babel-loader": "8.2.2",
"babel-plugin-named-asset-import": "^0.3.7",
"babel-preset-react-app": "^10.0.0",
"bs-css": "^11.0.0",
"bs-platform": "8.4.2",
"bs-css": "^15.3.0",
"bs-css-emotion": "^4.3.0",
"camelcase": "^5.2.0",
"case-sensitive-paths-webpack-plugin": "2.3.0",
"conventional-changelog-cli": "^2.1.1",
@@ -170,7 +171,7 @@
"pnp-webpack-plugin": "1.2.1",
"prettier": "^1.18.2",
"react-dev-utils": "^9.0.1",
"reason-react": "^0.7.1",
"rescript": "9.1.4",
"resolve": "1.17.0",
"semver": "6.0.0",
"string-replace-loader": "^2.3.0",
+4 -4
View File
@@ -51,15 +51,15 @@ const config = configFactory('production');
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
.then(() => new Promise((resolve, reject) => {
const bsb = exec('npm run bsb',
const res = exec('npm run res',
(err) => {
if (err) {
console.log("Couldn't build reason using bsb");
console.log("Couldn't build rescript using res");
reject(err)
}
});
bsb.stdout.pipe(process.stdout)
bsb.on('exit', resolve)
res.stdout.pipe(process.stdout)
res.on('exit', resolve)
}))
.then(() => {
// First, read the current file sizes in build directory.
+4 -4
View File
@@ -64,15 +64,15 @@ if (process.env.HOST) {
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
.then(() => new Promise((resolve, reject) => {
const bsb = exec('npm run bsb',
const res = exec('npm run res',
(err) => {
if (err) {
console.log("Couldn't build reason using bsb");
console.log("Couldn't build rescript using rescript");
reject(err)
}
});
bsb.stdout.pipe(process.stdout)
bsb.on('exit', resolve)
res.stdout.pipe(process.stdout)
res.on('exit', resolve)
}))
.then(() => {
// We attempt to use the default port but if it is busy, we offer the user to
@@ -7,43 +7,36 @@ type playingState = {
muted: bool,
hasNext: bool,
hasPrev: bool,
};
}
type song = {
id: int,
artist: string,
title: string,
};
}
type playlistItem = {
id: int,
path: string,
title: string,
artist: string,
};
}
type playlist = array(playlistItem);
type playlist = array<playlistItem>
type value = {
playingState,
playlist,
playingState: playingState,
playlist: playlist,
playlistID: string,
setPlaylist:
(~beatmapPlaylist: playlist, ~playlistID: string=?, unit) => unit,
setAudio:
(
~song: song,
~audioFilePath: option(string),
~previewOffset: option(int)
) =>
unit,
setPlaylist: (~beatmapPlaylist: playlist, ~playlistID: string=?, unit) => unit,
setAudio: (~song: song, ~audioFilePath: option<string>, ~previewOffset: option<int>) => unit,
setVolume: float => unit,
pause: unit => unit,
togglePlayPause: unit => unit,
setMuted: bool => unit,
playNext: unit => unit,
playPrevious: unit => unit,
};
}
let initialState: playingState = {
artist: "",
@@ -54,36 +47,35 @@ let initialState: playingState = {
muted: false,
hasNext: false,
hasPrev: false,
};
}
module Provider = {
let value = {
playingState: initialState,
playlist: [||],
playlist: [],
playlistID: "",
setPlaylist: (~beatmapPlaylist: playlist, ~playlistID=?, unit) => unit,
setAudio:
(
~song: song,
~audioFilePath: option(string),
~previewOffset: option(int),
) =>
(),
setVolume: (vol: float) => (),
setPlaylist: (~beatmapPlaylist as _: playlist, ~playlistID as _=?, unit) => unit,
setAudio: (
~song as _: song,
~audioFilePath as _: option<string>,
~previewOffset as _: option<int>,
) => (),
setVolume: (_vol: float) => (),
pause: () => (),
togglePlayPause: () => (),
setMuted: (muted: bool) => (),
setMuted: (_muted: bool) => (),
playNext: () => (),
playPrevious: () => (),
};
let audioPlayerContext = React.createContext(value);
}
let audioPlayerContext = React.createContext(value)
let makeProps = (~value, ~children, ()) => {
"value": value,
"children": children,
};
let makeProps = (~value, ~children, ()) =>
{
"value": value,
"children": children,
}
let make = React.Context.provider(audioPlayerContext);
};
let make = React.Context.provider(audioPlayerContext)
}
let useAudioPlayer = () => React.useContext(Provider.audioPlayerContext);
let useAudioPlayer = () => React.useContext(Provider.audioPlayerContext)
@@ -1,270 +0,0 @@
open AudioPlayerProvider;
[@bs.val] external alert: string => unit = "alert";
let audio = Audio.make();
let _play = () => {
Audio.play(audio);
};
let _updateMetadata = (song: song) => {
MediaMetadata.make({
title: song.title,
artist: song.artist,
album: "Beatconnect",
artwork: [|MediaMetadata.makeArtwork(song.id)|],
})
->MediaSession.setMediaSessionMetadata;
};
let _setPreviewAudio = (beatmapSetId: int) => {
Audio.setSrc(audio, {j|https://b.ppy.sh/preview/$beatmapSetId.mp3|j});
};
let _setAudioSrc = (~audioFilePath, ~previewOffset=?, ()) => {
Audio.setSrc(audio, audioFilePath);
switch (previewOffset) {
| Some(offset) => Audio.setCurrentTime(audio, offset)
| None => ()
};
};
let pause = () => {
Audio.pause(audio);
};
let togglePlayPause = () => Audio.paused(audio) ? _play() : pause();
let setVolume = Audio.setVolume(audio);
[@react.component]
let make = (~children) => {
let playlistErrorCount = React.useRef(0);
let (playingState, setPlayingState) = React.useState(() => initialState);
let (playlist: playlist, setPlaylist) = React.useState(() => [||]);
let (playlistID: string, setPlaylistID) = React.useState(() => "");
let _canPlay = (offset: int) =>
if (playlist->Belt_Array.length > 0) {
let currentSongIndex =
playlist->Belt_Array.getIndexBy(item =>
item.id == playingState.beatmapSetId
);
switch (currentSongIndex) {
| None => None
| Some(index) =>
index + offset < playlist->Js_array.length && index + offset > (-1)
? Some(index + offset) : None
};
} else {
None;
};
let _canPlayNextSong = () => _canPlay(1);
let _canPlayPrevSong = () => _canPlay(-1);
let setPlaylist = (~beatmapPlaylist: playlist, ~playlistID=?, ()) => {
setPlaylist(_ => beatmapPlaylist);
switch (playlistID) {
| Some(id) => setPlaylistID(_ => id)
| None => ()
};
};
let _stop = () => {
pause();
setPlaylist(~beatmapPlaylist=[||], ~playlistID="", ());
};
let playFromPlaylist = (playlistindex: int) => {
let nextSong = playlist[playlistindex];
Audio.setSrc(audio, nextSong.path);
_updateMetadata({
id: nextSong.id,
title: nextSong.title,
artist: nextSong.artist,
});
_play();
setPlayingState(oldState =>
{
...oldState,
isPlaying: false,
beatmapSetId: nextSong.id,
title: nextSong.title,
artist: nextSong.artist,
}
);
};
let playNext = () => {
switch (_canPlayNextSong()) {
| Some(nextSong) => playFromPlaylist(nextSong)
| None => ()
};
};
let playPrevious = () => {
switch (_canPlayPrevSong()) {
| Some(prevSong) => playFromPlaylist(prevSong)
| None => ()
};
};
React.useEffect3(
() => {
IPCRenderer.send(
UPDATE_THUMB_BAR({
isPlaying: playingState.isPlaying,
canPlayNext: playingState.hasNext,
canPlayPrev: playingState.hasPrev,
}),
);
IPCRenderer.on("EXEC_PREV", playPrevious);
IPCRenderer.on("EXEC_NEXT", playNext);
Some(
() => {
IPCRenderer.removeListener("EXEC_PREV", playPrevious);
IPCRenderer.removeListener("EXEC_NEXT", playNext);
},
);
},
(playingState.isPlaying, playingState.hasNext, playingState.hasPrev),
);
React.useEffect0(() => {
MediaSession.setActionHandler(`play, Some(_play));
MediaSession.setActionHandler(`pause, Some(pause));
MediaSession.setActionHandler(`stop, Some(_stop));
IPCRenderer.on("EXEC_PLAY_PAUSE", togglePlayPause);
None;
});
let updateMediaHandlers = () => {
let next =
switch (_canPlayNextSong()) {
| Some(nextSongindex) =>
setPlayingState(prevState => {...prevState, hasNext: true});
Some(() => playFromPlaylist(nextSongindex));
| None =>
setPlayingState(prevState => {...prevState, hasNext: false});
None;
};
MediaSession.setActionHandler(`nexttrack, next);
let previous =
switch (_canPlayPrevSong()) {
| Some(prevSongindex) =>
setPlayingState(prevState => {...prevState, hasPrev: true});
Some(() => playFromPlaylist(prevSongindex));
| None =>
setPlayingState(prevState => {...prevState, hasPrev: false});
None;
};
MediaSession.setActionHandler(`previoustrack, previous);
};
React.useEffect2(
() => {
updateMediaHandlers();
None;
},
(playingState.beatmapSetId, playlist),
);
let setAudio =
(
~song: song,
~audioFilePath: option(string),
~previewOffset: option(int),
) => {
setPlaylist(~beatmapPlaylist=[||], ~playlistID="", ());
_updateMetadata(song);
switch (audioFilePath, previewOffset) {
| (None, None) => _setPreviewAudio(song.id)
| (None, Some(_)) => _setPreviewAudio(song.id)
| (Some(audioFilePath), None) => _setAudioSrc(~audioFilePath, ())
| (Some(audioFilePath), Some(previewOffset)) =>
_setAudioSrc(~audioFilePath, ~previewOffset, ())
};
_play();
setPlayingState(oldState =>
{
...oldState,
isPlaying: false,
beatmapSetId: song.id,
artist: song.artist,
title: song.title,
}
);
};
let setMuted = muted => {
Audio.setMuted(audio, muted);
setPlayingState(oldState => {...oldState, muted});
};
Audio.onended(audio, _e => {
switch (_canPlayNextSong()) {
| Some(nextSongindex) => playFromPlaylist(nextSongindex)
| None => DiscordRPC.clearActivity()
}
});
Audio.onpause(
audio,
_e => {
Js.log("PAUSEEEE");
setPlayingState(oldState => {...oldState, isPlaying: false});
},
);
Audio.onplay(audio, _e => {
setPlayingState(oldState => {...oldState, isPlaying: true})
});
Audio.oncanplay(audio, _e =>
setPlayingState(oldState => {...oldState, isPlaying: true})
);
Audio.onvolumechange(audio, e => {
setPlayingState(oldState => {...oldState, volume: e.target.volume})
});
Audio.onerror(
audio,
_e => {
setPlayingState(oldState => {...oldState, isPlaying: false});
let skipCount = playlistErrorCount->React.Ref.current;
if (skipCount > 12 || skipCount >= playlist->Js_array.length - 1) {
playlistErrorCount->React.Ref.setCurrent(0);
setPlaylist(~beatmapPlaylist=[||], ~playlistID="", ());
alert(
"Failed to play a song one or multiple times, please check your osu songs folder setting in the Settings section",
);
} else {
playNext();
playlistErrorCount->React.Ref.setCurrent(skipCount + 1);
};
},
);
let value = {
playingState,
pause,
setAudio,
setPlaylist,
setVolume,
togglePlayPause,
setMuted,
playlist,
playNext,
playPrevious,
playlistID,
};
<Provider value> children </Provider>;
};
@@ -0,0 +1,232 @@
open AudioPlayerProvider
@val external alert: string => unit = "alert"
let audio = Audio.make()
let _play = () => Audio.play(audio)
let _updateMetadata = (song: song) =>
MediaMetadata.make({
title: song.title,
artist: song.artist,
album: "Beatconnect",
artwork: [MediaMetadata.makeArtwork(song.id->Js.Int.toString)],
})->MediaSession.setMediaSessionMetadata
let _setPreviewAudio = (beatmapSetId: int) =>
Audio.setSrc(audio, `https://b.ppy.sh/preview/${beatmapSetId->Js.Int.toString}.mp3`)
let _setAudioSrc = (~audioFilePath, ~previewOffset=?, ()) => {
Audio.setSrc(audio, audioFilePath)
switch previewOffset {
| Some(offset) => Audio.setCurrentTime(audio, offset)
| None => ()
}
}
let pause = () => Audio.pause(audio)
let togglePlayPause = () => Audio.paused(audio) ? _play() : pause()
let setVolume = Audio.setVolume(audio)
@react.component
let make = (~children) => {
let playlistErrorCount = React.useRef(0)
let (playingState, setPlayingState) = React.useState(() => initialState)
let (playlist: playlist, setPlaylist) = React.useState(() => [])
let (playlistID: string, setPlaylistID) = React.useState(() => "")
let _canPlay = (offset: int) =>
if playlist->Belt_Array.length > 0 {
let currentSongIndex =
playlist->Belt_Array.getIndexBy(item => item.id == playingState.beatmapSetId)
switch currentSongIndex {
| None => None
| Some(index) =>
index + offset < playlist->Js_array.length && index + offset > -1
? Some(index + offset)
: None
}
} else {
None
}
let _canPlayNextSong = () => _canPlay(1)
let _canPlayPrevSong = () => _canPlay(-1)
let setPlaylist = (~beatmapPlaylist: playlist, ~playlistID=?, ()) => {
setPlaylist(_ => beatmapPlaylist)
switch playlistID {
| Some(id) => setPlaylistID(_ => id)
| None => ()
}
}
let _stop = () => {
pause()
setPlaylist(~beatmapPlaylist=[], ~playlistID="", ())
}
let playFromPlaylist = (playlistindex: int) => {
let nextSong = playlist[playlistindex]
Audio.setSrc(audio, nextSong.path)
_updateMetadata({
id: nextSong.id,
title: nextSong.title,
artist: nextSong.artist,
})
_play()
setPlayingState(oldState => {
...oldState,
isPlaying: false,
beatmapSetId: nextSong.id,
title: nextSong.title,
artist: nextSong.artist,
})
}
let playNext = () =>
switch _canPlayNextSong() {
| Some(nextSong) => playFromPlaylist(nextSong)
| None => ()
}
let playPrevious = () =>
switch _canPlayPrevSong() {
| Some(prevSong) => playFromPlaylist(prevSong)
| None => ()
}
React.useEffect3(() => {
IPCRenderer.send(
UPDATE_THUMB_BAR({
isPlaying: playingState.isPlaying,
canPlayNext: playingState.hasNext,
canPlayPrev: playingState.hasPrev,
}),
)
IPCRenderer.on("EXEC_PREV", playPrevious)
IPCRenderer.on("EXEC_NEXT", playNext)
Some(
() => {
IPCRenderer.removeListener("EXEC_PREV", playPrevious)
IPCRenderer.removeListener("EXEC_NEXT", playNext)
},
)
}, (playingState.isPlaying, playingState.hasNext, playingState.hasPrev))
React.useEffect0(() => {
MediaSession.setActionHandler(#play, Some(_play))
MediaSession.setActionHandler(#pause, Some(pause))
MediaSession.setActionHandler(#stop, Some(_stop))
IPCRenderer.on("EXEC_PLAY_PAUSE", togglePlayPause)
None
})
let updateMediaHandlers = () => {
let next = switch _canPlayNextSong() {
| Some(nextSongindex) =>
setPlayingState(prevState => {...prevState, hasNext: true})
Some(() => playFromPlaylist(nextSongindex))
| None =>
setPlayingState(prevState => {...prevState, hasNext: false})
None
}
MediaSession.setActionHandler(#nexttrack, next)
let previous = switch _canPlayPrevSong() {
| Some(prevSongindex) =>
setPlayingState(prevState => {...prevState, hasPrev: true})
Some(() => playFromPlaylist(prevSongindex))
| None =>
setPlayingState(prevState => {...prevState, hasPrev: false})
None
}
MediaSession.setActionHandler(#previoustrack, previous)
}
React.useEffect2(() => {
updateMediaHandlers()
None
}, (playingState.beatmapSetId, playlist))
let setAudio = (~song: song, ~audioFilePath: option<string>, ~previewOffset: option<int>) => {
setPlaylist(~beatmapPlaylist=[], ~playlistID="", ())
_updateMetadata(song)
switch (audioFilePath, previewOffset) {
| (None, None) => _setPreviewAudio(song.id)
| (None, Some(_)) => _setPreviewAudio(song.id)
| (Some(audioFilePath), None) => _setAudioSrc(~audioFilePath, ())
| (Some(audioFilePath), Some(previewOffset)) => _setAudioSrc(~audioFilePath, ~previewOffset, ())
}
_play()
setPlayingState(oldState => {
...oldState,
isPlaying: false,
beatmapSetId: song.id,
artist: song.artist,
title: song.title,
})
}
let setMuted = muted => {
Audio.setMuted(audio, muted)
setPlayingState(oldState => {...oldState, muted: muted})
}
Audio.onended(audio, _e =>
switch _canPlayNextSong() {
| Some(nextSongindex) => playFromPlaylist(nextSongindex)
| None => DiscordRPC.clearActivity()
}
)
Audio.onpause(audio, _e => {
Js.log("PAUSEEEE")
setPlayingState(oldState => {...oldState, isPlaying: false})
})
Audio.onplay(audio, _e => setPlayingState(oldState => {...oldState, isPlaying: true}))
Audio.oncanplay(audio, _e => setPlayingState(oldState => {...oldState, isPlaying: true}))
Audio.onvolumechange(audio, e =>
setPlayingState(oldState => {...oldState, volume: e.target.volume})
)
Audio.onerror(audio, _e => {
setPlayingState(oldState => {...oldState, isPlaying: false})
let skipCount = playlistErrorCount.current
if skipCount > 12 || skipCount >= playlist->Js_array.length - 1 {
playlistErrorCount.current = 0
setPlaylist(~beatmapPlaylist=[], ~playlistID="", ())
alert(
"Failed to play a song one or multiple times, please check your osu songs folder setting in the Settings section",
)
} else {
playNext()
playlistErrorCount.current = skipCount + 1
}
})
let value = {
playingState: playingState,
pause: pause,
setAudio: setAudio,
setPlaylist: setPlaylist,
setVolume: setVolume,
togglePlayPause: togglePlayPause,
setMuted: setMuted,
playlist: playlist,
playNext: playNext,
playPrevious: playPrevious,
playlistID: playlistID,
}
<Provider value> children </Provider>
}
-109
View File
@@ -1,109 +0,0 @@
[@bs.deriving {jsConverter: newType}]
type status = [ | `running | `terminated | `error];
module Task = {
type t = {
name: string,
description: string,
status,
section: Sections.t,
};
module Setters = {
let setName = (name, t) => {...t, name};
let setDescription = (description, t) => {...t, description};
let setStatus = (status, t) => {...t, status};
let setSection = (section, t) => {...t, section};
};
};
type partialTask = {
name: string,
description: option(string),
status: option(status),
};
type value = {
add: Task.t => unit,
update: partialTask => unit,
terminate: string => unit,
tasks: Js.Array.t(Task.t),
};
module Provider = {
let value = {
add: task => Js.log(task),
update: partialTask => Js.log(partialTask),
terminate: taskName => Js.log(taskName),
tasks: [||],
};
let taskContext = React.createContext(value);
let makeProps = (~value, ~children, ()) => {
"value": value,
"children": children,
};
let make = React.Context.provider(taskContext);
let updateTaskAt = (index, tasks, task) =>
tasks->Belt.Array.mapWithIndex((i, currentTask) =>
i === index ? task : currentTask
);
};
let useTasks = () => React.useContext(Provider.taskContext);
[@react.component]
let make = (~children) => {
let (tasks, setTasks) = React.useState(() => [||]);
let tasksRef = React.useRef(tasks);
React.useEffect1(
() => {
React.Ref.setCurrent(tasksRef, tasks);
None;
},
[|tasks|],
);
let add = (task: Task.t) =>
setTasks(oldTasks => Js.Array.concat([|task|], oldTasks));
let update = (partialTask: partialTask) => {
let tasks = React.Ref.current(tasksRef);
let taskIndex =
Js.Array.findIndex(
(task: Task.t) => task.name == partialTask.name,
tasks,
);
if (taskIndex != (-1)) {
let {description, status} = partialTask;
switch (description, status) {
| (Some(description), Some(status)) =>
setTasks(tasks =>
tasks[taskIndex]
|> Task.Setters.setDescription(description)
|> Task.Setters.setStatus(status)
|> Provider.updateTaskAt(taskIndex, tasks)
)
| (Some(description), None) =>
setTasks(tasks =>
tasks[taskIndex]
|> Task.Setters.setDescription(description)
|> Provider.updateTaskAt(taskIndex, tasks)
)
| (None, Some(status)) =>
setTasks(tasks =>
tasks[taskIndex]
|> Task.Setters.setStatus(status)
|> Provider.updateTaskAt(taskIndex, tasks)
)
| (None, None) => ()
};
};
};
let terminate = (taskName: string) =>
setTasks(oldTasks =>
Js.Array.filter((task: Task.t) => !(task.name == taskName), oldTasks)
);
let value = {add, terminate, update, tasks};
<Provider value> children </Provider>;
};
+96
View File
@@ -0,0 +1,96 @@
@deriving({jsConverter: newType})
type status = [#running | #terminated | #error]
module Task = {
type t = {
name: string,
description: string,
status: status,
section: Sections.t,
}
module Setters = {
let setName = (t, name) => {...t, name: name}
let setDescription = (t, description) => {...t, description: description}
let setStatus = (t, status) => {...t, status: status}
let setSection = (t, section) => {...t, section: section}
}
}
type partialTask = {
name: string,
description: option<string>,
status: option<status>,
}
type value = {
add: Task.t => unit,
update: partialTask => unit,
terminate: string => unit,
tasks: Js.Array.t<Task.t>,
}
module Provider = {
let value = {
add: task => Js.log(task),
update: partialTask => Js.log(partialTask),
terminate: taskName => Js.log(taskName),
tasks: [],
}
let taskContext = React.createContext(value)
let makeProps = (~value, ~children, ()) =>
{
"value": value,
"children": children,
}
let make = React.Context.provider(taskContext)
let updateTaskAt = (task, index, tasks) =>
tasks->Belt.Array.mapWithIndex((i, currentTask) => i === index ? task : currentTask)
}
let useTasks = () => React.useContext(Provider.taskContext)
@react.component
let make = (~children) => {
let (tasks, setTasks) = React.useState(() => [])
let tasksRef = React.useRef(tasks)
React.useEffect1(() => {
tasksRef.current = tasks
None
}, [tasks])
let add = (task: Task.t) => setTasks(oldTasks => Js.Array.concat([task], oldTasks))
let update = (partialTask: partialTask) => {
let tasks = tasksRef.current
let taskIndex = Js.Array.findIndex((task: Task.t) => task.name == partialTask.name, tasks)
if taskIndex != -1 {
let {description, status} = partialTask
switch (description, status) {
| (Some(description), Some(status)) =>
setTasks(tasks =>
tasks[taskIndex]
->Task.Setters.setDescription(description)
->Task.Setters.setStatus(status)
->Provider.updateTaskAt(taskIndex, tasks)
)
| (Some(description), None) =>
setTasks(tasks =>
tasks[taskIndex]
->Task.Setters.setDescription(description)
->Provider.updateTaskAt(taskIndex, tasks)
)
| (None, Some(status)) =>
setTasks(tasks =>
tasks[taskIndex]->Task.Setters.setStatus(status)->Provider.updateTaskAt(taskIndex, tasks)
)
| (None, None) => ()
}
}
}
let terminate = (taskName: string) =>
setTasks(oldTasks => Js.Array.filter((task: Task.t) => !(task.name == taskName), oldTasks))
let value = {add: add, terminate: terminate, update: update, tasks: tasks}
<Provider value> children </Provider>
}
-5
View File
@@ -1,5 +0,0 @@
[@bs.module "./discordRPC.js"]
external setPlayingSongPresence: (string, string) => unit =
"setPlayingSongPresence";
[@bs.module "./discordRPC.js"]
external clearActivity: unit => unit = "clearActivity";
+4
View File
@@ -0,0 +1,4 @@
@module("./discordRPC.js")
external setPlayingSongPresence: (string, string) => unit = "setPlayingSongPresence"
@module("./discordRPC.js")
external clearActivity: unit => unit = "clearActivity"
-29
View File
@@ -1,29 +0,0 @@
[@bs.deriving jsConverter]
type jsRenderIconsArgs = {
name: string,
color: option(string),
width: option(int),
height: option(int),
};
[@bs.module "./renderIcons"]
external jsRenderIcons:
{
.
"color": option(string),
"height": option(int),
"name": string,
"width": option(int),
} =>
React.element =
"default";
let make =
(
~name: string,
~color: option(string)=?,
~width: option(int)=?,
~height: option(int)=?,
(),
) =>
{name, color, width, height}->jsRenderIconsArgsToJs->jsRenderIcons;
+23
View File
@@ -0,0 +1,23 @@
@deriving(jsConverter)
type jsRenderIconsArgs = {
name: string,
color: option<string>,
width: option<int>,
height: option<int>,
}
@module("./renderIcons")
external jsRenderIcons: {
"color": option<string>,
"height": option<int>,
"name": string,
"width": option<int>,
} => React.element = "default"
let make = (
~name: string,
~color: option<string>=?,
~width: option<int>=?,
~height: option<int>=?,
(),
) => {name: name, color: color, width: width, height: height}->jsRenderIconsArgsToJs->jsRenderIcons
-2
View File
@@ -1,2 +0,0 @@
[@bs.deriving {jsConverter: newType}]
type t = [ | `Beatmaps | `Packs | `Downloads | `Bot | `Settings];
+2
View File
@@ -0,0 +1,2 @@
@deriving({jsConverter: newType})
type t = [#Beatmaps | #Packs | #Downloads | #Bot | #Settings]
-138
View File
@@ -1,138 +0,0 @@
open Css;
let makeBaseStyle = () =>
style([
border(px(2), solid, hex("2c3e50")),
borderRadius(rem(0.25)),
fontSize(rem(0.8)),
padding3(~top=rem(0.1), ~h=rem(0.45), ~bottom=rem(0.2)),
display(inlineFlex),
alignItems(center),
whiteSpace(nowrap),
]);
let makeStatusStyle = (status, difficulty) =>
switch (status) {
| "info" => style([backgroundColor(hex("3498db"))])
| "pending" =>
style([
backgroundColor(
difficulty->Belt.Option.mapWithDefault(
rgba(241, 196, 15),
PpyHelpers.getBeatmapDifficultyColorRGBA,
0.5,
),
),
borderColor(
difficulty->Belt.Option.mapWithDefault(
rgba(241, 196, 15),
PpyHelpers.getBeatmapDifficultyColorRGBA,
0.8,
),
),
hover([
backgroundColor(rgba(241, 196, 15, 0.7)),
borderColor(rgb(241, 196, 15)),
]),
])
| "qualified" =>
style([
backgroundColor(rgba(241, 196, 15, 0.50)),
borderColor(rgba(241, 196, 15, 0.8)),
hover([
backgroundColor(rgba(241, 196, 15, 0.7)),
borderColor(rgb(241, 196, 15)),
]),
])
| "graveyard" =>
style([
backgroundColor(rgba(231, 76, 60, 0.50)),
borderColor(rgba(231, 76, 60, 0.8)),
hover([
backgroundColor(rgba(231, 76, 60, 0.7)),
borderColor(rgb(231, 76, 60)),
]),
])
| "WIP" =>
style([
backgroundColor(rgba(231, 76, 60, 0.50)),
borderColor(rgba(231, 76, 60, 0.8)),
hover([
backgroundColor(rgba(231, 76, 60, 0.7)),
borderColor(rgb(231, 76, 60)),
]),
])
| "loved" =>
style([
backgroundColor(rgba(222, 90, 148, 0.50)),
borderColor(rgba(222, 90, 148, 0.8)),
hover([
backgroundColor(rgba(222, 90, 148, 0.7)),
borderColor(rgb(222, 90, 148)),
]),
])
| "ranked" =>
style([
backgroundColor(
difficulty->Belt.Option.mapWithDefault(
rgba(46, 204, 113),
PpyHelpers.getBeatmapDifficultyColorRGBA,
0.5,
),
),
borderColor(
difficulty->Belt.Option.mapWithDefault(
rgba(46, 204, 113),
PpyHelpers.getBeatmapDifficultyColorRGBA,
0.8,
),
),
hover([
backgroundColor(rgba(46, 204, 113, 0.7)),
borderColor(rgb(46, 204, 113)),
]),
])
| _ => style([backgroundColor(hex("2c3e50"))])
};
let makeStyle = (status, difficulty) =>
[makeBaseStyle(), makeStatusStyle(status, difficulty)]->merge;
[@react.component]
let make =
(
~status,
~difficulty: option(float),
~difficultyText: option(string),
~style=?,
) => {
let (text, setText) = React.useState(() => status);
React.useEffect1(
() => {
setText(_ =>
switch (difficultyText) {
| Some(difficultyText) => difficultyText
| None => status
}
);
None;
},
[|difficultyText|],
);
<span
className={makeStyle(status, difficulty)}
?style
onMouseEnter={_ => setText(_ => status)}
onMouseLeave={_ =>
setText(_ =>
switch (difficultyText) {
| Some(difficultyText) => difficultyText
| None => status
}
)
}>
<span> text->React.string </span>
</span>;
};
+110
View File
@@ -0,0 +1,110 @@
open CssJs
let makeBaseStyle = () =>
style(. [
border(px(2), solid, hex("2c3e50")),
borderRadius(rem(0.25)),
fontSize(rem(0.8)),
padding3(~top=rem(0.1), ~h=rem(0.45), ~bottom=rem(0.2)),
display(inlineFlex),
alignItems(center),
whiteSpace(nowrap),
])
let makeStatusStyle = (status, difficulty) =>
switch status {
| "info" => style(. [backgroundColor(hex("3498db"))])
| "pending" =>
style(. [
backgroundColor(
difficulty->Belt.Option.mapWithDefault(
rgba(241, 196, 15),
PpyHelpers.getBeatmapDifficultyColorRGBA,
#num(0.5),
),
),
borderColor(
difficulty->Belt.Option.mapWithDefault(
rgba(241, 196, 15),
PpyHelpers.getBeatmapDifficultyColorRGBA,
#num(0.8),
),
),
hover([backgroundColor(rgba(241, 196, 15, #num(0.7))), borderColor(rgb(241, 196, 15))]),
])
| "qualified" =>
style(. [
backgroundColor(rgba(241, 196, 15, #num(0.50))),
borderColor(rgba(241, 196, 15, #num(0.8))),
hover([backgroundColor(rgba(241, 196, 15, #num(0.7))), borderColor(rgb(241, 196, 15))]),
])
| "graveyard" =>
style(. [
backgroundColor(rgba(231, 76, 60, #num(0.50))),
borderColor(rgba(231, 76, 60, #num(0.8))),
hover([backgroundColor(rgba(231, 76, 60, #num(0.7))), borderColor(rgb(231, 76, 60))]),
])
| "WIP" =>
style(. [
backgroundColor(rgba(231, 76, 60, #num(0.50))),
borderColor(rgba(231, 76, 60, #num(0.8))),
hover([backgroundColor(rgba(231, 76, 60, #num(0.7))), borderColor(rgb(231, 76, 60))]),
])
| "loved" =>
style(. [
backgroundColor(rgba(222, 90, 148, #num(0.50))),
borderColor(rgba(222, 90, 148, #num(0.8))),
hover([backgroundColor(rgba(222, 90, 148, #num(0.7))), borderColor(rgb(222, 90, 148))]),
])
| "ranked" =>
style(. [
backgroundColor(
difficulty->Belt.Option.mapWithDefault(
rgba(46, 204, 113),
PpyHelpers.getBeatmapDifficultyColorRGBA,
#num(0.5),
),
),
borderColor(
difficulty->Belt.Option.mapWithDefault(
rgba(46, 204, 113),
PpyHelpers.getBeatmapDifficultyColorRGBA,
#num(0.8),
),
),
hover([backgroundColor(rgba(46, 204, 113, #num(0.7))), borderColor(rgb(46, 204, 113))]),
])
| _ => style(. [backgroundColor(hex("2c3e50"))])
}
let makeStyle = (status, difficulty) =>
[makeBaseStyle(), makeStatusStyle(status, difficulty)]->merge(. _)
@react.component
let make = (~status, ~difficulty: option<float>, ~difficultyText: option<string>, ~style=?) => {
let (text, setText) = React.useState(() => status)
React.useEffect1(() => {
setText(_ =>
switch difficultyText {
| Some(difficultyText) => difficultyText
| None => status
}
)
None
}, [difficultyText])
<span
className={makeStyle(status, difficulty)}
?style
onMouseEnter={_ => setText(_ => status)}
onMouseLeave={_ =>
setText(_ =>
switch difficultyText {
| Some(difficultyText) => difficultyText
| None => status
}
)}>
<span> {text->React.string} </span>
</span>
}
-27
View File
@@ -1,27 +0,0 @@
open Css;
let checkBoxStyle = (~color, ~activeColor) =>
style([
unsafe("-webkit-appearance", "none"),
margin2(~v=auto, ~h=rem(1.)),
width(px(20)),
height(px(20)),
border(px(1), solid, hex(color)),
borderRadius(px(2)),
verticalAlign(middle),
backgroundColor(transparent),
selector(
"&:checked",
[borderColor(hex(activeColor)), backgroundColor(hex(activeColor))],
),
]);
[@react.component]
let make = (~checked, ~disabled, ~color, ~activeColor) => {
<input
className={checkBoxStyle(~color, ~activeColor)}
type_="checkbox"
checked
disabled
/>;
};
+18
View File
@@ -0,0 +1,18 @@
open CssJs
let checkBoxStyle = (~color, ~activeColor) =>
style(. [
unsafe("-webkit-appearance", "none"),
margin2(~v=auto, ~h=rem(1.)),
width(px(20)),
height(px(20)),
border(px(1), solid, hex(color)),
borderRadius(px(2)),
verticalAlign(middle),
backgroundColor(transparent),
selector(. "&:checked", [borderColor(hex(activeColor)), backgroundColor(hex(activeColor))]),
])
@react.component
let make = (~checked, ~disabled, ~color, ~activeColor) =>
<input className={checkBoxStyle(~color, ~activeColor)} type_="checkbox" checked disabled />
@@ -1,33 +1,32 @@
open Css;
open CssJs
let makeStyle = strokeDashoffset =>
style([
style(. [
transition(~duration=350, "stroke-dashoffset"),
transform(rotate(deg(-90.))),
transformOrigin(pct(50.), pct(50.)),
unsafe("stroke-dashoffset", strokeDashoffset),
]);
])
let floatToString = Js.Float.toString;
let floatToString = Js.Float.toString
[@react.component]
@react.component
let make = (~radius, ~stroke, ~progress) => {
let normalizedRadius = radius -. stroke *. 2.;
let circumference = normalizedRadius *. 2. *. Js.Math._PI;
let strokeDashoffset =
(circumference -. progress /. 100. *. circumference)->floatToString;
let size = (radius *. 2.)->floatToString;
let normalizedRadius = radius -. stroke *. 2.
let circumference = normalizedRadius *. 2. *. Js.Math._PI
let strokeDashoffset = (circumference -. progress /. 100. *. circumference)->floatToString
let size = (radius *. 2.)->floatToString
<svg height=size width=size style={ReactDOMRe.Style.make(~margin="0", ())}>
<svg height=size width=size style={ReactDOMStyle.make(~margin="0", ())}>
<circle
className={makeStyle(strokeDashoffset)}
stroke="white"
fill="transparent"
strokeWidth={stroke->floatToString}
strokeDasharray={j|$circumference $circumference|j}
strokeDasharray={`${circumference->Js.Float.toString} ${circumference->Js.Float.toString}`}
r={normalizedRadius->floatToString}
cx={radius->floatToString}
cy={radius->floatToString}
/>
</svg>;
};
</svg>
}
-66
View File
@@ -1,66 +0,0 @@
open Css;
let makeWrapperStyle = () =>
style([
zIndex(500),
position(fixed),
right(zero),
display(`flex),
alignItems(center),
]);
let makeControlStyle = (~bgColor=?, ~spacer=false, ()) =>
style([
unsafe("WebkitAppRegion", "no-drag"),
padding2(~v=zero, ~h=px(7)),
selector(
"&:hover",
[
backgroundColor(
switch (bgColor) {
| None => rgba(255, 255, 255, 0.25)
| Some(c) => c
},
),
],
),
marginLeft(spacer ? `auto : `zero),
]);
[@react.component]
let make = (~height: int) => {
let {AudioPlayerProvider.playingState} =
AudioPlayerProvider.useAudioPlayer();
let artist = playingState.artist;
let title = playingState.title;
let songTitle = {j|$artist - $title|j};
let title =
playingState.isPlaying
? {j|Beatconnect \u23F5 $songTitle|j} : "Beatconnect";
// let window = Remote.getCurrentWindow(Remote.remote);
// window->BrowserWindow.setTitle(title);
// let onMinimizeClick = _e => BrowserWindow.minimize(window);
// let onCloseClick = _e => BrowserWindow.close(window);
// let onMaximizeClick = _e =>
// BrowserWindow.isMaximized(window)
// ? BrowserWindow.unmaximize(window) : BrowserWindow.maximize(window);
let onMinimizeClick = _ => ();
let onCloseClick = _ => ();
let onMaximizeClick = _ => ();
<div className={makeWrapperStyle()}>
<div
className={makeControlStyle(~spacer=true, ())} onClick=onMinimizeClick>
{Icon.make(~name="Dash", ~width=height, ~height, ())}
</div>
<div className={makeControlStyle()} onClick=onMaximizeClick>
{Icon.make(~name="Square", ~width=height, ~height, ())}
</div>
<div className={makeControlStyle(~bgColor=red, ())} onClick=onCloseClick>
{Icon.make(~name="Cancel", ~width=height, ~height, ())}
</div>
</div>;
};
let default = make;
+60
View File
@@ -0,0 +1,60 @@
open CssJs
let makeWrapperStyle = () =>
style(. [zIndex(500), position(fixed), right(zero), display(#flex), alignItems(center)])
let makeControlStyle = (~bgColor=?, ~spacer=false, ()) =>
style(. [
unsafe("WebkitAppRegion", "no-drag"),
padding2(~v=zero, ~h=px(7)),
selector(.
"&:hover",
[
backgroundColor(
switch bgColor {
| None => rgba(255, 255, 255, #num(0.25))
| Some(c) => c
},
),
],
),
marginLeft(spacer ? #auto : #zero),
])
@react.component
let make = (~height: int) => {
let {AudioPlayerProvider.playingState: playingState} = AudioPlayerProvider.useAudioPlayer()
let artist = playingState.artist
let title = playingState.title
let songTitle = `${artist} - ${title}`
let _title = playingState.isPlaying ? `Beatconnect \\u23F5 ${songTitle}bot` : "Beatconnect"
// let window = Remote.getCurrentWindow(Remote.remote)
// window->BrowserWindow.setTitle(title)
// let onMinimizeClick = _e => BrowserWindow.minimize(window)
// let onCloseClick = _e => BrowserWindow.close(window)
// let onMaximizeClick = _e =>
// BrowserWindow.isMaximized(window)
// ? BrowserWindow.unmaximize(window)
// : BrowserWindow.maximize(window)
let onMinimizeClick = _ => ()
let onCloseClick = _ => ()
let onMaximizeClick = _ => ()
<div className={makeWrapperStyle()}>
<div className={makeControlStyle(~spacer=true, ())} onClick=onMinimizeClick>
{Icon.make(~name="Dash", ~width=height, ~height, ())}
</div>
<div className={makeControlStyle()} onClick=onMaximizeClick>
{Icon.make(~name="Square", ~width=height, ~height, ())}
</div>
<div className={makeControlStyle(~bgColor=red, ())} onClick=onCloseClick>
{Icon.make(~name="Cancel", ~width=height, ~height, ())}
</div>
</div>
}
let default = make
@@ -51,24 +51,23 @@ type beatmap = {
visual_override: bool,
last_modification_time_2: float,
mania_scroll_speed: int,
};
}
type osuDBData = {
beatmaps: list(beatmap),
beatmaps: list<beatmap>,
userperms: int,
isLocked: bool,
};
type t;
[@bs.deriving {jsConverter: newType}]
type osuDatabase = [ | `osudb | `collection];
}
type t
@deriving({jsConverter: newType})
type osuDatabase = [#osudb | #collection]
type osuDbParserInstance = {
.
[@bs.meth] "getOsuDBData": unit => osuDBData,
[@bs.meth] "setBuffer": (abs_osuDatabase, Buffer.t) => unit,
};
[@bs.new] [@bs.module]
external make: unit => osuDbParserInstance = "osu-db-parser";
let parser = make();
getOsuDBData: unit => osuDBData,
setBuffer: (abs_osuDatabase, Buffer.t) => unit,
}
@new @module
external make: unit => osuDbParserInstance = "osu-db-parser"
let parser = make()
let read = (buffer: Buffer.t) => {
parser##setBuffer(osuDatabaseToJs(`osudb), buffer);
parser##getOsuDBData();
};
parser.setBuffer(osuDatabaseToJs(#osudb), buffer)
parser.getOsuDBData()
}
-45
View File
@@ -1,45 +0,0 @@
let getThumbUrl = beatmapId => {j|https://b.ppy.sh/thumb/$beatmapId.jpg|j};
let getListCoverUrl = beatmapId => {j|https://assets.ppy.sh/beatmaps/$beatmapId/covers/list@2x.jpg|j};
let resolveThumbnail = (beatmapId, osuPath, fallbackUrl) => {
let localPath =
"file://"
++ osuPath
++ "/Data/bt/"
++ beatmapId
++ "l.jpg"
|> Js_global.encodeURI
|> Js_string.replaceByRe([%re "/\\/g/"], "/");
let thumbanil = Image.make();
thumbanil->Image.setSrc(localPath);
thumbanil->Image.decode
|> Js_promise.then_(() => Js_promise.resolve(localPath))
|> Js_promise.catch(_ => Js_promise.resolve(fallbackUrl));
};
let resolveThumbURL = (beatmapId, osuPath) =>
resolveThumbnail(beatmapId, osuPath, getListCoverUrl(beatmapId));
let getBeatmapDifficultyColorHex = (difficulty: float) => {
switch (difficulty) {
| difficulty when difficulty >= 6.5 => "#000"
| difficulty when difficulty >= 5.3 => "#8866ee"
| difficulty when difficulty >= 4.0 => "#ff66aa"
| difficulty when difficulty >= 2.7 => "#ffcc22"
| difficulty when difficulty >= 2.0 => "#66ccff"
| _ => "#88b300"
};
};
let getBeatmapDifficultyColorRGBA = (difficulty: float) => {
switch (difficulty) {
| difficulty when difficulty >= 6.5 => Css.rgba(0, 0, 0)
| difficulty when difficulty >= 5.3 => Css.rgba(136, 102, 238)
| difficulty when difficulty >= 4.0 => Css.rgba(255, 102, 170)
| difficulty when difficulty >= 2.7 => Css.rgba(255, 204, 34)
| difficulty when difficulty >= 2.0 => Css.rgba(102, 204, 255)
| _ => Css.rgba(136, 179, 0)
};
};
+40
View File
@@ -0,0 +1,40 @@
let getThumbUrl = beatmapId => `https://b.ppy.sh/thumb/${beatmapId}.jpg`
let getListCoverUrl = beatmapId => `https://assets.ppy.sh/beatmaps/${beatmapId}/covers/list@2x.jpg`
let resolveThumbnail = (beatmapId, osuPath, fallbackUrl) => {
let localPath =
"file://" ++
(osuPath ++ ("/Data/bt/" ++ (beatmapId ++ "l.jpg")))
->Js.Global.encodeURI
->Js.String2.replaceByRe(%re("/\\/g/"), "/")
let thumbanil = Image.make()
thumbanil->Image.setSrc(localPath)
thumbanil->Image.decode
|> Js.Promise.then_(() => Js_promise.resolve(localPath))
|> Js.Promise.catch(_ => Js_promise.resolve(fallbackUrl))
}
let resolveThumbURL = (beatmapId, osuPath) =>
resolveThumbnail(beatmapId, osuPath, getListCoverUrl(beatmapId))
let getBeatmapDifficultyColorHex = (difficulty: float) =>
switch difficulty {
| difficulty if difficulty >= 6.5 => "#000"
| difficulty if difficulty >= 5.3 => "#8866ee"
| difficulty if difficulty >= 4.0 => "#ff66aa"
| difficulty if difficulty >= 2.7 => "#ffcc22"
| difficulty if difficulty >= 2.0 => "#66ccff"
| _ => "#88b300"
}
let getBeatmapDifficultyColorRGBA = (difficulty: float) =>
switch difficulty {
| difficulty if difficulty >= 6.5 => Css.rgba(0, 0, 0)
| difficulty if difficulty >= 5.3 => Css.rgba(136, 102, 238)
| difficulty if difficulty >= 4.0 => Css.rgba(255, 102, 170)
| difficulty if difficulty >= 2.7 => Css.rgba(255, 204, 34)
| difficulty if difficulty >= 2.0 => Css.rgba(102, 204, 255)
| _ => Css.rgba(136, 179, 0)
}
-35
View File
@@ -1,35 +0,0 @@
type t;
type callback('a) = 'a => unit;
type target = {
readyState: int,
volume: int,
};
type partialAudioEvent = {target};
[@bs.new] external make: unit => t = "Audio";
[@bs.new] external makeWithSrc: (~src: string) => t = "Audio";
[@bs.get] external paused: t => bool = "paused";
[@bs.get] external getMuted: t => bool = "muted";
[@bs.get] external getVolume: t => int = "volume";
[@bs.set] external setSrc: (t, string) => unit = "src";
[@bs.set] external setVolume: (t, float) => unit = "volume";
[@bs.set] external setCurrentTime: (t, int) => unit = "currentTime";
[@bs.set] external setMuted: (t, bool) => unit = "muted";
[@bs.send] external pause: t => unit = "pause";
[@bs.send] external play: t => unit = "play";
[@bs.set]
external onended: (t, callback(partialAudioEvent)) => unit = "onended";
[@bs.set]
external onerror: (t, callback(partialAudioEvent)) => unit = "onerror";
[@bs.set]
external onpause: (t, callback(partialAudioEvent)) => unit = "onpause";
[@bs.set]
external onplay: (t, callback(partialAudioEvent)) => unit = "onplay";
[@bs.set]
external oncanplay: (t, callback(partialAudioEvent)) => unit = "oncanplay";
[@bs.set]
external onvolumechange: (t, callback(partialAudioEvent)) => unit =
"onvolumechange";
+34
View File
@@ -0,0 +1,34 @@
type t
type callback<'a> = 'a => unit
type target = {
readyState: int,
volume: int,
}
type partialAudioEvent = {target: target}
@new external make: unit => t = "Audio"
@new external makeWithSrc: (~src: string) => t = "Audio"
@get external paused: t => bool = "paused"
@get external getMuted: t => bool = "muted"
@get external getVolume: t => int = "volume"
@set external setSrc: (t, string) => unit = "src"
@set external setVolume: (t, float) => unit = "volume"
@set external setCurrentTime: (t, int) => unit = "currentTime"
@set external setMuted: (t, bool) => unit = "muted"
@send external pause: t => unit = "pause"
@send external play: t => unit = "play"
@set
external onended: (t, callback<partialAudioEvent>) => unit = "onended"
@set
external onerror: (t, callback<partialAudioEvent>) => unit = "onerror"
@set
external onpause: (t, callback<partialAudioEvent>) => unit = "onpause"
@set
external onplay: (t, callback<partialAudioEvent>) => unit = "onplay"
@set
external oncanplay: (t, callback<partialAudioEvent>) => unit = "oncanplay"
@set
external onvolumechange: (t, callback<partialAudioEvent>) => unit = "onvolumechange"
-7
View File
@@ -1,7 +0,0 @@
type t;
[@bs.new] external make: unit => t = "Image";
[@bs.new] external makeWithSize: (~width: int, ~height: int) => t = "Image";
[@bs.send] external decode: t => Js.Promise.t(unit) = "decode";
[@bs.set] external setSrc: (t, string) => unit = "src";
+7
View File
@@ -0,0 +1,7 @@
type t
@new external make: unit => t = "Image"
@new external makeWithSize: (~width: int, ~height: int) => t = "Image"
@send external decode: t => Js.Promise.t<unit> = "decode"
@set external setSrc: (t, string) => unit = "src"
@@ -1,22 +1,22 @@
type t;
type t
type artwork = {
src: string,
sizes: string,
type_: string,
};
}
type metadata = {
title: string,
artist: string,
album: string,
artwork: array(artwork),
};
artwork: array<artwork>,
}
[@bs.new] external make: metadata => t = "MediaMetadata";
@new external make: metadata => t = "MediaMetadata"
let makeArtwork = id => {
src: PpyHelpers.getListCoverUrl(id),
sizes: "160x100",
type_: "image/jpg",
};
}
-20
View File
@@ -1,20 +0,0 @@
type t;
type navigator;
[@bs.deriving {jsConverter: newType}]
type actionType = [ | `play | `pause | `previoustrack | `nexttrack | `stop];
[@bs.val] external mediaSession: t = "navigator.mediaSession";
[@bs.val] external navigator: navigator = "navigator";
[@bs.send]
external setActionHandler: (t, actionType, option(unit => unit)) => unit =
"setActionHandler";
let setActionHandler = setActionHandler(mediaSession);
[@bs.set]
external setMediaSessionMetadata: (t, MediaMetadata.t) => unit = "metadata";
let setMediaSessionMetadata = setMediaSessionMetadata(mediaSession);
+19
View File
@@ -0,0 +1,19 @@
type t
type navigator
@deriving({jsConverter: newType})
type actionType = [#play | #pause | #previoustrack | #nexttrack | #stop]
@val external mediaSession: t = "navigator.mediaSession"
@val external navigator: navigator = "navigator"
@send
external setActionHandler: (t, actionType, option<unit => unit>) => unit = "setActionHandler"
let setActionHandler = setActionHandler(mediaSession)
@set
external setMediaSessionMetadata: (t, MediaMetadata.t) => unit = "metadata"
let setMediaSessionMetadata = setMediaSessionMetadata(mediaSession)
-8
View File
@@ -1,8 +0,0 @@
type t;
[@bs.send] external setTitle: (t, string) => unit = "setTitle";
[@bs.send] external isMaximized: t => bool = "isMaximized";
[@bs.send] external unmaximize: t => unit = "unmaximize";
[@bs.send] external maximize: t => unit = "maximize";
[@bs.send] external close: t => unit = "close";
[@bs.send] external minimize: t => unit = "minimize";
+8
View File
@@ -0,0 +1,8 @@
type t
@send external setTitle: (t, string) => unit = "setTitle"
@send external isMaximized: t => bool = "isMaximized"
@send external unmaximize: t => unit = "unmaximize"
@send external maximize: t => unit = "maximize"
@send external close: t => unit = "close"
@send external minimize: t => unit = "minimize"
-29
View File
@@ -1,29 +0,0 @@
type t;
[@bs.module "electron"] external remote: t = "ipcRenderer";
[@bs.send] external send: (t, string, 'a) => unit = "send";
[@bs.send] external on: (t, string, 'a => unit) => unit = "on";
[@bs.send]
external removeListener: (t, string, 'a => unit) => unit = "removeListener";
type updateThumbBarData = {
isPlaying: bool,
canPlayNext: bool,
canPlayPrev: bool,
};
type channel =
| UPDATE_THUMB_BAR(updateThumbBarData)
| UPDATE_PLAY_STATE(bool);
let send = channel => {
switch (channel) {
| UPDATE_THUMB_BAR(data) => send(remote, "UPDATE_THUMB_BAR", data)
| UPDATE_PLAY_STATE(isPlaying) =>
send(remote, "UPDATE_PLAY_STATE", isPlaying)
};
};
let on = (channel, callback) => on(remote, channel, callback);
let removeListener = (channel, callback) =>
removeListener(remote, channel, callback);
+26
View File
@@ -0,0 +1,26 @@
type t
@module("electron") external remote: t = "ipcRenderer"
@send external send: (t, string, 'a) => unit = "send"
@send external on: (t, string, 'a => unit) => unit = "on"
@send
external removeListener: (t, string, 'a => unit) => unit = "removeListener"
type updateThumbBarData = {
isPlaying: bool,
canPlayNext: bool,
canPlayPrev: bool,
}
type channel =
| UPDATE_THUMB_BAR(updateThumbBarData)
| UPDATE_PLAY_STATE(bool)
let send = channel =>
switch channel {
| UPDATE_THUMB_BAR(data) => send(remote, "UPDATE_THUMB_BAR", data)
| UPDATE_PLAY_STATE(isPlaying) => send(remote, "UPDATE_PLAY_STATE", isPlaying)
}
let on = (channel, callback) => on(remote, channel, callback)
let removeListener = (channel, callback) => removeListener(remote, channel, callback)
-5
View File
@@ -1,5 +0,0 @@
type t;
[@bs.module "electron"] external remote: t = "remote";
[@bs.send]
external getCurrentWindow: t => BrowserWindow.t = "getCurrentWindow";
+5
View File
@@ -0,0 +1,5 @@
type t
@module("electron") external remote: t = "remote"
@send
external getCurrentWindow: t => BrowserWindow.t = "getCurrentWindow"
+148 -124
View File
@@ -249,7 +249,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-module-imports@npm:^7.0.0, @babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.16.7":
"@babel/helper-module-imports@npm:^7.12.13, @babel/helper-module-imports@npm:^7.16.7":
version: 7.16.7
resolution: "@babel/helper-module-imports@npm:7.16.7"
dependencies:
@@ -290,6 +290,13 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-plugin-utils@npm:^7.17.12":
version: 7.17.12
resolution: "@babel/helper-plugin-utils@npm:7.17.12"
checksum: 300da74658e2b958bb488d4ca27757e676b14f9978501dfb3a2fc505d4322d58fbd4f7b79470299828f61cf880dff8fce83bbe942f0a00e0b86a8fdc21f6cc8d
languageName: node
linkType: hard
"@babel/helper-remap-async-to-generator@npm:^7.16.8":
version: 7.16.8
resolution: "@babel/helper-remap-async-to-generator@npm:7.16.8"
@@ -735,6 +742,17 @@ __metadata:
languageName: node
linkType: hard
"@babel/plugin-syntax-jsx@npm:^7.12.13":
version: 7.17.12
resolution: "@babel/plugin-syntax-jsx@npm:7.17.12"
dependencies:
"@babel/helper-plugin-utils": ^7.17.12
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: a5dde0b0fc6493a4b1f0341a7b68dcfa0c1ba1553e8aa21e995320cb4267df619c7c20c0840f14f13c302f4bef4759a92cde3f69c0be1ff489dcc2835bc4de53
languageName: node
linkType: hard
"@babel/plugin-syntax-jsx@npm:^7.16.7":
version: 7.16.7
resolution: "@babel/plugin-syntax-jsx@npm:7.16.7"
@@ -1471,6 +1489,15 @@ __metadata:
languageName: node
linkType: hard
"@babel/runtime@npm:^7.13.10":
version: 7.18.3
resolution: "@babel/runtime@npm:7.18.3"
dependencies:
regenerator-runtime: ^0.13.4
checksum: 70aa471de7452e9ad6d1f7ef880b76558b102a97d81aaf3251a6d70b90de7388ac0e7f5a9f5b5266ee046df92451cd7e1b310c043a5f131704949138d4790643
languageName: node
linkType: hard
"@babel/template@npm:^7.12.13, @babel/template@npm:^7.16.7, @babel/template@npm:^7.3.3, @babel/template@npm:^7.4.0":
version: 7.16.7
resolution: "@babel/template@npm:7.16.7"
@@ -1576,19 +1603,60 @@ __metadata:
languageName: node
linkType: hard
"@emotion/cache@npm:^10.0.27":
version: 10.0.29
resolution: "@emotion/cache@npm:10.0.29"
"@emotion/babel-plugin@npm:^11.7.1":
version: 11.9.2
resolution: "@emotion/babel-plugin@npm:11.9.2"
dependencies:
"@emotion/sheet": 0.9.4
"@emotion/stylis": 0.8.5
"@emotion/utils": 0.11.3
"@emotion/weak-memoize": 0.2.5
checksum: a2bdf4dd21b63d5e88999bfefe841e5a7c64783d321f94bb930d28d18e8d15c80997c84a21996856d9356ef36d70e1ea1209f23de0c2f9e65c8b86e7d3a2f192
"@babel/helper-module-imports": ^7.12.13
"@babel/plugin-syntax-jsx": ^7.12.13
"@babel/runtime": ^7.13.10
"@emotion/hash": ^0.8.0
"@emotion/memoize": ^0.7.5
"@emotion/serialize": ^1.0.2
babel-plugin-macros: ^2.6.1
convert-source-map: ^1.5.0
escape-string-regexp: ^4.0.0
find-root: ^1.1.0
source-map: ^0.5.7
stylis: 4.0.13
peerDependencies:
"@babel/core": ^7.0.0
checksum: 5956bc9eefe556c95323b198aa337abdcca30bc0624c3cae93378f262bded7992582a7e7d2f86868dbd3a831f7ae78b79714e9a36a124e1f6763d8706d4716ad
languageName: node
linkType: hard
"@emotion/hash@npm:0.8.0":
"@emotion/cache@npm:^11.0.0, @emotion/cache@npm:^11.7.1":
version: 11.7.1
resolution: "@emotion/cache@npm:11.7.1"
dependencies:
"@emotion/memoize": ^0.7.4
"@emotion/sheet": ^1.1.0
"@emotion/utils": ^1.0.0
"@emotion/weak-memoize": ^0.2.5
stylis: 4.0.13
checksum: 60b5ce3fcc9caafa4d6a3c72079f1e4051bdfd50c44321f6263555cf5f6acc07706da5328615c1b0b52fa0dd7cf4a41bcf2ee1aae45b7aa25ad73d867f720189
languageName: node
linkType: hard
"@emotion/css@npm:^11.0.0":
version: 11.9.0
resolution: "@emotion/css@npm:11.9.0"
dependencies:
"@emotion/babel-plugin": ^11.7.1
"@emotion/cache": ^11.7.1
"@emotion/serialize": ^1.0.3
"@emotion/sheet": ^1.0.3
"@emotion/utils": ^1.0.0
peerDependencies:
"@babel/core": ^7.0.0
peerDependenciesMeta:
"@babel/core":
optional: true
checksum: 4c835e473e9dff1b9017fb6e782a356bb5b7593ef7f4c11f5827288f25ecdb970456f258c65bfa3cc7a48e6de0eea8287df605d1d8cda4b476fd5da7388e1789
languageName: node
linkType: hard
"@emotion/hash@npm:^0.8.0":
version: 0.8.0
resolution: "@emotion/hash@npm:0.8.0"
checksum: 8fd781e18428745d6c7121bebf3965cad12c61f3bd5fb773e46f16f1d7b7ae1346770df438fcfe8bc73ecf6762a54baef7cf259a694575d4f06cabb79ebcf7c0
@@ -1611,55 +1679,48 @@ __metadata:
languageName: node
linkType: hard
"@emotion/memoize@npm:0.7.4":
version: 0.7.4
resolution: "@emotion/memoize@npm:0.7.4"
checksum: 874123a94c89963dda3438d1ea7f7c17fa670d965610eefaa49e0dbf47cccee6f6108e04175867d7e485d2c04096a98bba5a4bef2606b3bf2070637327ebe3ff
"@emotion/memoize@npm:^0.7.4, @emotion/memoize@npm:^0.7.5":
version: 0.7.5
resolution: "@emotion/memoize@npm:0.7.5"
checksum: 09f5290283c50ae32f0808bebacf7eaac7ecdbd799a27698479bd03821f3acbd2ac512adcf0ae43394fed804007d6cd18de0c49475a14cd020d5f18fc7ab72bf
languageName: node
linkType: hard
"@emotion/serialize@npm:^0.11.15, @emotion/serialize@npm:^0.11.16":
version: 0.11.16
resolution: "@emotion/serialize@npm:0.11.16"
"@emotion/serialize@npm:^1.0.2, @emotion/serialize@npm:^1.0.3":
version: 1.0.3
resolution: "@emotion/serialize@npm:1.0.3"
dependencies:
"@emotion/hash": 0.8.0
"@emotion/memoize": 0.7.4
"@emotion/unitless": 0.7.5
"@emotion/utils": 0.11.3
csstype: ^2.5.7
checksum: b7d9a94a039ea5e86b0daccc29f0c40fbbda7004b1fde9ee14be38463c3aaabdd027b38c5c0e580f66883fb6e3b49160292843b0e14efe49e662a520c38f4c15
"@emotion/hash": ^0.8.0
"@emotion/memoize": ^0.7.4
"@emotion/unitless": ^0.7.5
"@emotion/utils": ^1.0.0
csstype: ^3.0.2
checksum: 4a02aaa3594cd2821f887212eb989459aea90bc47b166a1337838b76392267408dffb49b90e4d9dcb5a2b80e34ec71f0c9aea401a830604f93b7e2358c78ce2c
languageName: node
linkType: hard
"@emotion/sheet@npm:0.9.4":
version: 0.9.4
resolution: "@emotion/sheet@npm:0.9.4"
checksum: 48baa183e56d78df56ad474d833476fc26c0d563e931a82245e38975fe47ed338155d73dd5769c675aa821feef2e0480f4285c0584fc5271026f276316a299c1
"@emotion/sheet@npm:^1.0.3, @emotion/sheet@npm:^1.1.0":
version: 1.1.0
resolution: "@emotion/sheet@npm:1.1.0"
checksum: 9ffd9df2a86d0bf2b23b489adaebb38ec763be91772a25547164ed5058910270f59dc8fb8c181f2da000231deb35fc4b9cf31c00f70e15fb1ce89c76053f7f5f
languageName: node
linkType: hard
"@emotion/stylis@npm:0.8.5":
version: 0.8.5
resolution: "@emotion/stylis@npm:0.8.5"
checksum: bb43a77f784cce86f7a625519544aab56b8f341117957f7dd15315398780289784bd2ec0ba1bc1b19ac639bdb304a4ed08b1f8e3e4c13e8063b9824e551b3994
languageName: node
linkType: hard
"@emotion/unitless@npm:0.7.5":
"@emotion/unitless@npm:^0.7.5":
version: 0.7.5
resolution: "@emotion/unitless@npm:0.7.5"
checksum: 0be366ef09860037ef08aed0450cb5510f4be25886005e2f120f8e8b7385de6b41ac47df5b9bd55781e5153853e9ed5f49aa517dcbad34cc23bd8afb0201932a
languageName: node
linkType: hard
"@emotion/utils@npm:0.11.3":
version: 0.11.3
resolution: "@emotion/utils@npm:0.11.3"
checksum: b5c3a22204a878eafa8deb362493b48c3f3a7f7ec3e5a18634d14aa8d5c9d8274db0a9d0206b3c124fa170640880fbcda987971699a1cf69355f2cec994da487
"@emotion/utils@npm:^1.0.0":
version: 1.1.0
resolution: "@emotion/utils@npm:1.1.0"
checksum: ab04aafa16c7c3e0b98a68f10ca388edd4af0ca299861dc700c1b02f44e0a355f9af6732250a2772e572c607100abe6f09eec19a9a9631f50d5866c11750830a
languageName: node
linkType: hard
"@emotion/weak-memoize@npm:0.2.5":
"@emotion/weak-memoize@npm:^0.2.5":
version: 0.2.5
resolution: "@emotion/weak-memoize@npm:0.2.5"
checksum: 9fe31f0c9d761468d7868be2faf924ddef3506160c72a1979ced8f72cec5d90499403a29c904af570496ef06803e484495f84d4c311bd0787259c89dba4119ed
@@ -2147,6 +2208,16 @@ __metadata:
languageName: node
linkType: hard
"@rescript/react@npm:0.10.3":
version: 0.10.3
resolution: "@rescript/react@npm:0.10.3"
peerDependencies:
react: ">=16.8.1"
react-dom: ">=16.8.1"
checksum: 8b3ab98ed0b9122b878a4d3bf01ac2657059ad89642204948057bd822b0ff01c4631e113d57e9bf75b01fbe63a2f8dec149121643c9c3a47d9532d853fd07835
languageName: node
linkType: hard
"@sindresorhus/is@npm:^0.14.0":
version: 0.14.0
resolution: "@sindresorhus/is@npm:0.14.0"
@@ -3639,24 +3710,6 @@ __metadata:
languageName: node
linkType: hard
"babel-plugin-emotion@npm:^10.0.27":
version: 10.2.2
resolution: "babel-plugin-emotion@npm:10.2.2"
dependencies:
"@babel/helper-module-imports": ^7.0.0
"@emotion/hash": 0.8.0
"@emotion/memoize": 0.7.4
"@emotion/serialize": ^0.11.16
babel-plugin-macros: ^2.0.0
babel-plugin-syntax-jsx: ^6.18.0
convert-source-map: ^1.5.0
escape-string-regexp: ^1.0.5
find-root: ^1.1.0
source-map: ^0.5.7
checksum: 6def612598c8dbb74942271b8cb406eccad0d1a6b4ec1180f8205f5e92362b3e200bc566fdec58e37076a6a602e95c226fc9d28ed6a558fff748637cb214e3c9
languageName: node
linkType: hard
"babel-plugin-istanbul@npm:^5.1.0":
version: 5.2.0
resolution: "babel-plugin-istanbul@npm:5.2.0"
@@ -3694,7 +3747,7 @@ __metadata:
languageName: node
linkType: hard
"babel-plugin-macros@npm:^2.0.0":
"babel-plugin-macros@npm:^2.6.1":
version: 2.8.0
resolution: "babel-plugin-macros@npm:2.8.0"
dependencies:
@@ -3761,13 +3814,6 @@ __metadata:
languageName: node
linkType: hard
"babel-plugin-syntax-jsx@npm:^6.18.0":
version: 6.18.0
resolution: "babel-plugin-syntax-jsx@npm:6.18.0"
checksum: a5c8174ad6165d5f541f9f31cf4b6338ccfb7d586cec111537fa567f13b5fbdcf54f7928db44429d4610aa1be9d07bb03d017b22ba521ff819a6a2090b694797
languageName: node
linkType: hard
"babel-plugin-transform-react-remove-prop-types@npm:^0.4.24":
version: 0.4.24
resolution: "babel-plugin-transform-react-remove-prop-types@npm:0.4.24"
@@ -3884,6 +3930,7 @@ __metadata:
dependencies:
"@babel/core": 7.13.13
"@babel/preset-env": ^7.13.12
"@rescript/react": 0.10.3
"@svgr/webpack": 5.5.0
acorn-dynamic-import: ^4.0.0
babel-eslint: 10.1.0
@@ -3891,8 +3938,8 @@ __metadata:
babel-loader: 8.2.2
babel-plugin-named-asset-import: ^0.3.7
babel-preset-react-app: ^10.0.0
bs-css: ^11.0.0
bs-platform: 8.4.2
bs-css: ^15.3.0
bs-css-emotion: ^4.3.0
camelcase: ^5.2.0
case-sensitive-paths-webpack-plugin: 2.3.0
conventional-changelog-cli: ^2.1.1
@@ -3939,8 +3986,8 @@ __metadata:
react-jss: ^10.6.0
react-redux: ^7.2.3
react-window: ^1.8.6
reason-react: ^0.7.1
redux: ^4.0.5
rescript: 9.1.4
resolve: 1.17.0
semver: 6.0.0
string-replace-loader: ^2.3.0
@@ -4230,24 +4277,21 @@ __metadata:
languageName: node
linkType: hard
"bs-css@npm:^11.0.0":
version: 11.0.0
resolution: "bs-css@npm:11.0.0"
"bs-css-emotion@npm:^4.3.0":
version: 4.3.0
resolution: "bs-css-emotion@npm:4.3.0"
dependencies:
emotion: ^10.0.7
checksum: 299817a12c9d21d91b43d9309c738ff8e269eaa1f89fe725fc36dbd570072d65dfcad8a086994594b7e5ce4fc3a136ec1a36a16de612032a4cb2fc5184b6d26d
"@emotion/cache": ^11.0.0
"@emotion/css": ^11.0.0
bs-css: 15.3.0
checksum: df84803a9a8716010778999daf9283de29268ca1fcbaa558a95b312ba811f7c701845230bd2f50f6c19424528e8c3484c344d2ec702542687f32bdc9ab5f3c11
languageName: node
linkType: hard
"bs-platform@npm:8.4.2":
version: 8.4.2
resolution: "bs-platform@npm:8.4.2"
bin:
bsb: bsb
bsc: bsc
bsrefmt: bsrefmt
bstracing: lib/bstracing
checksum: 60f84c4c63809031d2e8609f90ef877174b9461d8ca8e3697c921548648a7187ddc977ce210dd2df91568c5ffc46a8ffe16dcf8836ddb6637d6dcbaaef306a08
"bs-css@npm:15.3.0, bs-css@npm:^15.3.0":
version: 15.3.0
resolution: "bs-css@npm:15.3.0"
checksum: 0b9b954541dd48d5b0d3accaad222c110d905725c7356f3dfd695296d75582ae976904fb80dedddbe1864803ef9cf6e221fbb6b4508e7740134da8bc0e4d5392
languageName: node
linkType: hard
@@ -5465,18 +5509,6 @@ __metadata:
languageName: node
linkType: hard
"create-emotion@npm:^10.0.27":
version: 10.0.27
resolution: "create-emotion@npm:10.0.27"
dependencies:
"@emotion/cache": ^10.0.27
"@emotion/serialize": ^0.11.15
"@emotion/sheet": 0.9.4
"@emotion/utils": 0.11.3
checksum: 59581a54fe4f6da6bcbae502df852f0cf0dc6f002aaf416701266a4d944e63d9e33ac0d733b35d8a6265f4052dfdba67222ae341e8f9382afb2e40ed03831053
languageName: node
linkType: hard
"create-hash@npm:^1.1.0, create-hash@npm:^1.1.2, create-hash@npm:^1.2.0":
version: 1.2.0
resolution: "create-hash@npm:1.2.0"
@@ -5694,13 +5726,6 @@ __metadata:
languageName: node
linkType: hard
"csstype@npm:^2.5.7":
version: 2.6.20
resolution: "csstype@npm:2.6.20"
checksum: 6f7ec09b01460f266d32b6963c386c0412b29775db5a1137986c85daf5df8db395d4b04d5e10b57f358e838fec278c9361263be7a675c67efa4fcb873fa073b3
languageName: node
linkType: hard
"csstype@npm:^3.0.2":
version: 3.0.11
resolution: "csstype@npm:3.0.11"
@@ -6507,16 +6532,6 @@ __metadata:
languageName: node
linkType: hard
"emotion@npm:^10.0.7":
version: 10.0.27
resolution: "emotion@npm:10.0.27"
dependencies:
babel-plugin-emotion: ^10.0.27
create-emotion: ^10.0.27
checksum: cc542b2df96732b0c60683a60c94e42d226ac37854c73d33c6e32f596dce52261a4c07a841edf621f44166a4b5a146e4cb18d294ae6759a5522f80e2dba5cd44
languageName: node
linkType: hard
"encodeurl@npm:^1.0.2, encodeurl@npm:~1.0.2":
version: 1.0.2
resolution: "encodeurl@npm:1.0.2"
@@ -13082,7 +13097,7 @@ fsevents@^1.2.7:
languageName: node
linkType: hard
"react-dom@npm:>=16.8.1, react-dom@npm:^17.0.2":
"react-dom@npm:^17.0.2":
version: 17.0.2
resolution: "react-dom@npm:17.0.2"
dependencies:
@@ -13171,7 +13186,7 @@ fsevents@^1.2.7:
languageName: node
linkType: hard
"react@npm:>=16.8.1, react@npm:^17.0.2":
"react@npm:^17.0.2":
version: 17.0.2
resolution: "react@npm:17.0.2"
dependencies:
@@ -13303,16 +13318,6 @@ fsevents@^1.2.7:
languageName: node
linkType: hard
"reason-react@npm:^0.7.1":
version: 0.7.1
resolution: "reason-react@npm:0.7.1"
dependencies:
react: ">=16.8.1"
react-dom: ">=16.8.1"
checksum: 66f0aa4817ad90db10e61dd18e7e6c5ee517dd54f6ad39c69cfdcfba03aecc2e330a3299fd735a3e7c781ae8f0b7b818c1eabcd42b38abae4eb23db3ddbece16
languageName: node
linkType: hard
"recursive-readdir@npm:2.2.2":
version: 2.2.2
resolution: "recursive-readdir@npm:2.2.2"
@@ -13571,6 +13576,18 @@ fsevents@^1.2.7:
languageName: node
linkType: hard
"rescript@npm:9.1.4":
version: 9.1.4
resolution: "rescript@npm:9.1.4"
bin:
bsc: bsc
bsrefmt: bsrefmt
bstracing: lib/bstracing
rescript: rescript
checksum: 96638bedf42159713b67dde4cd7c2008775f1f310ba41f65d879521983a011bcfbfb7ab0b404af9084ff25ccedc14f2289de8805002fe95154a11e75cbe87145
languageName: node
linkType: hard
"resolve-cwd@npm:^2.0.0":
version: 2.0.0
resolution: "resolve-cwd@npm:2.0.0"
@@ -14859,6 +14876,13 @@ resolve@^2.0.0-next.3:
languageName: node
linkType: hard
"stylis@npm:4.0.13":
version: 4.0.13
resolution: "stylis@npm:4.0.13"
checksum: 2c91ba8bd0de9c210d7a8ef94a0fbbef954f3e64af7185e21f6aa003d27d56f5073a7fb09075857e6ea056ca6c56feb8e55e8e9ddf44cea0f58d76e3b07fc3a8
languageName: node
linkType: hard
"sumchecker@npm:^3.0.1":
version: 3.0.1
resolution: "sumchecker@npm:3.0.1"