mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-11-09 20:44:03 -08:00
feat: withings segment
This commit is contained in:
parent
e5bf5db9c2
commit
2ec6b085fd
|
@ -53,7 +53,6 @@ if you do `Codespaces: Rebuild Container` again, you'll be back to the latest st
|
|||
[docs]: https://ohmyposh.dev/docs
|
||||
[guide]: https://ohmyposh.dev/docs/contributing/started
|
||||
[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary
|
||||
[conduct]: mailto:conduct@ohmyposh.dev
|
||||
[codespaces]: https://github.com/features/codespaces
|
||||
[devcontainer-ext]: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers
|
||||
[timezones]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
|
|
|
@ -190,6 +190,8 @@ const (
|
|||
WIFI SegmentType = "wifi"
|
||||
// WINREG queries the Windows registry.
|
||||
WINREG SegmentType = "winreg"
|
||||
// WITHINGS queries the Withings API.
|
||||
WITHINGS SegmentType = "withings"
|
||||
// YTM writes YouTube Music information and status
|
||||
YTM SegmentType = "ytm"
|
||||
)
|
||||
|
@ -318,6 +320,7 @@ func (segment *Segment) mapSegmentWithWriter(env environment.Environment) error
|
|||
WAKATIME: &segments.Wakatime{},
|
||||
WIFI: &segments.Wifi{},
|
||||
WINREG: &segments.WindowsRegistry{},
|
||||
WITHINGS: &segments.Withings{},
|
||||
YTM: &segments.Ytm{},
|
||||
}
|
||||
if segment.Properties == nil {
|
||||
|
|
|
@ -92,7 +92,7 @@ func uint32ToFloat64(num uint32) (float64, error) {
|
|||
}
|
||||
|
||||
func setupDiSetup(proc *windows.LazyProc, nargs, a1, a2, a3, a4, a5, a6 uintptr) (uintptr, error) {
|
||||
r1, _, errno := syscall.Syscall6(proc.Addr(), nargs, a1, a2, a3, a4, a5, a6)
|
||||
r1, _, errno := syscall.SyscallN(proc.Addr(), nargs, a1, a2, a3, a4, a5, a6)
|
||||
if windows.Handle(r1) == windows.InvalidHandle {
|
||||
if errno != 0 {
|
||||
return 0, error(errno)
|
||||
|
@ -103,7 +103,7 @@ func setupDiSetup(proc *windows.LazyProc, nargs, a1, a2, a3, a4, a5, a6 uintptr)
|
|||
}
|
||||
|
||||
func setupDiCall(proc *windows.LazyProc, nargs, a1, a2, a3, a4, a5, a6 uintptr) syscall.Errno {
|
||||
r1, _, errno := syscall.Syscall6(proc.Addr(), nargs, a1, a2, a3, a4, a5, a6)
|
||||
r1, _, errno := syscall.SyscallN(proc.Addr(), nargs, a1, a2, a3, a4, a5, a6)
|
||||
if r1 == 0 {
|
||||
if errno != 0 {
|
||||
return errno
|
||||
|
@ -148,7 +148,7 @@ func systemGet(idx int) (*battery, error) {
|
|||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_, _, _ = syscall.Syscall(setupDiDestroyDeviceInfoList.Addr(), 1, hdev, 0, 0)
|
||||
_, _, _ = syscall.SyscallN(setupDiDestroyDeviceInfoList.Addr(), 1, hdev, 0, 0)
|
||||
}()
|
||||
|
||||
var did spDeviceInterfaceData
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"oh-my-posh/environment/battery"
|
||||
"oh-my-posh/environment/cmd"
|
||||
"oh-my-posh/regex"
|
||||
|
@ -166,7 +167,7 @@ type Environment interface {
|
|||
BatteryState() (*battery.Info, error)
|
||||
QueryWindowTitles(processName, windowTitleRegex string) (string, error)
|
||||
WindowsRegistryKeyValue(path string) (*WindowsRegistryValue, error)
|
||||
HTTPRequest(url string, timeout int, requestModifiers ...HTTPRequestModifier) ([]byte, error)
|
||||
HTTPRequest(url string, body io.Reader, timeout int, requestModifiers ...HTTPRequestModifier) ([]byte, error)
|
||||
IsWsl() bool
|
||||
IsWsl2() bool
|
||||
StackCount() int
|
||||
|
@ -271,7 +272,7 @@ func (env *ShellEnvironment) resolveConfigPath() {
|
|||
func (env *ShellEnvironment) downloadConfig(location string) error {
|
||||
defer env.Trace(time.Now(), "downloadConfig", location)
|
||||
configPath := filepath.Join(env.CachePath(), "config.omp.json")
|
||||
cfg, err := env.HTTPRequest(location, 5000)
|
||||
cfg, err := env.HTTPRequest(location, nil, 5000)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -572,17 +573,21 @@ func (env *ShellEnvironment) Shell() string {
|
|||
return env.CmdFlags.Shell
|
||||
}
|
||||
|
||||
func (env *ShellEnvironment) HTTPRequest(targetURL string, timeout int, requestModifiers ...HTTPRequestModifier) ([]byte, error) {
|
||||
func (env *ShellEnvironment) HTTPRequest(targetURL string, body io.Reader, timeout int, requestModifiers ...HTTPRequestModifier) ([]byte, error) {
|
||||
defer env.Trace(time.Now(), "HTTPRequest", targetURL)
|
||||
ctx, cncl := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(timeout))
|
||||
defer cncl()
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, targetURL, nil)
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, targetURL, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, modifier := range requestModifiers {
|
||||
modifier(request)
|
||||
}
|
||||
if env.CmdFlags.Debug {
|
||||
dump, _ := httputil.DumpRequestOut(request, true)
|
||||
env.Log(Debug, "HTTPRequest", string(dump))
|
||||
}
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
env.Log(Error, "HTTPRequest", err.Error())
|
||||
|
@ -596,13 +601,13 @@ func (env *ShellEnvironment) HTTPRequest(targetURL string, timeout int, requestM
|
|||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
body, err := io.ReadAll(response.Body)
|
||||
responseBody, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
env.Log(Error, "HTTPRequest", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
env.Log(Debug, "HTTPRequest", string(body))
|
||||
return body, nil
|
||||
env.Log(Debug, "HTTPRequest", string(responseBody))
|
||||
return responseBody, nil
|
||||
}
|
||||
|
||||
func (env *ShellEnvironment) HasParentFilePath(path string) (*FileInfo, error) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"oh-my-posh/environment"
|
||||
"oh-my-posh/properties"
|
||||
|
@ -45,11 +46,11 @@ func (o *OAuth) error(err error) {
|
|||
|
||||
func (o *OAuth) getAccessToken() (string, error) {
|
||||
// get directly from cache
|
||||
if acccessToken, OK := o.Env.Cache().Get(o.AccessTokenKey); OK {
|
||||
if acccessToken, OK := o.Env.Cache().Get(o.AccessTokenKey); OK && len(acccessToken) != 0 {
|
||||
return acccessToken, nil
|
||||
}
|
||||
// use cached refresh token to get new access token
|
||||
if refreshToken, OK := o.Env.Cache().Get(o.RefreshTokenKey); OK {
|
||||
if refreshToken, OK := o.Env.Cache().Get(o.RefreshTokenKey); OK && len(refreshToken) != 0 {
|
||||
if acccessToken, err := o.refreshToken(refreshToken); err == nil {
|
||||
return acccessToken, nil
|
||||
}
|
||||
|
@ -70,7 +71,7 @@ func (o *OAuth) getAccessToken() (string, error) {
|
|||
func (o *OAuth) refreshToken(refreshToken string) (string, error) {
|
||||
httpTimeout := o.Props.GetInt(properties.HTTPTimeout, properties.DefaultHTTPTimeout)
|
||||
url := fmt.Sprintf("https://ohmyposh.dev/api/refresh?segment=%s&token=%s", o.SegmentName, refreshToken)
|
||||
body, err := o.Env.HTTPRequest(url, httpTimeout)
|
||||
body, err := o.Env.HTTPRequest(url, nil, httpTimeout)
|
||||
if err != nil {
|
||||
return "", &OAuthError{
|
||||
// This might happen if /api was asleep. Assume the user will just retry
|
||||
|
@ -90,7 +91,7 @@ func (o *OAuth) refreshToken(refreshToken string) (string, error) {
|
|||
return tokens.AccessToken, nil
|
||||
}
|
||||
|
||||
func OauthResult[a any](o *OAuth, url string) (a, error) {
|
||||
func OauthResult[a any](o *OAuth, url string, body io.Reader, requestModifiers ...environment.HTTPRequestModifier) (a, error) {
|
||||
var data a
|
||||
|
||||
getCacheValue := func(key string) (a, error) {
|
||||
|
@ -126,20 +127,26 @@ func OauthResult[a any](o *OAuth, url string) (a, error) {
|
|||
request.Header.Add("Authorization", "Bearer "+accessToken)
|
||||
}
|
||||
|
||||
body, err := o.Env.HTTPRequest(url, httpTimeout, addAuthHeader)
|
||||
if requestModifiers == nil {
|
||||
requestModifiers = []environment.HTTPRequestModifier{}
|
||||
}
|
||||
|
||||
requestModifiers = append(requestModifiers, addAuthHeader)
|
||||
|
||||
responseBody, err := o.Env.HTTPRequest(url, body, httpTimeout, requestModifiers...)
|
||||
if err != nil {
|
||||
o.error(err)
|
||||
return data, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &data)
|
||||
err = json.Unmarshal(responseBody, &data)
|
||||
if err != nil {
|
||||
o.error(err)
|
||||
return data, err
|
||||
}
|
||||
|
||||
if cacheTimeout > 0 {
|
||||
o.Env.Cache().Set(url, string(body), cacheTimeout)
|
||||
o.Env.Cache().Set(url, string(responseBody), cacheTimeout)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
|
|
|
@ -171,7 +171,7 @@ func TestOauthResult(t *testing.T) {
|
|||
SegmentName: "test",
|
||||
}
|
||||
|
||||
got, err := OauthResult[*data](oauth, url)
|
||||
got, err := OauthResult[*data](oauth, url, nil)
|
||||
assert.Equal(t, tc.ExpectedData, got, tc.Case)
|
||||
if len(tc.ExpectedErrorMessage) == 0 {
|
||||
assert.Nil(t, err, tc.Case)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"oh-my-posh/environment"
|
||||
"oh-my-posh/environment/battery"
|
||||
|
@ -143,7 +144,7 @@ func (env *MockedEnvironment) WindowsRegistryKeyValue(path string) (*environment
|
|||
return args.Get(0).(*environment.WindowsRegistryValue), args.Error(1)
|
||||
}
|
||||
|
||||
func (env *MockedEnvironment) HTTPRequest(url string, timeout int, requestModifiers ...environment.HTTPRequestModifier) ([]byte, error) {
|
||||
func (env *MockedEnvironment) HTTPRequest(url string, body io.Reader, timeout int, requestModifiers ...environment.HTTPRequestModifier) ([]byte, error) {
|
||||
args := env.Called(url)
|
||||
return args.Get(0).([]byte), args.Error(1)
|
||||
}
|
||||
|
|
|
@ -258,7 +258,7 @@ func (bf *Brewfather) getResult() (*Batch, error) {
|
|||
addAuthHeader := func(request *http.Request) {
|
||||
request.Header.Add("authorization", authHeader)
|
||||
}
|
||||
body, err := bf.env.HTTPRequest(batchURL, httpTimeout, addAuthHeader)
|
||||
body, err := bf.env.HTTPRequest(batchURL, nil, httpTimeout, addAuthHeader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -270,7 +270,7 @@ func (bf *Brewfather) getResult() (*Batch, error) {
|
|||
}
|
||||
|
||||
// readings
|
||||
body, err = bf.env.HTTPRequest(batchReadingsURL, httpTimeout, addAuthHeader)
|
||||
body, err = bf.env.HTTPRequest(batchReadingsURL, nil, httpTimeout, addAuthHeader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ func (i *IPify) getResult() (string, error) {
|
|||
|
||||
httpTimeout := i.props.GetInt(properties.HTTPTimeout, properties.DefaultHTTPTimeout)
|
||||
|
||||
body, err := i.env.HTTPRequest(url, httpTimeout)
|
||||
body, err := i.env.HTTPRequest(url, nil, httpTimeout)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ func (ns *Nightscout) getResult() (*NightscoutData, error) {
|
|||
}
|
||||
}
|
||||
|
||||
body, err := ns.env.HTTPRequest(url, httpTimeout)
|
||||
body, err := ns.env.HTTPRequest(url, nil, httpTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ func (d *Owm) getResult() (*owmDataResponse, error) {
|
|||
httpTimeout := d.props.GetInt(properties.HTTPTimeout, properties.DefaultHTTPTimeout)
|
||||
d.URL = fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather?q=%s&units=%s&appid=%s", location, units, apikey)
|
||||
|
||||
body, err := d.env.HTTPRequest(d.URL, httpTimeout)
|
||||
body, err := d.env.HTTPRequest(d.URL, nil, httpTimeout)
|
||||
if err != nil {
|
||||
return new(owmDataResponse), err
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ type stravaAPI struct {
|
|||
|
||||
func (s *stravaAPI) GetActivities() ([]*StravaData, error) {
|
||||
url := "https://www.strava.com/api/v3/athlete/activities?page=1&per_page=1"
|
||||
return http.OauthResult[[]*StravaData](&s.OAuth, url)
|
||||
return http.OauthResult[[]*StravaData](&s.OAuth, url, nil)
|
||||
}
|
||||
|
||||
// segment struct, makes templating easier
|
||||
|
|
|
@ -49,7 +49,7 @@ func (w *Wakatime) setAPIData() error {
|
|||
|
||||
httpTimeout := w.props.GetInt(properties.HTTPTimeout, properties.DefaultHTTPTimeout)
|
||||
|
||||
body, err := w.env.HTTPRequest(url, httpTimeout)
|
||||
body, err := w.env.HTTPRequest(url, nil, httpTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
219
src/segments/withings.go
Normal file
219
src/segments/withings.go
Normal file
|
@ -0,0 +1,219 @@
|
|||
package segments
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"oh-my-posh/environment"
|
||||
"oh-my-posh/http"
|
||||
"oh-my-posh/properties"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
http2 "net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// WithingsData struct contains the API data
|
||||
type WithingsData struct {
|
||||
Status int `json:"status"`
|
||||
Body *Body `json:"body"`
|
||||
}
|
||||
|
||||
type Body struct {
|
||||
MeasureGroups []*MeasureGroup `json:"measuregrps"`
|
||||
Activities []*Activity `json:"activities"`
|
||||
Series []*Series `json:"series"`
|
||||
}
|
||||
|
||||
type MeasureGroup struct {
|
||||
Measures []*Measure `json:"measures"`
|
||||
Comment interface{} `json:"comment"`
|
||||
}
|
||||
|
||||
type Measure struct {
|
||||
Value int `json:"value"`
|
||||
Type int `json:"type"`
|
||||
Unit int `json:"unit"`
|
||||
}
|
||||
|
||||
type Series struct {
|
||||
Startdate int64 `json:"startdate"`
|
||||
Enddate int64 `json:"enddate"`
|
||||
}
|
||||
|
||||
type Activity struct {
|
||||
Date string `json:"date"`
|
||||
Timezone string `json:"timezone"`
|
||||
Deviceid string `json:"deviceid"`
|
||||
HashDeviceid string `json:"hash_deviceid"`
|
||||
Brand int `json:"brand"`
|
||||
IsTracker bool `json:"is_tracker"`
|
||||
Steps int `json:"steps"`
|
||||
Distance int `json:"distance"`
|
||||
Elevation int `json:"elevation"`
|
||||
Soft int `json:"soft"`
|
||||
Moderate int `json:"moderate"`
|
||||
Intense int `json:"intense"`
|
||||
Active int `json:"active"`
|
||||
Calories int `json:"calories"`
|
||||
Totalcalories int `json:"totalcalories"`
|
||||
HrAverage int `json:"hr_average"`
|
||||
HrMin int `json:"hr_min"`
|
||||
HrMax int `json:"hr_max"`
|
||||
HrZone0 int `json:"hr_zone_0"`
|
||||
HrZone1 int `json:"hr_zone_1"`
|
||||
HrZone2 int `json:"hr_zone_2"`
|
||||
HrZone3 int `json:"hr_zone_3"`
|
||||
}
|
||||
|
||||
// WithingsAPI is a wrapper around http.Oauth
|
||||
type WithingsAPI interface {
|
||||
GetMeasures(meastypes string) (*WithingsData, error)
|
||||
GetActivities(activities string) (*WithingsData, error)
|
||||
GetSleep() (*WithingsData, error)
|
||||
}
|
||||
|
||||
type withingsAPI struct {
|
||||
*http.OAuth
|
||||
}
|
||||
|
||||
func (w *withingsAPI) GetMeasures(meastypes string) (*WithingsData, error) {
|
||||
twoWeeksAgo := strconv.FormatInt(time.Now().AddDate(0, 0, -14).Unix(), 10)
|
||||
formData := url.Values{
|
||||
"meastypes": {meastypes},
|
||||
"action": {"getmeas"},
|
||||
"lastupdate": {twoWeeksAgo},
|
||||
"category": {"1"},
|
||||
}
|
||||
return w.getWithingsData("https://wbsapi.withings.net/measure", formData)
|
||||
}
|
||||
|
||||
func (w *withingsAPI) GetActivities(activities string) (*WithingsData, error) {
|
||||
yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
|
||||
today := time.Now().Format("2006-01-02")
|
||||
formData := url.Values{
|
||||
"data_fields": {activities},
|
||||
"action": {"getactivity"},
|
||||
"startdateymd": {yesterday},
|
||||
"enddateymd": {today},
|
||||
"category": {"1"},
|
||||
}
|
||||
return w.getWithingsData("https://wbsapi.withings.net/v2/measure", formData)
|
||||
}
|
||||
|
||||
func (w *withingsAPI) GetSleep() (*WithingsData, error) {
|
||||
now := time.Now()
|
||||
formData := url.Values{
|
||||
"action": {"get"},
|
||||
"startdate": {strconv.FormatInt(now.AddDate(0, -1, 0).Unix(), 10)},
|
||||
"enddate": {strconv.FormatInt(now.Unix(), 10)},
|
||||
}
|
||||
return w.getWithingsData("https://wbsapi.withings.net/v2/sleep", formData)
|
||||
}
|
||||
|
||||
func (w *withingsAPI) getWithingsData(endpoint string, formData url.Values) (*WithingsData, error) {
|
||||
modifiers := func(request *http2.Request) {
|
||||
request.Method = http2.MethodPost
|
||||
request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
}
|
||||
body := strings.NewReader(formData.Encode())
|
||||
data, err := http.OauthResult[*WithingsData](w.OAuth, endpoint, body, modifiers)
|
||||
if data != nil && data.Status != 0 {
|
||||
return nil, errors.New("Withings API error: " + strconv.Itoa(data.Status))
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
|
||||
type Withings struct {
|
||||
props properties.Properties
|
||||
|
||||
Weight float64
|
||||
SleepHours string
|
||||
Steps int
|
||||
|
||||
api WithingsAPI
|
||||
}
|
||||
|
||||
const (
|
||||
WithingsAccessTokenKey = "withings_access_token"
|
||||
WithingsRefreshTokenKey = "withings_refresh_token"
|
||||
)
|
||||
|
||||
func (w *Withings) Template() string {
|
||||
return "{{ if gt .Weight 0.0 }} {{ round .Weight 2 }}kg {{ end }}"
|
||||
}
|
||||
|
||||
func (w *Withings) Enabled() bool {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(3)
|
||||
functions := []func() bool{
|
||||
w.getMeasures,
|
||||
w.getActivities,
|
||||
w.getSleep,
|
||||
}
|
||||
var enabled bool
|
||||
for _, function := range functions {
|
||||
go func(f func() bool) {
|
||||
defer wg.Done()
|
||||
success := f()
|
||||
if success {
|
||||
enabled = true
|
||||
}
|
||||
}(function)
|
||||
}
|
||||
wg.Wait()
|
||||
return enabled
|
||||
}
|
||||
|
||||
func (w *Withings) getMeasures() bool {
|
||||
data, err := w.api.GetMeasures("1")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// no data
|
||||
if len(data.Body.MeasureGroups) == 0 || len(data.Body.MeasureGroups[0].Measures) == 0 {
|
||||
return false
|
||||
}
|
||||
measure := data.Body.MeasureGroups[0].Measures[0]
|
||||
weight := measure.Value
|
||||
w.Weight = float64(weight) / math.Pow(10, math.Abs(float64(measure.Unit)))
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *Withings) getActivities() bool {
|
||||
data, err := w.api.GetActivities("steps")
|
||||
if err != nil || len(data.Body.Activities) == 0 {
|
||||
return false
|
||||
}
|
||||
w.Steps = data.Body.Activities[0].Steps
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *Withings) getSleep() bool {
|
||||
data, err := w.api.GetSleep()
|
||||
if err != nil || len(data.Body.Series) == 0 {
|
||||
return false
|
||||
}
|
||||
sleepStart := time.Unix(data.Body.Series[0].Startdate, 0)
|
||||
sleepEnd := time.Unix(data.Body.Series[0].Enddate, 0)
|
||||
sleepHours := sleepEnd.Sub(sleepStart).Hours()
|
||||
w.SleepHours = fmt.Sprintf("%0.1f", sleepHours)
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *Withings) Init(props properties.Properties, env environment.Environment) {
|
||||
w.props = props
|
||||
|
||||
w.api = &withingsAPI{
|
||||
OAuth: &http.OAuth{
|
||||
Props: props,
|
||||
Env: env,
|
||||
AccessTokenKey: WithingsAccessTokenKey,
|
||||
RefreshTokenKey: WithingsRefreshTokenKey,
|
||||
SegmentName: "withings",
|
||||
},
|
||||
}
|
||||
}
|
163
src/segments/withings_test.go
Normal file
163
src/segments/withings_test.go
Normal file
|
@ -0,0 +1,163 @@
|
|||
package segments
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"oh-my-posh/mock"
|
||||
"oh-my-posh/properties"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
mock2 "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
type mockedWithingsAPI struct {
|
||||
mock2.Mock
|
||||
}
|
||||
|
||||
func (s *mockedWithingsAPI) GetMeasures(meastypes string) (*WithingsData, error) {
|
||||
args := s.Called(meastypes)
|
||||
return args.Get(0).(*WithingsData), args.Error(1)
|
||||
}
|
||||
|
||||
func (s *mockedWithingsAPI) GetActivities(activities string) (*WithingsData, error) {
|
||||
args := s.Called(activities)
|
||||
return args.Get(0).(*WithingsData), args.Error(1)
|
||||
}
|
||||
|
||||
func (s *mockedWithingsAPI) GetSleep() (*WithingsData, error) {
|
||||
args := s.Called()
|
||||
return args.Get(0).(*WithingsData), args.Error(1)
|
||||
}
|
||||
|
||||
func TestWithingsSegment(t *testing.T) {
|
||||
cases := []struct {
|
||||
Case string
|
||||
ExpectedString string
|
||||
ExpectedEnabled bool
|
||||
Template string
|
||||
MeasuresError error
|
||||
ActivitiesError error
|
||||
SleepError error
|
||||
WithingsData *WithingsData
|
||||
}{
|
||||
{
|
||||
Case: "Error",
|
||||
MeasuresError: errors.New("error"),
|
||||
ActivitiesError: errors.New("error"),
|
||||
SleepError: errors.New("error"),
|
||||
ExpectedEnabled: false,
|
||||
},
|
||||
{
|
||||
Case: "Only Measures data",
|
||||
WithingsData: &WithingsData{
|
||||
Body: &Body{
|
||||
MeasureGroups: []*MeasureGroup{
|
||||
{
|
||||
Measures: []*Measure{
|
||||
{
|
||||
Value: 7077,
|
||||
Unit: -2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ActivitiesError: errors.New("error"),
|
||||
SleepError: errors.New("error"),
|
||||
ExpectedEnabled: true,
|
||||
ExpectedString: "70.77kg",
|
||||
},
|
||||
{
|
||||
Case: "Measures, no data",
|
||||
WithingsData: &WithingsData{
|
||||
Body: &Body{},
|
||||
},
|
||||
ActivitiesError: errors.New("error"),
|
||||
SleepError: errors.New("error"),
|
||||
ExpectedEnabled: false,
|
||||
},
|
||||
{
|
||||
Case: "Activities",
|
||||
Template: "{{ .Steps }} steps",
|
||||
ExpectedString: "7077 steps",
|
||||
WithingsData: &WithingsData{
|
||||
Body: &Body{
|
||||
Activities: []*Activity{
|
||||
{
|
||||
Steps: 7077,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MeasuresError: errors.New("error"),
|
||||
SleepError: errors.New("error"),
|
||||
ExpectedEnabled: true,
|
||||
},
|
||||
{
|
||||
Case: "Sleep",
|
||||
Template: "{{ .SleepHours }}hr",
|
||||
ExpectedString: "11.8hr",
|
||||
WithingsData: &WithingsData{
|
||||
Body: &Body{
|
||||
Series: []*Series{
|
||||
{
|
||||
Startdate: 1594159200,
|
||||
Enddate: 1594201500,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MeasuresError: errors.New("error"),
|
||||
ActivitiesError: errors.New("error"),
|
||||
ExpectedEnabled: true,
|
||||
},
|
||||
{
|
||||
Case: "Sleep and Activity",
|
||||
Template: "{{ .Steps }} steps with {{ .SleepHours }}hr of sleep",
|
||||
ExpectedString: "976 steps with 11.8hr of sleep",
|
||||
WithingsData: &WithingsData{
|
||||
Body: &Body{
|
||||
Series: []*Series{
|
||||
{
|
||||
Startdate: 1594159200,
|
||||
Enddate: 1594201500,
|
||||
},
|
||||
},
|
||||
Activities: []*Activity{
|
||||
{
|
||||
Steps: 976,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MeasuresError: errors.New("error"),
|
||||
ExpectedEnabled: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
api := &mockedWithingsAPI{}
|
||||
api.On("GetMeasures", "1").Return(tc.WithingsData, tc.MeasuresError)
|
||||
api.On("GetActivities", "steps").Return(tc.WithingsData, tc.ActivitiesError)
|
||||
api.On("GetSleep").Return(tc.WithingsData, tc.SleepError)
|
||||
|
||||
withings := &Withings{
|
||||
api: api,
|
||||
props: &properties.Map{},
|
||||
}
|
||||
|
||||
enabled := withings.Enabled()
|
||||
assert.Equal(t, tc.ExpectedEnabled, enabled, tc.Case)
|
||||
if !enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
if tc.Template == "" {
|
||||
tc.Template = withings.Template()
|
||||
}
|
||||
|
||||
var got = renderTemplate(&mock.MockedEnvironment{}, tc.Template, withings)
|
||||
assert.Equal(t, tc.ExpectedString, got, tc.Case)
|
||||
}
|
||||
}
|
|
@ -68,7 +68,7 @@ func (y *Ytm) setStatus() error {
|
|||
// https://github.com/ytmdesktop/ytmdesktop/wiki/Remote-Control-API
|
||||
url := y.props.GetString(APIURL, "http://127.0.0.1:9863")
|
||||
httpTimeout := y.props.GetInt(APIURL, properties.DefaultHTTPTimeout)
|
||||
body, err := y.env.HTTPRequest(url+"/query", httpTimeout)
|
||||
body, err := y.env.HTTPRequest(url+"/query", nil, httpTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -54,6 +54,30 @@
|
|||
"description": "Timeout value to use for http request",
|
||||
"default": 20
|
||||
},
|
||||
"cache_timeout": {
|
||||
"type": "integer",
|
||||
"title": "cache timeout",
|
||||
"description": "The number of minutes the response is cached. A value of 0 disables the cache.",
|
||||
"default": 10
|
||||
},
|
||||
"expires_in": {
|
||||
"type": "integer",
|
||||
"title": "Expires in",
|
||||
"description": "Access token expiration time in seconds",
|
||||
"default": 0
|
||||
},
|
||||
"access_token": {
|
||||
"type": "string",
|
||||
"title": "Access token",
|
||||
"description": "The initial access token",
|
||||
"default": ""
|
||||
},
|
||||
"refresh_token": {
|
||||
"type": "string",
|
||||
"title": "Refresh token",
|
||||
"description": "The initial refresh token",
|
||||
"default": ""
|
||||
},
|
||||
"display_mode": {
|
||||
"type": "string",
|
||||
"title": "Display Mode",
|
||||
|
@ -251,6 +275,7 @@
|
|||
"wakatime",
|
||||
"wifi",
|
||||
"winreg",
|
||||
"withings",
|
||||
"ytm"
|
||||
]
|
||||
},
|
||||
|
@ -1828,10 +1853,7 @@
|
|||
"$ref": "#/definitions/http_timeout"
|
||||
},
|
||||
"cache_timeout": {
|
||||
"type": "integer",
|
||||
"title": "cache timeout",
|
||||
"description": "The number of minutes the response is cached. A value of 0 disables the cache.",
|
||||
"default": 10
|
||||
"$ref": "#/definitions/cache_timeout"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1976,10 +1998,16 @@
|
|||
"$ref": "#/definitions/http_timeout"
|
||||
},
|
||||
"cache_timeout": {
|
||||
"type": "integer",
|
||||
"title": "cache timeout",
|
||||
"description": "The number of minutes the response is cached. A value of 0 disables the cache.",
|
||||
"default": 10
|
||||
"$ref": "#/definitions/cache_timeout"
|
||||
},
|
||||
"access_token": {
|
||||
"$ref": "#/definitions/access_token"
|
||||
},
|
||||
"refresh_token": {
|
||||
"$ref": "#/definitions/refresh_token"
|
||||
},
|
||||
"expires_in": {
|
||||
"$ref": "#/definitions/expires_in"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2097,10 +2125,7 @@
|
|||
"$ref": "#/definitions/http_timeout"
|
||||
},
|
||||
"cache_timeout": {
|
||||
"type": "integer",
|
||||
"title": "cache timeout",
|
||||
"description": "The number of minutes the response is cached. A value of 0 disables the cache.",
|
||||
"default": 10
|
||||
"$ref": "#/definitions/cache_timeout"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2151,6 +2176,40 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "withings"
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"title": "Display activity data from Withings",
|
||||
"description": "https://ohmyposh.dev/docs/segments/withings",
|
||||
"properties": {
|
||||
"properties": {
|
||||
"properties": {
|
||||
"http_timeout": {
|
||||
"$ref": "#/definitions/http_timeout"
|
||||
},
|
||||
"cache_timeout": {
|
||||
"$ref": "#/definitions/cache_timeout"
|
||||
},
|
||||
"access_token": {
|
||||
"$ref": "#/definitions/access_token"
|
||||
},
|
||||
"refresh_token": {
|
||||
"$ref": "#/definitions/refresh_token"
|
||||
},
|
||||
"expires_in": {
|
||||
"$ref": "#/definitions/expires_in"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
|
@ -2248,10 +2307,7 @@
|
|||
"$ref": "#/definitions/http_timeout"
|
||||
},
|
||||
"cache_timeout": {
|
||||
"type": "integer",
|
||||
"title": "cache timeout in minutes",
|
||||
"description": "The number of minutes the response is cached. A value of 0 disables the cache.",
|
||||
"default": 5
|
||||
"$ref": "#/definitions/cache_timeout"
|
||||
},
|
||||
"doubleup_icon": {
|
||||
"type": "string",
|
||||
|
@ -2360,10 +2416,7 @@
|
|||
"$ref": "#/definitions/http_timeout"
|
||||
},
|
||||
"cache_timeout": {
|
||||
"type": "integer",
|
||||
"title": "cache timeout",
|
||||
"description": "The number of minutes the response is cached. A value of 0 disables the cache.",
|
||||
"default": 10
|
||||
"$ref": "#/definitions/cache_timeout"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,6 @@ can get started even without having to understand the theming. So, let's no long
|
|||
installation guide to get started right away!
|
||||
|
||||
[omp]: https://github.com/JanDeDobbeleer/oh-my-posh2
|
||||
[omz]: https://github.com/ohmyzsh/ohmyzsh
|
||||
[block]: /docs/configuration/block
|
||||
[segment]: /docs/configuration/segment
|
||||
[themes]: https://github.com/JanDeDobbeleer/oh-my-posh/tree/main/themes
|
||||
|
|
|
@ -82,4 +82,3 @@ Only winget and scoop add the environment variable `POSH_THEMES_PATH` automatica
|
|||
[windows]: /docs/installation/windows
|
||||
[macos]: /docs/installation/macos
|
||||
[linux]: /docs/installation/linux
|
||||
[set-prompt]: /docs/installation/prompt
|
||||
|
|
|
@ -58,11 +58,11 @@ if that color is visible against any of your backgrounds.
|
|||
|
||||
## Properties
|
||||
|
||||
- access_token: `string` - token from Strava login, see login link in section above. It has the following format: `1111111111111111111111111`
|
||||
- refresh_token: `string` - token from Strava login, see login link in section above. It has the following format: `1111111111111111111111111`
|
||||
- access_token: `string` - token from Strava login, see login link in section above.
|
||||
- refresh_token: `string` - token from Strava login, see login link in section above.
|
||||
- expires_in: `int` - the default timeout of the token from the Strava login
|
||||
- http_timeout: `int` - how long do you want to wait before you want to see your prompt more than your strava data? - defaults to 500ms
|
||||
- CacheTimeout: `int` in minutes - How long do you want your numbers cached? - defaults to 5 min
|
||||
- CacheTimeout: `int` in minutes - How long do you want your Strava data cached? - defaults to 5 min
|
||||
- RideIcon - defaults to `\uf5a2`
|
||||
- RunIcon - defaults to `\ufc0c`
|
||||
- SkiingIcon - defaults to `\ue213`
|
||||
|
|
|
@ -18,9 +18,11 @@ This will give you an access and a refresh token. Paste the tokens into your Wit
|
|||
|
||||
Click the following link to connect with Withings:
|
||||
|
||||
<a href="https://account.withings.com/oauth2_user/authorize2?client_id=93675962e88ddfe53f83c0c900558f72174e0ac70ccfb57e48053530c7e6e494&response_type=code&redirect_uri=https://ohmyposh.dev/api/auth&scope=user.activity,user.metrics&state=withings">
|
||||
<WithingsConnect className="withings"/>
|
||||
</a>
|
||||
<div className="withings">
|
||||
<a href="https://account.withings.com/oauth2_user/authorize2?client_id=93675962e88ddfe53f83c0c900558f72174e0ac70ccfb57e48053530c7e6e494&response_type=code&redirect_uri=https://ohmyposh.dev/api/auth&scope=user.activity,user.metrics&state=withings">
|
||||
<WithingsConnect className="withings"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Sample Configuration
|
||||
|
||||
|
@ -31,7 +33,7 @@ Click the following link to connect with Withings:
|
|||
"powerline_symbol": "\uE0B0",
|
||||
"foreground": "#ffffff",
|
||||
"background": "#000000",
|
||||
"template": "{{.Name}} {{.Ago}} {{.Icon}}",
|
||||
"template": "{{ if gt .Weight 0.0 }} {{ round .Weight 2 }}kg {{ end }}",
|
||||
"properties": {
|
||||
"access_token":"11111111111111111",
|
||||
"refresh_token":"1111111111111111",
|
||||
|
@ -42,18 +44,18 @@ Click the following link to connect with Withings:
|
|||
|
||||
## Properties
|
||||
|
||||
- access_token: `string` - token from Withings login, see login link in section above. It has the following format: `1111111111111111111111111`
|
||||
- refresh_token: `string` - token from Withings login, see login link in section above. It has the following format: `1111111111111111111111111`
|
||||
- access_token: `string` - token from Withings login, see login link in section above.
|
||||
- refresh_token: `string` - token from Withings login, see login link in section above.
|
||||
- expires_in: `int` - the default timeout of the token from the Withings login
|
||||
- http_timeout: `int` - how long do you want to wait before you want to see your prompt more than your Withings data? - defaults to 500ms
|
||||
- CacheTimeout: `int` in minutes - How long do you want your numbers cached? - defaults to 5 min
|
||||
- CacheTimeout: `int` in minutes - How long do you want your Withings data cached? - defaults to 5 min
|
||||
|
||||
## Template ([info][templates])
|
||||
|
||||
:::note default template
|
||||
|
||||
``` template
|
||||
{{ if .Error }}{{ .Error }}{{ else }}{{ .Ago }}{{ end }}
|
||||
{{ if gt .Weight 0.0 }} {{ round .Weight 2 }}kg {{ end }}
|
||||
```
|
||||
|
||||
:::
|
||||
|
@ -62,7 +64,9 @@ Click the following link to connect with Withings:
|
|||
|
||||
The properties below are available for use in your template
|
||||
|
||||
- `.ID`: `time` - The id of the entry
|
||||
- `.Weight`: `float` - your last measured weight
|
||||
- `.SleepHours`: `string` - your last measured sleep SleepHours
|
||||
- `.Steps`: `int` - your last measured steps
|
||||
|
||||
Now, go out and be active!
|
||||
|
||||
|
|
|
@ -109,6 +109,7 @@ module.exports = {
|
|||
"segments/ui5tooling",
|
||||
"segments/wakatime",
|
||||
"segments/wifi",
|
||||
"segments/withings",
|
||||
"segments/winreg",
|
||||
"segments/ytm",
|
||||
],
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
}
|
||||
|
||||
.header-github-link:before {
|
||||
content: '';
|
||||
content: "";
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
|
@ -49,7 +49,7 @@
|
|||
no-repeat;
|
||||
}
|
||||
|
||||
html[data-theme='dark'] .header-github-link:before {
|
||||
html[data-theme="dark"] .header-github-link:before {
|
||||
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
|
||||
no-repeat;
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ html[data-theme='dark'] .header-github-link:before {
|
|||
}
|
||||
|
||||
.header-twitter-link:before {
|
||||
content: '';
|
||||
content: "";
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
display: flex;
|
||||
|
@ -68,7 +68,7 @@ html[data-theme='dark'] .header-github-link:before {
|
|||
no-repeat;
|
||||
}
|
||||
|
||||
html[data-theme='dark'] .header-twitter-link:before {
|
||||
html[data-theme="dark"] .header-twitter-link:before {
|
||||
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 50 50' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M25,2C12.317,2,2,12.317,2,25s10.317,23,23,23s23-10.317,23-23S37.683,2,25,2z M36.237,20.524 c0.01,0.236,0.016,0.476,0.016,0.717C36.253,28.559,30.68,37,20.491,37c-3.128,0-6.041-0.917-8.491-2.489 c0.433,0.052,0.872,0.077,1.321,0.077c2.596,0,4.985-0.884,6.879-2.37c-2.424-0.044-4.468-1.649-5.175-3.847 c0.339,0.065,0.686,0.1,1.044,0.1c0.505,0,0.995-0.067,1.458-0.195c-2.532-0.511-4.441-2.747-4.441-5.432c0-0.024,0-0.047,0-0.07 c0.747,0.415,1.6,0.665,2.509,0.694c-1.488-0.995-2.464-2.689-2.464-4.611c0-1.015,0.272-1.966,0.749-2.786 c2.733,3.351,6.815,5.556,11.418,5.788c-0.095-0.406-0.145-0.828-0.145-1.262c0-3.059,2.48-5.539,5.54-5.539 c1.593,0,3.032,0.672,4.042,1.749c1.261-0.248,2.448-0.709,3.518-1.343c-0.413,1.292-1.292,2.378-2.437,3.064 c1.122-0.136,2.188-0.432,3.183-0.873C38.257,18.766,37.318,19.743,36.237,20.524z'/%3E%3C/svg%3E")
|
||||
no-repeat;
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ html[data-theme='dark'] .header-twitter-link:before {
|
|||
}
|
||||
|
||||
.header-gk-link:before {
|
||||
content: '';
|
||||
content: "";
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
display: flex;
|
||||
|
@ -86,7 +86,7 @@ html[data-theme='dark'] .header-twitter-link:before {
|
|||
no-repeat;
|
||||
}
|
||||
|
||||
html[data-theme='dark'] .header-gk-link:before {
|
||||
html[data-theme="dark"] .header-gk-link:before {
|
||||
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 182 182' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='m 176.70092,47.979262 a 3.6,3.6 0 0 0 -7,1.3 2.5,2.5 0 0 0 0.2,1.2 82.6,82.6 0 0 1 5.3,29.5 83.8,83.8 0 0 1 -72.7,82.999998 v -39.1 a 60.2,60.2 0 0 0 7.3,-1.9 v 32.8 a 77,77 0 0 0 19,-142.199998 3.7,3.7 0 0 0 -5.1,1.6 4.5,4.5 0 0 0 -0.3,1.6 3.6,3.6 0 0 0 1.8,3.2 69.7,69.7 0 0 1 -8.1,125.899998 v -29.1 a 11.2,11.2 0 0 0 7.8,-10.6 11,11 0 0 0 -5.5,-9.699998 c 2.6,-25.1 14.1,-18.5 14.1,-26.5 v -4.6 c 0,-12.1 -27.8,-51.1 -40.799999,-52.1 h -2.4 c -13,1 -40.8,40 -40.8,52.1 v 4.6 c 0,8 11.5,1.4 14.1,26.5 a 11.2,11.2 0 0 0 2.4,20.299998 v 29.1 a 69.7,69.7 0 0 1 -8.2,-125.899998 3.6,3.6 0 0 0 1.5,-4.8 3.5,3.5 0 0 0 -3.3,-2 3.3,3.3 0 0 0 -1.7,0.4 77,77 0 0 0 19,142.199998 v -32.8 a 60.2,60.2 0 0 0 7.3,1.9 v 39.1 A 83.8,83.8 0 0 1 7.8009215,79.979262 a 84.8,84.8 0 0 1 5.2999995,-29.5 3.6,3.6 0 0 0 -1.9,-4.6 4,4 0 0 0 -1.4999998,-0.3 3.7,3.7 0 0 0 -3.3999997,2.4 91,91 0 0 0 81.4999995,122.899998 v -46 h 7.4 v 46 a 90.9,90.9 0 0 0 87.299999,-90.899998 89.1,89.1 0 0 0 -5.8,-32 z m -68.4,51.9 a 7.5,7.5 0 1 1 10.6,10.599998 7.5,7.5 0 1 1 -10.6,-10.599998 z M 74.700921,110.47926 a 7.4,7.4 0 0 1 -10.5,0 7.5,7.5 0 0 1 0,-10.599998 7.4,7.4 0 0 1 10.5,0 7.5,7.5 0 0 1 0,10.599998 z'/%3E%3C/svg%3E")
|
||||
no-repeat;
|
||||
}
|
||||
|
@ -103,19 +103,19 @@ iframe.youtube {
|
|||
|
||||
@media screen and (max-width: 350px) {
|
||||
iframe.youtube {
|
||||
height: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 350px) {
|
||||
iframe.youtube {
|
||||
height: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 600px) {
|
||||
iframe.youtube {
|
||||
height: 400px;
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,10 +129,44 @@ iframe.youtube {
|
|||
width: 150px;
|
||||
}
|
||||
|
||||
[data-theme='light'] .withings path {
|
||||
[data-theme="light"] .withings path {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .withings path {
|
||||
fill: black;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .withings path {
|
||||
fill: white;
|
||||
[data-theme="dark"] div.withings {
|
||||
background: white;
|
||||
padding: 17px 10px 10px 10px;
|
||||
display: inline;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
[data-theme="light"] div.withings {
|
||||
background: black;
|
||||
padding: 17px 10px 10px 10px;
|
||||
display: inline;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
h1.hero__title {
|
||||
color: white;
|
||||
}
|
||||
|
||||
p.hero__subtitle {
|
||||
color: white;
|
||||
}
|
||||
|
||||
a.getStarted_src-pages-styles-module {
|
||||
color: white;
|
||||
}
|
||||
|
||||
[data-theme="light"] .themedImage_node_modules-\@docusaurus-theme-classic-lib-theme-ThemedImage-styles-module {
|
||||
filter: invert(0%) sepia(8%) saturate(2885%) hue-rotate(67deg) brightness(84%) contrast(87%);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .themedImage_node_modules-\@docusaurus-theme-classic-lib-theme-ThemedImage-styles-module {
|
||||
filter: invert(100%) sepia(100%) saturate(0%) hue-rotate(226deg) brightness(102%) contrast(101%);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 30.851 30.851" style="enable-background:new 0 0 30.851 30.851;" xml:space="preserve">
|
||||
viewBox="0 0 30.851 30.851" xml:space="preserve">
|
||||
<g>
|
||||
<g id="c43_terminal">
|
||||
<path d="M28.645,2.203H2.204C0.987,2.203,0,3.188,0,4.405v22.038c0,1.216,0.987,2.204,2.204,2.204h26.441
|
||||
|
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
Loading…
Reference in a new issue