From 87f67a3eaf8494397bec5d1cc46900bc732f2730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 22 Nov 2021 09:54:44 -0500 Subject: [PATCH 01/54] update word spacing in docs --- src/css/definition-doc.css | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/css/definition-doc.css b/src/css/definition-doc.css index 32e856e..0db27d9 100644 --- a/src/css/definition-doc.css +++ b/src/css/definition-doc.css @@ -32,8 +32,12 @@ margin-right: 0.5ch; } -.definition-doc .group .join :nth-last-child(2) .word { - margin-right: 0; +.definition-doc .group .join > :last-child { + margin-left: -0.5ch; +} + +.definition-doc .group .join { + white-space: nowrap; } .definition-doc .source.code, From 6b86d70d56c67f6d7c013fa8c5b17fe8d68363de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 22 Nov 2021 11:47:18 -0500 Subject: [PATCH 02/54] Prefer "latest" route This change ensures that we always use the "latest" route. Note that this fix is a bit of a hack in that the datamodel doesn't reflect the new URL preference, a bigger re-org to mirror that should be done shortly. --- src/Route.elm | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Route.elm b/src/Route.elm index 533e72b..5e7c5f4 100644 --- a/src/Route.elm +++ b/src/Route.elm @@ -197,17 +197,17 @@ toUrlString route = ByNamespace Relative fqn -> if includeNamespacesSuffix then - "latest" :: NEL.toList (FQN.segments fqn) ++ [ namespaceSuffix ] + "latest" :: "namespaces" :: NEL.toList (FQN.segments fqn) ++ [ namespaceSuffix ] else - "latest" :: NEL.toList (FQN.segments fqn) + "latest" :: "namespaces" :: NEL.toList (FQN.segments fqn) - ByNamespace (Absolute hash) fqn -> + ByNamespace (Absolute _) fqn -> if includeNamespacesSuffix then - [ Hash.toUrlString hash, "namespaces" ] ++ NEL.toList (FQN.segments fqn) ++ [ namespaceSuffix ] + "latest" :: "namespaces" :: NEL.toList (FQN.segments fqn) ++ [ namespaceSuffix ] else - [ Hash.toUrlString hash, "namespaces" ] ++ NEL.toList (FQN.segments fqn) + "latest" :: "namespaces" :: NEL.toList (FQN.segments fqn) path = case route of From 3ad738f5126251b7f1c0bf70704a894df4669dad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 22 Nov 2021 14:12:13 -0500 Subject: [PATCH 03/54] Add spark article banner --- src/App.elm | 16 ++++++++++++++- src/UI/Banner.elm | 13 ++++++------ src/UI/Click.elm | 13 ++++++++---- src/css/elements/banner.css | 34 ++++++++++++++++++-------------- src/css/themes/unison/colors.css | 1 + 5 files changed, 51 insertions(+), 26 deletions(-) diff --git a/src/App.elm b/src/App.elm index a51df57..7590a5b 100644 --- a/src/App.elm +++ b/src/App.elm @@ -23,6 +23,7 @@ import RemoteData import Route exposing (Route) import UI import UI.AppHeader as AppHeader +import UI.Banner as Banner import UI.Button as Button import UI.Click as Click exposing (Click(..)) import UI.Icon as Icon @@ -461,11 +462,24 @@ viewAppHeader model = appTitle_ = appTitle (Just changePerspectiveMsg) appContext + + banner = + case appContext of + Ucm -> + Nothing + + UnisonShare -> + Just + (Banner.promotion "article" + "New Article: Spark-like distributed datasets in under 100 lines of Unison" + (ExternalHref "https://www.unison-lang.org/articles/distributed-datasets/") + "Check it out!" + ) in AppHeader.view { menuToggle = Just ToggleSidebar , appTitle = appTitle_ - , banner = Nothing + , banner = banner , rightButton = Just (Button.button (ShowModal PublishModal) "Publish on Unison Share" |> Button.share) } diff --git a/src/UI/Banner.elm b/src/UI/Banner.elm index 8be11c5..b18d20e 100644 --- a/src/UI/Banner.elm +++ b/src/UI/Banner.elm @@ -3,11 +3,12 @@ module UI.Banner exposing (..) import Html exposing (Html, a, span, text) import Html.Attributes exposing (class) import Html.Events exposing (onClick) +import UI.Click as Click exposing (Click) type Banner msg = Info { content : String } - | Promotion { promotionId : String, content : String, ctaMsg : msg, ctaLabel : String } + | Promotion { promotionId : String, content : String, ctaClick : Click msg, ctaLabel : String } info : String -> Banner msg @@ -15,12 +16,12 @@ info content = Info { content = content } -promotion : String -> String -> msg -> String -> Banner msg -promotion promotionId content ctaMsg ctaLabel = +promotion : String -> String -> Click msg -> String -> Banner msg +promotion promotionId content ctaClick ctaLabel = Promotion { promotionId = promotionId , content = content - , ctaMsg = ctaMsg + , ctaClick = ctaClick , ctaLabel = ctaLabel } @@ -32,12 +33,12 @@ view banner_ = span [ class "banner", class "info" ] [ text i.content ] Promotion p -> - a + Click.view [ class "banner" , class "promotion" , class p.promotionId - , onClick p.ctaMsg ] [ text p.content , span [ class "banner-cta" ] [ text p.ctaLabel ] ] + p.ctaClick diff --git a/src/UI/Click.elm b/src/UI/Click.elm index cf3f162..edd599c 100644 --- a/src/UI/Click.elm +++ b/src/UI/Click.elm @@ -10,11 +10,16 @@ type Click msg | OnClick msg -view : List (Attribute msg) -> List (Html msg) -> Click msg -> Html msg -view attrs content click = +attrs : Click msg -> List (Attribute msg) +attrs click = case click of ExternalHref href_ -> - a (attrs ++ [ href href_, rel "noopener", target "_blank" ]) content + [ href href_, rel "noopener", target "_blank" ] OnClick msg -> - a (attrs ++ [ onClick msg ]) content + [ onClick msg ] + + +view : List (Attribute msg) -> List (Html msg) -> Click msg -> Html msg +view attrs_ content click = + a (attrs_ ++ attrs click) content diff --git a/src/css/elements/banner.css b/src/css/elements/banner.css index ee158b8..c6957c0 100644 --- a/src/css/elements/banner.css +++ b/src/css/elements/banner.css @@ -16,21 +16,7 @@ transform: translate(0, 0.1rem); } -.banner.info { - --banner-fg: var(--banner-info-fg); - --banner-bg: var(--banner-info-bg); - --banner-fg-em: var(--banner-info-fg-em); - --banner-border: var(--banner-info-border); -} - -/* Seasonal Promotion */ -.banner.hacktoberfest { - --banner-fg: var(--color-orange-3); - --banner-bg: rgba(255, 136, 0, 0.2); /* color-orange-1 20% */ - --banner-fg-em: var(--color-orange-5); - --banner-border: var(--color-orange-1); - text-shadow: 0 1px rgba(0, 0, 0, 0.25); -} +/* Promotions */ .banner .banner-cta { color: var(--banner-fg-em); @@ -42,3 +28,21 @@ margin-left: 0.75rem; padding-left: 0.75rem; } + +.banner.article { + --banner-fg: var(--color-green-4); + --banner-bg: var(--color-gray-base); + --banner-fg-em: var(--color-green-3); + --banner-border: var(--color-gray-lighten-20); + text-shadow: 0 1px rgba(0, 0, 0, 0.25); + + color: var(--banner-fg); +} + +.banner.hacktoberfest { + --banner-fg: var(--color-orange-3); + --banner-bg: rgba(255, 136, 0, 0.2); /* color-orange-1 20% */ + --banner-fg-em: var(--color-orange-5); + --banner-border: var(--color-orange-1); + text-shadow: 0 1px rgba(0, 0, 0, 0.25); +} diff --git a/src/css/themes/unison/colors.css b/src/css/themes/unison/colors.css index eaf5c53..90d11f0 100644 --- a/src/css/themes/unison/colors.css +++ b/src/css/themes/unison/colors.css @@ -44,6 +44,7 @@ --color-green-1: #27ae60; --color-green-2: #52d188; --color-green-3: #88f3b5; + --color-green-4: #c6ffde; /* Blues */ --color-blue-1: #225ebe; From b48b0b56737dd66bfd37c8330d463cdf085580b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 22 Nov 2021 14:32:08 -0500 Subject: [PATCH 04/54] fix banner imports --- src/UI/Banner.elm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/UI/Banner.elm b/src/UI/Banner.elm index b18d20e..9955b09 100644 --- a/src/UI/Banner.elm +++ b/src/UI/Banner.elm @@ -1,8 +1,7 @@ module UI.Banner exposing (..) -import Html exposing (Html, a, span, text) +import Html exposing (Html, span, text) import Html.Attributes exposing (class) -import Html.Events exposing (onClick) import UI.Click as Click exposing (Click) From c244cabb0ad07222145475631663b106d181a73f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Tue, 23 Nov 2021 11:06:07 -0500 Subject: [PATCH 05/54] Organize the routing model to favor "latest" Ensure that we use Relative everywhere and disable Absolute URLs until we are ready to support them. Add comments to communicate why this is done (Share doesn't support history) and change PreApp to fetch the perspective information from Api.codebaseHash instead of Api.list (so we can limit Api to only use Perspective instead of PerspectiveParams). --- src/Api.elm | 39 ++++++++++++--------------------------- src/CodebaseTree.elm | 2 +- src/Perspective.elm | 10 +++++++--- src/PreApp.elm | 5 ++--- src/Route.elm | 11 ++++++++--- 5 files changed, 30 insertions(+), 37 deletions(-) diff --git a/src/Api.elm b/src/Api.elm index 335320f..15b3823 100644 --- a/src/Api.elm +++ b/src/Api.elm @@ -1,6 +1,7 @@ module Api exposing ( ApiBasePath(..) , ApiRequest + , codebaseHash , errorToString , find , getDefinition @@ -15,12 +16,7 @@ import FullyQualifiedName as FQN exposing (FQN) import Hash exposing (Hash) import Http import Json.Decode as Decode -import Perspective - exposing - ( CodebasePerspectiveParam(..) - , Perspective(..) - , PerspectiveParams(..) - ) +import Perspective exposing (Perspective(..)) import Regex import Syntax import Url.Builder exposing (QueryParameter, absolute, int, string) @@ -34,13 +30,18 @@ type Endpoint = Endpoint (List String) (List QueryParameter) -list : PerspectiveParams -> Maybe String -> Endpoint -list perspectiveParams fqnOrHash = +codebaseHash : Endpoint +codebaseHash = + Endpoint [ "list" ] [ string "namespace" "." ] + + +list : Perspective -> Maybe String -> Endpoint +list perspective fqnOrHash = let namespace_ = Maybe.withDefault "." fqnOrHash in - Endpoint [ "list" ] (string "namespace" namespace_ :: perspectiveParamsToQueryParams perspectiveParams) + Endpoint [ "list" ] (string "namespace" namespace_ :: perspectiveToQueryParams perspective) namespace : Perspective -> FQN -> Endpoint @@ -151,24 +152,8 @@ perspectiveToQueryParams perspective = Codebase h -> [ rootBranch h ] - Namespace { codebaseHash, fqn } -> - [ rootBranch codebaseHash, relativeTo fqn ] - - -perspectiveParamsToQueryParams : PerspectiveParams -> List QueryParameter -perspectiveParamsToQueryParams perspectiveParams = - case perspectiveParams of - ByCodebase Relative -> - [] - - ByCodebase (Absolute h) -> - [ rootBranch h ] - - ByNamespace Relative fqn -> - [ relativeTo fqn ] - - ByNamespace (Absolute h) fqn -> - [ rootBranch h, relativeTo fqn ] + Namespace d -> + [ rootBranch d.codebaseHash, relativeTo d.fqn ] rootBranch : Hash -> QueryParameter diff --git a/src/CodebaseTree.elm b/src/CodebaseTree.elm index d0a7684..6b164e4 100644 --- a/src/CodebaseTree.elm +++ b/src/CodebaseTree.elm @@ -175,7 +175,7 @@ fetchSubNamespaceListing perspective fqn = fetchNamespaceListing : Perspective -> Maybe FQN -> (Result Http.Error NamespaceListing -> msg) -> ApiRequest NamespaceListing msg fetchNamespaceListing perspective fqn toMsg = - Api.list (Perspective.toParams perspective) (Maybe.map FQN.toString fqn) + Api.list perspective (Maybe.map FQN.toString fqn) |> Api.toRequest (NamespaceListing.decode fqn) toMsg diff --git a/src/Perspective.elm b/src/Perspective.elm index f97db96..b193595 100644 --- a/src/Perspective.elm +++ b/src/Perspective.elm @@ -46,14 +46,18 @@ fqn perspective = d.fqn +{-| Even when we have a Codebase hash, we always constructor Relative params. +Absolute is currently not supported (until Unison Share includes historic +codebase), though the model allows it. +-} toParams : Perspective -> PerspectiveParams toParams perspective = case perspective of - Codebase hash -> - ByCodebase (Absolute hash) + Codebase _ -> + ByCodebase Relative Namespace d -> - ByNamespace (Absolute d.codebaseHash) d.fqn + ByNamespace Relative d.fqn fromParams : PerspectiveParams -> Maybe Perspective diff --git a/src/PreApp.elm b/src/PreApp.elm index eedfdf5..06311df 100644 --- a/src/PreApp.elm +++ b/src/PreApp.elm @@ -7,7 +7,7 @@ import Browser.Navigation as Nav import Env exposing (Flags) import Html import Http -import Perspective exposing (CodebasePerspectiveParam(..), Perspective, PerspectiveParams(..)) +import Perspective exposing (Perspective, PerspectiveParams) import Route exposing (Route) import Url exposing (Url) @@ -63,8 +63,7 @@ init flags url navKey = fetchPerspective : PreEnv -> ApiRequest Perspective Msg fetchPerspective preEnv = - Api.list (ByCodebase Relative) (Just ".") - |> Api.toRequest (Perspective.decode preEnv.perspectiveParams) (FetchPerspectiveFinished preEnv) + Api.codebaseHash |> Api.toRequest (Perspective.decode preEnv.perspectiveParams) (FetchPerspectiveFinished preEnv) type Msg diff --git a/src/Route.elm b/src/Route.elm index 5e7c5f4..9de9db9 100644 --- a/src/Route.elm +++ b/src/Route.elm @@ -202,12 +202,17 @@ toUrlString route = else "latest" :: "namespaces" :: NEL.toList (FQN.segments fqn) - ByNamespace (Absolute _) fqn -> + -- Currently the model supports Absolute URLs (aka Permalinks), + -- but we don't use it since Unison Share does not support any + -- history, meaning that everytime we deploy Unison Share, the + -- previous versions of the codebase are lost. + -- It's fully intended for this feature to be brought back + ByNamespace (Absolute hash) fqn -> if includeNamespacesSuffix then - "latest" :: "namespaces" :: NEL.toList (FQN.segments fqn) ++ [ namespaceSuffix ] + Hash.toUrlString hash :: "namespaces" :: NEL.toList (FQN.segments fqn) ++ [ namespaceSuffix ] else - "latest" :: "namespaces" :: NEL.toList (FQN.segments fqn) + Hash.toUrlString hash :: "namespaces" :: NEL.toList (FQN.segments fqn) path = case route of From 0fe8d704a5e2aa32f6e82559fa514caf0510ad2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Tue, 23 Nov 2021 13:14:30 -0500 Subject: [PATCH 06/54] Pin open definitions to hash on perspective change When there's any number of definitions open and a new perspective is selected, the definitions that were open with references based on names, will no longer have a resolvable name if they were to be re-queried (on a page refresh for instance). Fix this by changing all open definitions to be referenced by hash when the perspective is changed. --- src/App.elm | 18 ++++++++-- src/Definition/Reference.elm | 16 +++++++++ src/Route.elm | 1 + src/Workspace.elm | 10 ++++++ src/Workspace/WorkspaceItem.elm | 41 +++++++++++++++++++++++ src/Workspace/WorkspaceItems.elm | 7 ++++ tests/Workspace/WorkspaceItemsTests.elm | 44 +++++++++++++++++-------- 7 files changed, 122 insertions(+), 15 deletions(-) diff --git a/src/App.elm b/src/App.elm index 7590a5b..ba053b8 100644 --- a/src/App.elm +++ b/src/App.elm @@ -33,6 +33,7 @@ import UI.Tooltip as Tooltip import UnisonShare.SidebarContent import Url exposing (Url) import Workspace +import Workspace.WorkspaceItems as WorkspaceItems @@ -291,8 +292,21 @@ replacePerspective ({ env } as model) perspective = ( codebaseTree, codebaseTreeCmd ) = CodebaseTree.init newEnv + -- Update all open references to be hash based to ensure that we can + -- refresh the page and fetch them appropriately even if they are + -- outside of the current perspective + workspace = + Workspace.replaceWorkspaceItemReferencesWithHashOnly model.workspace + + -- Re-navigate to the currently open definition by hash + focusedReferenceRoute = + workspace.workspaceItems + |> WorkspaceItems.focusedReference + |> Maybe.map (Route.toDefinition model.route) + |> Maybe.withDefault model.route + changeRouteCmd = - Route.replacePerspective model.navKey (Perspective.toParams perspective) model.route + Route.replacePerspective model.navKey (Perspective.toParams perspective) focusedReferenceRoute fetchNamespaceDetailsCmd = perspective @@ -300,7 +314,7 @@ replacePerspective ({ env } as model) perspective = |> Maybe.map (Api.perform env.apiBasePath) |> Maybe.withDefault Cmd.none in - ( { model | env = newEnv, codebaseTree = codebaseTree } + ( { model | env = newEnv, codebaseTree = codebaseTree, workspace = workspace } , Cmd.batch [ Cmd.map CodebaseTreeMsg codebaseTreeCmd , changeRouteCmd diff --git a/src/Definition/Reference.elm b/src/Definition/Reference.elm index 1bfbc69..f0211cf 100644 --- a/src/Definition/Reference.elm +++ b/src/Definition/Reference.elm @@ -107,3 +107,19 @@ toIcon ref = DataConstructorReference _ -> Icon.dataConstructor + + +map : (HashQualified -> HashQualified) -> Reference -> Reference +map f ref = + case ref of + TermReference hq -> + TermReference (f hq) + + TypeReference hq -> + TypeReference (f hq) + + AbilityConstructorReference hq -> + AbilityConstructorReference (f hq) + + DataConstructorReference hq -> + DataConstructorReference (f hq) diff --git a/src/Route.elm b/src/Route.elm index 9de9db9..131def7 100644 --- a/src/Route.elm +++ b/src/Route.elm @@ -8,6 +8,7 @@ module Route exposing , navigateToPerspective , perspectiveParams , replacePerspective + , toDefinition , toRoute , toUrlString , updatePerspectiveParams diff --git a/src/Workspace.elm b/src/Workspace.elm index ad10d3a..9bacd8c 100644 --- a/src/Workspace.elm +++ b/src/Workspace.elm @@ -4,6 +4,7 @@ module Workspace exposing , OutMsg(..) , init , open + , replaceWorkspaceItemReferencesWithHashOnly , subscriptions , update , view @@ -222,6 +223,15 @@ open env model ref = openItem env model Nothing ref +replaceWorkspaceItemReferencesWithHashOnly : Model -> Model +replaceWorkspaceItemReferencesWithHashOnly model = + let + workspaceItems = + WorkspaceItems.map WorkspaceItem.toHashReference model.workspaceItems + in + { model | workspaceItems = workspaceItems } + + openItem : Env -> WithWorkspaceItems m -> Maybe Reference -> Reference -> ( WithWorkspaceItems m, Cmd Msg, OutMsg ) openItem env ({ workspaceItems } as model) relativeToRef ref = -- We don't want to refetch or replace any already open definitions, but we diff --git a/src/Workspace/WorkspaceItem.elm b/src/Workspace/WorkspaceItem.elm index ac5eee4..eaf3601 100644 --- a/src/Workspace/WorkspaceItem.elm +++ b/src/Workspace/WorkspaceItem.elm @@ -127,6 +127,31 @@ reference item = r +{-| Convert the Reference of a WorkspaceItem to be HashOnly +-} +toHashReference : WorkspaceItem -> WorkspaceItem +toHashReference workspaceItem = + let + toHashOnly hash hq = + case hq of + HQ.NameOnly _ -> + HQ.HashOnly hash + + HQ.HashOnly h -> + HQ.HashOnly h + + HQ.HashQualified _ h -> + HQ.HashOnly h + in + case workspaceItem of + Success r d -> + Success (Reference.map (toHashOnly (itemHash d.item)) r) d + + -- Can't change references where we don't have hash information + _ -> + workspaceItem + + {-| Builtins and Types can't be expanded, so we can skip the Medium Zoom level entirely TODO: Remove isTypeItem from this conditional when we can collapse types (TypeSummary) -} @@ -229,6 +254,22 @@ hasDoc item = False +itemHash : Item -> Hash +itemHash item = + case item of + TermItem (Term h _ _) -> + h + + TypeItem (Type h _ _) -> + h + + AbilityConstructorItem (AbilityConstructor h _) -> + h + + DataConstructorItem (DataConstructor h _) -> + h + + -- VIEW diff --git a/src/Workspace/WorkspaceItems.elm b/src/Workspace/WorkspaceItems.elm index e7af58a..e68f61d 100644 --- a/src/Workspace/WorkspaceItems.elm +++ b/src/Workspace/WorkspaceItems.elm @@ -288,6 +288,13 @@ focus items = Just data.focus +focusedReference : WorkspaceItems -> Maybe Reference +focusedReference items = + items + |> focus + |> Maybe.map WorkspaceItem.reference + + focusOn : WorkspaceItems -> Reference -> WorkspaceItems focusOn items ref = let diff --git a/tests/Workspace/WorkspaceItemsTests.elm b/tests/Workspace/WorkspaceItemsTests.elm index df8763c..910e2af 100644 --- a/tests/Workspace/WorkspaceItemsTests.elm +++ b/tests/Workspace/WorkspaceItemsTests.elm @@ -20,7 +20,7 @@ appendWithFocus = WorkspaceItems.appendWithFocus WorkspaceItems.empty term currentFocusedRef = - getFocusedRef result + WorkspaceItems.focusedReference result in describe "WorkspaceItems.appendWithFocus" [ test "Appends the term" <| @@ -39,7 +39,7 @@ prependWithFocus = WorkspaceItems.prependWithFocus WorkspaceItems.empty term currentFocusedRef = - getFocusedRef result + WorkspaceItems.focusedReference result in describe "WorkspaceItems.prependWithFocus" [ test "Prepends the term" <| @@ -73,7 +73,7 @@ insertWithFocusAfter = WorkspaceItems.insertWithFocusAfter workspaceItems afterRef toInsert currentFocusedRef = - getFocusedRef inserted + WorkspaceItems.focusedReference inserted in describe "WorkspaceItems.insertWithFocusAfter" [ test "Inserts after the 'after ref'" <| @@ -125,7 +125,7 @@ insertWithFocusBefore = WorkspaceItems.insertWithFocusBefore workspaceItems beforeRef toInsert currentFocusedRef = - getFocusedRef inserted + WorkspaceItems.focusedReference inserted in describe "WorkspaceItems.insertWithFocusBefore" [ test "Inserts before the 'before ref'" <| @@ -315,7 +315,7 @@ next = result = workspaceItems |> WorkspaceItems.next - |> getFocusedRef + |> WorkspaceItems.focusedReference |> Maybe.map Reference.toString in Expect.equal (Just "term__#c") result @@ -325,7 +325,7 @@ next = result = WorkspaceItems.fromItems before focused [] |> WorkspaceItems.next - |> getFocusedRef + |> WorkspaceItems.focusedReference |> Maybe.map Reference.toString in Expect.equal (Just "term__#focus") result @@ -341,7 +341,7 @@ prev = result = workspaceItems |> WorkspaceItems.prev - |> getFocusedRef + |> WorkspaceItems.focusedReference |> Maybe.map Reference.toString in Expect.equal (Just "term__#b") result @@ -351,13 +351,36 @@ prev = result = WorkspaceItems.fromItems [] focused after |> WorkspaceItems.prev - |> getFocusedRef + |> WorkspaceItems.focusedReference |> Maybe.map Reference.toString in Expect.equal (Just "term__#focus") result ] +focusedReference : Test +focusedReference = + describe "WorkspaceItems.focusedReference" + [ test "return the reference of the focused item when one exists" <| + \_ -> + let + result = + workspaceItems + |> WorkspaceItems.focusedReference + |> Maybe.map Reference.toString + in + Expect.equal (Just "term__#focus") result + , test "returns Nothing when Empty" <| + \_ -> + let + result = + WorkspaceItems.empty + |> WorkspaceItems.focusedReference + in + Expect.equal Nothing result + ] + + -- MOVE @@ -533,8 +556,3 @@ after = workspaceItems : WorkspaceItems workspaceItems = WorkspaceItems.fromItems before focused after - - -getFocusedRef : WorkspaceItems -> Maybe Reference -getFocusedRef = - WorkspaceItems.focus >> Maybe.map reference From 0a43ca574d54790250abed2c89a519f10e89261f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Tue, 23 Nov 2021 16:29:35 -0500 Subject: [PATCH 07/54] Perspective: Improve rendering of very long names Now, with a very long namespace perspective, always ensure that the visual slug is visible and clip the namespace on the left side, favoring the tail end of the namespace: `base.List.NonEmpty` might become `...List.NonEmpty` rather than `base.List.NonE...`. --- src/App.elm | 27 ++++++++++----------------- src/UI.elm | 5 +++++ src/css/app.css | 25 +++++++++++++++++++++++-- src/css/elements.css | 1 + 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/App.elm b/src/App.elm index ba053b8..d0778ab 100644 --- a/src/App.elm +++ b/src/App.elm @@ -504,26 +504,19 @@ viewPerspective env = Codebase _ -> UI.nothing - Namespace { codebaseHash, fqn } -> + Namespace { fqn } -> let - fqnText = - FQN.toString fqn - - context = - Env.appContextToString env.appContext - - back = - Tooltip.tooltip - (Button.icon (ChangePerspective (Codebase codebaseHash)) Icon.arrowLeftUp |> Button.small |> Button.uncontained |> Button.view) - (Tooltip.Text ("You're currently viewing a subset of " ++ context ++ " (" ++ fqnText ++ "), click to reveal everything.")) - |> Tooltip.withArrow Tooltip.Start - |> Tooltip.view + -- Imprecise, but close enough, approximation of overflowing, + -- which results in a slight faded left edge A better way would + -- be to measure the DOM like we do for overflowing docs, but + -- thats quite involved... + isOverflowing = + fqn |> FQN.toString |> String.length |> (\l -> l > 20) in header - [ class "perspective" ] - [ div [ class "namespace-slug" ] [] - , h2 [] [ FQN.view fqn ] - , back + [ classList [ ( "perspective", True ), ( "is-overflowing", isOverflowing ) ] ] + [ UI.namespaceSlug + , h2 [ class "namespace" ] [ FQN.view fqn ] ] diff --git a/src/UI.elm b/src/UI.elm index 7f923b3..2cba2b1 100644 --- a/src/UI.elm +++ b/src/UI.elm @@ -66,3 +66,8 @@ divider = charWidth : Int -> String charWidth numChars = String.fromInt numChars ++ "ch" + + +namespaceSlug : Html msg +namespaceSlug = + div [ class "namespace-slug" ] [] diff --git a/src/css/app.css b/src/css/app.css index 05525ba..bab39d1 100644 --- a/src/css/app.css +++ b/src/css/app.css @@ -192,6 +192,7 @@ display: flex; flex-direction: row; position: relative; + gap: 0.75rem; } #main-sidebar .perspective:after { @@ -209,14 +210,34 @@ } #main-sidebar .perspective .namespace-slug { - margin-right: 0.75rem; + position: relative; } -#main-sidebar .perspective h2 { +#main-sidebar .perspective.is-overflowing .namespace-slug:after { + position: absolute; + top: 0; + right: -1.5rem; + bottom: 0; + content: ""; + width: 1.5rem; + background: linear-gradient( + 90deg, + var(--color-sidebar-bg), + var(--color-sidebar-bg), + var(--color-sidebar-bg-transparent) + ); +} + +#main-sidebar .perspective .namespace { + display: inline-flex; color: var(--color-sidebar-fg-em); font-size: 1rem; font-weight: 500; height: 1.5rem; + overflow: hidden; + white-space: nowrap; + text-align: right; + flex-direction: row-reverse; } #main-sidebar .perspective .tooltip-bubble { diff --git a/src/css/elements.css b/src/css/elements.css index a659f6a..d6611ab 100644 --- a/src/css/elements.css +++ b/src/css/elements.css @@ -174,6 +174,7 @@ hr { /* Namespace Slug */ .namespace-slug { display: inline-flex; + flex: 0 0 1.5rem; width: 1.5rem; height: 1.5rem; background: url(../img/namespace-slug-untitled.svg); From 2524205a7550dd131f3882b140f43ce584c3fe6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Wed, 24 Nov 2021 13:59:13 -0500 Subject: [PATCH 08/54] Add DownloadModal with click to copy Add a new download button that appear under a namespace perspective. Clicking this button will launch a modal that has a "click to copy" functionality. * Copy on click is built with a WebComponent (`CopyOnClick.js`) rendered from Elm via a new `CopyField` module, which includes a new clipboard `Icon`. * The perspective in the sidebar is now rendered via `Sidebar.header` and `Sidebar.headerItem` to include proper padding and a bottom divider. --- src/App.elm | 64 ++++++++++++++++++++++---- src/UI.elm | 9 +++- src/UI/CopyField.elm | 69 ++++++++++++++++++++++++++++ src/UI/CopyOnClick.js | 27 +++++++++++ src/UI/Icon.elm | 9 ++++ src/UI/Sidebar.elm | 20 ++++++--- src/css/app.css | 51 ++++++++++++--------- src/css/composites.css | 1 + src/css/composites/copy-field.css | 75 +++++++++++++++++++++++++++++++ src/css/composites/modal.css | 5 +++ src/css/download-modal.css | 12 +++++ src/css/elements.css | 2 +- src/css/themes/unison/light.css | 1 + src/init.js | 3 ++ 14 files changed, 312 insertions(+), 36 deletions(-) create mode 100644 src/UI/CopyField.elm create mode 100644 src/UI/CopyOnClick.js create mode 100644 src/css/composites/copy-field.css create mode 100644 src/css/download-modal.css diff --git a/src/App.elm b/src/App.elm index d0778ab..58e29ad 100644 --- a/src/App.elm +++ b/src/App.elm @@ -9,7 +9,7 @@ import Env exposing (AppContext(..), Env, OperatingSystem(..)) import Finder import Finder.SearchOptions as SearchOptions import FullyQualifiedName as FQN exposing (FQN) -import Html exposing (Html, a, div, h1, h2, h3, header, nav, p, section, span, strong, text) +import Html exposing (Html, a, div, h1, h2, h3, nav, p, section, span, strong, text) import Html.Attributes exposing (class, classList, href, id, rel, target, title) import Html.Events exposing (onClick) import Http @@ -26,6 +26,7 @@ import UI.AppHeader as AppHeader import UI.Banner as Banner import UI.Button as Button import UI.Click as Click exposing (Click(..)) +import UI.CopyField as CopyField import UI.Icon as Icon import UI.Modal as Modal import UI.Sidebar as Sidebar @@ -46,6 +47,7 @@ type Modal | HelpModal | ReportBugModal | PublishModal + | DownloadModal FQN type alias Model = @@ -498,8 +500,8 @@ viewAppHeader model = } -viewPerspective : Env -> Html Msg -viewPerspective env = +viewSidebarHeader : Env -> Html Msg +viewSidebarHeader env = case env.perspective of Codebase _ -> UI.nothing @@ -512,11 +514,27 @@ viewPerspective env = -- thats quite involved... isOverflowing = fqn |> FQN.toString |> String.length |> (\l -> l > 20) + + download = + case env.appContext of + UnisonShare -> + Button.iconThenLabel (ShowModal (DownloadModal fqn)) Icon.download "Download latest version" + |> Button.small + |> Button.view + |> List.singleton + |> Sidebar.headerItem [] + + Ucm -> + UI.nothing in - header - [ classList [ ( "perspective", True ), ( "is-overflowing", isOverflowing ) ] ] - [ UI.namespaceSlug - , h2 [ class "namespace" ] [ FQN.view fqn ] + Sidebar.header + [ Sidebar.headerItem + [ classList [ ( "is-overflowing", isOverflowing ) ] ] + [ UI.namespaceSlug + , h2 [ class "namespace" ] [ FQN.view fqn ] + ] + , download + , UI.divider ] @@ -587,7 +605,7 @@ viewMainSidebar model = Sidebar.view [ viewMainSidebarCollapseButton model , div [ class "expanded-content" ] - [ viewPerspective model.env + [ viewSidebarHeader model.env , div [ class "sidebar-scroll-area" ] [ sidebarContent , Sidebar.section @@ -621,6 +639,33 @@ viewMainSidebar model = ] +viewDownloadModal : FQN -> Html Msg +viewDownloadModal fqn = + let + prettyName = + FQN.toString fqn + + unqualified = + FQN.unqualifiedName fqn + + pullCommand = + "pull git@github.com:unisonweb/share.git:." ++ prettyName ++ " ." ++ unqualified + + content = + Modal.Content + (section + [] + [ p [] [ text "Download ", UI.bold prettyName, text " by pulling the namespace from Unison Share into a namespace in your local codebase:" ] + , CopyField.copyField (\_ -> CloseModal) pullCommand |> CopyField.withPrefix ".>" |> CopyField.view + , div [ class "hint" ] [ text "Copy and paste this command into UCM." ] + ] + ) + in + Modal.modal "download-modal" CloseModal content + |> Modal.withHeader ("Download " ++ prettyName) + |> Modal.view + + viewHelpModal : OperatingSystem -> KeyboardShortcut.Model -> Html Msg viewHelpModal os keyboardShortcut = let @@ -770,6 +815,9 @@ viewModal model = ReportBugModal -> viewReportBugModal model.env.appContext + DownloadModal fqn -> + viewDownloadModal fqn + viewAppLoading : AppContext -> Html msg viewAppLoading appContext = diff --git a/src/UI.elm b/src/UI.elm index 2cba2b1..f726681 100644 --- a/src/UI.elm +++ b/src/UI.elm @@ -1,6 +1,6 @@ module UI exposing (..) -import Html exposing (Attribute, Html, code, div, hr, pre, span, text) +import Html exposing (Attribute, Html, code, div, hr, pre, span, strong, text) import Html.Attributes exposing (class) import Html.Events exposing (onClick) import UI.Icon as Icon @@ -11,6 +11,11 @@ codeBlock attrs code_ = pre attrs [ code [] [ code_ ] ] +bold : String -> Html msg +bold text_ = + strong [] [ text text_ ] + + inlineCode : List (Attribute msg) -> Html msg -> Html msg inlineCode attrs code_ = code (class "inline-code" :: attrs) [ code_ ] @@ -60,7 +65,7 @@ emptyStateMessage message = divider : Html msg divider = - hr [] [] + hr [ class "divider" ] [] charWidth : Int -> String diff --git a/src/UI/CopyField.elm b/src/UI/CopyField.elm new file mode 100644 index 0000000..ce78ff4 --- /dev/null +++ b/src/UI/CopyField.elm @@ -0,0 +1,69 @@ +module UI.CopyField exposing (..) + +import Html exposing (Html, button, div, input, node, text) +import Html.Attributes exposing (attribute, class, readonly, type_, value) +import UI +import UI.Icon as Icon + + +type alias CopyField msg = + { prefix : Maybe String + , toCopy : String + , onCopy : String -> msg + } + + +copyField : (String -> msg) -> String -> CopyField msg +copyField onCopy toCopy = + { prefix = Nothing, toCopy = toCopy, onCopy = onCopy } + + +withPrefix : String -> CopyField msg -> CopyField msg +withPrefix prefix field = + { field | prefix = Just prefix } + + +withToCopy : String -> CopyField msg -> CopyField msg +withToCopy toCopy field = + { field | toCopy = toCopy } + + +view : CopyField msg -> Html msg +view field = + let + prefix = + field.prefix + |> Maybe.map (\p -> div [ class "copy-field-prefix" ] [ text p ]) + |> Maybe.withDefault UI.nothing + in + div [ class "copy-field" ] + [ div [ class "copy-field-field" ] + [ prefix + , div + [ class "copy-field-input" ] + [ input + [ type_ "text" + , class "copy-field-to-copy" + , value field.toCopy + , readonly True + ] + [] + ] + ] + , copyButton field.toCopy + ] + + + +-- HELPERS -------------------------------------------------------------------- + + +{-| We're not using UI.Button here since a click handler is added from +the webcomponent in JS land. +-} +copyButton : String -> Html msg +copyButton toCopy = + node "copy-on-click" + [ attribute "text" toCopy ] + [ button [ class "button contained default" ] [ Icon.view Icon.clipboard ] + ] diff --git a/src/UI/CopyOnClick.js b/src/UI/CopyOnClick.js new file mode 100644 index 0000000..0420b8d --- /dev/null +++ b/src/UI/CopyOnClick.js @@ -0,0 +1,27 @@ +// +// clickable content +// +// +// Use from Elm with an Icon: +// node "copy-on-click" [ ] [ UI.Icon.view UI.Icon.clipboard ] +class CopyOnClick extends HTMLElement { + constructor() { + super(); + } + + connectedCallback() { + this.addEventListener("click", () => { + const text = this.getAttribute("text"); + + // writeText returns a promise with success/failure that we should + // probably do something with... + navigator.clipboard.writeText(text); + }); + } + + static get observedAttributes() { + return ["text"]; + } +} + +customElements.define("copy-on-click", CopyOnClick); diff --git a/src/UI/Icon.elm b/src/UI/Icon.elm index 75d27e0..e381d63 100644 --- a/src/UI/Icon.elm +++ b/src/UI/Icon.elm @@ -370,3 +370,12 @@ tagsOutlined = , path [ fill "currentColor", fillRule "evenodd", d "M8.62836 2.16552C8.81309 1.96026 9.12923 1.94362 9.33449 2.12835L13.6332 5.99715C14.2238 6.52872 14.2974 7.42865 13.801 8.04914L10.3904 12.3123C10.2179 12.528 9.90329 12.5629 9.68766 12.3904C9.47203 12.2179 9.43707 11.9033 9.60957 11.6877L13.0201 7.42444C13.1856 7.21761 13.1611 6.91763 12.9642 6.74045L8.66552 2.87165C8.46027 2.68692 8.44363 2.37077 8.62836 2.16552Z" ] [] , circle [ cx "4", cy "5", r "1.5", stroke "currentColor", fill "transparent" ] [] ] + + +clipboard : Icon msg +clipboard = + Icon "clipboard" + [] + [ path [ fill "currentColor", fillRule "evenodd", d "M8 2.25C8 2.11193 7.88807 2 7.75 2H6.25C6.11193 2 6 2.11193 6 2.25V2.75C6 2.88807 6.11193 3 6.25 3H7.75C7.88807 3 8 2.88807 8 2.75V2.25ZM6 1C5.44772 1 5 1.44772 5 2V3C5 3.55228 5.44772 4 6 4H8C8.55228 4 9 3.55228 9 3V2C9 1.44772 8.55228 1 8 1H6Z" ] [] + , path [ fill "currentColor", fillRule "evenodd", d "M3 2.5C3 2.22386 3.22386 2 3.5 2C3.77614 2 4 2.22386 4 2.5V10.5C4 10.7761 4.22386 11 4.5 11H9.5C9.77614 11 10 10.7761 10 10.5V2.5C10 2.22386 10.2239 2 10.5 2C10.7761 2 11 2.22386 11 2.5V11C11 11.5523 10.5523 12 10 12H4C3.44772 12 3 11.5523 3 11V2.5Z" ] [] + ] diff --git a/src/UI/Sidebar.elm b/src/UI/Sidebar.elm index e33ab18..17232bf 100644 --- a/src/UI/Sidebar.elm +++ b/src/UI/Sidebar.elm @@ -1,19 +1,29 @@ module UI.Sidebar exposing (..) -import Html exposing (Html, a, aside, h3, label, text) +import Html exposing (Attribute, Html, a, aside, div, h3, label, text) import Html.Attributes exposing (class, id) import Html.Events exposing (onClick) +header : List (Html msg) -> Html msg +header content = + Html.header [ class "sidebar-header" ] content + + +headerItem : List (Attribute msg) -> List (Html msg) -> Html msg +headerItem attrs content = + div (attrs ++ [ class "sidebar-header-item" ]) content + + section : String -> List (Html msg) -> Html msg section label content = Html.section [ class "sidebar-section" ] - (header label :: content) + (sectionTitle label :: content) -header : String -> Html msg -header label = - h3 [ class "sidebar-header" ] [ text label ] +sectionTitle : String -> Html msg +sectionTitle label = + h3 [ class "sidebar-section-title" ] [ text label ] item : msg -> String -> Html msg diff --git a/src/css/app.css b/src/css/app.css index bab39d1..5df5922 100644 --- a/src/css/app.css +++ b/src/css/app.css @@ -52,6 +52,8 @@ --color-tooltip-fg: var(--color-sidebar-tooltip-fg); --color-tooltip-bg: var(--color-sidebar-tooltip-bg); --color-tooltip-border: var(--color-sidebar-tooltip-border); + + --color-main-divider: var(--color-sidebar-divider); } #main-sidebar a:hover { @@ -105,7 +107,7 @@ margin-top: 1rem; } -#main-sidebar .sidebar-header { +#main-sidebar .sidebar-section-title { font-size: var(--font-size-medium); font-weight: normal; color: var(--color-sidebar-subtle-fg); @@ -144,6 +146,10 @@ text-decoration: none; } +#main-sidebar .divider { + margin: 0; +} + /* -- Collapsing ----------------------------------------------------------- */ #main-sidebar .collapse-sidebar-button { @@ -184,23 +190,21 @@ color: var(--color-sidebar-keyboard-shortcut-hover-key-fg); } -/* -- Perspective ---------------------------------------------------------- */ - -#main-sidebar .perspective { - padding: 1rem 1.5rem; - margin-bottom: 0.5rem; +#main-sidebar .sidebar-header { + padding: 1rem 1.5rem 0 1.5rem; display: flex; - flex-direction: row; + flex-direction: column; position: relative; - gap: 0.75rem; + gap: 1.5rem; + margin-bottom: 0.5rem; } -#main-sidebar .perspective:after { +#main-sidebar .sidebar-header:after { position: absolute; left: 1.5rem; right: 1.5rem; bottom: -2rem; - height: 3rem; + height: 1.75rem; content: ""; background: linear-gradient( var(--color-sidebar-bg), @@ -209,11 +213,11 @@ ); } -#main-sidebar .perspective .namespace-slug { +#main-sidebar .sidebar-header .namespace-slug { position: relative; } -#main-sidebar .perspective.is-overflowing .namespace-slug:after { +#main-sidebar .sidebar-header .is-overflowing .namespace-slug:after { position: absolute; top: 0; right: -1.5rem; @@ -228,7 +232,7 @@ ); } -#main-sidebar .perspective .namespace { +#main-sidebar .sidebar-header .namespace { display: inline-flex; color: var(--color-sidebar-fg-em); font-size: 1rem; @@ -240,17 +244,23 @@ flex-direction: row-reverse; } -#main-sidebar .perspective .tooltip-bubble { - width: 16rem; +#main-sidebar .sidebar-header-item { + display: flex; + flex: 1; + flex-direction: row; + user-select: none; + align-items: center; + border-radius: var(--border-radius-base); + height: 1.875rem; + gap: 0.75rem; } -#main-sidebar .perspective .button { - opacity: 0.5; - margin-left: 0.375rem; +#main-sidebar .sidebar-header-item .button { + width: 100%; } -#main-sidebar .perspective .button:hover { - opacity: 1; +#main-sidebar . { + margin-bottom: 0; } /* -- Main Sidebar Unison Submenu ----------------------------------------------------- */ @@ -451,6 +461,7 @@ @import "./help-modal.css"; @import "./publish-modal.css"; @import "./report-bug-modal.css"; +@import "./download-modal.css"; @import "./finder.css"; @import "./perspective-landing.css"; diff --git a/src/css/composites.css b/src/css/composites.css index b2d0303..a87d89d 100644 --- a/src/css/composites.css +++ b/src/css/composites.css @@ -2,3 +2,4 @@ @import "./composites/modal.css"; @import "./composites/codebase-tree.css"; @import "./composites/readme.css"; +@import "./composites/copy-field.css"; diff --git a/src/css/composites/copy-field.css b/src/css/composites/copy-field.css new file mode 100644 index 0000000..f58a509 --- /dev/null +++ b/src/css/composites/copy-field.css @@ -0,0 +1,75 @@ +.copy-field { + position: relative; + display: flex; + flex-direction: row; + height: 2.25rem; + font-family: var(--font-monospace); + + --height-without-border: calc(2.25rem - 2px); +} + +.copy-field .copy-field-field { + position: relative; + display: flex; + flex-direction: row; + background: var(--color-gray-lighten-60); + border: 1px solid var(--color-gray-lighten-40); + border-radius: var(--border-radius-base) 0 0 var(--border-radius-base); + flex-grow: 1; +} + +.copy-field .copy-field-field:focus-within { + box-shadow: 0 0 0 2px var(--color-blue-3); + border-color: var(--color-blue-1); + border-right: 1px solid var(--color-blue-1); + /* z-index is to help cover the left border of the button when focused */ + z-index: 2; +} + +.copy-field .copy-field-prefix { + height: var(--height-without-border); + padding: 0 0.5ch 0 0.5rem; + font-size: var(--font-size-medium); + align-items: center; + display: flex; + color: var(--color-gray-lighten-20); +} + +.copy-field .copy-field-input { + flex-grow: 1; +} + +.copy-field input { + width: 100%; + font-family: var(--font-monospace); + height: var(--height-without-border); + font-size: var(--font-size-medium); + font-weight: 600; + background: transparent; +} + +.copy-field input:focus { + outline: none; +} + +/* TODO: Should this button be more aligned with other buttons in the app? */ +.copy-field button { + width: 2.25rem; + height: 2.25rem; + border: 1px solid var(--color-gray-lighten-40); + border-radius: 0 var(--border-radius-base) var(--border-radius-base) 0; + /* move 1 px left such that borders of field and button overlap + * (visible when clicking the button) */ + margin-left: -1px; + position: relative; +} + +.copy-field button:hover { + border-color: var(--color-gray-lighten-30); + /* z-index is to show the buttons left border on hover */ + z-index: 1; +} + +.copy-field button .icon { + font-size: 2.25rem; +} diff --git a/src/css/composites/modal.css b/src/css/composites/modal.css index 0d8d660..7ac55d1 100644 --- a/src/css/composites/modal.css +++ b/src/css/composites/modal.css @@ -69,3 +69,8 @@ .modal:focus { outline: none; } + +.hint { + font-size: var(--font-size-small); + color: var(--color-modal-subtle-fg-em); +} diff --git a/src/css/download-modal.css b/src/css/download-modal.css new file mode 100644 index 0000000..d6c5c2f --- /dev/null +++ b/src/css/download-modal.css @@ -0,0 +1,12 @@ +#download-modal { + width: 32rem; +} + +#download-modal p { + margin-bottom: 1.5rem; +} + +#download-modal .hint { + margin-top: 0.5rem; + padding-left: calc(var(--border-radius-base) / 2); +} diff --git a/src/css/elements.css b/src/css/elements.css index d6611ab..2265f2f 100644 --- a/src/css/elements.css +++ b/src/css/elements.css @@ -41,7 +41,7 @@ p { margin-bottom: 1em; } -hr { +.divider { background: var(--color-main-divider); border: 0; margin: 1.5rem 0; diff --git a/src/css/themes/unison/light.css b/src/css/themes/unison/light.css index 5c84e29..7d4e8b7 100644 --- a/src/css/themes/unison/light.css +++ b/src/css/themes/unison/light.css @@ -53,6 +53,7 @@ --color-sidebar-button-default-bg: var(--color-gray-base); --color-sidebar-button-default-hover-fg: var(--color-gray-lighten-50); --color-sidebar-button-default-hover-bg: var(--color-gray-base); + --color-sidebar-divider: var(--color-gray-darken-10); --color-sidebar-tooltip-fg: var(--color-gray-lighten-60); --color-sidebar-tooltip-bg: var(--color-gray-darken-30); diff --git a/src/init.js b/src/init.js index 46b2178..fa41c1b 100644 --- a/src/init.js +++ b/src/init.js @@ -1,6 +1,9 @@ import "./css/fonts.css"; import "./css/main.css"; +// Include web components +import "./UI/CopyOnClick"; + console.log(` _____ _ | | |___|_|___ ___ ___ From 87705c100cc19fcff7a49a118ff85a7a3b7319f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Wed, 24 Nov 2021 14:45:14 -0500 Subject: [PATCH 09/54] Fix bug where builtin folds were not disabled Builtins can be unfolded, and the fold toggle should be disabled. --- src/UI/FoldToggle.elm | 10 ++++++++++ src/Workspace/WorkspaceItem.elm | 18 ++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/UI/FoldToggle.elm b/src/UI/FoldToggle.elm index 6222b75..1ffa19f 100644 --- a/src/UI/FoldToggle.elm +++ b/src/UI/FoldToggle.elm @@ -63,11 +63,21 @@ isOpen isOpen_ toggle = { toggle | position = position } +isClosed : Bool -> FoldToggle msg -> FoldToggle msg +isClosed isClosed_ toggle = + isOpen (not isClosed_) toggle + + open : FoldToggle msg -> FoldToggle msg open toggle = withPosition Opened toggle +close : FoldToggle msg -> FoldToggle msg +close toggle = + withPosition Closed toggle + + isDisabled : Bool -> FoldToggle msg -> FoldToggle msg isDisabled isDisabled_ toggle = if isDisabled_ then diff --git a/src/Workspace/WorkspaceItem.elm b/src/Workspace/WorkspaceItem.elm index eaf3601..ada9686 100644 --- a/src/Workspace/WorkspaceItem.elm +++ b/src/Workspace/WorkspaceItem.elm @@ -465,6 +465,9 @@ viewSource zoom onSourceToggleClick sourceConfig item = viewToggableSource foldToggle renderedSource = div [ class "definition-source" ] [ FoldToggle.view foldToggle, renderedSource ] + + isBuiltin_ = + isBuiltinItem item in case item of TermItem (Term _ _ detail) -> @@ -480,24 +483,31 @@ viewSource zoom onSourceToggleClick sourceConfig item = ( Source.numTermSignatureLines detail.source , Source.viewNamedTermSignature sourceConfig detail.info.name (Term.termSignature detail.source) ) + + foldToggle = + if isBuiltin_ then + FoldToggle.disabled |> FoldToggle.close + + else + FoldToggle.foldToggle onSourceToggleClick |> FoldToggle.isOpen (zoom == Near) in ( numLines, source ) - |> Tuple.mapBoth viewLineGutter (viewToggableSource (FoldToggle.foldToggle onSourceToggleClick |> FoldToggle.isOpen (zoom == Near))) + |> Tuple.mapBoth viewLineGutter (viewToggableSource foldToggle) TypeItem (Type _ _ detail) -> ( detail.source, detail.source ) |> Tuple.mapBoth Source.numTypeLines (Source.viewTypeSource sourceConfig) - |> Tuple.mapBoth viewLineGutter (viewToggableSource (FoldToggle.disabled |> FoldToggle.open)) + |> Tuple.mapBoth viewLineGutter (viewToggableSource (FoldToggle.disabled |> FoldToggle.isClosed isBuiltin_)) DataConstructorItem (DataConstructor _ detail) -> ( detail.source, detail.source ) |> Tuple.mapBoth Source.numTypeLines (Source.viewTypeSource sourceConfig) - |> Tuple.mapBoth viewLineGutter (viewToggableSource (FoldToggle.disabled |> FoldToggle.open)) + |> Tuple.mapBoth viewLineGutter (viewToggableSource (FoldToggle.disabled |> FoldToggle.isClosed isBuiltin_)) AbilityConstructorItem (AbilityConstructor _ detail) -> ( detail.source, detail.source ) |> Tuple.mapBoth Source.numTypeLines (Source.viewTypeSource sourceConfig) - |> Tuple.mapBoth viewLineGutter (viewToggableSource (FoldToggle.disabled |> FoldToggle.open)) + |> Tuple.mapBoth viewLineGutter (viewToggableSource (FoldToggle.disabled |> FoldToggle.isClosed isBuiltin_)) viewItem : Reference -> ItemData -> Bool -> Html Msg From 8188dd85a391121bb3dea19d703167b32e2805de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Wed, 24 Nov 2021 15:55:59 -0500 Subject: [PATCH 10/54] Clean AppContext and rename Ucm to Unison Local Move AppContext to its own module (under Env) and rename the Ucm variant to Unison Local so that it matches how we talk about that UI. --- src/App.elm | 26 ++++++++--------- src/Env.elm | 47 ++----------------------------- src/Env/AppContext.elm | 45 +++++++++++++++++++++++++++++ src/PerspectiveLanding.elm | 9 +++--- src/PreApp.elm | 3 +- src/{Ucm.elm => UnisonLocal.elm} | 2 +- src/css/composites/app-header.css | 4 +-- src/css/perspective-landing.css | 2 +- src/css/themes/unison/light.css | 2 +- src/{ucm.ejs => unisonLocal.ejs} | 0 src/{ucm.js => unisonLocal.js} | 8 +++--- webpack.dev.js | 4 +-- webpack.prod.js | 16 ++++++----- 13 files changed, 85 insertions(+), 83 deletions(-) create mode 100644 src/Env/AppContext.elm rename src/{Ucm.elm => UnisonLocal.elm} (92%) rename src/{ucm.ejs => unisonLocal.ejs} (100%) rename src/{ucm.js => unisonLocal.js} (74%) diff --git a/src/App.elm b/src/App.elm index d0778ab..976b471 100644 --- a/src/App.elm +++ b/src/App.elm @@ -5,7 +5,8 @@ import Browser import Browser.Navigation as Nav import CodebaseTree import Definition.Reference exposing (Reference) -import Env exposing (AppContext(..), Env, OperatingSystem(..)) +import Env exposing (Env, OperatingSystem(..)) +import Env.AppContext as AppContext exposing (AppContext(..)) import Finder import Finder.SearchOptions as SearchOptions import FullyQualifiedName as FQN exposing (FQN) @@ -451,10 +452,10 @@ appTitle clickMsg appContext = content = case appContext of - Env.Ucm -> - h1 [] [ text "Unison", span [ class "context ucm" ] [ text "Local" ] ] + UnisonLocal -> + h1 [] [ text "Unison", span [ class "context unison-local" ] [ text "Local" ] ] - Env.UnisonShare -> + UnisonShare -> h1 [] [ text "Unison", span [ class "context unison-share" ] [ text "Share" ] ] in appTitle_ content @@ -479,7 +480,7 @@ viewAppHeader model = banner = case appContext of - Ucm -> + UnisonLocal -> Nothing UnisonShare -> @@ -544,7 +545,7 @@ subMenu appContext = , ( "Community", ExternalHref "https://unisonweb.org/community" ) , ( "Report a bug", OnClick (ShowModal ReportBugModal) ) ] - ++ (if Env.isUnisonLocal appContext then + ++ (if AppContext.isUnisonLocal appContext then [ ( "Unison Share", ExternalHref "https://share.unison-lang.org" ) ] else @@ -578,7 +579,7 @@ viewMainSidebar model = Perspective.toNamespacePerspective perspective >> ChangePerspective sidebarContent = - if Perspective.isCodebasePerspective perspective && Env.isUnisonShare appContext then + if Perspective.isCodebasePerspective perspective && AppContext.isUnisonShare appContext then UnisonShare.SidebarContent.view changePerspectiveMsg else @@ -732,7 +733,7 @@ viewReportBugModal appContext = , div [ class "action" ] [ githubLinkButton "unisonweb/codebase-ui" , text "for reports on" - , strong [] [ text (Env.appContextToString appContext) ] + , strong [] [ text (AppContext.toString appContext) ] , span [ class "subtle" ] [ text "(this UI)" ] ] , div [ class "action" ] @@ -784,7 +785,7 @@ viewAppError : AppContext -> Http.Error -> Html msg viewAppError appContext error = let context = - Env.appContextToString appContext + AppContext.toString appContext in div [ id "app" ] [ AppHeader.view (AppHeader.appHeader (appTitle Nothing appContext)) @@ -801,12 +802,7 @@ view : Model -> Browser.Document Msg view model = let title_ = - case model.env.appContext of - UnisonShare -> - "Unison Share" - - Ucm -> - "Unison Local" + AppContext.toString model.env.appContext page = case model.route of diff --git a/src/Env.elm b/src/Env.elm index f0637f0..15bc6dc 100644 --- a/src/Env.elm +++ b/src/Env.elm @@ -1,14 +1,10 @@ module Env exposing (..) import Api exposing (ApiBasePath(..)) +import Env.AppContext as AppContext exposing (AppContext) import Perspective exposing (Perspective) -type AppContext - = UnisonShare - | Ucm - - type OperatingSystem = MacOS | Windows @@ -40,50 +36,11 @@ init flags perspective = { operatingSystem = operatingSystemFromString flags.operatingSystem , basePath = flags.basePath , apiBasePath = ApiBasePath flags.apiBasePath - , appContext = appContextFromString flags.appContext + , appContext = AppContext.fromString flags.appContext , perspective = perspective } -appContextFromString : String -> AppContext -appContextFromString rawContext = - if rawContext == "UnisonShare" then - UnisonShare - - else - Ucm - - -appContextToString : AppContext -> String -appContextToString appContext = - case appContext of - UnisonShare -> - "Unison Share" - - Ucm -> - "Unison Local" - - -isUnisonShare : AppContext -> Bool -isUnisonShare appContext = - case appContext of - UnisonShare -> - True - - Ucm -> - False - - -isUnisonLocal : AppContext -> Bool -isUnisonLocal appContext = - case appContext of - UnisonShare -> - False - - Ucm -> - True - - operatingSystemFromString : String -> OperatingSystem operatingSystemFromString rawOs = case rawOs of diff --git a/src/Env/AppContext.elm b/src/Env/AppContext.elm new file mode 100644 index 0000000..ca80bd5 --- /dev/null +++ b/src/Env/AppContext.elm @@ -0,0 +1,45 @@ +module Env.AppContext exposing (..) + + +type AppContext + = UnisonShare + | UnisonLocal + + +fromString : String -> AppContext +fromString rawContext = + if rawContext == "UnisonShare" then + UnisonShare + + else + UnisonLocal + + +toString : AppContext -> String +toString appContext = + case appContext of + UnisonShare -> + "Unison Share" + + UnisonLocal -> + "Unison Local" + + +isUnisonShare : AppContext -> Bool +isUnisonShare appContext = + case appContext of + UnisonShare -> + True + + UnisonLocal -> + False + + +isUnisonLocal : AppContext -> Bool +isUnisonLocal appContext = + case appContext of + UnisonShare -> + False + + UnisonLocal -> + True diff --git a/src/PerspectiveLanding.elm b/src/PerspectiveLanding.elm index fde9e4e..fcb2e99 100644 --- a/src/PerspectiveLanding.elm +++ b/src/PerspectiveLanding.elm @@ -5,6 +5,7 @@ import Definition.Doc as Doc import Definition.Readme as Readme import Definition.Reference exposing (Reference) import Env exposing (Env) +import Env.AppContext exposing (AppContext(..)) import FullyQualifiedName as FQN exposing (FQN) import Html exposing (Html, a, article, div, h2, header, p, section, span, strong, text) import Html.Attributes exposing (class, href, id, rel, target) @@ -109,7 +110,7 @@ viewEmptyState title description cta = ] -viewEmptyStateCodebase : Env.AppContext -> Html Msg +viewEmptyStateCodebase : AppContext -> Html Msg viewEmptyStateCodebase appContext = let button = @@ -118,9 +119,9 @@ viewEmptyStateCodebase appContext = |> Button.medium in case appContext of - Env.Ucm -> + UnisonLocal -> viewEmptyState - (span [ class "ucm" ] [ text "Your ", span [ class "context" ] [ text "Local" ], text " Unison Codebase" ]) + (span [ class "unison-local" ] [ text "Your ", span [ class "context" ] [ text "Local" ], text " Unison Codebase" ]) [ p [] [ text "Browse, search, read docs, open definitions, and explore your local codebase." ] , p [] [ text "Check out " @@ -130,7 +131,7 @@ viewEmptyStateCodebase appContext = ] button - Env.UnisonShare -> + UnisonShare -> viewEmptyState (span [ class "unison-share" ] [ text "Unison ", span [ class "context" ] [ text "Share" ] ]) [ p [] [ text "Explore to discover and share Unison libraries, documentation, types, and terms." ] ] diff --git a/src/PreApp.elm b/src/PreApp.elm index 06311df..cca3106 100644 --- a/src/PreApp.elm +++ b/src/PreApp.elm @@ -5,6 +5,7 @@ import App import Browser import Browser.Navigation as Nav import Env exposing (Flags) +import Env.AppContext as AppContext import Html import Http import Perspective exposing (Perspective, PerspectiveParams) @@ -121,7 +122,7 @@ view : Model -> Browser.Document Msg view model = let appContext flags = - Env.appContextFromString flags.appContext + AppContext.fromString flags.appContext in case model of Initializing preEnv -> diff --git a/src/Ucm.elm b/src/UnisonLocal.elm similarity index 92% rename from src/Ucm.elm rename to src/UnisonLocal.elm index 9c26aa9..95c5b68 100644 --- a/src/Ucm.elm +++ b/src/UnisonLocal.elm @@ -1,4 +1,4 @@ -module Ucm exposing (..) +module UnisonLocal exposing (..) import App import Browser diff --git a/src/css/composites/app-header.css b/src/css/composites/app-header.css index 3f638fa..0a708b2 100644 --- a/src/css/composites/app-header.css +++ b/src/css/composites/app-header.css @@ -51,8 +51,8 @@ color: var(--color-app-header-context-unison-share-fg); } -#app-header .app-title .ucm { - color: var(--color-app-header-context-ucm-fg); +#app-header .app-title .unison-local { + color: var(--color-app-header-context-unison-local-fg); } #app-header .right { diff --git a/src/css/perspective-landing.css b/src/css/perspective-landing.css index b4cc43d..2e508c8 100644 --- a/src/css/perspective-landing.css +++ b/src/css/perspective-landing.css @@ -101,7 +101,7 @@ width: 28rem; } -.perspective-landing-empty-state .ucm .context { +.perspective-landing-empty-state .unison-local .context { color: var(--color-pink-2); } diff --git a/src/css/themes/unison/light.css b/src/css/themes/unison/light.css index 5c84e29..df16a8b 100644 --- a/src/css/themes/unison/light.css +++ b/src/css/themes/unison/light.css @@ -22,7 +22,7 @@ --color-app-header-subtle-fg: var(--color-gray-lighten-20); --color-app-header-subtle-fg-em: var(--color-gray-lighten-50); --color-app-header-context-unison-share-fg: var(--color-purple-4); - --color-app-header-context-ucm-fg: var(--color-pink-3); + --color-app-header-context-unison-local-fg: var(--color-pink-3); --color-app-header-border: var(--color-gray-base); --color-keyboard-shortcut-key-fg: var(--color-gray-base); diff --git a/src/ucm.ejs b/src/unisonLocal.ejs similarity index 100% rename from src/ucm.ejs rename to src/unisonLocal.ejs diff --git a/src/ucm.js b/src/unisonLocal.js similarity index 74% rename from src/ucm.js rename to src/unisonLocal.js index 82d8dea..d410a6f 100644 --- a/src/ucm.js +++ b/src/unisonLocal.js @@ -1,7 +1,7 @@ import "./init"; import detectOs from "./detectOs"; import preventDefaultGlobalKeyboardEvents from "./preventDefaultGlobalKeyboardEvents"; -import { Elm } from "./Ucm.elm"; +import { Elm } from "./UnisonLocal.elm"; const basePath = new URL(document.baseURI).pathname; @@ -20,10 +20,10 @@ const flags = { operatingSystem: detectOs(window.navigator), basePath, apiBasePath, - appContext: "Ucm", + appContext: "UnisonLocal", }; preventDefaultGlobalKeyboardEvents(); -// The main entry point for the `ucm` target of the Codebase UI. -Elm.Ucm.init({ flags }); +// The main entry point for the `UnisonLocal` target of the Codebase UI. +Elm.UnisonLocal.init({ flags }); diff --git a/webpack.dev.js b/webpack.dev.js index 6f39bac..2ed3912 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -4,7 +4,7 @@ const HtmlWebpackPlugin = require("html-webpack-plugin"); const API_URL = process.env.API_URL || "127.0.0.1:8080"; module.exports = { - entry: "./src/ucm.js", + entry: "./src/unisonLocal.js", module: { rules: [ @@ -42,7 +42,7 @@ module.exports = { plugins: [ new HtmlWebpackPlugin({ favicon: "./static/favicon.ico", - template: "./src/ucm.ejs", + template: "./src/unisonLocal.ejs", inject: "body", publicPath: "/", base: "/", diff --git a/webpack.prod.js b/webpack.prod.js index 327110e..a1f01b7 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -80,25 +80,27 @@ const unisonShareCfg = { }, }; -const ucmCfg = { +const unisonLocalCfg = { ...shared, - entry: "./src/ucm.js", + entry: "./src/unisonLocal.js", plugins: [ new HtmlWebpackPlugin({ favicon: "./static/favicon.ico", - template: "./src/ucm.ejs", + template: "./src/unisonLocal.ejs", inject: "body", publicPath: "/static/", base: false, // set dynamically by grabbing the 2 first path segments in the url. - filename: path.resolve(__dirname, "dist/ucm/index.html"), + filename: path.resolve(__dirname, "dist/unisonLocal/index.html"), }), new FileManagerPlugin({ events: { onEnd: { - archive: [{ source: "dist/ucm", destination: "dist/ucm.zip" }], + archive: [ + { source: "dist/unisonLocal", destination: "dist/unisonLocal.zip" }, + ], }, }, }), @@ -106,9 +108,9 @@ const ucmCfg = { output: { filename: "[name].[contenthash].js", - path: path.resolve(__dirname, "dist/ucm/static"), + path: path.resolve(__dirname, "dist/unisonLocal/static"), clean: true, }, }; -module.exports = [unisonShareCfg, ucmCfg]; +module.exports = [unisonShareCfg, unisonLocalCfg]; From 7bd3f81e3ceca4c4b1da2ae2b75b1dfa73e16a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Tue, 30 Nov 2021 12:51:49 -0500 Subject: [PATCH 11/54] Ensure Definitions are opened via Route changes We want the URL to be main way definitions are opened and fetched. Previously this was an afterthought such that we fetched a definition and then changed the URL, resulting in broken back-button behavior. This changes how we load definitions such that a URL is changed which, in a route handler, causes the Workspace to open and fetch that definition, with 1 exception: when a definition is opened from another definition, it is important that we open the new definition relative to the originating definition (above it or below it). In this case we open the definition, then change the URL, which subsequently tries to open the definition again; seeing that the definition has been opened already the fetch attempt is cancelled. --- src/App.elm | 44 +++++++++++++++++++-------------------- src/Route.elm | 6 +++--- src/Workspace.elm | 52 ++++++++++++++++++++++++++--------------------- 3 files changed, 54 insertions(+), 48 deletions(-) diff --git a/src/App.elm b/src/App.elm index d0778ab..10fff12 100644 --- a/src/App.elm +++ b/src/App.elm @@ -134,10 +134,20 @@ update msg ({ env } as model) = ( model, Cmd.none ) UrlChanged url -> - -- URL changes happen when setting focus on a definitions. - -- Currently, the URL change is a result of that as oppose to focus - -- being a result of a URL change - ( { model | route = Route.fromUrl env.basePath url }, Cmd.none ) + let + route = + Route.fromUrl env.basePath url + in + case route of + Route.Definition _ ref -> + let + ( workspace, cmd ) = + Workspace.open env model.workspace ref + in + ( { model | route = route, workspace = workspace }, Cmd.map WorkspaceMsg cmd ) + + _ -> + ( { model | route = route }, Cmd.none ) ChangePerspective perspective -> replacePerspective model perspective @@ -165,7 +175,7 @@ update msg ({ env } as model) = keydown model event OpenDefinition ref -> - openDefinition model ref + navigateToDefinition model ref ShowModal modal -> ( { model | modal = modal }, Cmd.none ) @@ -200,7 +210,7 @@ update msg ({ env } as model) = in case outMsg of PerspectiveLanding.OpenDefinition ref -> - openDefinition model2 ref + navigateToDefinition model2 ref PerspectiveLanding.ShowFinderRequest -> showFinder model2 Nothing @@ -227,7 +237,7 @@ update msg ({ env } as model) = model4 = { model2 | sidebarToggled = False } in - openDefinition model4 ref + navigateToDefinition model4 ref CodebaseTree.ChangePerspectiveToNamespace fqn -> fqn @@ -251,7 +261,7 @@ update msg ({ env } as model) = ( { model | modal = NoModal }, Cmd.none ) Finder.OpenDefinition ref -> - openDefinition { model | modal = NoModal } ref + navigateToDefinition { model | modal = NoModal } ref _ -> ( model, Cmd.none ) @@ -268,19 +278,9 @@ update msg ({ env } as model) = -- UPDATE HELPERS -openDefinition : Model -> Reference -> ( Model, Cmd Msg ) -openDefinition model ref = - let - ( workspace, wCmd, outMsg ) = - Workspace.open model.env model.workspace ref - - model2 = - { model | workspace = workspace } - - ( model3, cmd ) = - handleWorkspaceOutMsg model2 outMsg - in - ( model3, Cmd.batch [ cmd, Cmd.map WorkspaceMsg wCmd ] ) +navigateToDefinition : Model -> Reference -> ( Model, Cmd Msg ) +navigateToDefinition model ref = + ( model, Route.navigateToDefinition model.navKey model.route ref ) replacePerspective : Model -> Perspective -> ( Model, Cmd Msg ) @@ -333,7 +333,7 @@ handleWorkspaceOutMsg model out = showFinder model withinNamespace Workspace.Focused ref -> - ( model, Route.navigateToByReference model.navKey model.route ref ) + ( model, Route.navigateToDefinition model.navKey model.route ref ) Workspace.Emptied -> ( model, Route.navigateToCurrentPerspective model.navKey model.route ) diff --git a/src/Route.elm b/src/Route.elm index 131def7..e3be702 100644 --- a/src/Route.elm +++ b/src/Route.elm @@ -2,8 +2,8 @@ module Route exposing ( Route(..) , fromUrl , navigate - , navigateToByReference , navigateToCurrentPerspective + , navigateToDefinition , navigateToLatestCodebase , navigateToPerspective , perspectiveParams @@ -263,8 +263,8 @@ navigateToLatestCodebase navKey = navigateToPerspective navKey (ByCodebase Relative) -navigateToByReference : Nav.Key -> Route -> Reference -> Cmd msg -navigateToByReference navKey currentRoute reference = +navigateToDefinition : Nav.Key -> Route -> Reference -> Cmd msg +navigateToDefinition navKey currentRoute reference = navigate navKey (toDefinition currentRoute reference) diff --git a/src/Workspace.elm b/src/Workspace.elm index 9bacd8c..ff1f14d 100644 --- a/src/Workspace.elm +++ b/src/Workspace.elm @@ -27,7 +27,7 @@ import Perspective exposing (Perspective) import Task import UI.Button as Button import UI.Icon as Icon -import Workspace.WorkspaceItem as WorkspaceItem exposing (Item, WorkspaceItem(..)) +import Workspace.WorkspaceItem as WorkspaceItem exposing (Item, WorkspaceItem) import Workspace.WorkspaceItems as WorkspaceItems exposing (WorkspaceItems) @@ -55,7 +55,7 @@ init env mRef = Just ref -> let - ( m, c, _ ) = + ( m, c ) = open env model ref in ( m, c ) @@ -116,7 +116,7 @@ update env msg ({ workspaceItems } as model) = in ( { model | workspaceItems = nextWorkspaceItems } , cmd - , openDefinitionsFocusToOutMsg nextWorkspaceItems + , None ) IsDocCropped ref res -> @@ -157,7 +157,7 @@ update env msg ({ workspaceItems } as model) = WorkspaceItemMsg wiMsg -> case wiMsg of WorkspaceItem.OpenReference relativeToRef ref -> - openItem env model (Just relativeToRef) ref + openReference env model relativeToRef ref WorkspaceItem.Close ref -> let @@ -218,11 +218,6 @@ type alias WithWorkspaceItems m = { m | workspaceItems : WorkspaceItems } -open : Env -> WithWorkspaceItems m -> Reference -> ( WithWorkspaceItems m, Cmd Msg, OutMsg ) -open env model ref = - openItem env model Nothing ref - - replaceWorkspaceItemReferencesWithHashOnly : Model -> Model replaceWorkspaceItemReferencesWithHashOnly model = let @@ -232,7 +227,29 @@ replaceWorkspaceItemReferencesWithHashOnly model = { model | workspaceItems = workspaceItems } -openItem : Env -> WithWorkspaceItems m -> Maybe Reference -> Reference -> ( WithWorkspaceItems m, Cmd Msg, OutMsg ) +open : Env -> WithWorkspaceItems m -> Reference -> ( WithWorkspaceItems m, Cmd Msg ) +open env model ref = + openItem env model Nothing ref + + +{-| openReference opens a definition relative to another definition. This is +done within Workspace, as opposed to from the outside via a URL change. This +function returns a Focused command for the newly opened reference and as such +changes the URL. +-} +openReference : Env -> WithWorkspaceItems m -> Reference -> Reference -> ( WithWorkspaceItems m, Cmd Msg, OutMsg ) +openReference env model relativeToRef ref = + let + ( newModel, cmd ) = + openItem env model (Just relativeToRef) ref + + out = + openDefinitionsFocusToOutMsg newModel.workspaceItems + in + ( newModel, cmd, out ) + + +openItem : Env -> WithWorkspaceItems m -> Maybe Reference -> Reference -> ( WithWorkspaceItems m, Cmd Msg ) openItem env ({ workspaceItems } as model) relativeToRef ref = -- We don't want to refetch or replace any already open definitions, but we -- do want to focus and scroll to it @@ -243,7 +260,6 @@ openItem env ({ workspaceItems } as model) relativeToRef ref = in ( { model | workspaceItems = nextWorkspaceItems } , scrollToDefinition ref - , openDefinitionsFocusToOutMsg nextWorkspaceItems ) else @@ -261,24 +277,14 @@ openItem env ({ workspaceItems } as model) relativeToRef ref = in ( { model | workspaceItems = nextWorkspaceItems } , Cmd.batch [ Api.perform env.apiBasePath (fetchDefinition env.perspective ref), scrollToDefinition ref ] - , openDefinitionsFocusToOutMsg nextWorkspaceItems ) openDefinitionsFocusToOutMsg : WorkspaceItems -> OutMsg openDefinitionsFocusToOutMsg openDefs = - let - toFocusedOut workspaceItem = - case workspaceItem of - Success ref _ -> - Focused ref - - _ -> - None - in openDefs - |> WorkspaceItems.focus - |> Maybe.map toFocusedOut + |> WorkspaceItems.focusedReference + |> Maybe.map Focused |> Maybe.withDefault Emptied From 89589e90982b4b1593c52d053507f6b26bb8401d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Tue, 30 Nov 2021 13:57:37 -0500 Subject: [PATCH 12/54] fix bad merge --- src/App.elm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.elm b/src/App.elm index de01b4b..41b20b6 100644 --- a/src/App.elm +++ b/src/App.elm @@ -525,7 +525,7 @@ viewSidebarHeader env = |> List.singleton |> Sidebar.headerItem [] - Ucm -> + UnisonLocal -> UI.nothing in Sidebar.header From 42f3789634975129ebae85171828b475d9e762cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Wed, 1 Dec 2021 21:04:00 -0500 Subject: [PATCH 13/54] Drive perspective changes off of Route changes Instead of changing perspectives directly and fetching details before changing the URL; first change the URL and then change the `Perspective`. Ensure that we don't fetch the `Perspective` when changing URLs to the same `Perspective` with a different definition and ensure we correctly dedupe `WorkspaceItems` when we change `Perspective`. To support this add a bunch of helper functions: * Equality and sameness functions to `Reference`, `Perspective`, and `HashQualified` * List-like functions; `find`, `any`, and `all` to `WorkspaceItems` With regards to deduping `WorkspaceItems`, this is needed because when we change `Perspective` with any open defintions, we migrate the `WorkspaceItems` to be indexed by `Hash` instead of `FQN`, and then when the user subsequently uses the back button to the previous URL that was `FQN` based, we want to avoid re-fetching and duplicating the same item (their `Reference` are technically different in that one is `Hash` based and one is `FQN` based for the same `WorkspaceItem`). So as soon as we get the data back from the server and can see that it includes the `Hash` of an already fetched definition, we dedupe. --- src/App.elm | 66 +++++++++++++++++++------------ src/Definition/Reference.elm | 47 ++++++++++++++++++++++ src/HashQualified.elm | 50 ++++++++++++++++++++++++ src/Perspective.elm | 60 ++++++++++++++++++++++++++++ src/Workspace.elm | 67 ++++++++++++++++++++++---------- src/Workspace/WorkspaceItem.elm | 32 ++++++++++++--- src/Workspace/WorkspaceItems.elm | 31 +++++++++++++++ 7 files changed, 302 insertions(+), 51 deletions(-) diff --git a/src/App.elm b/src/App.elm index 41b20b6..cacf0d3 100644 --- a/src/App.elm +++ b/src/App.elm @@ -140,20 +140,32 @@ update msg ({ env } as model) = let route = Route.fromUrl env.basePath url + + model2 = + { model | route = route } + + newEnv params = + { env | perspective = Perspective.nextFromParams env.perspective params } in case route of - Route.Definition _ ref -> + Route.Definition params ref -> let ( workspace, cmd ) = - Workspace.open env model.workspace ref + Workspace.open (newEnv params) model.workspace ref + + model3 = + { model2 | workspace = workspace, env = newEnv params } + + ( model4, fetchPerspectiveCmd ) = + fetchPerspective model3 in - ( { model | route = route, workspace = workspace }, Cmd.map WorkspaceMsg cmd ) + ( model4, Cmd.batch [ Cmd.map WorkspaceMsg cmd, fetchPerspectiveCmd ] ) - _ -> - ( { model | route = route }, Cmd.none ) + Route.Perspective params -> + fetchPerspective { model2 | env = newEnv params } ChangePerspective perspective -> - replacePerspective model perspective + navigateToPerspective model perspective FetchPerspectiveNamespaceDetailsFinished fqn details -> let @@ -245,7 +257,7 @@ update msg ({ env } as model) = CodebaseTree.ChangePerspectiveToNamespace fqn -> fqn |> Perspective.toNamespacePerspective model.env.perspective - |> replacePerspective model + |> navigateToPerspective model in ( model3, Cmd.batch [ cmd, Cmd.map CodebaseTreeMsg cCmd ] ) @@ -286,15 +298,9 @@ navigateToDefinition model ref = ( model, Route.navigateToDefinition model.navKey model.route ref ) -replacePerspective : Model -> Perspective -> ( Model, Cmd Msg ) -replacePerspective ({ env } as model) perspective = +navigateToPerspective : Model -> Perspective -> ( Model, Cmd Msg ) +navigateToPerspective model perspective = let - newEnv = - { env | perspective = perspective } - - ( codebaseTree, codebaseTreeCmd ) = - CodebaseTree.init newEnv - -- Update all open references to be hash based to ensure that we can -- refresh the page and fetch them appropriately even if they are -- outside of the current perspective @@ -310,20 +316,32 @@ replacePerspective ({ env } as model) perspective = changeRouteCmd = Route.replacePerspective model.navKey (Perspective.toParams perspective) focusedReferenceRoute + in + ( { model | workspace = workspace }, changeRouteCmd ) + + +fetchPerspective : Model -> ( Model, Cmd Msg ) +fetchPerspective ({ env } as model) = + let + ( codebaseTree, codebaseTreeCmd ) = + CodebaseTree.init env fetchNamespaceDetailsCmd = - perspective + env.perspective |> fetchNamespaceDetails |> Maybe.map (Api.perform env.apiBasePath) |> Maybe.withDefault Cmd.none in - ( { model | env = newEnv, codebaseTree = codebaseTree, workspace = workspace } - , Cmd.batch - [ Cmd.map CodebaseTreeMsg codebaseTreeCmd - , changeRouteCmd - , fetchNamespaceDetailsCmd - ] - ) + if Perspective.needsFetching env.perspective then + ( { model | codebaseTree = codebaseTree } + , Cmd.batch + [ Cmd.map CodebaseTreeMsg codebaseTreeCmd + , fetchNamespaceDetailsCmd + ] + ) + + else + ( model, Cmd.none ) handleWorkspaceOutMsg : Model -> Workspace.OutMsg -> ( Model, Cmd Msg ) @@ -344,7 +362,7 @@ handleWorkspaceOutMsg model out = Workspace.ChangePerspectiveToNamespace fqn -> fqn |> Perspective.toNamespacePerspective model.env.perspective - |> replacePerspective model + |> navigateToPerspective model keydown : Model -> KeyboardEvent -> ( Model, Cmd Msg ) diff --git a/src/Definition/Reference.elm b/src/Definition/Reference.elm index f0211cf..b285317 100644 --- a/src/Definition/Reference.elm +++ b/src/Definition/Reference.elm @@ -1,6 +1,7 @@ module Definition.Reference exposing (..) import FullyQualifiedName exposing (FQN) +import Hash exposing (Hash) import HashQualified as HQ exposing (HashQualified) import UI.Icon as Icon exposing (Icon) import Url.Parser @@ -36,6 +37,47 @@ urlParser toRef = -- HELPERS +equals : Reference -> Reference -> Bool +equals a b = + case ( a, b ) of + ( TermReference aHq, TermReference bHq ) -> + HQ.equals aHq bHq + + ( TypeReference aHq, TypeReference bHq ) -> + HQ.equals aHq bHq + + ( AbilityConstructorReference aHq, AbilityConstructorReference bHq ) -> + HQ.equals aHq bHq + + ( DataConstructorReference aHq, DataConstructorReference bHq ) -> + HQ.equals aHq bHq + + _ -> + False + + +{-| Like `equals`, but compares deeper such that a HashQualified with the same +Hash as a HashOnly are considered the same +-} +same : Reference -> Reference -> Bool +same a b = + case ( a, b ) of + ( TermReference aHq, TermReference bHq ) -> + HQ.same aHq bHq + + ( TypeReference aHq, TypeReference bHq ) -> + HQ.same aHq bHq + + ( AbilityConstructorReference aHq, AbilityConstructorReference bHq ) -> + HQ.same aHq bHq + + ( DataConstructorReference aHq, DataConstructorReference bHq ) -> + HQ.same aHq bHq + + _ -> + False + + hashQualified : Reference -> HashQualified hashQualified ref = case ref of @@ -57,6 +99,11 @@ fqn = hashQualified >> HQ.name +hash : Reference -> Maybe Hash +hash = + hashQualified >> HQ.hash + + -- TRANSFORM diff --git a/src/HashQualified.elm b/src/HashQualified.elm index 7e2251a..278911f 100644 --- a/src/HashQualified.elm +++ b/src/HashQualified.elm @@ -1,9 +1,11 @@ module HashQualified exposing ( HashQualified(..) + , equals , fromString , fromUrlString , hash , name + , same , toString , toUrlString , urlParser @@ -77,6 +79,54 @@ urlParser = -- HELPERS +equals : HashQualified -> HashQualified -> Bool +equals a b = + case ( a, b ) of + ( NameOnly aFqn, NameOnly bFqn ) -> + FQN.equals aFqn bFqn + + ( HashOnly aH, HashOnly bH ) -> + Hash.equals aH bH + + ( HashQualified aFqn aH, HashQualified bFqn bH ) -> + FQN.equals aFqn bFqn && Hash.equals aH bH + + _ -> + False + + +{-| Like `equals`, but compares deeper such that a HashQualified with the same +Hash as a HashOnly are considered the same, and HashQualified with the same FQN +as a NameOnly are considered the same. +-} +same : HashQualified -> HashQualified -> Bool +same a b = + case ( a, b ) of + ( NameOnly aFqn, NameOnly bFqn ) -> + FQN.equals aFqn bFqn + + ( HashOnly aH, HashOnly bH ) -> + Hash.equals aH bH + + ( HashQualified aFqn aH, HashQualified bFqn bH ) -> + FQN.equals aFqn bFqn && Hash.equals aH bH + + ( HashQualified _ aH, HashOnly bH ) -> + Hash.equals aH bH + + ( HashOnly aH, HashQualified _ bH ) -> + Hash.equals aH bH + + ( HashQualified aFqn _, NameOnly bFqn ) -> + FQN.equals aFqn bFqn + + ( NameOnly aFqn, HashQualified bFqn _ ) -> + FQN.equals aFqn bFqn + + _ -> + False + + name : HashQualified -> Maybe FQN name hq = case hq of diff --git a/src/Perspective.elm b/src/Perspective.elm index b193595..8539542 100644 --- a/src/Perspective.elm +++ b/src/Perspective.elm @@ -46,6 +46,19 @@ fqn perspective = d.fqn +equals : Perspective -> Perspective -> Bool +equals a b = + case ( a, b ) of + ( Codebase ah, Codebase bh ) -> + Hash.equals ah bh + + ( Namespace ans, Namespace bns ) -> + Hash.equals ans.codebaseHash bns.codebaseHash && FQN.equals ans.fqn bns.fqn + + _ -> + False + + {-| Even when we have a Codebase hash, we always constructor Relative params. Absolute is currently not supported (until Unison Share includes historic codebase), though the model allows it. @@ -76,6 +89,53 @@ fromParams params = Just (Namespace { codebaseHash = h, fqn = fqn_, details = NotAsked }) +{-| Similar to `fromParams`, but requires a previous `Perspective` (with a +codebase hash) to migrate from +-} +nextFromParams : Perspective -> PerspectiveParams -> Perspective +nextFromParams perspective params = + let + codebaseHash_ = + codebaseHash perspective + in + case ( params, perspective ) of + ( ByNamespace Relative fqn_, Namespace d ) -> + if Hash.equals codebaseHash_ d.codebaseHash && FQN.equals fqn_ d.fqn then + Namespace d + + else + Namespace { codebaseHash = codebaseHash_, fqn = fqn_, details = NotAsked } + + ( ByNamespace (Absolute h) fqn_, Namespace d ) -> + if Hash.equals h d.codebaseHash && FQN.equals fqn_ d.fqn then + Namespace d + + else + Namespace { codebaseHash = h, fqn = fqn_, details = NotAsked } + + ( ByNamespace Relative fqn_, _ ) -> + Namespace { codebaseHash = codebaseHash_, fqn = fqn_, details = NotAsked } + + ( ByNamespace (Absolute h) fqn_, _ ) -> + Namespace { codebaseHash = h, fqn = fqn_, details = NotAsked } + + ( ByCodebase Relative, _ ) -> + Codebase codebaseHash_ + + ( ByCodebase (Absolute h), _ ) -> + Codebase h + + +needsFetching : Perspective -> Bool +needsFetching perspective = + case perspective of + Namespace d -> + d.details == NotAsked + + _ -> + False + + isCodebasePerspective : Perspective -> Bool isCodebasePerspective perspective = case perspective of diff --git a/src/Workspace.elm b/src/Workspace.elm index ff1f14d..c57a156 100644 --- a/src/Workspace.elm +++ b/src/Workspace.elm @@ -16,6 +16,7 @@ import Definition.Doc as Doc import Definition.Reference as Reference exposing (Reference) import Env exposing (Env) import FullyQualifiedName exposing (FQN) +import Hash import HashQualified as HQ import Html exposing (Html, article, div, header, section) import Html.Attributes exposing (class, id) @@ -93,31 +94,55 @@ update env msg ({ workspaceItems } as model) = ( model, Cmd.none, ShowFinderRequest Nothing ) FetchItemFinished ref itemResult -> - let - ( workspaceItem, cmd ) = - case itemResult of - Err e -> - ( WorkspaceItem.Failure ref e, Cmd.none ) + case itemResult of + Err e -> + ( { model | workspaceItems = WorkspaceItems.replace workspaceItems ref (WorkspaceItem.Failure ref e) } + , Cmd.none + , None + ) + + Ok i -> + let + cmd = + -- Docs items are always shown in full and never cropped + if WorkspaceItem.isDocItem i then + Cmd.none + + else + isDocCropped ref - Ok i -> + isDupe wi = let - c = - -- Docs items are always shown in full and never cropped - if WorkspaceItem.isDocItem i then - Cmd.none + ref_ = + WorkspaceItem.reference wi - else - isDocCropped ref - in - ( WorkspaceItem.fromItem ref i, c ) + refEqs = + Reference.equals ref ref_ - nextWorkspaceItems = - WorkspaceItems.replace workspaceItems ref workspaceItem - in - ( { model | workspaceItems = nextWorkspaceItems } - , cmd - , None - ) + hashEqs = + wi + |> WorkspaceItem.hash + |> Maybe.map (Hash.equals (WorkspaceItem.itemHash i)) + |> Maybe.withDefault False + in + (Reference.same ref ref_ && not refEqs) || (hashEqs && not refEqs) + + -- In some cases (like using the back button between + -- perspectives) we try and fetch the same item twice, not + -- knowing we've fetched it before since one was by hash + -- and the other by name. If found to already be fetched, + -- we favor the newly fetched item and discard the old + deduped = + workspaceItems + |> WorkspaceItems.find isDupe + |> Maybe.map WorkspaceItem.reference + |> Maybe.map (WorkspaceItems.remove workspaceItems) + |> Maybe.withDefault workspaceItems + + nextWorkspaceItems = + WorkspaceItems.replace deduped ref (WorkspaceItem.fromItem ref i) + in + ( { model | workspaceItems = nextWorkspaceItems }, cmd, None ) IsDocCropped ref res -> let diff --git a/src/Workspace/WorkspaceItem.elm b/src/Workspace/WorkspaceItem.elm index ada9686..4f48b27 100644 --- a/src/Workspace/WorkspaceItem.elm +++ b/src/Workspace/WorkspaceItem.elm @@ -132,10 +132,10 @@ reference item = toHashReference : WorkspaceItem -> WorkspaceItem toHashReference workspaceItem = let - toHashOnly hash hq = + toHashOnly hash_ hq = case hq of HQ.NameOnly _ -> - HQ.HashOnly hash + HQ.HashOnly hash_ HQ.HashOnly h -> HQ.HashOnly h @@ -254,6 +254,26 @@ hasDoc item = False +{-| Attempt to get the Hash of a WorkspaceItem. First by checking if the +Reference includes the Hash, secondly by checking the item data itself. +-} +hash : WorkspaceItem -> Maybe Hash +hash wItem = + let + itemHash_ = + case wItem of + Success _ d -> + Just (itemHash d.item) + + _ -> + Nothing + in + wItem + |> reference + |> Reference.hash + |> MaybeE.orElse itemHash_ + + itemHash : Item -> Hash itemHash item = case item of @@ -387,21 +407,21 @@ viewInfoItems hash_ info = formattedHash = hash_ |> Hash.toShortString |> Hash.stripHashPrefix - hash = + hashTooltip = Tooltip.tooltip (viewInfoItem Icon.hash formattedHash) (Tooltip.Text (Hash.toString hash_)) |> Tooltip.withArrow Tooltip.Start |> Tooltip.view in - div [ class "info-items" ] [ hash, namespace, otherNames ] + div [ class "info-items" ] [ hashTooltip, namespace, otherNames ] viewInfo : Zoom -> Msg -> Hash -> Info -> Category -> Html Msg -viewInfo zoom onClick_ hash info category = +viewInfo zoom onClick_ hash_ info category = div [ class "info" ] [ FoldToggle.foldToggle onClick_ |> FoldToggle.isOpen (zoom /= Far) |> FoldToggle.view , div [ class "category-icon" ] [ Icon.view (Category.icon category) ] , h3 [ class "name" ] [ FQN.view info.name ] - , viewInfoItems hash info + , viewInfoItems hash_ info ] diff --git a/src/Workspace/WorkspaceItems.elm b/src/Workspace/WorkspaceItems.elm index e68f61d..2342321 100644 --- a/src/Workspace/WorkspaceItems.elm +++ b/src/Workspace/WorkspaceItems.elm @@ -18,8 +18,10 @@ module Workspace.WorkspaceItems exposing (..) import Definition.Reference exposing (Reference) +import Hash exposing (Hash) import List import List.Extra as ListE +import Maybe.Extra as MaybeE import Workspace.WorkspaceItem as WorkspaceItem exposing (WorkspaceItem) @@ -253,6 +255,14 @@ member items ref = items |> references |> List.member ref +hashes : WorkspaceItems -> List Hash +hashes items = + items + |> toList + |> List.map WorkspaceItem.hash + |> MaybeE.values + + references : WorkspaceItems -> List Reference references items = items @@ -445,6 +455,27 @@ map f wItems = } +find : (WorkspaceItem -> Bool) -> WorkspaceItems -> Maybe WorkspaceItem +find pred wItems = + wItems + |> toList + |> ListE.find pred + + +all : (WorkspaceItem -> Bool) -> WorkspaceItems -> Bool +all pred wItems = + wItems + |> toList + |> List.all pred + + +any : (WorkspaceItem -> Bool) -> WorkspaceItems -> Bool +any pred wItems = + wItems + |> toList + |> List.any pred + + mapToList : (WorkspaceItem -> Bool -> a) -> WorkspaceItems -> List a mapToList f wItems = case wItems of From ed9f8f81d490fd5cc323cf817b5b806f9cafed57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Fri, 3 Dec 2021 12:59:15 -0500 Subject: [PATCH 14/54] Separate UnisonLocal and UnisonShare Apps --- src/UnisonLocal.elm | 4 +- src/{ => UnisonLocal}/App.elm | 2 +- src/{ => UnisonLocal}/PreApp.elm | 4 +- src/UnisonShare.elm | 4 +- src/UnisonShare/App.elm | 894 +++++++++++++++++++++++++++++++ src/UnisonShare/PreApp.elm | 145 +++++ 6 files changed, 1046 insertions(+), 7 deletions(-) rename src/{ => UnisonLocal}/App.elm (99%) rename src/{ => UnisonLocal}/PreApp.elm (98%) create mode 100644 src/UnisonShare/App.elm create mode 100644 src/UnisonShare/PreApp.elm diff --git a/src/UnisonLocal.elm b/src/UnisonLocal.elm index 95c5b68..23ac4e0 100644 --- a/src/UnisonLocal.elm +++ b/src/UnisonLocal.elm @@ -1,9 +1,9 @@ module UnisonLocal exposing (..) -import App import Browser import Env exposing (Flags) -import PreApp +import UnisonLocal.App as App +import UnisonLocal.PreApp as PreApp main : Program Flags PreApp.Model PreApp.Msg diff --git a/src/App.elm b/src/UnisonLocal/App.elm similarity index 99% rename from src/App.elm rename to src/UnisonLocal/App.elm index cacf0d3..62edd75 100644 --- a/src/App.elm +++ b/src/UnisonLocal/App.elm @@ -1,4 +1,4 @@ -module App exposing (..) +module UnisonLocal.App exposing (..) import Api import Browser diff --git a/src/PreApp.elm b/src/UnisonLocal/PreApp.elm similarity index 98% rename from src/PreApp.elm rename to src/UnisonLocal/PreApp.elm index cca3106..288adec 100644 --- a/src/PreApp.elm +++ b/src/UnisonLocal/PreApp.elm @@ -1,7 +1,6 @@ -module PreApp exposing (..) +module UnisonLocal.PreApp exposing (..) import Api exposing (ApiBasePath(..), ApiRequest) -import App import Browser import Browser.Navigation as Nav import Env exposing (Flags) @@ -10,6 +9,7 @@ import Html import Http import Perspective exposing (Perspective, PerspectiveParams) import Route exposing (Route) +import UnisonLocal.App as App import Url exposing (Url) diff --git a/src/UnisonShare.elm b/src/UnisonShare.elm index 99fc7c6..4e71dc4 100644 --- a/src/UnisonShare.elm +++ b/src/UnisonShare.elm @@ -1,9 +1,9 @@ module UnisonShare exposing (..) -import App import Browser import Env exposing (Flags) -import PreApp +import UnisonShare.App as App +import UnisonShare.PreApp as PreApp main : Program Flags PreApp.Model PreApp.Msg diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm new file mode 100644 index 0000000..2275cbf --- /dev/null +++ b/src/UnisonShare/App.elm @@ -0,0 +1,894 @@ +module UnisonShare.App exposing (..) + +import Api +import Browser +import Browser.Navigation as Nav +import CodebaseTree +import Definition.Reference exposing (Reference) +import Env exposing (Env, OperatingSystem(..)) +import Env.AppContext as AppContext exposing (AppContext(..)) +import Finder +import Finder.SearchOptions as SearchOptions +import FullyQualifiedName as FQN exposing (FQN) +import Html exposing (Html, a, div, h1, h2, h3, nav, p, section, span, strong, text) +import Html.Attributes exposing (class, classList, href, id, rel, target, title) +import Html.Events exposing (onClick) +import Http +import KeyboardShortcut +import KeyboardShortcut.Key as Key exposing (Key(..)) +import KeyboardShortcut.KeyboardEvent as KeyboardEvent exposing (KeyboardEvent) +import Namespace exposing (NamespaceDetails) +import Perspective exposing (Perspective(..)) +import PerspectiveLanding +import RemoteData +import Route exposing (Route) +import UI +import UI.AppHeader as AppHeader +import UI.Banner as Banner +import UI.Button as Button +import UI.Click as Click exposing (Click(..)) +import UI.CopyField as CopyField +import UI.Icon as Icon +import UI.Modal as Modal +import UI.Sidebar as Sidebar +import UI.Tooltip as Tooltip +import UnisonShare.SidebarContent +import Url exposing (Url) +import Workspace +import Workspace.WorkspaceItems as WorkspaceItems + + + +-- MODEL + + +type Modal + = NoModal + | FinderModal Finder.Model + | HelpModal + | ReportBugModal + | PublishModal + | DownloadModal FQN + + +type alias Model = + { navKey : Nav.Key + , route : Route + , codebaseTree : CodebaseTree.Model + , workspace : Workspace.Model + , perspectiveLanding : PerspectiveLanding.Model + , modal : Modal + , keyboardShortcut : KeyboardShortcut.Model + , env : Env + + -- This is called "toggled" and not "hidden" because the behavior of + -- toggling the sidebar on/off is inverse on mobile vs desktop + , sidebarToggled : Bool + } + + +init : Env -> Route -> Nav.Key -> ( Model, Cmd Msg ) +init env route navKey = + let + ( workspace, workspaceCmd ) = + case route of + Route.Definition _ ref -> + Workspace.init env (Just ref) + + _ -> + Workspace.init env Nothing + + ( codebaseTree, codebaseTreeCmd ) = + CodebaseTree.init env + + fetchNamespaceDetailsCmd = + env.perspective + |> fetchNamespaceDetails + |> Maybe.map (Api.perform env.apiBasePath) + |> Maybe.withDefault Cmd.none + + model = + { navKey = navKey + , route = route + , workspace = workspace + , perspectiveLanding = PerspectiveLanding.init + , codebaseTree = codebaseTree + , modal = NoModal + , keyboardShortcut = KeyboardShortcut.init env.operatingSystem + , env = env + , sidebarToggled = False + } + in + ( model + , Cmd.batch + [ Cmd.map CodebaseTreeMsg codebaseTreeCmd + , Cmd.map WorkspaceMsg workspaceCmd + , fetchNamespaceDetailsCmd + ] + ) + + + +-- UPDATE + + +type Msg + = LinkClicked Browser.UrlRequest + | UrlChanged Url + | ChangePerspective Perspective + | FetchPerspectiveNamespaceDetailsFinished FQN (Result Http.Error NamespaceDetails) + | Keydown KeyboardEvent + | OpenDefinition Reference + | ShowModal Modal + | CloseModal + | ToggleSidebar + -- sub msgs + | FinderMsg Finder.Msg + | WorkspaceMsg Workspace.Msg + | PerspectiveLandingMsg PerspectiveLanding.Msg + | CodebaseTreeMsg CodebaseTree.Msg + | KeyboardShortcutMsg KeyboardShortcut.Msg + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg ({ env } as model) = + case msg of + LinkClicked _ -> + ( model, Cmd.none ) + + UrlChanged url -> + let + route = + Route.fromUrl env.basePath url + + model2 = + { model | route = route } + + newEnv params = + { env | perspective = Perspective.nextFromParams env.perspective params } + in + case route of + Route.Definition params ref -> + let + ( workspace, cmd ) = + Workspace.open (newEnv params) model.workspace ref + + model3 = + { model2 | workspace = workspace, env = newEnv params } + + ( model4, fetchPerspectiveCmd ) = + fetchPerspective model3 + in + ( model4, Cmd.batch [ Cmd.map WorkspaceMsg cmd, fetchPerspectiveCmd ] ) + + Route.Perspective params -> + fetchPerspective { model2 | env = newEnv params } + + ChangePerspective perspective -> + navigateToPerspective model perspective + + FetchPerspectiveNamespaceDetailsFinished fqn details -> + let + perspective = + case env.perspective of + Namespace p -> + if FQN.equals p.fqn fqn then + Namespace { p | details = RemoteData.fromResult details } + + else + env.perspective + + _ -> + env.perspective + + nextEnv = + { env | perspective = perspective } + in + ( { model | env = nextEnv }, Cmd.none ) + + Keydown event -> + keydown model event + + OpenDefinition ref -> + navigateToDefinition model ref + + ShowModal modal -> + ( { model | modal = modal }, Cmd.none ) + + CloseModal -> + ( { model | modal = NoModal }, Cmd.none ) + + ToggleSidebar -> + ( { model | sidebarToggled = not model.sidebarToggled }, Cmd.none ) + + -- Sub msgs + WorkspaceMsg wMsg -> + let + ( workspace, wCmd, outMsg ) = + Workspace.update env wMsg model.workspace + + model2 = + { model | workspace = workspace } + + ( model3, cmd ) = + handleWorkspaceOutMsg model2 outMsg + in + ( model3, Cmd.batch [ cmd, Cmd.map WorkspaceMsg wCmd ] ) + + PerspectiveLandingMsg rMsg -> + let + ( perspectiveLanding, outMsg ) = + PerspectiveLanding.update rMsg model.perspectiveLanding + + model2 = + { model | perspectiveLanding = perspectiveLanding } + in + case outMsg of + PerspectiveLanding.OpenDefinition ref -> + navigateToDefinition model2 ref + + PerspectiveLanding.ShowFinderRequest -> + showFinder model2 Nothing + + PerspectiveLanding.None -> + ( model2, Cmd.none ) + + CodebaseTreeMsg cMsg -> + let + ( codebaseTree, cCmd, outMsg ) = + CodebaseTree.update env cMsg model.codebaseTree + + model2 = + { model | codebaseTree = codebaseTree } + + ( model3, cmd ) = + case outMsg of + CodebaseTree.None -> + ( model2, Cmd.none ) + + CodebaseTree.OpenDefinition ref -> + -- reset sidebarToggled to close it on mobile, but keep it open on desktop + let + model4 = + { model2 | sidebarToggled = False } + in + navigateToDefinition model4 ref + + CodebaseTree.ChangePerspectiveToNamespace fqn -> + fqn + |> Perspective.toNamespacePerspective model.env.perspective + |> navigateToPerspective model + in + ( model3, Cmd.batch [ cmd, Cmd.map CodebaseTreeMsg cCmd ] ) + + FinderMsg fMsg -> + case model.modal of + FinderModal fModel -> + let + ( fm, fc, out ) = + Finder.update env fMsg fModel + in + case out of + Finder.Remain -> + ( { model | modal = FinderModal fm }, Cmd.map FinderMsg fc ) + + Finder.Exit -> + ( { model | modal = NoModal }, Cmd.none ) + + Finder.OpenDefinition ref -> + navigateToDefinition { model | modal = NoModal } ref + + _ -> + ( model, Cmd.none ) + + KeyboardShortcutMsg kMsg -> + let + ( keyboardShortcut, cmd ) = + KeyboardShortcut.update kMsg model.keyboardShortcut + in + ( { model | keyboardShortcut = keyboardShortcut }, Cmd.map KeyboardShortcutMsg cmd ) + + + +-- UPDATE HELPERS + + +navigateToDefinition : Model -> Reference -> ( Model, Cmd Msg ) +navigateToDefinition model ref = + ( model, Route.navigateToDefinition model.navKey model.route ref ) + + +navigateToPerspective : Model -> Perspective -> ( Model, Cmd Msg ) +navigateToPerspective model perspective = + let + -- Update all open references to be hash based to ensure that we can + -- refresh the page and fetch them appropriately even if they are + -- outside of the current perspective + workspace = + Workspace.replaceWorkspaceItemReferencesWithHashOnly model.workspace + + -- Re-navigate to the currently open definition by hash + focusedReferenceRoute = + workspace.workspaceItems + |> WorkspaceItems.focusedReference + |> Maybe.map (Route.toDefinition model.route) + |> Maybe.withDefault model.route + + changeRouteCmd = + Route.replacePerspective model.navKey (Perspective.toParams perspective) focusedReferenceRoute + in + ( { model | workspace = workspace }, changeRouteCmd ) + + +fetchPerspective : Model -> ( Model, Cmd Msg ) +fetchPerspective ({ env } as model) = + let + ( codebaseTree, codebaseTreeCmd ) = + CodebaseTree.init env + + fetchNamespaceDetailsCmd = + env.perspective + |> fetchNamespaceDetails + |> Maybe.map (Api.perform env.apiBasePath) + |> Maybe.withDefault Cmd.none + in + if Perspective.needsFetching env.perspective then + ( { model | codebaseTree = codebaseTree } + , Cmd.batch + [ Cmd.map CodebaseTreeMsg codebaseTreeCmd + , fetchNamespaceDetailsCmd + ] + ) + + else + ( model, Cmd.none ) + + +handleWorkspaceOutMsg : Model -> Workspace.OutMsg -> ( Model, Cmd Msg ) +handleWorkspaceOutMsg model out = + case out of + Workspace.None -> + ( model, Cmd.none ) + + Workspace.ShowFinderRequest withinNamespace -> + showFinder model withinNamespace + + Workspace.Focused ref -> + ( model, Route.navigateToDefinition model.navKey model.route ref ) + + Workspace.Emptied -> + ( model, Route.navigateToCurrentPerspective model.navKey model.route ) + + Workspace.ChangePerspectiveToNamespace fqn -> + fqn + |> Perspective.toNamespacePerspective model.env.perspective + |> navigateToPerspective model + + +keydown : Model -> KeyboardEvent -> ( Model, Cmd Msg ) +keydown model keyboardEvent = + let + shortcut = + KeyboardShortcut.fromKeyboardEvent model.keyboardShortcut keyboardEvent + + noOp = + ( model, Cmd.none ) + + toggleSidebar = + ( { model | sidebarToggled = not model.sidebarToggled }, Cmd.none ) + in + case shortcut of + KeyboardShortcut.Chord Ctrl (K _) -> + showFinder model Nothing + + KeyboardShortcut.Chord Meta (K _) -> + if model.env.operatingSystem == Env.MacOS then + showFinder model Nothing + + else + noOp + + KeyboardShortcut.Chord Ctrl (B _) -> + toggleSidebar + + KeyboardShortcut.Chord Meta (B _) -> + toggleSidebar + + KeyboardShortcut.Sequence _ ForwardSlash -> + showFinder model Nothing + + KeyboardShortcut.Chord Shift QuestionMark -> + ( { model | modal = HelpModal }, Cmd.none ) + + KeyboardShortcut.Sequence _ Escape -> + if model.modal == HelpModal then + ( { model | modal = NoModal }, Cmd.none ) + + else + noOp + + _ -> + noOp + + +showFinder : + { m | env : Env, modal : Modal } + -> Maybe FQN + -> ( { m | env : Env, modal : Modal }, Cmd Msg ) +showFinder model withinNamespace = + let + options = + SearchOptions.init model.env.perspective withinNamespace + + ( fm, fcmd ) = + Finder.init model.env options + in + ( { model | modal = FinderModal fm }, Cmd.map FinderMsg fcmd ) + + + +-- EFFECTS + + +fetchNamespaceDetails : Perspective -> Maybe (Api.ApiRequest NamespaceDetails Msg) +fetchNamespaceDetails perspective = + case perspective of + Namespace { fqn } -> + fqn + |> Api.namespace perspective + |> Api.toRequest Namespace.decodeDetails (FetchPerspectiveNamespaceDetailsFinished fqn) + |> Just + + _ -> + Nothing + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.batch + [ KeyboardEvent.subscribe KeyboardEvent.Keydown Keydown + , Sub.map WorkspaceMsg (Workspace.subscriptions model.workspace) + ] + + + +-- VIEW + + +appTitle : Maybe msg -> AppContext -> AppHeader.AppTitle msg +appTitle clickMsg appContext = + let + appTitle_ = + case clickMsg of + Nothing -> + AppHeader.Disabled + + Just msg -> + AppHeader.Clickable msg + + content = + case appContext of + UnisonLocal -> + h1 [] [ text "Unison", span [ class "context unison-local" ] [ text "Local" ] ] + + UnisonShare -> + h1 [] [ text "Unison", span [ class "context unison-share" ] [ text "Share" ] ] + in + appTitle_ content + + +viewAppHeader : Model -> Html Msg +viewAppHeader model = + let + { appContext, perspective } = + model.env + + changePerspectiveMsg = + case perspective of + Codebase codebaseHash -> + ChangePerspective (Codebase codebaseHash) + + Namespace { codebaseHash } -> + ChangePerspective (Codebase codebaseHash) + + appTitle_ = + appTitle (Just changePerspectiveMsg) appContext + + banner = + case appContext of + UnisonLocal -> + Nothing + + UnisonShare -> + Just + (Banner.promotion "article" + "New Article: Spark-like distributed datasets in under 100 lines of Unison" + (ExternalHref "https://www.unison-lang.org/articles/distributed-datasets/") + "Check it out!" + ) + in + AppHeader.view + { menuToggle = Just ToggleSidebar + , appTitle = appTitle_ + , banner = banner + , rightButton = Just (Button.button (ShowModal PublishModal) "Publish on Unison Share" |> Button.share) + } + + +viewSidebarHeader : Env -> Html Msg +viewSidebarHeader env = + case env.perspective of + Codebase _ -> + UI.nothing + + Namespace { fqn } -> + let + -- Imprecise, but close enough, approximation of overflowing, + -- which results in a slight faded left edge A better way would + -- be to measure the DOM like we do for overflowing docs, but + -- thats quite involved... + isOverflowing = + fqn |> FQN.toString |> String.length |> (\l -> l > 20) + + download = + case env.appContext of + UnisonShare -> + Button.iconThenLabel (ShowModal (DownloadModal fqn)) Icon.download "Download latest version" + |> Button.small + |> Button.view + |> List.singleton + |> Sidebar.headerItem [] + + UnisonLocal -> + UI.nothing + in + Sidebar.header + [ Sidebar.headerItem + [ classList [ ( "is-overflowing", isOverflowing ) ] ] + [ UI.namespaceSlug + , h2 [ class "namespace" ] [ FQN.view fqn ] + ] + , download + , UI.divider + ] + + +viewMainSidebarCollapseButton : Model -> Html Msg +viewMainSidebarCollapseButton model = + div + [ class "collapse-sidebar-button" ] + [ Button.icon ToggleSidebar + (if model.sidebarToggled then + Icon.chevronRight + + else + Icon.chevronLeft + ) + |> Button.small + |> Button.view + ] + + +subMenu : AppContext -> List ( String, Click Msg ) +subMenu appContext = + [ ( "Unison website", ExternalHref "https://unisonweb.org" ) + , ( "Docs", ExternalHref "https://unisonweb.org/docs" ) + , ( "Language Reference", ExternalHref "https://unisonweb.org/docs/language-reference" ) + , ( "Community", ExternalHref "https://unisonweb.org/community" ) + , ( "Report a bug", OnClick (ShowModal ReportBugModal) ) + ] + ++ (if AppContext.isUnisonLocal appContext then + [ ( "Unison Share", ExternalHref "https://share.unison-lang.org" ) ] + + else + [] + ) + + +unisonSubmenu : AppContext -> Html Msg +unisonSubmenu appContext = + Tooltip.tooltip + (Icon.unisonMark + |> Icon.withClass "sidebar-unison-submenu" + |> Icon.view + ) + (subMenu appContext |> Tooltip.textMenu) + |> Tooltip.withPosition Tooltip.RightOf + |> Tooltip.withArrow Tooltip.End + |> Tooltip.view + + +viewMainSidebar : Model -> Html Msg +viewMainSidebar model = + let + perspective = + model.env.perspective + + appContext = + model.env.appContext + + changePerspectiveMsg = + Perspective.toNamespacePerspective perspective >> ChangePerspective + + sidebarContent = + if Perspective.isCodebasePerspective perspective && AppContext.isUnisonShare appContext then + UnisonShare.SidebarContent.view changePerspectiveMsg + + else + UI.nothing + in + Sidebar.view + [ viewMainSidebarCollapseButton model + , div [ class "expanded-content" ] + [ viewSidebarHeader model.env + , div [ class "sidebar-scroll-area" ] + [ sidebarContent + , Sidebar.section + "Namespaces and Definitions" + [ Html.map CodebaseTreeMsg (CodebaseTree.view model.codebaseTree) ] + , nav [] + (List.map + (\( l, c ) -> Click.view [] [ text l ] c) + (subMenu appContext) + ++ [ a [ class "show-help", onClick (ShowModal HelpModal) ] + [ text "Keyboard Shortcuts" + , KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) + ] + ] + ) + ] + ] + , div [ class "collapsed-content" ] + [ unisonSubmenu appContext + , Tooltip.tooltip + (a + [ class "show-help-collapsed", onClick (ShowModal HelpModal) ] + [ KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) + ] + ) + (Tooltip.Text "Keyboard Shortcuts") + |> Tooltip.withPosition Tooltip.RightOf + |> Tooltip.withArrow Tooltip.Middle + |> Tooltip.view + ] + ] + + +viewDownloadModal : FQN -> Html Msg +viewDownloadModal fqn = + let + prettyName = + FQN.toString fqn + + unqualified = + FQN.unqualifiedName fqn + + pullCommand = + "pull git@github.com:unisonweb/share.git:." ++ prettyName ++ " ." ++ unqualified + + content = + Modal.Content + (section + [] + [ p [] [ text "Download ", UI.bold prettyName, text " by pulling the namespace from Unison Share into a namespace in your local codebase:" ] + , CopyField.copyField (\_ -> CloseModal) pullCommand |> CopyField.withPrefix ".>" |> CopyField.view + , div [ class "hint" ] [ text "Copy and paste this command into UCM." ] + ] + ) + in + Modal.modal "download-modal" CloseModal content + |> Modal.withHeader ("Download " ++ prettyName) + |> Modal.view + + +viewHelpModal : OperatingSystem -> KeyboardShortcut.Model -> Html Msg +viewHelpModal os keyboardShortcut = + let + viewRow label instructions = + div + [ class "row" ] + [ label + , div [ class "instructions" ] instructions + ] + + viewInstructions label shortcuts = + viewRow label [ KeyboardShortcut.viewShortcuts keyboardShortcut shortcuts ] + + openFinderInstructions = + case os of + MacOS -> + [ KeyboardShortcut.Chord Meta (K Key.Lower), KeyboardShortcut.Chord Ctrl (K Key.Lower), KeyboardShortcut.single ForwardSlash ] + + _ -> + [ KeyboardShortcut.Chord Ctrl (K Key.Lower), KeyboardShortcut.single ForwardSlash ] + + toggleSidebarInstructions = + case os of + MacOS -> + [ KeyboardShortcut.Chord Meta (B Key.Lower), KeyboardShortcut.Chord Ctrl (B Key.Lower) ] + + _ -> + [ KeyboardShortcut.Chord Ctrl (B Key.Lower), KeyboardShortcut.single (S Key.Lower) ] + + content = + Modal.Content + (section + [ class "shortcuts" ] + [ div [ class "shortcut-group" ] + [ h3 [] [ text "General" ] + , viewInstructions (span [] [ text "Keyboard shortcuts", UI.subtle " (this dialog)" ]) [ KeyboardShortcut.single QuestionMark ] + , viewInstructions (text "Open Finder") openFinderInstructions + , viewInstructions (text "Toggle sidebar") toggleSidebarInstructions + , viewInstructions (text "Move focus up") [ KeyboardShortcut.single ArrowUp, KeyboardShortcut.single (K Key.Lower) ] + , viewInstructions (text "Move focus down") [ KeyboardShortcut.single ArrowDown, KeyboardShortcut.single (J Key.Lower) ] + , viewInstructions (text "Close focused definition") [ KeyboardShortcut.single (X Key.Lower) ] + , viewInstructions (text "Expand/Collapse focused definition") [ KeyboardShortcut.single Space ] + ] + , div [ class "shortcut-group" ] + [ h3 [] [ text "Finder" ] + , viewInstructions (text "Clear search query") [ KeyboardShortcut.single Escape ] + , viewInstructions (span [] [ text "Close", UI.subtle " (when search query is empty)" ]) [ KeyboardShortcut.single Escape ] + , viewInstructions (text "Move focus up") [ KeyboardShortcut.single ArrowUp ] + , viewInstructions (text "Move focus down") [ KeyboardShortcut.single ArrowDown ] + , viewInstructions (text "Open focused definition") [ KeyboardShortcut.single Enter ] + , viewRow (text "Open definition") + [ KeyboardShortcut.viewBase + [ KeyboardShortcut.viewKey os Semicolon False + , KeyboardShortcut.viewThen + , KeyboardShortcut.viewKeyBase "1-9" False + ] + ] + ] + ] + ) + in + Modal.modal "help-modal" CloseModal content + |> Modal.withHeader "Keyboard shortcuts" + |> Modal.view + + +githubLinkButton : String -> Html msg +githubLinkButton repo = + Button.linkIconThenLabel ("https://github.com/" ++ repo) Icon.github repo + |> Button.small + |> Button.contained + |> Button.view + + +viewPublishModal : Html Msg +viewPublishModal = + let + content = + Modal.Content + (section + [] + [ p [ class "main" ] + [ text "With your Unison codebase on GitHub, open a Pull Request against " + , githubLinkButton "unisonweb/share" + , text " to list (or unlist) your project on Unison Share." + ] + , a [ class "help", href "https://www.unisonweb.org/docs/codebase-organization/#day-to-day-development-creating-and-merging-pull-requests", rel "noopener", target "_blank" ] [ text "How do I get my code on GitHub?" ] + ] + ) + in + Modal.modal "publish-modal" CloseModal content + |> Modal.withHeader "Publish your project on Unison Share" + |> Modal.view + + +viewReportBugModal : AppContext -> Html Msg +viewReportBugModal appContext = + let + content = + Modal.Content + (div [] + [ section [] + [ p [] [ text "We try our best, but bugs unfortunately creep through :(" ] + , p [] [ text "We greatly appreciate feedback and bug reports—its very helpful for providing the best developer experience when working with Unison." ] + ] + , UI.divider + , section [ class "actions" ] + [ p [] [ text "Visit our GitHub repositories to report bugs and provide feedback" ] + , div [ class "action" ] + [ githubLinkButton "unisonweb/codebase-ui" + , text "for reports on" + , strong [] [ text (AppContext.toString appContext) ] + , span [ class "subtle" ] [ text "(this UI)" ] + ] + , div [ class "action" ] + [ githubLinkButton "unisonweb/unison" + , text "for reports on the" + , strong [] [ text "Unison Language" ] + , span [ class "subtle" ] [ text "(UCM)" ] + ] + ] + ] + ) + in + Modal.modal "report-bug-modal" CloseModal content + |> Modal.withHeader "Report a Bug" + |> Modal.view + + +viewModal : + { m | env : Env, modal : Modal, keyboardShortcut : KeyboardShortcut.Model } + -> Html Msg +viewModal model = + case model.modal of + NoModal -> + UI.nothing + + FinderModal m -> + Html.map FinderMsg (Finder.view m) + + HelpModal -> + viewHelpModal model.env.operatingSystem model.keyboardShortcut + + PublishModal -> + viewPublishModal + + ReportBugModal -> + viewReportBugModal model.env.appContext + + DownloadModal fqn -> + viewDownloadModal fqn + + +viewAppLoading : AppContext -> Html msg +viewAppLoading appContext = + div [ id "app" ] + [ AppHeader.view (AppHeader.appHeader (appTitle Nothing appContext)) + , Sidebar.view [] + , div [ id "main-content" ] [] + ] + + +viewAppError : AppContext -> Http.Error -> Html msg +viewAppError appContext error = + let + context = + AppContext.toString appContext + in + div [ id "app" ] + [ AppHeader.view (AppHeader.appHeader (appTitle Nothing appContext)) + , Sidebar.view [] + , div [ id "main-content", class "app-error" ] + [ Icon.view Icon.warn + , p [ title (Api.errorToString error) ] + [ text (context ++ " could not be started.") ] + ] + ] + + +view : Model -> Browser.Document Msg +view model = + let + title_ = + AppContext.toString model.env.appContext + + page = + case model.route of + Route.Perspective _ -> + Html.map PerspectiveLandingMsg + (PerspectiveLanding.view + model.env + model.perspectiveLanding + ) + + Route.Definition _ _ -> + Html.map WorkspaceMsg (Workspace.view model.workspace) + in + { title = title_ + , body = + [ div [ id "app", classList [ ( "sidebar-toggled", model.sidebarToggled ) ] ] + [ viewAppHeader model + , viewMainSidebar model + , div [ id "main-content" ] [ page ] + , viewModal model + ] + ] + } diff --git a/src/UnisonShare/PreApp.elm b/src/UnisonShare/PreApp.elm new file mode 100644 index 0000000..4a1140f --- /dev/null +++ b/src/UnisonShare/PreApp.elm @@ -0,0 +1,145 @@ +module UnisonShare.PreApp exposing (..) + +import Api exposing (ApiBasePath(..), ApiRequest) +import Browser +import Browser.Navigation as Nav +import Env exposing (Flags) +import Env.AppContext as AppContext +import Html +import Http +import Perspective exposing (Perspective, PerspectiveParams) +import Route exposing (Route) +import UnisonShare.App as App +import Url exposing (Url) + + +type Model + = Initializing PreEnv + | InitializationError PreEnv Http.Error + | Initialized App.Model + + +type alias PreEnv = + { flags : Flags + , route : Route + , navKey : Nav.Key + , perspectiveParams : PerspectiveParams + } + + +init : Flags -> Url -> Nav.Key -> ( Model, Cmd Msg ) +init flags url navKey = + let + route = + Route.fromUrl flags.basePath url + + preEnv = + { flags = flags + , route = route + , navKey = navKey + , perspectiveParams = Route.perspectiveParams route + } + + perspectiveToAppInit perspective = + let + env = + Env.init preEnv.flags perspective + + ( app, cmd ) = + App.init env preEnv.route preEnv.navKey + in + ( Initialized app, Cmd.map AppMsg cmd ) + + fetchPerspective_ = + ( Initializing preEnv, Api.perform (ApiBasePath flags.apiBasePath) (fetchPerspective preEnv) ) + in + -- If we have a codebase hash we can construct a full perspective, + -- otherwise we have to fetch the hash before being able to start up the + -- app + preEnv.perspectiveParams + |> Perspective.fromParams + |> Maybe.map perspectiveToAppInit + |> Maybe.withDefault fetchPerspective_ + + +fetchPerspective : PreEnv -> ApiRequest Perspective Msg +fetchPerspective preEnv = + Api.codebaseHash |> Api.toRequest (Perspective.decode preEnv.perspectiveParams) (FetchPerspectiveFinished preEnv) + + +type Msg + = FetchPerspectiveFinished PreEnv (Result Http.Error Perspective) + | AppMsg App.Msg + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + FetchPerspectiveFinished preEnv result -> + case result of + Ok perspective -> + let + env = + Env.init preEnv.flags perspective + + newRoute = + perspective + |> Perspective.toParams + |> Route.updatePerspectiveParams preEnv.route + + ( app, cmd ) = + App.init env newRoute preEnv.navKey + in + ( Initialized app, Cmd.map AppMsg cmd ) + + Err err -> + ( InitializationError preEnv err, Cmd.none ) + + AppMsg appMsg -> + case model of + Initialized a -> + let + ( app, cmd ) = + App.update appMsg a + in + ( Initialized app, Cmd.map AppMsg cmd ) + + _ -> + ( model, Cmd.none ) + + +subscriptions : Model -> Sub Msg +subscriptions model = + case model of + Initialized app -> + Sub.map AppMsg (App.subscriptions app) + + _ -> + Sub.none + + +view : Model -> Browser.Document Msg +view model = + let + appContext flags = + AppContext.fromString flags.appContext + in + case model of + Initializing preEnv -> + { title = "Loading.." + , body = [ App.viewAppLoading (appContext preEnv.flags) ] + } + + InitializationError preEnv error -> + { title = "Application Error" + , body = [ App.viewAppError (appContext preEnv.flags) error ] + } + + Initialized appModel -> + let + app = + App.view appModel + in + { title = app.title + , body = List.map (Html.map AppMsg) app.body + } From d04433b343cabc1d31babbfa49b9dd9704931934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Fri, 3 Dec 2021 13:34:28 -0500 Subject: [PATCH 15/54] Remove out of context App bits --- src/UnisonLocal/App.elm | 156 +++++++------------------------------ src/UnisonLocal/PreApp.elm | 13 +--- src/UnisonShare/App.elm | 110 +++++++++----------------- src/UnisonShare/PreApp.elm | 13 +--- 4 files changed, 71 insertions(+), 221 deletions(-) diff --git a/src/UnisonLocal/App.elm b/src/UnisonLocal/App.elm index 62edd75..fe17ebb 100644 --- a/src/UnisonLocal/App.elm +++ b/src/UnisonLocal/App.elm @@ -6,7 +6,6 @@ import Browser.Navigation as Nav import CodebaseTree import Definition.Reference exposing (Reference) import Env exposing (Env, OperatingSystem(..)) -import Env.AppContext as AppContext exposing (AppContext(..)) import Finder import Finder.SearchOptions as SearchOptions import FullyQualifiedName as FQN exposing (FQN) @@ -24,15 +23,12 @@ import RemoteData import Route exposing (Route) import UI import UI.AppHeader as AppHeader -import UI.Banner as Banner import UI.Button as Button import UI.Click as Click exposing (Click(..)) -import UI.CopyField as CopyField import UI.Icon as Icon import UI.Modal as Modal import UI.Sidebar as Sidebar import UI.Tooltip as Tooltip -import UnisonShare.SidebarContent import Url exposing (Url) import Workspace import Workspace.WorkspaceItems as WorkspaceItems @@ -48,7 +44,6 @@ type Modal | HelpModal | ReportBugModal | PublishModal - | DownloadModal FQN type alias Model = @@ -459,8 +454,8 @@ subscriptions model = -- VIEW -appTitle : Maybe msg -> AppContext -> AppHeader.AppTitle msg -appTitle clickMsg appContext = +appTitle : Maybe msg -> AppHeader.AppTitle msg +appTitle clickMsg = let appTitle_ = case clickMsg of @@ -469,26 +464,15 @@ appTitle clickMsg appContext = Just msg -> AppHeader.Clickable msg - - content = - case appContext of - UnisonLocal -> - h1 [] [ text "Unison", span [ class "context unison-local" ] [ text "Local" ] ] - - UnisonShare -> - h1 [] [ text "Unison", span [ class "context unison-share" ] [ text "Share" ] ] in - appTitle_ content + appTitle_ (h1 [] [ text "Unison", span [ class "context unison-local" ] [ text "Local" ] ]) viewAppHeader : Model -> Html Msg viewAppHeader model = let - { appContext, perspective } = - model.env - changePerspectiveMsg = - case perspective of + case model.env.perspective of Codebase codebaseHash -> ChangePerspective (Codebase codebaseHash) @@ -496,25 +480,12 @@ viewAppHeader model = ChangePerspective (Codebase codebaseHash) appTitle_ = - appTitle (Just changePerspectiveMsg) appContext - - banner = - case appContext of - UnisonLocal -> - Nothing - - UnisonShare -> - Just - (Banner.promotion "article" - "New Article: Spark-like distributed datasets in under 100 lines of Unison" - (ExternalHref "https://www.unison-lang.org/articles/distributed-datasets/") - "Check it out!" - ) + appTitle (Just changePerspectiveMsg) in AppHeader.view { menuToggle = Just ToggleSidebar , appTitle = appTitle_ - , banner = banner + , banner = Nothing , rightButton = Just (Button.button (ShowModal PublishModal) "Publish on Unison Share" |> Button.share) } @@ -533,18 +504,6 @@ viewSidebarHeader env = -- thats quite involved... isOverflowing = fqn |> FQN.toString |> String.length |> (\l -> l > 20) - - download = - case env.appContext of - UnisonShare -> - Button.iconThenLabel (ShowModal (DownloadModal fqn)) Icon.download "Download latest version" - |> Button.small - |> Button.view - |> List.singleton - |> Sidebar.headerItem [] - - UnisonLocal -> - UI.nothing in Sidebar.header [ Sidebar.headerItem @@ -552,7 +511,6 @@ viewSidebarHeader env = [ UI.namespaceSlug , h2 [ class "namespace" ] [ FQN.view fqn ] ] - , download , UI.divider ] @@ -573,30 +531,25 @@ viewMainSidebarCollapseButton model = ] -subMenu : AppContext -> List ( String, Click Msg ) -subMenu appContext = +subMenu : List ( String, Click Msg ) +subMenu = [ ( "Unison website", ExternalHref "https://unisonweb.org" ) , ( "Docs", ExternalHref "https://unisonweb.org/docs" ) , ( "Language Reference", ExternalHref "https://unisonweb.org/docs/language-reference" ) , ( "Community", ExternalHref "https://unisonweb.org/community" ) , ( "Report a bug", OnClick (ShowModal ReportBugModal) ) + , ( "Unison Share", ExternalHref "https://share.unison-lang.org" ) ] - ++ (if AppContext.isUnisonLocal appContext then - [ ( "Unison Share", ExternalHref "https://share.unison-lang.org" ) ] - - else - [] - ) -unisonSubmenu : AppContext -> Html Msg -unisonSubmenu appContext = +unisonSubmenu : Html Msg +unisonSubmenu = Tooltip.tooltip (Icon.unisonMark |> Icon.withClass "sidebar-unison-submenu" |> Icon.view ) - (subMenu appContext |> Tooltip.textMenu) + (Tooltip.textMenu subMenu) |> Tooltip.withPosition Tooltip.RightOf |> Tooltip.withArrow Tooltip.End |> Tooltip.view @@ -604,36 +557,18 @@ unisonSubmenu appContext = viewMainSidebar : Model -> Html Msg viewMainSidebar model = - let - perspective = - model.env.perspective - - appContext = - model.env.appContext - - changePerspectiveMsg = - Perspective.toNamespacePerspective perspective >> ChangePerspective - - sidebarContent = - if Perspective.isCodebasePerspective perspective && AppContext.isUnisonShare appContext then - UnisonShare.SidebarContent.view changePerspectiveMsg - - else - UI.nothing - in Sidebar.view [ viewMainSidebarCollapseButton model , div [ class "expanded-content" ] [ viewSidebarHeader model.env , div [ class "sidebar-scroll-area" ] - [ sidebarContent - , Sidebar.section + [ Sidebar.section "Namespaces and Definitions" [ Html.map CodebaseTreeMsg (CodebaseTree.view model.codebaseTree) ] , nav [] (List.map (\( l, c ) -> Click.view [] [ text l ] c) - (subMenu appContext) + subMenu ++ [ a [ class "show-help", onClick (ShowModal HelpModal) ] [ text "Keyboard Shortcuts" , KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) @@ -643,7 +578,7 @@ viewMainSidebar model = ] ] , div [ class "collapsed-content" ] - [ unisonSubmenu appContext + [ unisonSubmenu , Tooltip.tooltip (a [ class "show-help-collapsed", onClick (ShowModal HelpModal) ] @@ -658,33 +593,6 @@ viewMainSidebar model = ] -viewDownloadModal : FQN -> Html Msg -viewDownloadModal fqn = - let - prettyName = - FQN.toString fqn - - unqualified = - FQN.unqualifiedName fqn - - pullCommand = - "pull git@github.com:unisonweb/share.git:." ++ prettyName ++ " ." ++ unqualified - - content = - Modal.Content - (section - [] - [ p [] [ text "Download ", UI.bold prettyName, text " by pulling the namespace from Unison Share into a namespace in your local codebase:" ] - , CopyField.copyField (\_ -> CloseModal) pullCommand |> CopyField.withPrefix ".>" |> CopyField.view - , div [ class "hint" ] [ text "Copy and paste this command into UCM." ] - ] - ) - in - Modal.modal "download-modal" CloseModal content - |> Modal.withHeader ("Download " ++ prettyName) - |> Modal.view - - viewHelpModal : OperatingSystem -> KeyboardShortcut.Model -> Html Msg viewHelpModal os keyboardShortcut = let @@ -780,8 +688,8 @@ viewPublishModal = |> Modal.view -viewReportBugModal : AppContext -> Html Msg -viewReportBugModal appContext = +viewReportBugModal : Html Msg +viewReportBugModal = let content = Modal.Content @@ -796,7 +704,7 @@ viewReportBugModal appContext = , div [ class "action" ] [ githubLinkButton "unisonweb/codebase-ui" , text "for reports on" - , strong [] [ text (AppContext.toString appContext) ] + , strong [] [ text "Unison Local" ] , span [ class "subtle" ] [ text "(this UI)" ] ] , div [ class "action" ] @@ -832,34 +740,27 @@ viewModal model = viewPublishModal ReportBugModal -> - viewReportBugModal model.env.appContext + viewReportBugModal - DownloadModal fqn -> - viewDownloadModal fqn - -viewAppLoading : AppContext -> Html msg -viewAppLoading appContext = +viewAppLoading : Html msg +viewAppLoading = div [ id "app" ] - [ AppHeader.view (AppHeader.appHeader (appTitle Nothing appContext)) + [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) , Sidebar.view [] , div [ id "main-content" ] [] ] -viewAppError : AppContext -> Http.Error -> Html msg -viewAppError appContext error = - let - context = - AppContext.toString appContext - in +viewAppError : Http.Error -> Html msg +viewAppError error = div [ id "app" ] - [ AppHeader.view (AppHeader.appHeader (appTitle Nothing appContext)) + [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) , Sidebar.view [] , div [ id "main-content", class "app-error" ] [ Icon.view Icon.warn , p [ title (Api.errorToString error) ] - [ text (context ++ " could not be started.") ] + [ text "Unison Local could not be started." ] ] ] @@ -867,9 +768,6 @@ viewAppError appContext error = view : Model -> Browser.Document Msg view model = let - title_ = - AppContext.toString model.env.appContext - page = case model.route of Route.Perspective _ -> @@ -882,7 +780,7 @@ view model = Route.Definition _ _ -> Html.map WorkspaceMsg (Workspace.view model.workspace) in - { title = title_ + { title = "Unison Local" , body = [ div [ id "app", classList [ ( "sidebar-toggled", model.sidebarToggled ) ] ] [ viewAppHeader model diff --git a/src/UnisonLocal/PreApp.elm b/src/UnisonLocal/PreApp.elm index 288adec..24cf378 100644 --- a/src/UnisonLocal/PreApp.elm +++ b/src/UnisonLocal/PreApp.elm @@ -4,7 +4,6 @@ import Api exposing (ApiBasePath(..), ApiRequest) import Browser import Browser.Navigation as Nav import Env exposing (Flags) -import Env.AppContext as AppContext import Html import Http import Perspective exposing (Perspective, PerspectiveParams) @@ -120,19 +119,15 @@ subscriptions model = view : Model -> Browser.Document Msg view model = - let - appContext flags = - AppContext.fromString flags.appContext - in case model of - Initializing preEnv -> + Initializing _ -> { title = "Loading.." - , body = [ App.viewAppLoading (appContext preEnv.flags) ] + , body = [ App.viewAppLoading ] } - InitializationError preEnv error -> + InitializationError _ error -> { title = "Application Error" - , body = [ App.viewAppError (appContext preEnv.flags) error ] + , body = [ App.viewAppError error ] } Initialized appModel -> diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index 2275cbf..3e3c18c 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -6,7 +6,6 @@ import Browser.Navigation as Nav import CodebaseTree import Definition.Reference exposing (Reference) import Env exposing (Env, OperatingSystem(..)) -import Env.AppContext as AppContext exposing (AppContext(..)) import Finder import Finder.SearchOptions as SearchOptions import FullyQualifiedName as FQN exposing (FQN) @@ -459,8 +458,8 @@ subscriptions model = -- VIEW -appTitle : Maybe msg -> AppContext -> AppHeader.AppTitle msg -appTitle clickMsg appContext = +appTitle : Maybe msg -> AppHeader.AppTitle msg +appTitle clickMsg = let appTitle_ = case clickMsg of @@ -469,26 +468,15 @@ appTitle clickMsg appContext = Just msg -> AppHeader.Clickable msg - - content = - case appContext of - UnisonLocal -> - h1 [] [ text "Unison", span [ class "context unison-local" ] [ text "Local" ] ] - - UnisonShare -> - h1 [] [ text "Unison", span [ class "context unison-share" ] [ text "Share" ] ] in - appTitle_ content + appTitle_ (h1 [] [ text "Unison", span [ class "context unison-share" ] [ text "Share" ] ]) viewAppHeader : Model -> Html Msg viewAppHeader model = let - { appContext, perspective } = - model.env - changePerspectiveMsg = - case perspective of + case model.env.perspective of Codebase codebaseHash -> ChangePerspective (Codebase codebaseHash) @@ -496,20 +484,15 @@ viewAppHeader model = ChangePerspective (Codebase codebaseHash) appTitle_ = - appTitle (Just changePerspectiveMsg) appContext + appTitle (Just changePerspectiveMsg) banner = - case appContext of - UnisonLocal -> - Nothing - - UnisonShare -> - Just - (Banner.promotion "article" - "New Article: Spark-like distributed datasets in under 100 lines of Unison" - (ExternalHref "https://www.unison-lang.org/articles/distributed-datasets/") - "Check it out!" - ) + Just + (Banner.promotion "article" + "New Article: Spark-like distributed datasets in under 100 lines of Unison" + (ExternalHref "https://www.unison-lang.org/articles/distributed-datasets/") + "Check it out!" + ) in AppHeader.view { menuToggle = Just ToggleSidebar @@ -535,16 +518,11 @@ viewSidebarHeader env = fqn |> FQN.toString |> String.length |> (\l -> l > 20) download = - case env.appContext of - UnisonShare -> - Button.iconThenLabel (ShowModal (DownloadModal fqn)) Icon.download "Download latest version" - |> Button.small - |> Button.view - |> List.singleton - |> Sidebar.headerItem [] - - UnisonLocal -> - UI.nothing + Button.iconThenLabel (ShowModal (DownloadModal fqn)) Icon.download "Download latest version" + |> Button.small + |> Button.view + |> List.singleton + |> Sidebar.headerItem [] in Sidebar.header [ Sidebar.headerItem @@ -573,30 +551,24 @@ viewMainSidebarCollapseButton model = ] -subMenu : AppContext -> List ( String, Click Msg ) -subMenu appContext = +subMenu : List ( String, Click Msg ) +subMenu = [ ( "Unison website", ExternalHref "https://unisonweb.org" ) , ( "Docs", ExternalHref "https://unisonweb.org/docs" ) , ( "Language Reference", ExternalHref "https://unisonweb.org/docs/language-reference" ) , ( "Community", ExternalHref "https://unisonweb.org/community" ) , ( "Report a bug", OnClick (ShowModal ReportBugModal) ) ] - ++ (if AppContext.isUnisonLocal appContext then - [ ( "Unison Share", ExternalHref "https://share.unison-lang.org" ) ] - - else - [] - ) -unisonSubmenu : AppContext -> Html Msg -unisonSubmenu appContext = +unisonSubmenu : Html Msg +unisonSubmenu = Tooltip.tooltip (Icon.unisonMark |> Icon.withClass "sidebar-unison-submenu" |> Icon.view ) - (subMenu appContext |> Tooltip.textMenu) + (Tooltip.textMenu subMenu) |> Tooltip.withPosition Tooltip.RightOf |> Tooltip.withArrow Tooltip.End |> Tooltip.view @@ -608,14 +580,11 @@ viewMainSidebar model = perspective = model.env.perspective - appContext = - model.env.appContext - changePerspectiveMsg = Perspective.toNamespacePerspective perspective >> ChangePerspective sidebarContent = - if Perspective.isCodebasePerspective perspective && AppContext.isUnisonShare appContext then + if Perspective.isCodebasePerspective perspective then UnisonShare.SidebarContent.view changePerspectiveMsg else @@ -633,7 +602,7 @@ viewMainSidebar model = , nav [] (List.map (\( l, c ) -> Click.view [] [ text l ] c) - (subMenu appContext) + subMenu ++ [ a [ class "show-help", onClick (ShowModal HelpModal) ] [ text "Keyboard Shortcuts" , KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) @@ -643,7 +612,7 @@ viewMainSidebar model = ] ] , div [ class "collapsed-content" ] - [ unisonSubmenu appContext + [ unisonSubmenu , Tooltip.tooltip (a [ class "show-help-collapsed", onClick (ShowModal HelpModal) ] @@ -780,8 +749,8 @@ viewPublishModal = |> Modal.view -viewReportBugModal : AppContext -> Html Msg -viewReportBugModal appContext = +viewReportBugModal : Html Msg +viewReportBugModal = let content = Modal.Content @@ -796,7 +765,7 @@ viewReportBugModal appContext = , div [ class "action" ] [ githubLinkButton "unisonweb/codebase-ui" , text "for reports on" - , strong [] [ text (AppContext.toString appContext) ] + , strong [] [ text "Unison Share" ] , span [ class "subtle" ] [ text "(this UI)" ] ] , div [ class "action" ] @@ -832,34 +801,30 @@ viewModal model = viewPublishModal ReportBugModal -> - viewReportBugModal model.env.appContext + viewReportBugModal DownloadModal fqn -> viewDownloadModal fqn -viewAppLoading : AppContext -> Html msg -viewAppLoading appContext = +viewAppLoading : Html msg +viewAppLoading = div [ id "app" ] - [ AppHeader.view (AppHeader.appHeader (appTitle Nothing appContext)) + [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) , Sidebar.view [] , div [ id "main-content" ] [] ] -viewAppError : AppContext -> Http.Error -> Html msg -viewAppError appContext error = - let - context = - AppContext.toString appContext - in +viewAppError : Http.Error -> Html msg +viewAppError error = div [ id "app" ] - [ AppHeader.view (AppHeader.appHeader (appTitle Nothing appContext)) + [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) , Sidebar.view [] , div [ id "main-content", class "app-error" ] [ Icon.view Icon.warn , p [ title (Api.errorToString error) ] - [ text (context ++ " could not be started.") ] + [ text "Unison Share could not be started." ] ] ] @@ -867,9 +832,6 @@ viewAppError appContext error = view : Model -> Browser.Document Msg view model = let - title_ = - AppContext.toString model.env.appContext - page = case model.route of Route.Perspective _ -> @@ -882,7 +844,7 @@ view model = Route.Definition _ _ -> Html.map WorkspaceMsg (Workspace.view model.workspace) in - { title = title_ + { title = "Unison Share" , body = [ div [ id "app", classList [ ( "sidebar-toggled", model.sidebarToggled ) ] ] [ viewAppHeader model diff --git a/src/UnisonShare/PreApp.elm b/src/UnisonShare/PreApp.elm index 4a1140f..f7c36ff 100644 --- a/src/UnisonShare/PreApp.elm +++ b/src/UnisonShare/PreApp.elm @@ -4,7 +4,6 @@ import Api exposing (ApiBasePath(..), ApiRequest) import Browser import Browser.Navigation as Nav import Env exposing (Flags) -import Env.AppContext as AppContext import Html import Http import Perspective exposing (Perspective, PerspectiveParams) @@ -120,19 +119,15 @@ subscriptions model = view : Model -> Browser.Document Msg view model = - let - appContext flags = - AppContext.fromString flags.appContext - in case model of - Initializing preEnv -> + Initializing _ -> { title = "Loading.." - , body = [ App.viewAppLoading (appContext preEnv.flags) ] + , body = [ App.viewAppLoading ] } - InitializationError preEnv error -> + InitializationError _ error -> { title = "Application Error" - , body = [ App.viewAppError (appContext preEnv.flags) error ] + , body = [ App.viewAppError error ] } Initialized appModel -> From 844051654b72af4563dc983c9581ae0c2e6c03e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Fri, 3 Dec 2021 16:38:53 -0500 Subject: [PATCH 16/54] Perspective change: Fix CodebaseTree re-render --- src/UnisonLocal/App.elm | 11 +++++++---- src/UnisonShare/App.elm | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/UnisonLocal/App.elm b/src/UnisonLocal/App.elm index fe17ebb..7b2a1be 100644 --- a/src/UnisonLocal/App.elm +++ b/src/UnisonLocal/App.elm @@ -152,12 +152,12 @@ update msg ({ env } as model) = { model2 | workspace = workspace, env = newEnv params } ( model4, fetchPerspectiveCmd ) = - fetchPerspective model3 + fetchPerspectiveAndCodebaseTree env.perspective model3 in ( model4, Cmd.batch [ Cmd.map WorkspaceMsg cmd, fetchPerspectiveCmd ] ) Route.Perspective params -> - fetchPerspective { model2 | env = newEnv params } + fetchPerspectiveAndCodebaseTree env.perspective { model2 | env = newEnv params } ChangePerspective perspective -> navigateToPerspective model perspective @@ -315,8 +315,8 @@ navigateToPerspective model perspective = ( { model | workspace = workspace }, changeRouteCmd ) -fetchPerspective : Model -> ( Model, Cmd Msg ) -fetchPerspective ({ env } as model) = +fetchPerspectiveAndCodebaseTree : Perspective -> Model -> ( Model, Cmd Msg ) +fetchPerspectiveAndCodebaseTree oldPerspective ({ env } as model) = let ( codebaseTree, codebaseTreeCmd ) = CodebaseTree.init env @@ -335,6 +335,9 @@ fetchPerspective ({ env } as model) = ] ) + else if not (Perspective.equals oldPerspective env.perspective) then + ( model, Cmd.map CodebaseTreeMsg codebaseTreeCmd ) + else ( model, Cmd.none ) diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index 3e3c18c..faf7b05 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -156,12 +156,12 @@ update msg ({ env } as model) = { model2 | workspace = workspace, env = newEnv params } ( model4, fetchPerspectiveCmd ) = - fetchPerspective model3 + fetchPerspectiveAndCodebaseTree env.perspective model3 in ( model4, Cmd.batch [ Cmd.map WorkspaceMsg cmd, fetchPerspectiveCmd ] ) Route.Perspective params -> - fetchPerspective { model2 | env = newEnv params } + fetchPerspectiveAndCodebaseTree env.perspective { model2 | env = newEnv params } ChangePerspective perspective -> navigateToPerspective model perspective @@ -319,8 +319,8 @@ navigateToPerspective model perspective = ( { model | workspace = workspace }, changeRouteCmd ) -fetchPerspective : Model -> ( Model, Cmd Msg ) -fetchPerspective ({ env } as model) = +fetchPerspectiveAndCodebaseTree : Perspective -> Model -> ( Model, Cmd Msg ) +fetchPerspectiveAndCodebaseTree oldPerspective ({ env } as model) = let ( codebaseTree, codebaseTreeCmd ) = CodebaseTree.init env @@ -339,6 +339,9 @@ fetchPerspective ({ env } as model) = ] ) + else if not (Perspective.equals oldPerspective env.perspective) then + ( model, Cmd.map CodebaseTreeMsg codebaseTreeCmd ) + else ( model, Cmd.none ) From 22866ce4fff556a19a6defd8f4b3c11605c3d9cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Tue, 7 Dec 2021 11:35:14 -0500 Subject: [PATCH 17/54] Move modals out of UnisonShare.App Refactor modals out of UnisonShare.App into UnisonShare.AppModal. This leaves UnisonLocal alone, though that could also benefit from a similar refactor. Also change the webpack config and npm start scripts to allow for starting the UnisonLocal and UnisonShare apps separately. --- README.md | 2 +- package.json | 2 + src/UI/Button.elm | 12 + src/UnisonLocal/App.elm | 4 +- src/UnisonShare/App.elm | 292 +++--------------- src/UnisonShare/AppModal.elm | 293 +++++++++++++++++++ src/css/app.css | 10 +- webpack.dev.js => webpack.unisonLocal.dev.js | 0 webpack.unisonShare.dev.js | 71 +++++ 9 files changed, 433 insertions(+), 253 deletions(-) create mode 100644 src/UnisonShare/AppModal.elm rename webpack.dev.js => webpack.unisonLocal.dev.js (100%) create mode 100644 webpack.unisonShare.dev.js diff --git a/README.md b/README.md index 9925a69..3a2c8bf 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Running Development Server 2. Make sure the latest dependencies are installed with by running `npm install` -3. Start the development server, run: `API_URL="" npm start` +3. Start the Unison Share dev server with: `API_URL="" npm run start:unisonShare` or the Unison Local dev server with: `API_URL="" npm run start:unisonLocal` 4. Visit `http://localhost:1234` in a browser. diff --git a/package.json b/package.json index d7e6506..3536762 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ "build": "webpack --mode production --config webpack.prod.js", "clean": "rm -rf dist", "start": "webpack serve --mode development --port 1234 --config webpack.dev.js", + "start:unisonLocal": "webpack serve --mode development --port 1234 --config webpack.unisonLocal.dev.js", + "start:unisonShare": "webpack serve --mode development --port 1234 --config webpack.unisonShare.dev.js", "test": "elm-test", "review": "elm-review" }, diff --git a/src/UI/Button.elm b/src/UI/Button.elm index 8882049..b29678a 100644 --- a/src/UI/Button.elm +++ b/src/UI/Button.elm @@ -6,6 +6,7 @@ module UI.Button exposing , contained , danger , default + , github , icon , iconThenLabel , large @@ -310,6 +311,17 @@ withSize size button_ = +-- COMMON INSTANCES + + +github : String -> Button msg +github repo = + linkIconThenLabel ("https://github.com/" ++ repo) I.github repo + |> small + |> contained + + + -- INTERNAL diff --git a/src/UnisonLocal/App.elm b/src/UnisonLocal/App.elm index 7b2a1be..ac2e655 100644 --- a/src/UnisonLocal/App.elm +++ b/src/UnisonLocal/App.elm @@ -572,7 +572,7 @@ viewMainSidebar model = (List.map (\( l, c ) -> Click.view [] [ text l ] c) subMenu - ++ [ a [ class "show-help", onClick (ShowModal HelpModal) ] + ++ [ a [ class "show-keyboard-shortcuts", onClick (ShowModal HelpModal) ] [ text "Keyboard Shortcuts" , KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) ] @@ -584,7 +584,7 @@ viewMainSidebar model = [ unisonSubmenu , Tooltip.tooltip (a - [ class "show-help-collapsed", onClick (ShowModal HelpModal) ] + [ class "show-keyboard-shortcuts-collapsed", onClick (ShowModal HelpModal) ] [ KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) ] ) diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index faf7b05..ac92443 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -5,16 +5,14 @@ import Browser import Browser.Navigation as Nav import CodebaseTree import Definition.Reference exposing (Reference) -import Env exposing (Env, OperatingSystem(..)) -import Finder -import Finder.SearchOptions as SearchOptions +import Env exposing (Env) import FullyQualifiedName as FQN exposing (FQN) -import Html exposing (Html, a, div, h1, h2, h3, nav, p, section, span, strong, text) -import Html.Attributes exposing (class, classList, href, id, rel, target, title) +import Html exposing (Html, a, div, h1, h2, nav, p, span, text) +import Html.Attributes exposing (class, classList, id, title) import Html.Events exposing (onClick) import Http import KeyboardShortcut -import KeyboardShortcut.Key as Key exposing (Key(..)) +import KeyboardShortcut.Key exposing (Key(..)) import KeyboardShortcut.KeyboardEvent as KeyboardEvent exposing (KeyboardEvent) import Namespace exposing (NamespaceDetails) import Perspective exposing (Perspective(..)) @@ -26,11 +24,10 @@ import UI.AppHeader as AppHeader import UI.Banner as Banner import UI.Button as Button import UI.Click as Click exposing (Click(..)) -import UI.CopyField as CopyField import UI.Icon as Icon -import UI.Modal as Modal import UI.Sidebar as Sidebar import UI.Tooltip as Tooltip +import UnisonShare.AppModal as AppModal import UnisonShare.SidebarContent import Url exposing (Url) import Workspace @@ -41,22 +38,13 @@ import Workspace.WorkspaceItems as WorkspaceItems -- MODEL -type Modal - = NoModal - | FinderModal Finder.Model - | HelpModal - | ReportBugModal - | PublishModal - | DownloadModal FQN - - type alias Model = { navKey : Nav.Key , route : Route , codebaseTree : CodebaseTree.Model , workspace : Workspace.Model , perspectiveLanding : PerspectiveLanding.Model - , modal : Modal + , appModal : AppModal.Model , keyboardShortcut : KeyboardShortcut.Model , env : Env @@ -92,7 +80,7 @@ init env route navKey = , workspace = workspace , perspectiveLanding = PerspectiveLanding.init , codebaseTree = codebaseTree - , modal = NoModal + , appModal = AppModal.init , keyboardShortcut = KeyboardShortcut.init env.operatingSystem , env = env , sidebarToggled = False @@ -118,11 +106,10 @@ type Msg | FetchPerspectiveNamespaceDetailsFinished FQN (Result Http.Error NamespaceDetails) | Keydown KeyboardEvent | OpenDefinition Reference - | ShowModal Modal - | CloseModal + | ShowModal AppModal.AppModal | ToggleSidebar -- sub msgs - | FinderMsg Finder.Msg + | AppModalMsg AppModal.Msg | WorkspaceMsg Workspace.Msg | PerspectiveLandingMsg PerspectiveLanding.Msg | CodebaseTreeMsg CodebaseTree.Msg @@ -192,15 +179,31 @@ update msg ({ env } as model) = navigateToDefinition model ref ShowModal modal -> - ( { model | modal = modal }, Cmd.none ) - - CloseModal -> - ( { model | modal = NoModal }, Cmd.none ) + let + ( appModal, cmd ) = + AppModal.show modal + in + ( { model | appModal = appModal }, Cmd.map AppModalMsg cmd ) ToggleSidebar -> ( { model | sidebarToggled = not model.sidebarToggled }, Cmd.none ) -- Sub msgs + AppModalMsg amMsg -> + let + ( am, amCmd, out ) = + AppModal.update env amMsg model.appModal + + ( newModel, cmd ) = + case out of + AppModal.OpenDefinition ref -> + navigateToDefinition { model | appModal = am } ref + + _ -> + ( { model | appModal = am }, Cmd.none ) + in + ( newModel, Cmd.batch [ Cmd.map AppModalMsg amCmd, cmd ] ) + WorkspaceMsg wMsg -> let ( workspace, wCmd, outMsg ) = @@ -260,26 +263,6 @@ update msg ({ env } as model) = in ( model3, Cmd.batch [ cmd, Cmd.map CodebaseTreeMsg cCmd ] ) - FinderMsg fMsg -> - case model.modal of - FinderModal fModel -> - let - ( fm, fc, out ) = - Finder.update env fMsg fModel - in - case out of - Finder.Remain -> - ( { model | modal = FinderModal fm }, Cmd.map FinderMsg fc ) - - Finder.Exit -> - ( { model | modal = NoModal }, Cmd.none ) - - Finder.OpenDefinition ref -> - navigateToDefinition { model | modal = NoModal } ref - - _ -> - ( model, Cmd.none ) - KeyboardShortcutMsg kMsg -> let ( keyboardShortcut, cmd ) = @@ -400,11 +383,16 @@ keydown model keyboardEvent = showFinder model Nothing KeyboardShortcut.Chord Shift QuestionMark -> - ( { model | modal = HelpModal }, Cmd.none ) + let + ( am, amCmd ) = + AppModal.show AppModal.KeyboardShortcutsModal + in + ( { model | appModal = am }, Cmd.map AppModalMsg amCmd ) + -- TODO: Move exit by Escape into AppModal module KeyboardShortcut.Sequence _ Escape -> - if model.modal == HelpModal then - ( { model | modal = NoModal }, Cmd.none ) + if AppModal.modalIs model.appModal AppModal.KeyboardShortcutsModal then + ( { model | appModal = AppModal.close }, Cmd.none ) else noOp @@ -413,19 +401,13 @@ keydown model keyboardEvent = noOp -showFinder : - { m | env : Env, modal : Modal } - -> Maybe FQN - -> ( { m | env : Env, modal : Modal }, Cmd Msg ) +showFinder : Model -> Maybe FQN -> ( Model, Cmd Msg ) showFinder model withinNamespace = let - options = - SearchOptions.init model.env.perspective withinNamespace - - ( fm, fcmd ) = - Finder.init model.env options + ( am, amCmd ) = + AppModal.showFinder model.env withinNamespace in - ( { model | modal = FinderModal fm }, Cmd.map FinderMsg fcmd ) + ( { model | appModal = am }, Cmd.map AppModalMsg amCmd ) @@ -501,7 +483,7 @@ viewAppHeader model = { menuToggle = Just ToggleSidebar , appTitle = appTitle_ , banner = banner - , rightButton = Just (Button.button (ShowModal PublishModal) "Publish on Unison Share" |> Button.share) + , rightButton = Just (Button.button (ShowModal AppModal.PublishModal) "Publish on Unison Share" |> Button.share) } @@ -521,7 +503,7 @@ viewSidebarHeader env = fqn |> FQN.toString |> String.length |> (\l -> l > 20) download = - Button.iconThenLabel (ShowModal (DownloadModal fqn)) Icon.download "Download latest version" + Button.iconThenLabel (ShowModal (AppModal.DownloadModal fqn)) Icon.download "Download latest version" |> Button.small |> Button.view |> List.singleton @@ -560,7 +542,7 @@ subMenu = , ( "Docs", ExternalHref "https://unisonweb.org/docs" ) , ( "Language Reference", ExternalHref "https://unisonweb.org/docs/language-reference" ) , ( "Community", ExternalHref "https://unisonweb.org/community" ) - , ( "Report a bug", OnClick (ShowModal ReportBugModal) ) + , ( "Report a bug", OnClick (ShowModal AppModal.ReportBugModal) ) ] @@ -606,7 +588,7 @@ viewMainSidebar model = (List.map (\( l, c ) -> Click.view [] [ text l ] c) subMenu - ++ [ a [ class "show-help", onClick (ShowModal HelpModal) ] + ++ [ a [ class "show-keyboard-shortcuts", onClick (ShowModal AppModal.KeyboardShortcutsModal) ] [ text "Keyboard Shortcuts" , KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) ] @@ -618,7 +600,7 @@ viewMainSidebar model = [ unisonSubmenu , Tooltip.tooltip (a - [ class "show-help-collapsed", onClick (ShowModal HelpModal) ] + [ class "show-keyboard-shortcuts-collapsed", onClick (ShowModal AppModal.KeyboardShortcutsModal) ] [ KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) ] ) @@ -630,186 +612,6 @@ viewMainSidebar model = ] -viewDownloadModal : FQN -> Html Msg -viewDownloadModal fqn = - let - prettyName = - FQN.toString fqn - - unqualified = - FQN.unqualifiedName fqn - - pullCommand = - "pull git@github.com:unisonweb/share.git:." ++ prettyName ++ " ." ++ unqualified - - content = - Modal.Content - (section - [] - [ p [] [ text "Download ", UI.bold prettyName, text " by pulling the namespace from Unison Share into a namespace in your local codebase:" ] - , CopyField.copyField (\_ -> CloseModal) pullCommand |> CopyField.withPrefix ".>" |> CopyField.view - , div [ class "hint" ] [ text "Copy and paste this command into UCM." ] - ] - ) - in - Modal.modal "download-modal" CloseModal content - |> Modal.withHeader ("Download " ++ prettyName) - |> Modal.view - - -viewHelpModal : OperatingSystem -> KeyboardShortcut.Model -> Html Msg -viewHelpModal os keyboardShortcut = - let - viewRow label instructions = - div - [ class "row" ] - [ label - , div [ class "instructions" ] instructions - ] - - viewInstructions label shortcuts = - viewRow label [ KeyboardShortcut.viewShortcuts keyboardShortcut shortcuts ] - - openFinderInstructions = - case os of - MacOS -> - [ KeyboardShortcut.Chord Meta (K Key.Lower), KeyboardShortcut.Chord Ctrl (K Key.Lower), KeyboardShortcut.single ForwardSlash ] - - _ -> - [ KeyboardShortcut.Chord Ctrl (K Key.Lower), KeyboardShortcut.single ForwardSlash ] - - toggleSidebarInstructions = - case os of - MacOS -> - [ KeyboardShortcut.Chord Meta (B Key.Lower), KeyboardShortcut.Chord Ctrl (B Key.Lower) ] - - _ -> - [ KeyboardShortcut.Chord Ctrl (B Key.Lower), KeyboardShortcut.single (S Key.Lower) ] - - content = - Modal.Content - (section - [ class "shortcuts" ] - [ div [ class "shortcut-group" ] - [ h3 [] [ text "General" ] - , viewInstructions (span [] [ text "Keyboard shortcuts", UI.subtle " (this dialog)" ]) [ KeyboardShortcut.single QuestionMark ] - , viewInstructions (text "Open Finder") openFinderInstructions - , viewInstructions (text "Toggle sidebar") toggleSidebarInstructions - , viewInstructions (text "Move focus up") [ KeyboardShortcut.single ArrowUp, KeyboardShortcut.single (K Key.Lower) ] - , viewInstructions (text "Move focus down") [ KeyboardShortcut.single ArrowDown, KeyboardShortcut.single (J Key.Lower) ] - , viewInstructions (text "Close focused definition") [ KeyboardShortcut.single (X Key.Lower) ] - , viewInstructions (text "Expand/Collapse focused definition") [ KeyboardShortcut.single Space ] - ] - , div [ class "shortcut-group" ] - [ h3 [] [ text "Finder" ] - , viewInstructions (text "Clear search query") [ KeyboardShortcut.single Escape ] - , viewInstructions (span [] [ text "Close", UI.subtle " (when search query is empty)" ]) [ KeyboardShortcut.single Escape ] - , viewInstructions (text "Move focus up") [ KeyboardShortcut.single ArrowUp ] - , viewInstructions (text "Move focus down") [ KeyboardShortcut.single ArrowDown ] - , viewInstructions (text "Open focused definition") [ KeyboardShortcut.single Enter ] - , viewRow (text "Open definition") - [ KeyboardShortcut.viewBase - [ KeyboardShortcut.viewKey os Semicolon False - , KeyboardShortcut.viewThen - , KeyboardShortcut.viewKeyBase "1-9" False - ] - ] - ] - ] - ) - in - Modal.modal "help-modal" CloseModal content - |> Modal.withHeader "Keyboard shortcuts" - |> Modal.view - - -githubLinkButton : String -> Html msg -githubLinkButton repo = - Button.linkIconThenLabel ("https://github.com/" ++ repo) Icon.github repo - |> Button.small - |> Button.contained - |> Button.view - - -viewPublishModal : Html Msg -viewPublishModal = - let - content = - Modal.Content - (section - [] - [ p [ class "main" ] - [ text "With your Unison codebase on GitHub, open a Pull Request against " - , githubLinkButton "unisonweb/share" - , text " to list (or unlist) your project on Unison Share." - ] - , a [ class "help", href "https://www.unisonweb.org/docs/codebase-organization/#day-to-day-development-creating-and-merging-pull-requests", rel "noopener", target "_blank" ] [ text "How do I get my code on GitHub?" ] - ] - ) - in - Modal.modal "publish-modal" CloseModal content - |> Modal.withHeader "Publish your project on Unison Share" - |> Modal.view - - -viewReportBugModal : Html Msg -viewReportBugModal = - let - content = - Modal.Content - (div [] - [ section [] - [ p [] [ text "We try our best, but bugs unfortunately creep through :(" ] - , p [] [ text "We greatly appreciate feedback and bug reports—its very helpful for providing the best developer experience when working with Unison." ] - ] - , UI.divider - , section [ class "actions" ] - [ p [] [ text "Visit our GitHub repositories to report bugs and provide feedback" ] - , div [ class "action" ] - [ githubLinkButton "unisonweb/codebase-ui" - , text "for reports on" - , strong [] [ text "Unison Share" ] - , span [ class "subtle" ] [ text "(this UI)" ] - ] - , div [ class "action" ] - [ githubLinkButton "unisonweb/unison" - , text "for reports on the" - , strong [] [ text "Unison Language" ] - , span [ class "subtle" ] [ text "(UCM)" ] - ] - ] - ] - ) - in - Modal.modal "report-bug-modal" CloseModal content - |> Modal.withHeader "Report a Bug" - |> Modal.view - - -viewModal : - { m | env : Env, modal : Modal, keyboardShortcut : KeyboardShortcut.Model } - -> Html Msg -viewModal model = - case model.modal of - NoModal -> - UI.nothing - - FinderModal m -> - Html.map FinderMsg (Finder.view m) - - HelpModal -> - viewHelpModal model.env.operatingSystem model.keyboardShortcut - - PublishModal -> - viewPublishModal - - ReportBugModal -> - viewReportBugModal - - DownloadModal fqn -> - viewDownloadModal fqn - - viewAppLoading : Html msg viewAppLoading = div [ id "app" ] @@ -853,7 +655,7 @@ view model = [ viewAppHeader model , viewMainSidebar model , div [ id "main-content" ] [ page ] - , viewModal model + , Html.map AppModalMsg (AppModal.view model.env model.appModal) ] ] } diff --git a/src/UnisonShare/AppModal.elm b/src/UnisonShare/AppModal.elm new file mode 100644 index 0000000..ba9b6ab --- /dev/null +++ b/src/UnisonShare/AppModal.elm @@ -0,0 +1,293 @@ +module UnisonShare.AppModal exposing (..) + +import Definition.Reference exposing (Reference) +import Env exposing (Env, OperatingSystem(..)) +import Finder +import Finder.SearchOptions as SearchOptions +import FullyQualifiedName as FQN exposing (FQN) +import Html exposing (Html, a, div, h3, p, section, span, strong, text) +import Html.Attributes exposing (class, href, rel, target) +import KeyboardShortcut +import KeyboardShortcut.Key as Key exposing (Key(..)) +import UI +import UI.Button as Button +import UI.CopyField as CopyField +import UI.Modal as Modal + + + +-- MODEL + + +type AppModal + = FinderModal Finder.Model + | KeyboardShortcutsModal + | ReportBugModal + | PublishModal + | DownloadModal FQN + + +type Model + = NoModal + | Visible AppModal + + +init : Model +init = + NoModal + + + +-- UPDATE + + +type Msg + = Close + | FinderMsg Finder.Msg + + +type OutMsg + = None + | OpenDefinition Reference + + +update : Env -> Msg -> Model -> ( Model, Cmd Msg, OutMsg ) +update env msg model = + case ( model, msg ) of + ( Visible (FinderModal fModel), FinderMsg fMsg ) -> + let + ( fm, fc, out ) = + Finder.update env fMsg fModel + in + case out of + Finder.Remain -> + ( Visible (FinderModal fm), Cmd.map FinderMsg fc, None ) + + Finder.Exit -> + ( close, Cmd.none, None ) + + Finder.OpenDefinition ref -> + ( close, Cmd.none, OpenDefinition ref ) + + ( Visible _, Close ) -> + ( close, Cmd.none, None ) + + _ -> + ( model, Cmd.none, None ) + + + +-- API + + +show : AppModal -> ( Model, Cmd Msg ) +show modal = + ( Visible modal, Cmd.none ) + + +showFinder : Env -> Maybe FQN -> ( Model, Cmd Msg ) +showFinder env withinNamespace = + let + options = + SearchOptions.init env.perspective withinNamespace + + ( fm, fcmd ) = + Finder.init env options + in + ( Visible (FinderModal fm), Cmd.map FinderMsg fcmd ) + + +close : Model +close = + NoModal + + +isOpen : Model -> Bool +isOpen model = + model == NoModal + + +modalIs : Model -> AppModal -> Bool +modalIs model modal = + case model of + NoModal -> + False + + Visible m -> + m == modal + + + +-- VIEW + + +viewDownloadModal : FQN -> Html Msg +viewDownloadModal fqn = + let + prettyName = + FQN.toString fqn + + unqualified = + FQN.unqualifiedName fqn + + pullCommand = + "pull git@github.com:unisonweb/share.git:." ++ prettyName ++ " ." ++ unqualified + + content = + Modal.Content + (section + [] + [ p [] [ text "Download ", UI.bold prettyName, text " by pulling the namespace from Unison Share into a namespace in your local codebase:" ] + , CopyField.copyField (\_ -> Close) pullCommand |> CopyField.withPrefix ".>" |> CopyField.view + , div [ class "hint" ] [ text "Copy and paste this command into UCM." ] + ] + ) + in + Modal.modal "download-modal" Close content + |> Modal.withHeader ("Download " ++ prettyName) + |> Modal.view + + +viewKeyboardShortcutsModal : OperatingSystem -> KeyboardShortcut.Model -> Html Msg +viewKeyboardShortcutsModal os keyboardShortcut = + let + viewRow label instructions = + div + [ class "row" ] + [ label + , div [ class "instructions" ] instructions + ] + + viewInstructions label shortcuts = + viewRow label [ KeyboardShortcut.viewShortcuts keyboardShortcut shortcuts ] + + openFinderInstructions = + case os of + MacOS -> + [ KeyboardShortcut.Chord Meta (K Key.Lower), KeyboardShortcut.Chord Ctrl (K Key.Lower), KeyboardShortcut.single ForwardSlash ] + + _ -> + [ KeyboardShortcut.Chord Ctrl (K Key.Lower), KeyboardShortcut.single ForwardSlash ] + + toggleSidebarInstructions = + case os of + MacOS -> + [ KeyboardShortcut.Chord Meta (B Key.Lower), KeyboardShortcut.Chord Ctrl (B Key.Lower) ] + + _ -> + [ KeyboardShortcut.Chord Ctrl (B Key.Lower), KeyboardShortcut.single (S Key.Lower) ] + + content = + Modal.Content + (section + [ class "shortcuts" ] + [ div [ class "shortcut-group" ] + [ h3 [] [ text "General" ] + , viewInstructions (span [] [ text "Keyboard shortcuts", UI.subtle " (this dialog)" ]) [ KeyboardShortcut.single QuestionMark ] + , viewInstructions (text "Open Finder") openFinderInstructions + , viewInstructions (text "Toggle sidebar") toggleSidebarInstructions + , viewInstructions (text "Move focus up") [ KeyboardShortcut.single ArrowUp, KeyboardShortcut.single (K Key.Lower) ] + , viewInstructions (text "Move focus down") [ KeyboardShortcut.single ArrowDown, KeyboardShortcut.single (J Key.Lower) ] + , viewInstructions (text "Close focused definition") [ KeyboardShortcut.single (X Key.Lower) ] + , viewInstructions (text "Expand/Collapse focused definition") [ KeyboardShortcut.single Space ] + ] + , div [ class "shortcut-group" ] + [ h3 [] [ text "Finder" ] + , viewInstructions (text "Clear search query") [ KeyboardShortcut.single Escape ] + , viewInstructions (span [] [ text "Close", UI.subtle " (when search query is empty)" ]) [ KeyboardShortcut.single Escape ] + , viewInstructions (text "Move focus up") [ KeyboardShortcut.single ArrowUp ] + , viewInstructions (text "Move focus down") [ KeyboardShortcut.single ArrowDown ] + , viewInstructions (text "Open focused definition") [ KeyboardShortcut.single Enter ] + , viewRow (text "Open definition") + [ KeyboardShortcut.viewBase + [ KeyboardShortcut.viewKey os Semicolon False + , KeyboardShortcut.viewThen + , KeyboardShortcut.viewKeyBase "1-9" False + ] + ] + ] + ] + ) + in + Modal.modal "help-modal" Close content + |> Modal.withHeader "Keyboard shortcuts" + |> Modal.view + + +viewPublishModal : Html Msg +viewPublishModal = + let + content = + Modal.Content + (section + [] + [ p [ class "main" ] + [ text "With your Unison codebase on GitHub, open a Pull Request against " + , Button.github "unisonweb/share" |> Button.view + , text " to list (or unlist) your project on Unison Share." + ] + , a [ class "help", href "https://www.unisonweb.org/docs/codebase-organization/#day-to-day-development-creating-and-merging-pull-requests", rel "noopener", target "_blank" ] [ text "How do I get my code on GitHub?" ] + ] + ) + in + Modal.modal "publish-modal" Close content + |> Modal.withHeader "Publish your project on Unison Share" + |> Modal.view + + +viewReportBugModal : Html Msg +viewReportBugModal = + let + content = + Modal.Content + (div [] + [ section [] + [ p [] [ text "We try our best, but bugs unfortunately creep through :(" ] + , p [] [ text "We greatly appreciate feedback and bug reports—its very helpful for providing the best developer experience when working with Unison." ] + ] + , UI.divider + , section [ class "actions" ] + [ p [] [ text "Visit our GitHub repositories to report bugs and provide feedback" ] + , div [ class "action" ] + [ Button.github "unisonweb/codebase-ui" |> Button.view + , text "for reports on" + , strong [] [ text "Unison Share" ] + , span [ class "subtle" ] [ text "(this UI)" ] + ] + , div [ class "action" ] + [ Button.github "unisonweb/unison" |> Button.view + , text "for reports on the" + , strong [] [ text "Unison Language" ] + , span [ class "subtle" ] [ text "(UCM)" ] + ] + ] + ] + ) + in + Modal.modal "report-bug-modal" Close content + |> Modal.withHeader "Report a Bug" + |> Modal.view + + +view : Env -> Model -> Html Msg +view env model = + case model of + NoModal -> + UI.nothing + + Visible (FinderModal m) -> + Html.map FinderMsg (Finder.view m) + + Visible KeyboardShortcutsModal -> + viewKeyboardShortcutsModal + env.operatingSystem + (KeyboardShortcut.init env.operatingSystem) + + Visible PublishModal -> + viewPublishModal + + Visible ReportBugModal -> + viewReportBugModal + + Visible (DownloadModal fqn) -> + viewDownloadModal fqn diff --git a/src/css/app.css b/src/css/app.css index 5df5922..fc0b60c 100644 --- a/src/css/app.css +++ b/src/css/app.css @@ -312,7 +312,7 @@ text-decoration: none; } -#main-sidebar .show-help { +#main-sidebar .show-keyboard-shortcuts { position: relative; line-height: 1; display: flex; @@ -324,11 +324,11 @@ margin-top: 1.5rem; } -#main-sidebar .show-help:hover { +#main-sidebar .show-keyboard-shortcuts:hover { text-decoration: none; } -#main-sidebar .show-help .keyboard-shortcut { +#main-sidebar .show-keyboard-shortcuts .keyboard-shortcut { justify-self: flex-end; margin-left: auto; margin-right: 0.1875rem; @@ -340,10 +340,10 @@ font-weight: normal; } -#main-sidebar .show-help:hover { +#main-sidebar .show-keyboard-shortcuts:hover { background: var(--color-sidebar-focus-bg); } -#main-sidebar .show-help:hover .key { +#main-sidebar .show-keyboard-shortcuts:hover .key { color: var(--color-sidebar-keyboard-shortcut-hover-key-fg); } diff --git a/webpack.dev.js b/webpack.unisonLocal.dev.js similarity index 100% rename from webpack.dev.js rename to webpack.unisonLocal.dev.js diff --git a/webpack.unisonShare.dev.js b/webpack.unisonShare.dev.js new file mode 100644 index 0000000..348e56d --- /dev/null +++ b/webpack.unisonShare.dev.js @@ -0,0 +1,71 @@ +const path = require("path"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); + +const API_URL = process.env.API_URL || "127.0.0.1:8080"; + +module.exports = { + entry: "./src/unisonShare.js", + + module: { + rules: [ + { + test: /\.css$/i, + use: ["style-loader", "css-loader"], + }, + { + test: /\.(png|svg|jpg|jpeg|gif)$/i, + type: "asset/resource", + }, + { + test: /\.(woff(2)?|ttf|eot)$/i, + type: "asset/resource", + }, + { + test: /\.elm$/, + exclude: [/elm-stuff/, /node_modules/], + use: [ + { + loader: "elm-asset-webpack-loader", + }, + { + loader: "elm-webpack-loader", + options: { + debug: false, + cwd: __dirname, + }, + }, + ], + }, + ], + }, + + plugins: [ + new HtmlWebpackPlugin({ + favicon: "./static/favicon.ico", + template: "./src/unisonShare.ejs", + inject: "body", + publicPath: "/", + base: "/", + filename: path.resolve(__dirname, "dist/dev/index.html"), + }), + ], + + output: { + filename: "[name].[contenthash].js", + path: path.resolve(__dirname, "dist/dev"), + clean: true, + }, + + devServer: { + historyApiFallback: { + disableDotRule: true, + }, + proxy: { + "/api": { + target: API_URL, + pathRewrite: { "^/api": "" }, + logLevel: "debug", + }, + }, + }, +}; From fb0bacff1bf2a8631940fefb35ab12472ab3db7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Tue, 7 Dec 2021 13:46:02 -0500 Subject: [PATCH 18/54] UnisonShare: Add foundations of Catalog page Add a new page module for the Catalog and add placeholder model, update, and view functionality. This includes a very early iteration of a Project type. --- package.json | 2 +- src/Api.elm | 6 ++ src/Project.elm | 22 +++++++ src/UnisonShare/Page/Catalog.elm | 109 +++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 src/Project.elm create mode 100644 src/UnisonShare/Page/Catalog.elm diff --git a/package.json b/package.json index d7e6506..f7b2911 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "clean": "rm -rf dist", "start": "webpack serve --mode development --port 1234 --config webpack.dev.js", "test": "elm-test", - "review": "elm-review" + "review": "elm-review --ignore-files src/UnisonShare/Page/Catalog.elm" }, "bugs": { "url": "https://github.com/unisonweb/codebase-ui/issues" diff --git a/src/Api.elm b/src/Api.elm index 15b3823..1ec8794 100644 --- a/src/Api.elm +++ b/src/Api.elm @@ -8,6 +8,7 @@ module Api exposing , list , namespace , perform + , projects , toRequest , toUrl ) @@ -53,6 +54,11 @@ namespace perspective fqn = Endpoint [ "namespaces", FQN.toString fqn ] queryParams +projects : Endpoint +projects = + Endpoint [ "projects" ] [] + + getDefinition : Perspective -> List String -> Endpoint getDefinition perspective fqnsOrHashes = let diff --git a/src/Project.elm b/src/Project.elm new file mode 100644 index 0000000..ee282d4 --- /dev/null +++ b/src/Project.elm @@ -0,0 +1,22 @@ +module Project exposing (..) + +import FullyQualifiedName exposing (FQN) +import Hash exposing (Hash) +import Json.Decode as Decode + + +type Owner + = Owner String + + +type alias Project a = + { a | owner : Owner, name : FQN, hash : Hash } + + +type alias ProjectListing = + Project () + + +decodeList : Decode.Decoder (List ProjectListing) +decodeList = + Decode.succeed [] diff --git a/src/UnisonShare/Page/Catalog.elm b/src/UnisonShare/Page/Catalog.elm new file mode 100644 index 0000000..6de9f29 --- /dev/null +++ b/src/UnisonShare/Page/Catalog.elm @@ -0,0 +1,109 @@ +module UnisonShare.Page.Catalog exposing (..) + +import Api +import Dict exposing (Dict) +import Env exposing (Env) +import Html exposing (Html, div, text) +import Html.Attributes exposing (class) +import Http +import Project exposing (ProjectListing) +import RemoteData exposing (RemoteData(..), WebData) +import UI + + + +-- MODEL + + +type Category + = Category String + + +type Catalog + = Catalog (Dict Category ProjectListing) + + +type alias LoadedModel = + { query : String + , catalog : Catalog + } + + +type alias Model = + WebData LoadedModel + + +init : Env -> ( Model, Cmd Msg ) +init env = + let + fetchCmd = + Api.projects + |> Api.toRequest Project.decodeList FetchProjectsFinished + |> Api.perform env.apiBasePath + in + ( Loading, fetchCmd ) + + + +-- UPDATE + + +type Msg + = UpdateQuery String + | ClearQuery + | FetchProjectsFinished (Result Http.Error (List ProjectListing)) + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case ( msg, model ) of + ( FetchProjectsFinished projectsResult, _ ) -> + case projectsResult of + Err e -> + ( Failure e, Cmd.none ) + + Ok projects -> + let + catalog = + projectsToCatalog projects + in + ( Success { query = "", catalog = catalog }, Cmd.none ) + + ( UpdateQuery query, Success m ) -> + ( Success { m | query = query }, Cmd.none ) + + ( ClearQuery, Success m ) -> + ( Success { m | query = "" }, Cmd.none ) + + _ -> + ( model, Cmd.none ) + + +projectsToCatalog : List ProjectListing -> Catalog +projectsToCatalog _ = + Catalog Dict.empty + + + +-- VIEW + + +viewLoaded : LoadedModel -> Html Msg +viewLoaded _ = + div [] [ text "Catalog" ] + + +view : Model -> Html Msg +view model = + case model of + NotAsked -> + UI.nothing + + Loading -> + UI.nothing + + Failure _ -> + div [ class "" ] [ text "Error..." ] + + Success m -> + viewLoaded m From 90b7efb08fa6a89df6efd3a0155b01856d683593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Wed, 8 Dec 2021 15:01:13 -0500 Subject: [PATCH 19/54] Add doc divider class --- src/Definition/Doc.elm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Definition/Doc.elm b/src/Definition/Doc.elm index 7c0e47f..012ec8a 100644 --- a/src/Definition/Doc.elm +++ b/src/Definition/Doc.elm @@ -415,7 +415,7 @@ view refToMsg toggleFoldMsg docFoldToggles document = br [] [] SectionBreak -> - hr [] [] + hr [ class "divider" ] [] Tooltip triggerContent tooltipContent -> Tooltip.tooltip From dd0a7b02a1c8db83a89768cec5390bfa77ef5339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Tue, 7 Dec 2021 18:55:48 -0500 Subject: [PATCH 20/54] Add UI.Page Add a common component to layout pages used by both Unison Local and Unison Share (though it's likely that Unison Local will only every use 1 of the layouts). --- src/UI/Page.elm | 71 +++++++++++++++++++++ src/UnisonLocal/App.elm | 133 +++++++++++++++++++++------------------- src/UnisonShare/App.elm | 128 ++++++++++++++++++++------------------ src/css/app.css | 61 +++++------------- src/css/page.css | 34 ++++++++++ 5 files changed, 259 insertions(+), 168 deletions(-) create mode 100644 src/UI/Page.elm create mode 100644 src/css/page.css diff --git a/src/UI/Page.elm b/src/UI/Page.elm new file mode 100644 index 0000000..7d0183f --- /dev/null +++ b/src/UI/Page.elm @@ -0,0 +1,71 @@ +module UI.Page exposing (..) + +import Html exposing (Html, div, header, section) +import Html.Attributes exposing (class, classList) +import UI.AppHeader as AppHeader exposing (AppHeader) +import UI.Sidebar as Sidebar + + +type Hero msg + = Hero (Html msg) + + +type PageContent msg + = PageContent (List (Html msg)) + + +type Page msg + = HeroLayout + { header : AppHeader msg + , hero : Hero msg + , content : + PageContent msg + } + | SidebarLayout + { header : AppHeader msg + , sidebar : List (Html msg) + , sidebarToggled : Bool + , content : PageContent msg + } + | FullLayout { header : AppHeader msg, content : PageContent msg } + + + +-- VIEW + + +viewHero : Hero msg -> Html msg +viewHero (Hero content) = + header [ class "page-hero" ] [ content ] + + +viewContent : PageContent msg -> Html msg +viewContent (PageContent content) = + section [ class "page-content" ] content + + +view : Page msg -> Html msg +view page = + case page of + HeroLayout { header, hero, content } -> + div [ class "page hero-layout" ] + [ AppHeader.view header + , viewHero hero + , viewContent content + ] + + SidebarLayout { header, sidebar, sidebarToggled, content } -> + div + [ class "page sidebar-layout" + , classList [ ( "sidebar-toggled", sidebarToggled ) ] + ] + [ AppHeader.view header + , Sidebar.view sidebar + , viewContent content + ] + + FullLayout { header, content } -> + div [ class "page full-layout" ] + [ AppHeader.view header + , viewContent content + ] diff --git a/src/UnisonLocal/App.elm b/src/UnisonLocal/App.elm index ac2e655..2faa20f 100644 --- a/src/UnisonLocal/App.elm +++ b/src/UnisonLocal/App.elm @@ -27,6 +27,7 @@ import UI.Button as Button import UI.Click as Click exposing (Click(..)) import UI.Icon as Icon import UI.Modal as Modal +import UI.Page as Page import UI.Sidebar as Sidebar import UI.Tooltip as Tooltip import Url exposing (Url) @@ -471,7 +472,7 @@ appTitle clickMsg = appTitle_ (h1 [] [ text "Unison", span [ class "context unison-local" ] [ text "Local" ] ]) -viewAppHeader : Model -> Html Msg +viewAppHeader : Model -> AppHeader.AppHeader Msg viewAppHeader model = let changePerspectiveMsg = @@ -485,12 +486,11 @@ viewAppHeader model = appTitle_ = appTitle (Just changePerspectiveMsg) in - AppHeader.view - { menuToggle = Just ToggleSidebar - , appTitle = appTitle_ - , banner = Nothing - , rightButton = Just (Button.button (ShowModal PublishModal) "Publish on Unison Share" |> Button.share) - } + { menuToggle = Just ToggleSidebar + , appTitle = appTitle_ + , banner = Nothing + , rightButton = Just (Button.button (ShowModal PublishModal) "Publish on Unison Share" |> Button.share) + } viewSidebarHeader : Env -> Html Msg @@ -558,42 +558,41 @@ unisonSubmenu = |> Tooltip.view -viewMainSidebar : Model -> Html Msg +viewMainSidebar : Model -> List (Html Msg) viewMainSidebar model = - Sidebar.view - [ viewMainSidebarCollapseButton model - , div [ class "expanded-content" ] - [ viewSidebarHeader model.env - , div [ class "sidebar-scroll-area" ] - [ Sidebar.section - "Namespaces and Definitions" - [ Html.map CodebaseTreeMsg (CodebaseTree.view model.codebaseTree) ] - , nav [] - (List.map - (\( l, c ) -> Click.view [] [ text l ] c) - subMenu - ++ [ a [ class "show-keyboard-shortcuts", onClick (ShowModal HelpModal) ] - [ text "Keyboard Shortcuts" - , KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) - ] - ] - ) - ] - ] - , div [ class "collapsed-content" ] - [ unisonSubmenu - , Tooltip.tooltip - (a - [ class "show-keyboard-shortcuts-collapsed", onClick (ShowModal HelpModal) ] - [ KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) - ] + [ viewMainSidebarCollapseButton model + , div [ class "expanded-content" ] + [ viewSidebarHeader model.env + , div [ class "sidebar-scroll-area" ] + [ Sidebar.section + "Namespaces and Definitions" + [ Html.map CodebaseTreeMsg (CodebaseTree.view model.codebaseTree) ] + , nav [] + (List.map + (\( l, c ) -> Click.view [] [ text l ] c) + subMenu + ++ [ a [ class "show-keyboard-shortcuts", onClick (ShowModal HelpModal) ] + [ text "Keyboard Shortcuts" + , KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) + ] + ] ) - (Tooltip.Text "Keyboard Shortcuts") - |> Tooltip.withPosition Tooltip.RightOf - |> Tooltip.withArrow Tooltip.Middle - |> Tooltip.view ] ] + , div [ class "collapsed-content" ] + [ unisonSubmenu + , Tooltip.tooltip + (a + [ class "show-help-collapsed", onClick (ShowModal HelpModal) ] + [ KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) + ] + ) + (Tooltip.Text "Keyboard Shortcuts") + |> Tooltip.withPosition Tooltip.RightOf + |> Tooltip.withArrow Tooltip.Middle + |> Tooltip.view + ] + ] viewHelpModal : OperatingSystem -> KeyboardShortcut.Model -> Html Msg @@ -748,30 +747,39 @@ viewModal model = viewAppLoading : Html msg viewAppLoading = - div [ id "app" ] - [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) - , Sidebar.view [] - , div [ id "main-content" ] [] - ] + Page.view + (Page.SidebarLayout + { header = AppHeader.appHeader (appTitle Nothing) + , sidebar = [] + , sidebarToggled = False + , content = Page.PageContent [] + } + ) viewAppError : Http.Error -> Html msg viewAppError error = - div [ id "app" ] - [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) - , Sidebar.view [] - , div [ id "main-content", class "app-error" ] - [ Icon.view Icon.warn - , p [ title (Api.errorToString error) ] - [ text "Unison Local could not be started." ] - ] - ] + Page.view + (Page.SidebarLayout + { header = AppHeader.appHeader (appTitle Nothing) + , sidebar = [] + , sidebarToggled = False + , content = + Page.PageContent + [ div [ class "app-error" ] + [ Icon.view Icon.warn + , p [ title (Api.errorToString error) ] + [ text "Unison Local could not be started." ] + ] + ] + } + ) view : Model -> Browser.Document Msg view model = let - page = + pageContent = case model.route of Route.Perspective _ -> Html.map PerspectiveLandingMsg @@ -782,14 +790,15 @@ view model = Route.Definition _ _ -> Html.map WorkspaceMsg (Workspace.view model.workspace) + + page = + Page.SidebarLayout + { header = viewAppHeader model + , sidebar = viewMainSidebar model + , sidebarToggled = model.sidebarToggled + , content = Page.PageContent [ pageContent ] + } in { title = "Unison Local" - , body = - [ div [ id "app", classList [ ( "sidebar-toggled", model.sidebarToggled ) ] ] - [ viewAppHeader model - , viewMainSidebar model - , div [ id "main-content" ] [ page ] - , viewModal model - ] - ] + , body = [ div [ id "app" ] [ Page.view page, viewModal model ] ] } diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index ac92443..61902dc 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -25,6 +25,7 @@ import UI.Banner as Banner import UI.Button as Button import UI.Click as Click exposing (Click(..)) import UI.Icon as Icon +import UI.Page as Page import UI.Sidebar as Sidebar import UI.Tooltip as Tooltip import UnisonShare.AppModal as AppModal @@ -457,7 +458,7 @@ appTitle clickMsg = appTitle_ (h1 [] [ text "Unison", span [ class "context unison-share" ] [ text "Share" ] ]) -viewAppHeader : Model -> Html Msg +viewAppHeader : Model -> AppHeader.AppHeader Msg viewAppHeader model = let changePerspectiveMsg = @@ -479,12 +480,11 @@ viewAppHeader model = "Check it out!" ) in - AppHeader.view - { menuToggle = Just ToggleSidebar - , appTitle = appTitle_ - , banner = banner - , rightButton = Just (Button.button (ShowModal AppModal.PublishModal) "Publish on Unison Share" |> Button.share) - } + { menuToggle = Just ToggleSidebar + , appTitle = appTitle_ + , banner = banner + , rightButton = Just (Button.button (ShowModal AppModal.PublishModal) "Publish on Unison Share" |> Button.share) + } viewSidebarHeader : Env -> Html Msg @@ -559,7 +559,7 @@ unisonSubmenu = |> Tooltip.view -viewMainSidebar : Model -> Html Msg +viewMainSidebar : Model -> List (Html Msg) viewMainSidebar model = let perspective = @@ -575,69 +575,73 @@ viewMainSidebar model = else UI.nothing in - Sidebar.view - [ viewMainSidebarCollapseButton model - , div [ class "expanded-content" ] - [ viewSidebarHeader model.env - , div [ class "sidebar-scroll-area" ] - [ sidebarContent - , Sidebar.section - "Namespaces and Definitions" - [ Html.map CodebaseTreeMsg (CodebaseTree.view model.codebaseTree) ] - , nav [] - (List.map - (\( l, c ) -> Click.view [] [ text l ] c) - subMenu - ++ [ a [ class "show-keyboard-shortcuts", onClick (ShowModal AppModal.KeyboardShortcutsModal) ] - [ text "Keyboard Shortcuts" - , KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) - ] - ] - ) - ] - ] - , div [ class "collapsed-content" ] - [ unisonSubmenu - , Tooltip.tooltip - (a - [ class "show-keyboard-shortcuts-collapsed", onClick (ShowModal AppModal.KeyboardShortcutsModal) ] - [ KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) - ] + [ viewMainSidebarCollapseButton model + , div [ class "expanded-content" ] + [ viewSidebarHeader model.env + , div [ class "sidebar-scroll-area" ] + [ sidebarContent + , Sidebar.section + "Namespaces and Definitions" + [ Html.map CodebaseTreeMsg (CodebaseTree.view model.codebaseTree) ] + , nav [] + (List.map + (\( l, c ) -> Click.view [] [ text l ] c) + subMenu + ++ [ a [ class "show-keyboard-shortcuts", onClick (ShowModal AppModal.KeyboardShortcutsModal) ] + [ text "Keyboard Shortcuts" + , KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) + ] + ] ) - (Tooltip.Text "Keyboard Shortcuts") - |> Tooltip.withPosition Tooltip.RightOf - |> Tooltip.withArrow Tooltip.Middle - |> Tooltip.view ] ] + , div [ class "collapsed-content" ] + [ unisonSubmenu + , Tooltip.tooltip + (a + [ class "show-help-collapsed", onClick (ShowModal AppModal.KeyboardShortcutsModal) ] + [ KeyboardShortcut.view model.keyboardShortcut (KeyboardShortcut.single QuestionMark) + ] + ) + (Tooltip.Text "Keyboard Shortcuts") + |> Tooltip.withPosition Tooltip.RightOf + |> Tooltip.withArrow Tooltip.Middle + |> Tooltip.view + ] + ] viewAppLoading : Html msg viewAppLoading = - div [ id "app" ] - [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) - , Sidebar.view [] - , div [ id "main-content" ] [] - ] + Page.view + (Page.FullLayout + { header = AppHeader.appHeader (appTitle Nothing) + , content = Page.PageContent [] + } + ) viewAppError : Http.Error -> Html msg viewAppError error = - div [ id "app" ] - [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) - , Sidebar.view [] - , div [ id "main-content", class "app-error" ] - [ Icon.view Icon.warn - , p [ title (Api.errorToString error) ] - [ text "Unison Share could not be started." ] - ] - ] + Page.view + (Page.FullLayout + { header = AppHeader.appHeader (appTitle Nothing) + , content = + Page.PageContent + [ div [ class "app-error" ] + [ Icon.view Icon.warn + , p [ title (Api.errorToString error) ] + [ text "Unison Share could not be started." ] + ] + ] + } + ) view : Model -> Browser.Document Msg view model = let - page = + pageContent = case model.route of Route.Perspective _ -> Html.map PerspectiveLandingMsg @@ -648,13 +652,19 @@ view model = Route.Definition _ _ -> Html.map WorkspaceMsg (Workspace.view model.workspace) + + page = + Page.SidebarLayout + { header = viewAppHeader model + , sidebar = viewMainSidebar model + , sidebarToggled = model.sidebarToggled + , content = Page.PageContent [ pageContent ] + } in { title = "Unison Share" , body = - [ div [ id "app", classList [ ( "sidebar-toggled", model.sidebarToggled ) ] ] - [ viewAppHeader model - , viewMainSidebar model - , div [ id "main-content" ] [ page ] + [ div [ id "app" ] + [ Page.view page , Html.map AppModalMsg (AppModal.view model.env model.appModal) ] ] diff --git a/src/css/app.css b/src/css/app.css index fc0b60c..081685e 100644 --- a/src/css/app.css +++ b/src/css/app.css @@ -1,16 +1,5 @@ /* -- Application ---------------------------------------------------------- */ -#app { - display: grid; - grid-template-rows: var(--app-header-height) auto; - grid-template-columns: var(--main-sidebar-width) auto; - grid-template-areas: - "app-header app-header" - "main-sidebar main-content"; - transition: grid-template-columns var(--transition-sidebar-collapse-time); - --transition-sidebar-collapse-time: 0.3s; -} - #app .app-error { display: flex; flex: 1; @@ -347,54 +336,30 @@ color: var(--color-sidebar-keyboard-shortcut-hover-key-fg); } -/* -- Main Content --------------------------------------------------------- */ - -#main-content { - grid-area: main-content; - height: calc(100vh - var(--app-header-height)); - overflow: auto; - scroll-behavior: smooth; - scrollbar-width: auto; - scrollbar-color: var(--color-main-subtle-fg) var(--color-main-bg); -} -#main-content::-webkit-scrollbar { - width: 0.5rem; - height: 0.5rem; -} - -#main-content::-webkit-scrollbar-track { - background: var(--color-main-bg); -} - -#main-content::-webkit-scrollbar-thumb { - background-color: var(--color-main-subtle-fg); - border-radius: var(--border-radius-base); -} - /* -- Responsive ----------------------------------------------------------- */ @media only screen and (min-width: 1025px) { - #app.sidebar-toggled { + .page.sidebar-toggled { --main-sidebar-width: var(--main-sidebar-collapsed-width); } - #app.sidebar-toggled .collapse-sidebar-button { + .page.sidebar-toggled .collapse-sidebar-button { animation: collapse-sidebar-button var(--transition-sidebar-collapse-time) ease-in-out; transition: none; animation-fill-mode: forwards; } - #app.sidebar-toggled #main-sidebar { + .page.sidebar-toggled #main-sidebar { overflow: visible; } - #app.sidebar-toggled #main-sidebar .collapsed-content { + .page.sidebar-toggled #main-sidebar .collapsed-content { opacity: 1; transform: translateX(0); } - #app.sidebar-toggled #main-sidebar .expanded-content { + .page.sidebar-toggled #main-sidebar .expanded-content { transition: opacity; transition-duration: var(--transition-sidebar-collapse-time) * 1.1; transition-delay: 0; @@ -402,7 +367,7 @@ overflow: hidden; } - #app.sidebar-toggled .sidebar-scroll-area { + .page.sidebar-toggled .sidebar-scroll-area { overflow: hidden; } } @@ -413,18 +378,18 @@ grid-template-columns: auto auto; grid-template-areas: "app-header app-header" - "main-content main-content"; + "page-content page-content"; } #main-sidebar { display: none; } - #main-content { + .page-content { width: 100vw; } - #app.sidebar-toggled { + .page.sidebar-toggled { grid-template-rows: 3.5rem auto; grid-template-columns: auto auto; grid-template-areas: @@ -432,13 +397,13 @@ "main-sidebar main-sidebar"; } - #app.sidebar-toggled #main-sidebar { + .page.sidebar-toggled #main-sidebar { display: flex; width: 100vw; } - #app.sidebar-toggled .collapse-sidebar-button, - #app.sidebar-toggled #main-content { + .page.sidebar-toggled .collapse-sidebar-button, + .page.sidebar-toggled .page-content { display: none; } } @@ -458,6 +423,8 @@ } } +@import "./page.css"; + @import "./help-modal.css"; @import "./publish-modal.css"; @import "./report-bug-modal.css"; diff --git a/src/css/page.css b/src/css/page.css new file mode 100644 index 0000000..35a5280 --- /dev/null +++ b/src/css/page.css @@ -0,0 +1,34 @@ +.page { + display: grid; + grid-template-rows: var(--app-header-height) auto; + grid-template-columns: var(--main-sidebar-width) auto; + grid-template-areas: + "app-header app-header" + "main-sidebar page-content"; + transition: grid-template-columns var(--transition-sidebar-collapse-time); + --transition-sidebar-collapse-time: 0.3s; +} + +/* -- Main Content --------------------------------------------------------- */ + +.page-content { + grid-area: page-content; + height: calc(100vh - var(--app-header-height)); + overflow: auto; + scroll-behavior: smooth; + scrollbar-width: auto; + scrollbar-color: var(--color-main-subtle-fg) var(--color-main-bg); +} +.page-content::-webkit-scrollbar { + width: 0.5rem; + height: 0.5rem; +} + +.page-content::-webkit-scrollbar-track { + background: var(--color-main-bg); +} + +.page-content::-webkit-scrollbar-thumb { + background-color: var(--color-main-subtle-fg); + border-radius: var(--border-radius-base); +} From d92ac04d94ea1e33768bc9a540c415ddcdb1f6c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Thu, 9 Dec 2021 13:21:44 -0500 Subject: [PATCH 21/54] Fix error rendering --- src/css/app.css | 6 +++--- src/css/main.css | 1 + src/css/page.css | 24 ++++++++++++++++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/css/app.css b/src/css/app.css index 081685e..55fd87c 100644 --- a/src/css/app.css +++ b/src/css/app.css @@ -1,6 +1,6 @@ -/* -- Application ---------------------------------------------------------- */ +/* -- App Error ---------------------------------------------------------- */ -#app .app-error { +.app-error { display: flex; flex: 1; flex-direction: column; @@ -10,7 +10,7 @@ color: var(--color-main-mg); } -#app .app-error .icon { +.app-error .icon { font-size: 4rem; color: var(--color-main-alert); } diff --git a/src/css/main.css b/src/css/main.css index 76615cb..d70a7f4 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -1,6 +1,7 @@ :root { --size-base: 16px; --app-header-height: 3.5rem; + --page-hero-height: 16rem; --main-sidebar-width: 17rem; --main-sidebar-collapsed-width: 4.5rem; --main-content-width: 45.5rem; diff --git a/src/css/page.css b/src/css/page.css index 35a5280..785de5c 100644 --- a/src/css/page.css +++ b/src/css/page.css @@ -1,12 +1,32 @@ .page { display: grid; + transition: grid-template-columns var(--transition-sidebar-collapse-time); + --transition-sidebar-collapse-time: 0.3s; +} + +.page.sidebar-layout { grid-template-rows: var(--app-header-height) auto; grid-template-columns: var(--main-sidebar-width) auto; grid-template-areas: "app-header app-header" "main-sidebar page-content"; - transition: grid-template-columns var(--transition-sidebar-collapse-time); - --transition-sidebar-collapse-time: 0.3s; +} + +.page.full-layout { + grid-template-rows: var(--app-header-height) auto; + grid-template-columns: auto; + grid-template-areas: + "app-header" + "page-content"; +} + +.page.hero-layout { + grid-template-rows: var(--app-header-height) var(page-hero-height) auto; + grid-template-columns: auto; + grid-template-areas: + "app-header" + "page-hero" + "page-content"; } /* -- Main Content --------------------------------------------------------- */ From cb13e9ae7eee2533196b0c2ef4f005240db18904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Thu, 9 Dec 2021 14:03:04 -0500 Subject: [PATCH 22/54] Duplicate Route into UnisonLocal and UnisonShare --- src/UnisonLocal/App.elm | 2 +- src/UnisonLocal/PreApp.elm | 2 +- src/{ => UnisonLocal}/Route.elm | 2 +- src/UnisonShare/App.elm | 11 +- src/UnisonShare/PreApp.elm | 2 +- src/UnisonShare/Route.elm | 282 +++++++++++++++++++++++++ src/UnisonShare/SidebarContent.elm | 16 -- tests/{ => UnisonLocal}/RouteTests.elm | 4 +- tests/UnisonShare/RouteTests.elm | 239 +++++++++++++++++++++ 9 files changed, 535 insertions(+), 25 deletions(-) rename src/{ => UnisonLocal}/Route.elm (99%) create mode 100644 src/UnisonShare/Route.elm delete mode 100644 src/UnisonShare/SidebarContent.elm rename tests/{ => UnisonLocal}/RouteTests.elm (99%) create mode 100644 tests/UnisonShare/RouteTests.elm diff --git a/src/UnisonLocal/App.elm b/src/UnisonLocal/App.elm index 2faa20f..6b719e6 100644 --- a/src/UnisonLocal/App.elm +++ b/src/UnisonLocal/App.elm @@ -20,7 +20,6 @@ import Namespace exposing (NamespaceDetails) import Perspective exposing (Perspective(..)) import PerspectiveLanding import RemoteData -import Route exposing (Route) import UI import UI.AppHeader as AppHeader import UI.Button as Button @@ -30,6 +29,7 @@ import UI.Modal as Modal import UI.Page as Page import UI.Sidebar as Sidebar import UI.Tooltip as Tooltip +import UnisonLocal.Route as Route exposing (Route) import Url exposing (Url) import Workspace import Workspace.WorkspaceItems as WorkspaceItems diff --git a/src/UnisonLocal/PreApp.elm b/src/UnisonLocal/PreApp.elm index 24cf378..9c2d798 100644 --- a/src/UnisonLocal/PreApp.elm +++ b/src/UnisonLocal/PreApp.elm @@ -7,8 +7,8 @@ import Env exposing (Flags) import Html import Http import Perspective exposing (Perspective, PerspectiveParams) -import Route exposing (Route) import UnisonLocal.App as App +import UnisonLocal.Route as Route exposing (Route) import Url exposing (Url) diff --git a/src/Route.elm b/src/UnisonLocal/Route.elm similarity index 99% rename from src/Route.elm rename to src/UnisonLocal/Route.elm index e3be702..6c00e99 100644 --- a/src/Route.elm +++ b/src/UnisonLocal/Route.elm @@ -1,4 +1,4 @@ -module Route exposing +module UnisonLocal.Route exposing ( Route(..) , fromUrl , navigate diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index 61902dc..1e16601 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -18,7 +18,6 @@ import Namespace exposing (NamespaceDetails) import Perspective exposing (Perspective(..)) import PerspectiveLanding import RemoteData -import Route exposing (Route) import UI import UI.AppHeader as AppHeader import UI.Banner as Banner @@ -29,7 +28,7 @@ import UI.Page as Page import UI.Sidebar as Sidebar import UI.Tooltip as Tooltip import UnisonShare.AppModal as AppModal -import UnisonShare.SidebarContent +import UnisonShare.Route as Route exposing (Route) import Url exposing (Url) import Workspace import Workspace.WorkspaceItems as WorkspaceItems @@ -570,7 +569,13 @@ viewMainSidebar model = sidebarContent = if Perspective.isCodebasePerspective perspective then - UnisonShare.SidebarContent.view changePerspectiveMsg + let + base = + FQN.fromString "unison.base" + in + Sidebar.section "Popular libraries" + [ Sidebar.item (changePerspectiveMsg base) (FQN.toString base) + ] else UI.nothing diff --git a/src/UnisonShare/PreApp.elm b/src/UnisonShare/PreApp.elm index f7c36ff..f974bb2 100644 --- a/src/UnisonShare/PreApp.elm +++ b/src/UnisonShare/PreApp.elm @@ -7,8 +7,8 @@ import Env exposing (Flags) import Html import Http import Perspective exposing (Perspective, PerspectiveParams) -import Route exposing (Route) import UnisonShare.App as App +import UnisonShare.Route as Route exposing (Route) import Url exposing (Url) diff --git a/src/UnisonShare/Route.elm b/src/UnisonShare/Route.elm new file mode 100644 index 0000000..53217aa --- /dev/null +++ b/src/UnisonShare/Route.elm @@ -0,0 +1,282 @@ +module UnisonShare.Route exposing + ( Route(..) + , fromUrl + , navigate + , navigateToCurrentPerspective + , navigateToDefinition + , navigateToLatestCodebase + , navigateToPerspective + , perspectiveParams + , replacePerspective + , toDefinition + , toRoute + , toUrlString + , updatePerspectiveParams + ) + +import Browser.Navigation as Nav +import Definition.Reference exposing (Reference(..)) +import FullyQualifiedName as FQN +import Hash +import HashQualified exposing (HashQualified(..)) +import List.Nonempty as NEL +import Parser exposing ((|.), (|=), Parser, end, oneOf, succeed) +import Perspective exposing (CodebasePerspectiveParam(..), PerspectiveParams(..)) +import Route.Parsers as RP exposing (b, reference, slash) +import Url exposing (Url) +import Url.Builder exposing (relative) + + + +{- + + Routing + ======= + + URL Scheme + ---------- + + Directly on the codebase + /[latest|:codebase-hash]/[namespaces|types|terms]/[:namespace-name|:definition-name|:definition-hash] + + + Within a namespace + /[latest|:codebase-hash]/[namespaces]/[:namespace-name]/-/[types|terms]/[:definition-name|:definition-hash] + + + Relative examples + ----------------- + + Top level of a Codebase: / + Top level of a Codebase: /latest + With namespace context: /latest/namespaces/base/List + Definitions: /latest/[types|terms]/base/List/map + Disambiguated definitions: /latest/[types|terms]/base/List@je2wR6 + Definitions within namespace: /latest/namespaces/base/List/-/[types|terms]/map + Disambiguated definitions within namespace: /latest/namespaces/base/List/-/[types|terms]/map@je2wR6 + + + Absolute examples + ----------------- + + Definitions: /@785shikvuihsdfd/[types|terms]/@jf615sgdkvuihskrt + Disambiguated definitions: /@785shikvuihsdfd/[types|terms]/Nonempty/map@dkqA42 + With namespace context: /@785shikvuihsdfd/namespaces/base/List + Definitions within namespace: /@785shikvuihsdfd/namespaces/base/List/-/[types|terms]/base/List/map + Disambiguated definitions within namespace: /@785shikvuihsdfd/namespaces/base/List/-/[types|terms]/Nonempty/map@dkqA42 + + + Note: @785shikvuihsdfd here refers to the hash of the codebase + +-} + + +type Route + = Perspective PerspectiveParams + | Definition PerspectiveParams Reference + + +updatePerspectiveParams : Route -> PerspectiveParams -> Route +updatePerspectiveParams route params = + case route of + Perspective _ -> + Perspective params + + Definition _ ref -> + Definition params ref + + + +-- PARSER --------------------------------------------------------------------- + + +perspective : Parser Route +perspective = + succeed Perspective |. slash |= RP.perspectiveParams |. end + + +definition : Parser Route +definition = + succeed Definition |. slash |= RP.perspectiveParams |. slash |= reference |. end + + +toRoute : Parser Route +toRoute = + oneOf [ b perspective, b definition ] + + +{-| In environments like Unison Local, the UI is served with a base path + +This means that a route to a definition might look like: + + - "/:some-token/ui/latest/terms/base/List/map" + (where "/:some-token/ui/" is the base path.) + +The base path is determined outside of the Elm app using the tag in the + section of the document. The Browser uses this tag to prefix all links. + +The base path must end in a slash for links to work correctly, but our parser +expects a path to starts with a slash. When parsing the URL we thus pre-process +the path to strip the base path and ensure a slash prefix before we parse. + +-} +fromUrl : String -> Url -> Route +fromUrl basePath url = + let + stripBasePath path = + if basePath == "/" then + path + + else + String.replace basePath "" path + + ensureSlashPrefix path = + if String.startsWith "/" path then + path + + else + "/" ++ path + + parse url_ = + Result.withDefault (Perspective (ByCodebase Relative)) (Parser.run toRoute url_) + in + url + |> .path + |> stripBasePath + |> ensureSlashPrefix + |> parse + + + +-- HELPERS -------------------------------------------------------------------- + + +perspectiveParams : Route -> PerspectiveParams +perspectiveParams route = + case route of + Perspective nsRef -> + nsRef + + Definition nsRef _ -> + nsRef + + + +-- TRANSFORM + + +toDefinition : Route -> Reference -> Route +toDefinition oldRoute ref = + Definition (perspectiveParams oldRoute) ref + + +toUrlString : Route -> String +toUrlString route = + let + hqToPath hq = + case hq of + NameOnly fqn -> + fqn |> FQN.toUrlSegments |> NEL.toList + + HashOnly h -> + [ Hash.toUrlString h ] + + HashQualified fqn h -> + NEL.toList (FQN.toUrlSegments fqn) ++ [ Hash.toUrlString h ] + + namespaceSuffix = + ";" + + -- used to mark the end of a namespace FQN + perspectiveParamsToPath pp includeNamespacesSuffix = + case pp of + ByCodebase Relative -> + [ "latest" ] + + ByCodebase (Absolute hash) -> + [ Hash.toUrlString hash ] + + ByNamespace Relative fqn -> + if includeNamespacesSuffix then + "latest" :: "namespaces" :: NEL.toList (FQN.segments fqn) ++ [ namespaceSuffix ] + + else + "latest" :: "namespaces" :: NEL.toList (FQN.segments fqn) + + -- Currently the model supports Absolute URLs (aka Permalinks), + -- but we don't use it since Unison Share does not support any + -- history, meaning that everytime we deploy Unison Share, the + -- previous versions of the codebase are lost. + -- It's fully intended for this feature to be brought back + ByNamespace (Absolute hash) fqn -> + if includeNamespacesSuffix then + Hash.toUrlString hash :: "namespaces" :: NEL.toList (FQN.segments fqn) ++ [ namespaceSuffix ] + + else + Hash.toUrlString hash :: "namespaces" :: NEL.toList (FQN.segments fqn) + + path = + case route of + Perspective pp -> + perspectiveParamsToPath pp False + + Definition pp ref -> + case ref of + TypeReference hq -> + perspectiveParamsToPath pp True ++ ("types" :: hqToPath hq) + + TermReference hq -> + perspectiveParamsToPath pp True ++ ("terms" :: hqToPath hq) + + AbilityConstructorReference hq -> + perspectiveParamsToPath pp True ++ ("ability-constructors" :: hqToPath hq) + + DataConstructorReference hq -> + perspectiveParamsToPath pp True ++ ("data-constructors" :: hqToPath hq) + in + relative path [] + + + +-- EFFECTS + + +navigate : Nav.Key -> Route -> Cmd msg +navigate navKey route = + route + |> toUrlString + |> Nav.pushUrl navKey + + +navigateToPerspective : Nav.Key -> PerspectiveParams -> Cmd msg +navigateToPerspective navKey perspectiveParams_ = + navigate navKey (Perspective perspectiveParams_) + + +navigateToCurrentPerspective : Nav.Key -> Route -> Cmd msg +navigateToCurrentPerspective navKey oldRoute = + navigateToPerspective navKey (perspectiveParams oldRoute) + + +navigateToLatestCodebase : Nav.Key -> Cmd msg +navigateToLatestCodebase navKey = + navigateToPerspective navKey (ByCodebase Relative) + + +navigateToDefinition : Nav.Key -> Route -> Reference -> Cmd msg +navigateToDefinition navKey currentRoute reference = + navigate navKey (toDefinition currentRoute reference) + + +replacePerspective : Nav.Key -> PerspectiveParams -> Route -> Cmd msg +replacePerspective navKey perspectiveParams_ oldRoute = + let + newRoute = + case oldRoute of + Perspective _ -> + Perspective perspectiveParams_ + + Definition _ ref -> + Definition perspectiveParams_ ref + in + navigate navKey newRoute diff --git a/src/UnisonShare/SidebarContent.elm b/src/UnisonShare/SidebarContent.elm deleted file mode 100644 index 9a587dc..0000000 --- a/src/UnisonShare/SidebarContent.elm +++ /dev/null @@ -1,16 +0,0 @@ -module UnisonShare.SidebarContent exposing (..) - -import FullyQualifiedName as FQN exposing (FQN) -import Html exposing (Html) -import UI.Sidebar as Sidebar - - -view : (FQN -> msg) -> Html msg -view changePerspectiveMsg = - let - base = - FQN.fromString "unison.base" - in - Sidebar.section "Popular libraries" - [ Sidebar.item (changePerspectiveMsg base) (FQN.toString base) - ] diff --git a/tests/RouteTests.elm b/tests/UnisonLocal/RouteTests.elm similarity index 99% rename from tests/RouteTests.elm rename to tests/UnisonLocal/RouteTests.elm index 595e57a..572510c 100644 --- a/tests/RouteTests.elm +++ b/tests/UnisonLocal/RouteTests.elm @@ -1,4 +1,4 @@ -module RouteTests exposing (..) +module UnisonLocal.RouteTests exposing (..) import Definition.Reference as Reference exposing (Reference(..)) import Expect @@ -6,8 +6,8 @@ import FullyQualifiedName as FQN exposing (FQN) import Hash exposing (Hash) import HashQualified as HQ import Perspective exposing (CodebasePerspectiveParam(..), PerspectiveParams(..)) -import Route import Test exposing (..) +import UnisonLocal.Route as Route import Url exposing (Url) diff --git a/tests/UnisonShare/RouteTests.elm b/tests/UnisonShare/RouteTests.elm new file mode 100644 index 0000000..9794295 --- /dev/null +++ b/tests/UnisonShare/RouteTests.elm @@ -0,0 +1,239 @@ +module UnisonShare.RouteTests exposing (..) + +import Definition.Reference as Reference exposing (Reference(..)) +import Expect +import FullyQualifiedName as FQN exposing (FQN) +import Hash exposing (Hash) +import HashQualified as HQ +import Perspective exposing (CodebasePerspectiveParam(..), PerspectiveParams(..)) +import Test exposing (..) +import UnisonShare.Route as Route +import Url exposing (Url) + + +perspectiveRoute : Test +perspectiveRoute = + describe "Route.fromUrl : perspective route" + [ test "Matches root to relative codease perspective" <| + \_ -> + let + url = + mkUrl "/" + + expected = + Route.Perspective (ByCodebase Relative) + in + Expect.equal expected (Route.fromUrl "" url) + , test "Matches a codebase relative perspective" <| + \_ -> + let + url = + mkUrl "/latest" + + expected = + Route.Perspective (ByCodebase Relative) + in + Expect.equal expected (Route.fromUrl "" url) + , test "Matches a codebase relative perspective with namespace" <| + \_ -> + let + url = + mkUrl "/latest/namespaces/base/List" + + expected = + Route.Perspective (ByNamespace Relative (fqn "base.List")) + in + Expect.equal expected (Route.fromUrl "" url) + , test "Matches a codebase absolute perspective" <| + \_ -> + let + url = + mkUrl "/@codebasehash" + + expected = + hash "@codebasehash" + |> Maybe.map (\h -> Route.Perspective (ByCodebase (Absolute h))) + in + Expect.equal expected (Just (Route.fromUrl "" url)) + , test "Matches a codebase absolute perspective with namespace" <| + \_ -> + let + url = + mkUrl "/@codebasehash/namespaces/base/List" + + expected = + hash "@codebasehash" + |> Maybe.map (\h -> Route.Perspective (ByNamespace (Absolute h) (fqn "base.List"))) + in + Expect.equal expected (Just (Route.fromUrl "" url)) + , test "Matches a namespace with special characters" <| + \_ -> + let + url = + mkUrl "/latest/namespaces/base/List/;./%2F/docs" + + expected = + Route.Perspective (ByNamespace Relative (segments [ "base", "List", ".", "/", "docs" ])) + in + Expect.equal expected (Route.fromUrl "" url) + ] + + +definitionRoute : Test +definitionRoute = + describe "Route.fromUrl : definition route" + [ test "Matches a codebase relative and relative definition" <| + \_ -> + let + url = + mkUrl "/latest/terms/base/List/map" + + expected = + Route.Definition (ByCodebase Relative) (Reference.fromString TermReference "base.List.map") + in + Expect.equal expected (Route.fromUrl "" url) + , test "Matches a codebase relative and relative definition within a namespace" <| + \_ -> + let + url = + mkUrl "/latest/namespaces/base/List/;/terms/map" + + expected = + Route.Definition (ByNamespace Relative (fqn "base.List")) (Reference.fromString TermReference "map") + in + Expect.equal expected (Route.fromUrl "" url) + , test "Matches a codebase relative and absolute definition" <| + \_ -> + let + url = + mkUrl "/latest/terms/@definitionhash" + + expected = + Route.Definition (ByCodebase Relative) (Reference.fromString TermReference "#definitionhash") + in + Expect.equal expected (Route.fromUrl "" url) + , test "Matches a codebase relative and absolute definition within a namespace" <| + \_ -> + let + url = + mkUrl "/latest/namespaces/base/List/;/terms/@definitionhash" + + expected = + Route.Definition (ByNamespace Relative (fqn "base.List")) (Reference.fromString TermReference "#definitionhash") + in + Expect.equal expected (Route.fromUrl "" url) + , test "Matches a codebase absolute and relative definition " <| + \_ -> + let + url = + mkUrl "/@codebasehash/terms/base/List/map" + + expected = + hash "@codebasehash" + |> Maybe.map (\h -> Route.Definition (ByCodebase (Absolute h)) (Reference.fromString TermReference "base.List.map")) + in + Expect.equal expected (Just (Route.fromUrl "" url)) + , test "Matches a codebase absolute and relative definition within a namespace" <| + \_ -> + let + url = + mkUrl "/@codebasehash/namespaces/base/List/;/terms/map" + + expected = + hash "@codebasehash" + |> Maybe.map (\h -> Route.Definition (ByNamespace (Absolute h) (fqn "base.List")) (Reference.fromString TermReference "map")) + in + Expect.equal expected (Just (Route.fromUrl "" url)) + , test "Matches a codebase absolute and absolute definition " <| + \_ -> + let + url = + mkUrl "/@codebasehash/terms/@definitionhash" + + expected = + hash "@codebasehash" + |> Maybe.map (\h -> Route.Definition (ByCodebase (Absolute h)) (Reference.fromString TermReference "#definitionhash")) + in + Expect.equal expected (Just (Route.fromUrl "" url)) + , test "Matches a codebase absolute and absolute definition within a namespace" <| + \_ -> + let + url = + mkUrl "/@codebasehash/namespaces/base/List/;/terms/map" + + expected = + hash "@codebasehash" + |> Maybe.map (\h -> Route.Definition (ByNamespace (Absolute h) (fqn "base.List")) (Reference.fromString TermReference "map")) + in + Expect.equal expected (Just (Route.fromUrl "" url)) + , test "Matches a namespace and definition with special characters" <| + \_ -> + let + url = + mkUrl "/latest/namespaces/base/List/;./%2F/docs/;/terms/docs/about/;./and/%2F/doc" + + expected = + Route.Definition + (ByNamespace Relative (segments [ "base", "List", ".", "/", "docs" ])) + (TermReference (HQ.NameOnly (segments [ "docs", "about", ".", "and", "/", "doc" ]))) + in + Expect.equal expected (Route.fromUrl "" url) + ] + + +fromUrlBasePath : Test +fromUrlBasePath = + describe "Route.fromUrl : basePath" + [ test "Matches with a basePath prefix" <| + \_ -> + let + url = + mkUrl "/some-token/ui/latest/terms/@abc123" + + basePath = + "/some-token/ui/" + + expected = + Route.Definition (ByCodebase Relative) (Reference.fromString TermReference "#abc123") + in + Expect.equal expected (Route.fromUrl basePath url) + , test "Matches with a root basePath prefix" <| + \_ -> + let + url = + mkUrl "/latest/terms/@abc123" + + basePath = + "/" + + expected = + Route.Definition (ByCodebase Relative) (Reference.fromString TermReference "#abc123") + in + Expect.equal expected (Route.fromUrl basePath url) + ] + + +segments : List String -> FQN +segments = + FQN.fromList + + +fqn : String -> FQN +fqn = + FQN.fromString + + +hash : String -> Maybe Hash +hash = + Hash.fromUrlString + + +mkUrl : String -> Url +mkUrl path = + { protocol = Url.Https + , host = "unison-lang.org" + , port_ = Just 443 + , path = path + , query = Nothing + , fragment = Nothing + } From 20f788291a8b183ebe508d655d7e25d3b6f09e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 13 Dec 2021 12:47:46 -0500 Subject: [PATCH 23/54] update PR template --- .github/PULL_REQUEST_TEMPLATE.md | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9eb64f0..05977a1 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,14 +1,8 @@ -## Overview -What does this change accomplish and why? -Include a screenshot or GIF of the change if appropriate. +## Problem +Describe the problem this PR intend to solve -## Implementation notes -How does it accomplish it, in broad strokes? +## Solution +Describe the solution this PR takes to solve the problem and any alternative approach considered -## Interesting/controversial decisions -Include anything that you thought twice about, debated, chose arbitrarily, etc. -What could have been done differently, but wasn't? And why? - -## Loose ends -Link to related issues that address things you didn't get to. Stuff you -encountered on the way and decided not to include in this PR. +## Caveats/Notes +Issues that address things you didn't get to or general notes From 00fdbbdd20cc020912f36ecfc437b336c491da7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 13 Dec 2021 12:44:31 -0500 Subject: [PATCH 24/54] Add Catalog Route and render Catalog page Add a new Catalog Route to render the Catalog page on /catalog (this is intended to eventually be the front page of Unison Share) --- package.json | 2 +- src/Project.elm | 2 +- src/UnisonShare/App.elm | 56 ++++++++++++++++++++++++++------ src/UnisonShare/Page/Catalog.elm | 31 +++++++----------- src/UnisonShare/PreApp.elm | 7 +++- src/UnisonShare/Route.elm | 46 +++++++++++++++++++++----- 6 files changed, 104 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index 8e6c9d6..0ac8cb1 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "build": "webpack --mode production --config webpack.prod.js", "clean": "rm -rf dist", - "start": "webpack serve --mode development --port 1234 --config webpack.dev.js", + "start": "echo 'Please use either `npm run start:unisonLocal` or `npm run start:unisonShare`'", "start:unisonLocal": "webpack serve --mode development --port 1234 --config webpack.unisonLocal.dev.js", "start:unisonShare": "webpack serve --mode development --port 1234 --config webpack.unisonShare.dev.js", "test": "elm-test", diff --git a/src/Project.elm b/src/Project.elm index ee282d4..4557b64 100644 --- a/src/Project.elm +++ b/src/Project.elm @@ -14,7 +14,7 @@ type alias Project a = type alias ProjectListing = - Project () + Project {} decodeList : Decode.Decoder (List ProjectListing) diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index 1e16601..ac91f0b 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -28,6 +28,7 @@ import UI.Page as Page import UI.Sidebar as Sidebar import UI.Tooltip as Tooltip import UnisonShare.AppModal as AppModal +import UnisonShare.Page.Catalog as Catalog import UnisonShare.Route as Route exposing (Route) import Url exposing (Url) import Workspace @@ -44,6 +45,7 @@ type alias Model = , codebaseTree : CodebaseTree.Model , workspace : Workspace.Model , perspectiveLanding : PerspectiveLanding.Model + , catalog : Catalog.Model , appModal : AppModal.Model , keyboardShortcut : KeyboardShortcut.Model , env : Env @@ -74,6 +76,9 @@ init env route navKey = |> Maybe.map (Api.perform env.apiBasePath) |> Maybe.withDefault Cmd.none + ( catalog, _ ) = + Catalog.init env + model = { navKey = navKey , route = route @@ -84,6 +89,7 @@ init env route navKey = , keyboardShortcut = KeyboardShortcut.init env.operatingSystem , env = env , sidebarToggled = False + , catalog = catalog } in ( model @@ -110,6 +116,7 @@ type Msg | ToggleSidebar -- sub msgs | AppModalMsg AppModal.Msg + | CatalogMsg Catalog.Msg | WorkspaceMsg Workspace.Msg | PerspectiveLandingMsg PerspectiveLanding.Msg | CodebaseTreeMsg CodebaseTree.Msg @@ -134,6 +141,13 @@ update msg ({ env } as model) = { env | perspective = Perspective.nextFromParams env.perspective params } in case route of + Route.Catalog -> + let + ( catalog, cmd ) = + Catalog.init model.env + in + ( { model | catalog = catalog }, Cmd.map CatalogMsg cmd ) + Route.Definition params ref -> let ( workspace, cmd ) = @@ -204,6 +218,13 @@ update msg ({ env } as model) = in ( newModel, Cmd.batch [ Cmd.map AppModalMsg amCmd, cmd ] ) + CatalogMsg cMsg -> + let + ( catalog, cmd ) = + Catalog.update cMsg model.catalog + in + ( { model | catalog = catalog }, Cmd.map CatalogMsg cmd ) + WorkspaceMsg wMsg -> let ( workspace, wCmd, outMsg ) = @@ -646,30 +667,45 @@ viewAppError error = view : Model -> Browser.Document Msg view model = let - pageContent = + appHeader = + viewAppHeader model + + withSidebar pageContent = + Page.SidebarLayout + { header = viewAppHeader model + , sidebar = viewMainSidebar model + , sidebarToggled = model.sidebarToggled + , content = Page.PageContent [ pageContent ] + } + + page = case model.route of + Route.Catalog -> + let + ( m, _ ) = + Catalog.init model.env + in + -- Html.map CatalogMsg (Catalog.view appHeader m) + Catalog.view appHeader m + Route.Perspective _ -> Html.map PerspectiveLandingMsg (PerspectiveLanding.view model.env model.perspectiveLanding ) + |> withSidebar + |> Page.view Route.Definition _ _ -> Html.map WorkspaceMsg (Workspace.view model.workspace) - - page = - Page.SidebarLayout - { header = viewAppHeader model - , sidebar = viewMainSidebar model - , sidebarToggled = model.sidebarToggled - , content = Page.PageContent [ pageContent ] - } + |> withSidebar + |> Page.view in { title = "Unison Share" , body = [ div [ id "app" ] - [ Page.view page + [ page , Html.map AppModalMsg (AppModal.view model.env model.appModal) ] ] diff --git a/src/UnisonShare/Page/Catalog.elm b/src/UnisonShare/Page/Catalog.elm index 6de9f29..b682fa6 100644 --- a/src/UnisonShare/Page/Catalog.elm +++ b/src/UnisonShare/Page/Catalog.elm @@ -9,6 +9,8 @@ import Http import Project exposing (ProjectListing) import RemoteData exposing (RemoteData(..), WebData) import UI +import UI.AppHeader exposing (AppHeader) +import UI.Page as Page exposing (Page) @@ -88,22 +90,13 @@ projectsToCatalog _ = -- VIEW -viewLoaded : LoadedModel -> Html Msg -viewLoaded _ = - div [] [ text "Catalog" ] - - -view : Model -> Html Msg -view model = - case model of - NotAsked -> - UI.nothing - - Loading -> - UI.nothing - - Failure _ -> - div [ class "" ] [ text "Error..." ] - - Success m -> - viewLoaded m +view : AppHeader msg -> Model -> Html msg +view appHeader _ = + let + page = + Page.FullLayout + { header = appHeader + , content = Page.PageContent [ div [] [ text "Catalog" ] ] + } + in + Page.view page diff --git a/src/UnisonShare/PreApp.elm b/src/UnisonShare/PreApp.elm index f974bb2..c58d988 100644 --- a/src/UnisonShare/PreApp.elm +++ b/src/UnisonShare/PreApp.elm @@ -32,11 +32,16 @@ init flags url navKey = route = Route.fromUrl flags.basePath url + perspectiveParams = + route + |> Route.perspectiveParams + |> Maybe.withDefault (Perspective.ByCodebase Perspective.Relative) + preEnv = { flags = flags , route = route , navKey = navKey - , perspectiveParams = Route.perspectiveParams route + , perspectiveParams = perspectiveParams } perspectiveToAppInit perspective = diff --git a/src/UnisonShare/Route.elm b/src/UnisonShare/Route.elm index 53217aa..f114544 100644 --- a/src/UnisonShare/Route.elm +++ b/src/UnisonShare/Route.elm @@ -22,7 +22,7 @@ import HashQualified exposing (HashQualified(..)) import List.Nonempty as NEL import Parser exposing ((|.), (|=), Parser, end, oneOf, succeed) import Perspective exposing (CodebasePerspectiveParam(..), PerspectiveParams(..)) -import Route.Parsers as RP exposing (b, reference, slash) +import Route.Parsers as RP exposing (b, reference, s, slash) import Url exposing (Url) import Url.Builder exposing (relative) @@ -72,13 +72,17 @@ import Url.Builder exposing (relative) type Route - = Perspective PerspectiveParams + = Catalog + | Perspective PerspectiveParams | Definition PerspectiveParams Reference updatePerspectiveParams : Route -> PerspectiveParams -> Route updatePerspectiveParams route params = case route of + Catalog -> + Catalog + Perspective _ -> Perspective params @@ -90,6 +94,11 @@ updatePerspectiveParams route params = -- PARSER --------------------------------------------------------------------- +catalog : Parser Route +catalog = + succeed Catalog |. slash |. s "catalog" + + perspective : Parser Route perspective = succeed Perspective |. slash |= RP.perspectiveParams |. end @@ -102,7 +111,7 @@ definition = toRoute : Parser Route toRoute = - oneOf [ b perspective, b definition ] + oneOf [ b catalog, b perspective, b definition ] {-| In environments like Unison Local, the UI is served with a base path @@ -151,14 +160,17 @@ fromUrl basePath url = -- HELPERS -------------------------------------------------------------------- -perspectiveParams : Route -> PerspectiveParams +perspectiveParams : Route -> Maybe PerspectiveParams perspectiveParams route = case route of + Catalog -> + Nothing + Perspective nsRef -> - nsRef + Just nsRef Definition nsRef _ -> - nsRef + Just nsRef @@ -167,7 +179,13 @@ perspectiveParams route = toDefinition : Route -> Reference -> Route toDefinition oldRoute ref = - Definition (perspectiveParams oldRoute) ref + let + params = + oldRoute + |> perspectiveParams + |> Maybe.withDefault (ByCodebase Relative) + in + Definition params ref toUrlString : Route -> String @@ -217,6 +235,9 @@ toUrlString route = path = case route of + Catalog -> + [ "catalog" ] + Perspective pp -> perspectiveParamsToPath pp False @@ -255,7 +276,13 @@ navigateToPerspective navKey perspectiveParams_ = navigateToCurrentPerspective : Nav.Key -> Route -> Cmd msg navigateToCurrentPerspective navKey oldRoute = - navigateToPerspective navKey (perspectiveParams oldRoute) + let + params = + oldRoute + |> perspectiveParams + |> Maybe.withDefault (ByCodebase Relative) + in + navigateToPerspective navKey params navigateToLatestCodebase : Nav.Key -> Cmd msg @@ -273,6 +300,9 @@ replacePerspective navKey perspectiveParams_ oldRoute = let newRoute = case oldRoute of + Catalog -> + Catalog + Perspective _ -> Perspective perspectiveParams_ From c41cdf52644847d7f24d43d8b22f4353431bfd0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 13 Dec 2021 15:42:08 -0500 Subject: [PATCH 25/54] Add new Card component --- package.json | 2 +- src/UI/Card.elm | 44 ++++++++++++++++++++++++++++++++ src/UnisonShare/Page/Catalog.elm | 4 +-- src/css/card.css | 15 +++++++++++ src/css/themes/unison/light.css | 9 +++++-- 5 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 src/UI/Card.elm create mode 100644 src/css/card.css diff --git a/package.json b/package.json index 0ac8cb1..9e0bff2 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "start:unisonLocal": "webpack serve --mode development --port 1234 --config webpack.unisonLocal.dev.js", "start:unisonShare": "webpack serve --mode development --port 1234 --config webpack.unisonShare.dev.js", "test": "elm-test", - "review": "elm-review --ignore-files src/UnisonShare/Page/Catalog.elm" + "review": "elm-review --ignore-files src/UI/Card.elm" }, "bugs": { "url": "https://github.com/unisonweb/codebase-ui/issues" diff --git a/src/UI/Card.elm b/src/UI/Card.elm new file mode 100644 index 0000000..1b14757 --- /dev/null +++ b/src/UI/Card.elm @@ -0,0 +1,44 @@ +module UI.Card exposing (..) + +import Html exposing (Html, div, text) +import Html.Attributes exposing (class) + + +type alias Card msg = + { title : Maybe String, items : List (Html msg) } + + +card : List (Html msg) -> Card msg +card items = + { title = Nothing, items = items } + + +titled : String -> List (Html msg) -> Card msg +titled title items = + { title = Just title, items = items } + + +withTitle : String -> Card msg -> Card msg +withTitle title card_ = + { card_ | title = Just title } + + +withItems : List (Html msg) -> Card msg -> Card msg +withItems items card_ = + { card_ | items = items } + + +withItem : Html msg -> Card msg -> Card msg +withItem item card_ = + { card_ | items = card_.items ++ [ item ] } + + +view : Card msg -> Html msg +view card_ = + let + items = + case card_.title of + Just t -> + h3 [ class "card-title" ] [ text t ] :: card_.items + in + div [ class "card" ] items diff --git a/src/UnisonShare/Page/Catalog.elm b/src/UnisonShare/Page/Catalog.elm index b682fa6..c9ecbc9 100644 --- a/src/UnisonShare/Page/Catalog.elm +++ b/src/UnisonShare/Page/Catalog.elm @@ -4,13 +4,11 @@ import Api import Dict exposing (Dict) import Env exposing (Env) import Html exposing (Html, div, text) -import Html.Attributes exposing (class) import Http import Project exposing (ProjectListing) import RemoteData exposing (RemoteData(..), WebData) -import UI import UI.AppHeader exposing (AppHeader) -import UI.Page as Page exposing (Page) +import UI.Page as Page diff --git a/src/css/card.css b/src/css/card.css new file mode 100644 index 0000000..4c65615 --- /dev/null +++ b/src/css/card.css @@ -0,0 +1,15 @@ +.card { + padding: 1rem 1.5rem; + display: flex; + flex-direction: column; + gap: 1rem; + color: var(--color-card-fg); + border-radius: var(--border-radius-base); + background: var(--color-card-bg); +} + +.card .card-title { + color: var(--color-card-title); + text-transform: uppercase; + font-size: var(--font-size-medium); +} diff --git a/src/css/themes/unison/light.css b/src/css/themes/unison/light.css index ed0f0d5..1e2d729 100644 --- a/src/css/themes/unison/light.css +++ b/src/css/themes/unison/light.css @@ -208,7 +208,12 @@ --color-button-danger-hover-fg: var(--color-gray-lighten-100); --color-button-danger-hover-bg: var(--color-pink-2); - /* Badges */ + /* Card */ + --color-card-bg: var(--color-gray-lighten-100); + --color-card-fg: var(--color-gray-darken-30); + --color-card-title: var(--color-gray-lighten-30); + + /* Badge */ --color-badge-fg: var(--color-gray-base); --color-badge-bg: var(--color-gray-lighten-60); --color-badge-border: var(--color-gray-lighten-50); @@ -220,7 +225,7 @@ --color-option-badge-bg: var(--color-gray-darken-30); --color-option-badge-border: var(--color-transparent); - /* Tooltips */ + /* Tooltip */ --color-tooltip-fg: var(--color-gray-lighten-60); --color-tooltip-subtle-fg: var(--color-gray-lighten-20); --color-tooltip-bg: var(--color-gray-darken-30); From c2b9f8cb343d01aca2ce2fa732d00f16c0a06199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Tue, 14 Dec 2021 16:28:04 -0500 Subject: [PATCH 26/54] Add (and style) the basic Catalog page layout * To support the Catalog page, add a unison-share css folder for the catalog page and a new Card module for cards holding each category in the catalog. * Rename `UI.Page` to `UI.PageLayout` to better communicate what that module is for. * Add a way to generate a Route from a Project (exclusively to the Unison Share target) to allow clicking to through to the project page from the catalog --- package.json | 2 +- src/FullyQualifiedName.elm | 12 ++ src/Project.elm | 8 +- src/UI/Card.elm | 5 +- src/UI/{Page.elm => PageLayout.elm} | 16 +-- src/UnisonLocal/App.elm | 31 +++-- src/UnisonShare/App.elm | 33 +++-- src/UnisonShare/Page/Catalog.elm | 154 ++++++++++++++++++++--- src/UnisonShare/Route.elm | 15 +++ src/css/app.css | 2 - src/css/elements.css | 1 + src/css/{ => elements}/card.css | 1 + src/css/main.css | 1 + src/css/{page.css => ui/page-layout.css} | 28 ++++- src/css/unison-share.css | 1 + src/css/unison-share/page/catalog.css | 61 +++++++++ src/img/circle-grid-color.svg | 1 + src/unisonShare.js | 1 + tests/FullyQualifiedNameTests.elm | 32 ++++- 19 files changed, 341 insertions(+), 64 deletions(-) rename src/UI/{Page.elm => PageLayout.elm} (87%) rename src/css/{ => elements}/card.css (93%) rename src/css/{page.css => ui/page-layout.css} (69%) create mode 100644 src/css/unison-share.css create mode 100644 src/css/unison-share/page/catalog.css create mode 100644 src/img/circle-grid-color.svg diff --git a/package.json b/package.json index 9e0bff2..e7dd117 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "start:unisonLocal": "webpack serve --mode development --port 1234 --config webpack.unisonLocal.dev.js", "start:unisonShare": "webpack serve --mode development --port 1234 --config webpack.unisonShare.dev.js", "test": "elm-test", - "review": "elm-review --ignore-files src/UI/Card.elm" + "review": "elm-review" }, "bugs": { "url": "https://github.com/unisonweb/codebase-ui/issues" diff --git a/src/FullyQualifiedName.elm b/src/FullyQualifiedName.elm index 90505c4..1282d47 100644 --- a/src/FullyQualifiedName.elm +++ b/src/FullyQualifiedName.elm @@ -1,6 +1,7 @@ module FullyQualifiedName exposing ( FQN , append + , cons , decode , decodeFromParent , equals @@ -16,6 +17,7 @@ module FullyQualifiedName exposing , namespaceOf , numSegments , segments + , snoc , toString , toUrlSegments , toUrlString @@ -179,6 +181,16 @@ append (FQN a) (FQN b) = FQN (NEL.append a b) +cons : String -> FQN -> FQN +cons s (FQN segments_) = + FQN (NEL.cons s segments_) + + +snoc : FQN -> String -> FQN +snoc (FQN segments_) s = + FQN (NEL.append segments_ (NEL.fromElement s)) + + {-| This is passed through a string as a suffix name can include namespaces like List.map (where the FQN would be base.List.map) diff --git a/src/Project.elm b/src/Project.elm index 4557b64..1ede6e5 100644 --- a/src/Project.elm +++ b/src/Project.elm @@ -1,7 +1,6 @@ module Project exposing (..) import FullyQualifiedName exposing (FQN) -import Hash exposing (Hash) import Json.Decode as Decode @@ -10,13 +9,18 @@ type Owner type alias Project a = - { a | owner : Owner, name : FQN, hash : Hash } + { a | owner : Owner, name : FQN } type alias ProjectListing = Project {} +ownerToString : Owner -> String +ownerToString (Owner o) = + o + + decodeList : Decode.Decoder (List ProjectListing) decodeList = Decode.succeed [] diff --git a/src/UI/Card.elm b/src/UI/Card.elm index 1b14757..edbbc7f 100644 --- a/src/UI/Card.elm +++ b/src/UI/Card.elm @@ -1,6 +1,6 @@ module UI.Card exposing (..) -import Html exposing (Html, div, text) +import Html exposing (Html, div, h3, text) import Html.Attributes exposing (class) @@ -40,5 +40,8 @@ view card_ = case card_.title of Just t -> h3 [ class "card-title" ] [ text t ] :: card_.items + + Nothing -> + card_.items in div [ class "card" ] items diff --git a/src/UI/Page.elm b/src/UI/PageLayout.elm similarity index 87% rename from src/UI/Page.elm rename to src/UI/PageLayout.elm index 7d0183f..271e48f 100644 --- a/src/UI/Page.elm +++ b/src/UI/PageLayout.elm @@ -1,4 +1,4 @@ -module UI.Page exposing (..) +module UI.PageLayout exposing (..) import Html exposing (Html, div, header, section) import Html.Attributes exposing (class, classList) @@ -6,18 +6,18 @@ import UI.AppHeader as AppHeader exposing (AppHeader) import UI.Sidebar as Sidebar -type Hero msg - = Hero (Html msg) +type PageHero msg + = PageHero (Html msg) type PageContent msg = PageContent (List (Html msg)) -type Page msg +type PageLayout msg = HeroLayout { header : AppHeader msg - , hero : Hero msg + , hero : PageHero msg , content : PageContent msg } @@ -34,8 +34,8 @@ type Page msg -- VIEW -viewHero : Hero msg -> Html msg -viewHero (Hero content) = +viewHero : PageHero msg -> Html msg +viewHero (PageHero content) = header [ class "page-hero" ] [ content ] @@ -44,7 +44,7 @@ viewContent (PageContent content) = section [ class "page-content" ] content -view : Page msg -> Html msg +view : PageLayout msg -> Html msg view page = case page of HeroLayout { header, hero, content } -> diff --git a/src/UnisonLocal/App.elm b/src/UnisonLocal/App.elm index 6b719e6..3060fcf 100644 --- a/src/UnisonLocal/App.elm +++ b/src/UnisonLocal/App.elm @@ -26,7 +26,7 @@ import UI.Button as Button import UI.Click as Click exposing (Click(..)) import UI.Icon as Icon import UI.Modal as Modal -import UI.Page as Page +import UI.PageLayout as PageLayout import UI.Sidebar as Sidebar import UI.Tooltip as Tooltip import UnisonLocal.Route as Route exposing (Route) @@ -129,8 +129,15 @@ type Msg update : Msg -> Model -> ( Model, Cmd Msg ) update msg ({ env } as model) = case msg of - LinkClicked _ -> - ( model, Cmd.none ) + LinkClicked urlRequest -> + case urlRequest of + Browser.Internal url -> + ( model, Nav.pushUrl model.navKey (Url.toString url) ) + + -- External links are handled via target blank and never end up + -- here + Browser.External _ -> + ( model, Cmd.none ) UrlChanged url -> let @@ -747,25 +754,25 @@ viewModal model = viewAppLoading : Html msg viewAppLoading = - Page.view - (Page.SidebarLayout + PageLayout.view + (PageLayout.SidebarLayout { header = AppHeader.appHeader (appTitle Nothing) , sidebar = [] , sidebarToggled = False - , content = Page.PageContent [] + , content = PageLayout.PageContent [] } ) viewAppError : Http.Error -> Html msg viewAppError error = - Page.view - (Page.SidebarLayout + PageLayout.view + (PageLayout.SidebarLayout { header = AppHeader.appHeader (appTitle Nothing) , sidebar = [] , sidebarToggled = False , content = - Page.PageContent + PageLayout.PageContent [ div [ class "app-error" ] [ Icon.view Icon.warn , p [ title (Api.errorToString error) ] @@ -792,13 +799,13 @@ view model = Html.map WorkspaceMsg (Workspace.view model.workspace) page = - Page.SidebarLayout + PageLayout.SidebarLayout { header = viewAppHeader model , sidebar = viewMainSidebar model , sidebarToggled = model.sidebarToggled - , content = Page.PageContent [ pageContent ] + , content = PageLayout.PageContent [ pageContent ] } in { title = "Unison Local" - , body = [ div [ id "app" ] [ Page.view page, viewModal model ] ] + , body = [ div [ id "app" ] [ PageLayout.view page, viewModal model ] ] } diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index ac91f0b..fdc7e1b 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -24,7 +24,7 @@ import UI.Banner as Banner import UI.Button as Button import UI.Click as Click exposing (Click(..)) import UI.Icon as Icon -import UI.Page as Page +import UI.PageLayout as PageLayout import UI.Sidebar as Sidebar import UI.Tooltip as Tooltip import UnisonShare.AppModal as AppModal @@ -126,8 +126,15 @@ type Msg update : Msg -> Model -> ( Model, Cmd Msg ) update msg ({ env } as model) = case msg of - LinkClicked _ -> - ( model, Cmd.none ) + LinkClicked urlRequest -> + case urlRequest of + Browser.Internal url -> + ( model, Nav.pushUrl model.navKey (Url.toString url) ) + + -- External links are handled via target blank and never end up + -- here + Browser.External _ -> + ( model, Cmd.none ) UrlChanged url -> let @@ -639,21 +646,21 @@ viewMainSidebar model = viewAppLoading : Html msg viewAppLoading = - Page.view - (Page.FullLayout + PageLayout.view + (PageLayout.FullLayout { header = AppHeader.appHeader (appTitle Nothing) - , content = Page.PageContent [] + , content = PageLayout.PageContent [] } ) viewAppError : Http.Error -> Html msg viewAppError error = - Page.view - (Page.FullLayout + PageLayout.view + (PageLayout.FullLayout { header = AppHeader.appHeader (appTitle Nothing) , content = - Page.PageContent + PageLayout.PageContent [ div [ class "app-error" ] [ Icon.view Icon.warn , p [ title (Api.errorToString error) ] @@ -671,11 +678,11 @@ view model = viewAppHeader model withSidebar pageContent = - Page.SidebarLayout + PageLayout.SidebarLayout { header = viewAppHeader model , sidebar = viewMainSidebar model , sidebarToggled = model.sidebarToggled - , content = Page.PageContent [ pageContent ] + , content = PageLayout.PageContent [ pageContent ] } page = @@ -695,12 +702,12 @@ view model = model.perspectiveLanding ) |> withSidebar - |> Page.view + |> PageLayout.view Route.Definition _ _ -> Html.map WorkspaceMsg (Workspace.view model.workspace) |> withSidebar - |> Page.view + |> PageLayout.view in { title = "Unison Share" , body = diff --git a/src/UnisonShare/Page/Catalog.elm b/src/UnisonShare/Page/Catalog.elm index c9ecbc9..5ade928 100644 --- a/src/UnisonShare/Page/Catalog.elm +++ b/src/UnisonShare/Page/Catalog.elm @@ -1,14 +1,21 @@ module UnisonShare.Page.Catalog exposing (..) -import Api -import Dict exposing (Dict) +-- import Api + import Env exposing (Env) -import Html exposing (Html, div, text) +import FullyQualifiedName as FQN +import Html exposing (Html, a, div, h1, input, strong, text) +import Html.Attributes exposing (class, href, placeholder) import Http import Project exposing (ProjectListing) import RemoteData exposing (RemoteData(..), WebData) +import Set exposing (Set) +import UI import UI.AppHeader exposing (AppHeader) -import UI.Page as Page +import UI.Card as Card +import UI.Icon as Icon +import UI.PageLayout as PageLayout exposing (PageLayout) +import UnisonShare.Route as Route @@ -16,11 +23,11 @@ import UI.Page as Page type Category - = Category String + = Category String (List ProjectListing) type Catalog - = Catalog (Dict Category ProjectListing) + = Catalog (Set Category) type alias LoadedModel = @@ -34,14 +41,27 @@ type alias Model = init : Env -> ( Model, Cmd Msg ) -init env = +init _ = + {- + let + fetchCmd = + Api.projects + |> Api.toRequest Project.decodeList FetchProjectsFinished + |> Api.perform env.apiBasePath + in + ( Loading, fetchCmd ) + -} let - fetchCmd = - Api.projects - |> Api.toRequest Project.decodeList FetchProjectsFinished - |> Api.perform env.apiBasePath + categories = + Set.empty + + model = + Success + { catalog = Catalog categories + , query = "" + } in - ( Loading, fetchCmd ) + ( model, Cmd.none ) @@ -81,20 +101,116 @@ update msg model = projectsToCatalog : List ProjectListing -> Catalog projectsToCatalog _ = - Catalog Dict.empty + Catalog Set.empty -- VIEW +viewProjectListing : ProjectListing -> Html msg +viewProjectListing project = + let + slug = + FQN.cons (Project.ownerToString project.owner) project.name + + url = + project + |> Route.forProject + |> Route.toUrlString + in + a [ class "project-listing", href url ] [ FQN.view slug ] + + +viewCategory : Category -> Html msg +viewCategory (Category category projects) = + let + projectLinks = + projects + |> List.map viewProjectListing + in + Card.titled category projectLinks + |> Card.view + + +viewLoaded : AppHeader msg -> LoadedModel -> PageLayout msg +viewLoaded appHeader _ = + let + content = + [ div [ class "categories" ] + [ viewCategory + (Category "Featured" + [ { owner = Project.Owner "unison", name = FQN.fromString "base" } + , { owner = Project.Owner "unison", name = FQN.fromString "distributed" } + ] + ) + , viewCategory + (Category "Parsers & Text Manipulation" + [ { owner = Project.Owner "rlmark", name = FQN.fromString "parsing" } + , { owner = Project.Owner "stew", name = FQN.fromString "json" } + ] + ) + , viewCategory (Category "Databases" []) + , viewCategory (Category "Datatypes" []) + , viewCategory (Category "Math" []) + , viewCategory (Category "Instrumentation" []) + , viewCategory (Category "Multimedia" []) + , viewCategory + (Category "Networking" + [ { owner = Project.Owner "unison", name = FQN.fromString "http" } + ] + ) + , viewCategory (Category "Utilities" []) + ] + ] + in + PageLayout.HeroLayout + { header = appHeader + , hero = + PageLayout.PageHero + (div [ class "catalog-hero" ] + [ h1 [] + [ div [] + [ strong [ class "explore" ] [ text "Explore" ] + , text ", " + , strong [ class "discover" ] [ text "Discover" ] + , text ", and " + , strong [ class "share" ] [ text "Share" ] + , text " Unison Code" + ] + , div [] [ text "Projects, libraries, documention, terms, and types" ] + ] + , div [ class "catalog-search" ] [ Icon.view Icon.search, input [ placeholder "Search for projects" ] [] ] + ] + ) + , content = PageLayout.PageContent content + } + + +disabledPage : AppHeader msg -> Html msg -> PageLayout msg +disabledPage appHeader content = + PageLayout.HeroLayout + { header = appHeader + , hero = PageLayout.PageHero UI.nothing + , content = PageLayout.PageContent [ content ] + } + + view : AppHeader msg -> Model -> Html msg -view appHeader _ = +view appHeader model = let page = - Page.FullLayout - { header = appHeader - , content = Page.PageContent [ div [] [ text "Catalog" ] ] - } + case model of + NotAsked -> + disabledPage appHeader (div [] []) + + Loading -> + disabledPage appHeader (div [] []) + + Failure _ -> + disabledPage appHeader (div [] []) + + Success m -> + viewLoaded appHeader m in - Page.view page + PageLayout.view page diff --git a/src/UnisonShare/Route.elm b/src/UnisonShare/Route.elm index f114544..ec7df46 100644 --- a/src/UnisonShare/Route.elm +++ b/src/UnisonShare/Route.elm @@ -1,5 +1,6 @@ module UnisonShare.Route exposing ( Route(..) + , forProject , fromUrl , navigate , navigateToCurrentPerspective @@ -22,6 +23,7 @@ import HashQualified exposing (HashQualified(..)) import List.Nonempty as NEL import Parser exposing ((|.), (|=), Parser, end, oneOf, succeed) import Perspective exposing (CodebasePerspectiveParam(..), PerspectiveParams(..)) +import Project exposing (Project) import Route.Parsers as RP exposing (b, reference, s, slash) import Url exposing (Url) import Url.Builder exposing (relative) @@ -174,6 +176,19 @@ perspectiveParams route = +-- Create + + +forProject : Project a -> Route +forProject project_ = + let + fqn = + FQN.cons (Project.ownerToString project_.owner) project_.name + in + Perspective (Perspective.ByNamespace Relative fqn) + + + -- TRANSFORM diff --git a/src/css/app.css b/src/css/app.css index 55fd87c..9edd20c 100644 --- a/src/css/app.css +++ b/src/css/app.css @@ -423,8 +423,6 @@ } } -@import "./page.css"; - @import "./help-modal.css"; @import "./publish-modal.css"; @import "./report-bug-modal.css"; diff --git a/src/css/elements.css b/src/css/elements.css index 2265f2f..3ec1006 100644 --- a/src/css/elements.css +++ b/src/css/elements.css @@ -186,3 +186,4 @@ p { @import "./elements/tooltip.css"; @import "./elements/fold-toggle.css"; @import "./elements/fully-qualified-name.css"; +@import "./elements/card.css"; diff --git a/src/css/card.css b/src/css/elements/card.css similarity index 93% rename from src/css/card.css rename to src/css/elements/card.css index 4c65615..35c6998 100644 --- a/src/css/card.css +++ b/src/css/elements/card.css @@ -12,4 +12,5 @@ color: var(--color-card-title); text-transform: uppercase; font-size: var(--font-size-medium); + font-weight: normal; } diff --git a/src/css/main.css b/src/css/main.css index d70a7f4..57ffb15 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -31,4 +31,5 @@ @import "./animations.css"; @import "./elements.css"; @import "./composites.css"; +@import "./ui/page-layout.css"; @import "./app.css"; diff --git a/src/css/page.css b/src/css/ui/page-layout.css similarity index 69% rename from src/css/page.css rename to src/css/ui/page-layout.css index 785de5c..16385cd 100644 --- a/src/css/page.css +++ b/src/css/ui/page-layout.css @@ -29,12 +29,10 @@ "page-content"; } -/* -- Main Content --------------------------------------------------------- */ +/* -- Page Content --------------------------------------------------------- */ .page-content { grid-area: page-content; - height: calc(100vh - var(--app-header-height)); - overflow: auto; scroll-behavior: smooth; scrollbar-width: auto; scrollbar-color: var(--color-main-subtle-fg) var(--color-main-bg); @@ -52,3 +50,27 @@ background-color: var(--color-main-subtle-fg); border-radius: var(--border-radius-base); } + +/* -- Sidebar -------------------------------------------------------------- */ + +.page.sidebar-layout .page-content { + height: calc(100vh - var(--app-header-height)); + overflow: auto; +} + +/* -- Hero ----------------------------------------------------------------- */ + +.page.hero-layout .page-hero { + position: relative; + display: flex; + align-items: center; + justify-content: center; + background: var(--color-gray-darken-10); + color: var(--color-gray-lighten-100); + height: 16rem; +} + +.page.hero-layout .page-content { + width: 62.5rem; + margin: auto; +} diff --git a/src/css/unison-share.css b/src/css/unison-share.css new file mode 100644 index 0000000..95b3ef1 --- /dev/null +++ b/src/css/unison-share.css @@ -0,0 +1 @@ +@import "./unison-share/page/catalog.css"; diff --git a/src/css/unison-share/page/catalog.css b/src/css/unison-share/page/catalog.css new file mode 100644 index 0000000..4f44104 --- /dev/null +++ b/src/css/unison-share/page/catalog.css @@ -0,0 +1,61 @@ +.catalog-hero h1 { + font-size: 2.375rem; + font-weight: normal; + text-align: center; +} + +.catalog-hero h1 .explore { + color: var(--color-green-4); +} + +.catalog-hero h1 .discover { + color: var(--color-blue-4); +} + +.catalog-hero h1 .share { + color: var(--color-purple-4); +} + +.catalog-hero .catalog-search { + width: 50rem; + height: 3.5rem; + background: var(--color-main-bg); + color: var(--color-main-fg); + border-radius: var(--border-radius-base); + position: absolute; + bottom: -1.75rem; + border: 2px solid var(--color-gray-darken-10); + box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.25); + display: flex; + flex-direction: row; + align-items: center; + padding: 1rem; + left: 50%; + transform: translateX(-50%); +} +.catalog-hero .catalog-search input { + width: 100%; + height: 1.5rem; + margin-left: 0.75rem; + font-size: 1.125rem; +} +.catalog-hero .catalog-search input:focus { + outline: none; +} + +.catalog-hero .catalog-search .icon { + font-size: 1.5rem; + color: var(--color-gray-lighten-30); +} + +.categories { + margin-top: 6.25rem; + display: flex; + flex-direction: row; + flex-flow: row wrap; + gap: 3.125rem; +} + +.categories .card { + width: 18.75rem; +} diff --git a/src/img/circle-grid-color.svg b/src/img/circle-grid-color.svg new file mode 100644 index 0000000..2d60d4e --- /dev/null +++ b/src/img/circle-grid-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/unisonShare.js b/src/unisonShare.js index d695fb7..d1f9ab0 100644 --- a/src/unisonShare.js +++ b/src/unisonShare.js @@ -1,4 +1,5 @@ import "./init"; +import "./css/unison-share.css"; import detectOs from "./detectOs"; import preventDefaultGlobalKeyboardEvents from "./preventDefaultGlobalKeyboardEvents"; import { Elm } from "./UnisonShare.elm"; diff --git a/tests/FullyQualifiedNameTests.elm b/tests/FullyQualifiedNameTests.elm index 8838d22..5dfba05 100644 --- a/tests/FullyQualifiedNameTests.elm +++ b/tests/FullyQualifiedNameTests.elm @@ -6,6 +6,32 @@ import List.Nonempty as NEL import Test exposing (..) +cons : Test +cons = + describe "FullyQualifiedName.cons" + [ test "cons a String to the front of the FQN segments" <| + \_ -> + let + list = + FQN.fromString "List" + in + Expect.equal [ "base", "List" ] (segments (FQN.cons "base" list)) + ] + + +snoc : Test +snoc = + describe "FullyQualifiedName.snoc" + [ test "snoc a String to the end of the FQN segments" <| + \_ -> + let + list = + FQN.fromString "List" + in + Expect.equal [ "List", "map" ] (segments (FQN.snoc list "map")) + ] + + append : Test append = describe "FullyQualifiedName.append" @@ -222,9 +248,9 @@ namespaceOf = ] -namepsace : Test -namepsace = - describe "FullyQualifiedName.namepsace" +namespace : Test +namespace = + describe "FullyQualifiedName.namespace" [ test "removes qualified name" <| \_ -> let From ca30843289f4739d2e9459e4c85ea7b06e37148f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Wed, 15 Dec 2021 12:29:07 -0500 Subject: [PATCH 27/54] Add style and background image to Catalog hero Add a colored grid based background (perhaps later we can animate it) as a background pattern for the Catalog hero. Also update the focused styling of the search input to match the design. --- src/css/app.css | 2 + src/css/main.css | 1 + src/css/themes/unison/colors.css | 2 + src/css/themes/unison/light.css | 5 ++- src/css/unison-share/page/catalog.css | 61 +++++++++++++++++++++++---- src/img/circle-grid-color.svg | 2 +- 6 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/css/app.css b/src/css/app.css index 9edd20c..1a96768 100644 --- a/src/css/app.css +++ b/src/css/app.css @@ -30,6 +30,8 @@ font-size: var(--font-size-medium); z-index: var(--layer-popover); + box-shadow: 0 -1px 0 var(--color-sidebar-border); + --color-main-fg: var(--color-sidebar-fg); --color-main-subtle-fg: var(--color-sidebar-subtle-fg); diff --git a/src/css/main.css b/src/css/main.css index 57ffb15..bc59123 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -8,6 +8,7 @@ --border-radius-base: 0.25rem; /* -- Layers ------------------------------------------------------------- */ + --layer-beneath: 0; --layer-base: 1; --layer-popover: 50; --layer-tooltip: 75; diff --git a/src/css/themes/unison/colors.css b/src/css/themes/unison/colors.css index 90d11f0..592ee8a 100644 --- a/src/css/themes/unison/colors.css +++ b/src/css/themes/unison/colors.css @@ -10,6 +10,7 @@ */ --color-transparent: rgba(255, 255, 255, 0); --color-gray-darken-20-transparent: rgba(45, 46, 53, 0); + --color-gray-darken-10-transparent: rgba(65, 66, 75, 0); /* Brand colors */ --color-brand-bright-red: #ff4756; @@ -52,6 +53,7 @@ --color-blue-3: #9ec5ff; --color-blue-4: #cbe0ff; --color-blue-5: #ecf2fa; + --color-blue-2-25-pct: rgba(85, 149, 255, 0.25); /* Oranges */ --color-orange-1: #ff8800; diff --git a/src/css/themes/unison/light.css b/src/css/themes/unison/light.css index 1e2d729..da85c42 100644 --- a/src/css/themes/unison/light.css +++ b/src/css/themes/unison/light.css @@ -12,6 +12,7 @@ --color-main-subtle-border: var(--color-gray-lighten-50); --color-main-divider: var(--color-gray-lighten-55); --color-main-focus-fg: var(--color-blue-2); + --color-main-focus-outline: var(--color-blue-2-25-pct); --color-main-focus-bg: var(--color-blue-2); --color-main-alert: var(--color-pink-1); --color-main-mark-fg: var(--color-blue-2); @@ -23,7 +24,7 @@ --color-app-header-subtle-fg-em: var(--color-gray-lighten-50); --color-app-header-context-unison-share-fg: var(--color-purple-4); --color-app-header-context-unison-local-fg: var(--color-pink-3); - --color-app-header-border: var(--color-gray-base); + --color-app-header-border: transparent; --color-keyboard-shortcut-key-fg: var(--color-gray-base); --color-keyboard-shortcut-key-bg: var(--color-gray-lighten-50); @@ -35,7 +36,7 @@ --color-sidebar-bg: var(--color-gray-darken-20); /* see color files for why this is needed */ --color-sidebar-bg-transparent: var(--color-gray-darken-20-transparent); - --color-sidebar-border: var(--color-transparent); + --color-sidebar-border: var(--color-gray-base); --color-sidebar-subtle-fg: var(--color-gray-lighten-20); --color-sidebar-subtle-fg-em: var(--color-gray-lighten-30); --color-sidebar-subtle-bg: var(--color-transparent); diff --git a/src/css/unison-share/page/catalog.css b/src/css/unison-share/page/catalog.css index 4f44104..5d7c84a 100644 --- a/src/css/unison-share/page/catalog.css +++ b/src/css/unison-share/page/catalog.css @@ -1,19 +1,57 @@ +.catalog-hero { + --color-catalog-hero-bg: var(--color-gray-darken-10); + --color-catalog-hero-bg-transparent: var(--color-gray-darken-10-transparent); + --color-catalog-hero-explore: var(--color-green-4); + --color-catalog-hero-discover: var(--color-blue-4); + --color-catalog-hero-share: var(--color-purple-4); + + display: flex; + flex: 1; + height: 100%; + background-image: linear-gradient( + to bottom, + var(--color-catalog-hero-bg), + var(--color-catalog-hero-bg-transparent) + ), + url("../../../img/circle-grid-color.svg"); + background-repeat: no-repeat; + background-position: center center; + background-size: cover; + justify-content: center; + align-items: center; +} + +.catalog-hero:after { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + content: " "; + background: var(--color-catalog-hero-bg); + opacity: 0.9; + z-index: var(--layer-beneath); +} + .catalog-hero h1 { + position: relative; + z-index: var(--layer-base); font-size: 2.375rem; font-weight: normal; text-align: center; + margin-bottom: 1.375rem; } .catalog-hero h1 .explore { - color: var(--color-green-4); + color: var(--color-catalog-hero-explore); } .catalog-hero h1 .discover { - color: var(--color-blue-4); + color: var(--color-catalog-hero-discover); } .catalog-hero h1 .share { - color: var(--color-purple-4); + color: var(--color-catalog-hero-share); } .catalog-hero .catalog-search { @@ -24,28 +62,35 @@ border-radius: var(--border-radius-base); position: absolute; bottom: -1.75rem; - border: 2px solid var(--color-gray-darken-10); - box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.25); + border: 2px solid var(--color-catalog-hero-bg); + box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.1); display: flex; flex-direction: row; align-items: center; - padding: 1rem; + padding: 1rem 0 1rem 1rem; left: 50%; transform: translateX(-50%); + z-index: var(--layer-base); + transition: all 0.2s; } .catalog-hero .catalog-search input { width: 100%; - height: 1.5rem; + height: calc(3.5rem - 4px); margin-left: 0.75rem; font-size: 1.125rem; + border-radius: var(--border-radius-base); } .catalog-hero .catalog-search input:focus { outline: none; } +.catalog-hero .catalog-search:focus-within { + border-color: var(--color-main-focus-fg); + box-shadow: 0 0 0 2px var(--color-main-focus-outline); +} .catalog-hero .catalog-search .icon { font-size: 1.5rem; - color: var(--color-gray-lighten-30); + color: var(--color-main-subtle-fg); } .categories { diff --git a/src/img/circle-grid-color.svg b/src/img/circle-grid-color.svg index 2d60d4e..ecc08bf 100644 --- a/src/img/circle-grid-color.svg +++ b/src/img/circle-grid-color.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From aa127fe7949b4c90a72bf3f3bf11b2acbd58848a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Wed, 15 Dec 2021 14:25:22 -0500 Subject: [PATCH 28/54] Move AppHeader back up to App The AppHeader has App specifics, not Page specifics and should not be part of a PageLayout. --- src/UI/PageLayout.elm | 24 +- src/UnisonLocal/App.elm | 53 ++-- src/UnisonShare/App.elm | 54 ++-- src/UnisonShare/Page/Catalog.elm | 37 +-- src/css/app.css | 419 +------------------------------ src/css/ui/page-layout.css | 419 ++++++++++++++++++++++++++++++- 6 files changed, 502 insertions(+), 504 deletions(-) diff --git a/src/UI/PageLayout.elm b/src/UI/PageLayout.elm index 271e48f..1f4e568 100644 --- a/src/UI/PageLayout.elm +++ b/src/UI/PageLayout.elm @@ -2,7 +2,6 @@ module UI.PageLayout exposing (..) import Html exposing (Html, div, header, section) import Html.Attributes exposing (class, classList) -import UI.AppHeader as AppHeader exposing (AppHeader) import UI.Sidebar as Sidebar @@ -16,18 +15,16 @@ type PageContent msg type PageLayout msg = HeroLayout - { header : AppHeader msg - , hero : PageHero msg + { hero : PageHero msg , content : PageContent msg } | SidebarLayout - { header : AppHeader msg - , sidebar : List (Html msg) + { sidebar : List (Html msg) , sidebarToggled : Bool , content : PageContent msg } - | FullLayout { header : AppHeader msg, content : PageContent msg } + | FullLayout { content : PageContent msg } @@ -47,25 +44,22 @@ viewContent (PageContent content) = view : PageLayout msg -> Html msg view page = case page of - HeroLayout { header, hero, content } -> + HeroLayout { hero, content } -> div [ class "page hero-layout" ] - [ AppHeader.view header - , viewHero hero + [ viewHero hero , viewContent content ] - SidebarLayout { header, sidebar, sidebarToggled, content } -> + SidebarLayout { sidebar, sidebarToggled, content } -> div [ class "page sidebar-layout" , classList [ ( "sidebar-toggled", sidebarToggled ) ] ] - [ AppHeader.view header - , Sidebar.view sidebar + [ Sidebar.view sidebar , viewContent content ] - FullLayout { header, content } -> + FullLayout { content } -> div [ class "page full-layout" ] - [ AppHeader.view header - , viewContent content + [ viewContent content ] diff --git a/src/UnisonLocal/App.elm b/src/UnisonLocal/App.elm index 3060fcf..0a3188d 100644 --- a/src/UnisonLocal/App.elm +++ b/src/UnisonLocal/App.elm @@ -754,33 +754,37 @@ viewModal model = viewAppLoading : Html msg viewAppLoading = - PageLayout.view - (PageLayout.SidebarLayout - { header = AppHeader.appHeader (appTitle Nothing) - , sidebar = [] - , sidebarToggled = False - , content = PageLayout.PageContent [] - } - ) + div [ id "app" ] + [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) + , PageLayout.view + (PageLayout.SidebarLayout + { sidebar = [] + , sidebarToggled = False + , content = PageLayout.PageContent [] + } + ) + ] viewAppError : Http.Error -> Html msg viewAppError error = - PageLayout.view - (PageLayout.SidebarLayout - { header = AppHeader.appHeader (appTitle Nothing) - , sidebar = [] - , sidebarToggled = False - , content = - PageLayout.PageContent - [ div [ class "app-error" ] - [ Icon.view Icon.warn - , p [ title (Api.errorToString error) ] - [ text "Unison Local could not be started." ] + div [ id "app" ] + [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) + , PageLayout.view + (PageLayout.SidebarLayout + { sidebar = [] + , sidebarToggled = False + , content = + PageLayout.PageContent + [ div [ class "app-error" ] + [ Icon.view Icon.warn + , p [ title (Api.errorToString error) ] + [ text "Unison Local could not be started." ] + ] ] - ] - } - ) + } + ) + ] view : Model -> Browser.Document Msg @@ -800,12 +804,11 @@ view model = page = PageLayout.SidebarLayout - { header = viewAppHeader model - , sidebar = viewMainSidebar model + { sidebar = viewMainSidebar model , sidebarToggled = model.sidebarToggled , content = PageLayout.PageContent [ pageContent ] } in { title = "Unison Local" - , body = [ div [ id "app" ] [ PageLayout.view page, viewModal model ] ] + , body = [ div [ id "app" ] [ AppHeader.view (viewAppHeader model), PageLayout.view page, viewModal model ] ] } diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index fdc7e1b..f8794a0 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -646,41 +646,43 @@ viewMainSidebar model = viewAppLoading : Html msg viewAppLoading = - PageLayout.view - (PageLayout.FullLayout - { header = AppHeader.appHeader (appTitle Nothing) - , content = PageLayout.PageContent [] - } - ) + div [ id "app" ] + [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) + , PageLayout.view + (PageLayout.FullLayout + { content = PageLayout.PageContent [] } + ) + ] viewAppError : Http.Error -> Html msg viewAppError error = - PageLayout.view - (PageLayout.FullLayout - { header = AppHeader.appHeader (appTitle Nothing) - , content = - PageLayout.PageContent - [ div [ class "app-error" ] - [ Icon.view Icon.warn - , p [ title (Api.errorToString error) ] - [ text "Unison Share could not be started." ] + div [ id "app" ] + [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) + , PageLayout.view + (PageLayout.FullLayout + { content = + PageLayout.PageContent + [ div [ class "app-error" ] + [ Icon.view Icon.warn + , p [ title (Api.errorToString error) ] + [ text "Unison Share could not be started." ] + ] ] - ] - } - ) + } + ) + ] view : Model -> Browser.Document Msg view model = let appHeader = - viewAppHeader model + AppHeader.view (viewAppHeader model) withSidebar pageContent = PageLayout.SidebarLayout - { header = viewAppHeader model - , sidebar = viewMainSidebar model + { sidebar = viewMainSidebar model , sidebarToggled = model.sidebarToggled , content = PageLayout.PageContent [ pageContent ] } @@ -688,12 +690,7 @@ view model = page = case model.route of Route.Catalog -> - let - ( m, _ ) = - Catalog.init model.env - in - -- Html.map CatalogMsg (Catalog.view appHeader m) - Catalog.view appHeader m + Html.map CatalogMsg (Catalog.view model.catalog) Route.Perspective _ -> Html.map PerspectiveLandingMsg @@ -712,7 +709,8 @@ view model = { title = "Unison Share" , body = [ div [ id "app" ] - [ page + [ appHeader + , page , Html.map AppModalMsg (AppModal.view model.env model.appModal) ] ] diff --git a/src/UnisonShare/Page/Catalog.elm b/src/UnisonShare/Page/Catalog.elm index 5ade928..0b9ea8b 100644 --- a/src/UnisonShare/Page/Catalog.elm +++ b/src/UnisonShare/Page/Catalog.elm @@ -6,12 +6,12 @@ import Env exposing (Env) import FullyQualifiedName as FQN import Html exposing (Html, a, div, h1, input, strong, text) import Html.Attributes exposing (class, href, placeholder) +import Html.Events exposing (onInput) import Http import Project exposing (ProjectListing) import RemoteData exposing (RemoteData(..), WebData) import Set exposing (Set) import UI -import UI.AppHeader exposing (AppHeader) import UI.Card as Card import UI.Icon as Icon import UI.PageLayout as PageLayout exposing (PageLayout) @@ -133,8 +133,8 @@ viewCategory (Category category projects) = |> Card.view -viewLoaded : AppHeader msg -> LoadedModel -> PageLayout msg -viewLoaded appHeader _ = +viewLoaded : LoadedModel -> PageLayout Msg +viewLoaded model = let content = [ div [ class "categories" ] @@ -165,8 +165,7 @@ viewLoaded appHeader _ = ] in PageLayout.HeroLayout - { header = appHeader - , hero = + { hero = PageLayout.PageHero (div [ class "catalog-hero" ] [ h1 [] @@ -180,37 +179,43 @@ viewLoaded appHeader _ = ] , div [] [ text "Projects, libraries, documention, terms, and types" ] ] - , div [ class "catalog-search" ] [ Icon.view Icon.search, input [ placeholder "Search for projects" ] [] ] + , div [ class "catalog-search" ] + [ Icon.view Icon.search + , input + [ placeholder "Search for projects" + , onInput UpdateQuery + ] + [] + ] ] ) , content = PageLayout.PageContent content } -disabledPage : AppHeader msg -> Html msg -> PageLayout msg -disabledPage appHeader content = +disabledPage : Html Msg -> PageLayout Msg +disabledPage content = PageLayout.HeroLayout - { header = appHeader - , hero = PageLayout.PageHero UI.nothing + { hero = PageLayout.PageHero UI.nothing , content = PageLayout.PageContent [ content ] } -view : AppHeader msg -> Model -> Html msg -view appHeader model = +view : Model -> Html Msg +view model = let page = case model of NotAsked -> - disabledPage appHeader (div [] []) + disabledPage (div [] []) Loading -> - disabledPage appHeader (div [] []) + disabledPage (div [] []) Failure _ -> - disabledPage appHeader (div [] []) + disabledPage (div [] []) Success m -> - viewLoaded appHeader m + viewLoaded m in PageLayout.view page diff --git a/src/css/app.css b/src/css/app.css index 1a96768..f1b1aaa 100644 --- a/src/css/app.css +++ b/src/css/app.css @@ -1,3 +1,12 @@ +#app { + display: grid; + grid-template-rows: var(--app-header-height) auto; + grid-template-columns: auto; + grid-template-areas: + "app-header" + "page-layout"; +} + /* -- App Error ---------------------------------------------------------- */ .app-error { @@ -15,416 +24,6 @@ color: var(--color-main-alert); } -/* -- Main Sidebar --------------------------------------------------------- */ - -#main-sidebar { - grid-area: main-sidebar; - display: flex; - flex-direction: column; - position: relative; - overflow: visible; - height: calc(100vh - var(--app-header-height)); - background: var(--color-sidebar-bg); - color: var(--color-sidebar-fg); - border-right: 1px solid var(--color-sidebar-border); - font-size: var(--font-size-medium); - z-index: var(--layer-popover); - - box-shadow: 0 -1px 0 var(--color-sidebar-border); - - --color-main-fg: var(--color-sidebar-fg); - --color-main-subtle-fg: var(--color-sidebar-subtle-fg); - - --color-button-default-fg: var(--color-sidebar-button-default-fg); - --color-button-default-bg: var(--color-sidebar-button-default-bg); - --color-button-default-hover-fg: var(--color-sidebar-button-default-hover-fg); - --color-button-default-hover-bg: var(--color-sidebar-button-default-hover-bg); - - --color-tooltip-fg: var(--color-sidebar-tooltip-fg); - --color-tooltip-bg: var(--color-sidebar-tooltip-bg); - --color-tooltip-border: var(--color-sidebar-tooltip-border); - - --color-main-divider: var(--color-sidebar-divider); -} - -#main-sidebar a:hover { - text-decoration: none; -} - -#main-sidebar .expanded-content { - display: flex; - flex-direction: column; - height: calc(100vh - var(--app-header-height)); - opacity: 1; - transition: all var(--transition-sidebar-collapse-time); - transition-delay: var(--transition-sidebar-collapse-time); -} - -#main-sidebar .sidebar-scroll-area { - display: flex; - flex-direction: column; - overflow-y: auto; - overflow-x: hidden; - height: calc(100vh - var(--app-header-height)); - scrollbar-width: thin; - scrollbar-color: var(--color-sidebar-subtle-fg) var(--color-sidebar-bg); - padding: 0rem 1rem 1rem 1.5rem; -} - -#main-sidebar .sidebar-scroll-area::-webkit-scrollbar { - width: 0.4rem; - height: 0.4rem; -} - -#main-sidebar .sidebar-scroll-area::-webkit-scrollbar-track { - background: var(--color-sidebar-bg); -} - -#main-sidebar .sidebar-scroll-area::-webkit-scrollbar-thumb { - background-color: var(--color-sidebar-subtle-fg); - border-radius: 0.2rem; -} - -#main-sidebar .loading-placeholder { - background: var(--color-sidebar-subtle-fg); - opacity: 0.5; -} - -#main-sidebar .sidebar-section { - margin-top: 1.5rem; -} - -#main-sidebar .sidebar-section:first-child { - margin-top: 1rem; -} - -#main-sidebar .sidebar-section-title { - font-size: var(--font-size-medium); - font-weight: normal; - color: var(--color-sidebar-subtle-fg); - text-transform: uppercase; - font-size: 0.75rem; - height: 1.875rem; - display: flex; - align-items: center; - white-space: nowrap; -} - -/* TODO: Consolidate with codebase-tree .node */ - -#main-sidebar .sidebar-item { - margin-left: -0.5rem; - display: flex; - user-select: none; - align-items: center; - border-radius: var(--border-radius-base); - padding-left: 0.5rem; - margin-bottom: 0.125rem; - height: 1.875rem; -} - -#main-sidebar .sidebar-item > label { - color: var(--color-sidebar-fg); - transition: all 0.2s; - cursor: pointer; - overflow: hidden; - text-overflow: ellipsis; - line-height: 1.875; -} - -#main-sidebar .sidebar-item:hover { - background: var(--color-sidebar-focus-bg); - text-decoration: none; -} - -#main-sidebar .divider { - margin: 0; -} - -/* -- Collapsing ----------------------------------------------------------- */ - -#main-sidebar .collapse-sidebar-button { - position: absolute; - left: var(--main-sidebar-width); - top: 1rem; - z-index: var(--layer-popover); - opacity: 0; - transition: opacity 0.2s !important; -} - -#app:not(.sidebar-toggled) .collapse-sidebar-button .button { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - padding-left: 0.2rem; - background: var(--color-sidebar-bg); -} - -#main-sidebar:not(.sidebar-toggled):hover .collapse-sidebar-button { - opacity: 1; -} - -#main-sidebar .collapsed-content { - transform: translateX(-17rem); - width: var(--main-sidebar-collapsed-width); - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - opacity: 0; - transition: all var(--transition-sidebar-collapse-time) ease-in-out; - position: absolute; - bottom: 1rem; - gap: 1.5rem; -} - -#main-sidebar .collapsed-content .keyboard-shortcut:hover .key { - color: var(--color-sidebar-keyboard-shortcut-hover-key-fg); -} - -#main-sidebar .sidebar-header { - padding: 1rem 1.5rem 0 1.5rem; - display: flex; - flex-direction: column; - position: relative; - gap: 1.5rem; - margin-bottom: 0.5rem; -} - -#main-sidebar .sidebar-header:after { - position: absolute; - left: 1.5rem; - right: 1.5rem; - bottom: -2rem; - height: 1.75rem; - content: ""; - background: linear-gradient( - var(--color-sidebar-bg), - var(--color-sidebar-bg), - var(--color-sidebar-bg-transparent) - ); -} - -#main-sidebar .sidebar-header .namespace-slug { - position: relative; -} - -#main-sidebar .sidebar-header .is-overflowing .namespace-slug:after { - position: absolute; - top: 0; - right: -1.5rem; - bottom: 0; - content: ""; - width: 1.5rem; - background: linear-gradient( - 90deg, - var(--color-sidebar-bg), - var(--color-sidebar-bg), - var(--color-sidebar-bg-transparent) - ); -} - -#main-sidebar .sidebar-header .namespace { - display: inline-flex; - color: var(--color-sidebar-fg-em); - font-size: 1rem; - font-weight: 500; - height: 1.5rem; - overflow: hidden; - white-space: nowrap; - text-align: right; - flex-direction: row-reverse; -} - -#main-sidebar .sidebar-header-item { - display: flex; - flex: 1; - flex-direction: row; - user-select: none; - align-items: center; - border-radius: var(--border-radius-base); - height: 1.875rem; - gap: 0.75rem; -} - -#main-sidebar .sidebar-header-item .button { - width: 100%; -} - -#main-sidebar . { - margin-bottom: 0; -} - -/* -- Main Sidebar Unison Submenu ----------------------------------------------------- */ - -#main-sidebar .sidebar-unison-submenu { - color: var(--color-sidebar-subtle-fg); - width: 1.5rem; - height: 1.5rem; -} - -#main-sidebar .sidebar-unison-submenu:hover { - color: var(--color-sidebar-focus-fg); -} - -#main-sidebar nav a.tooltip-menu-item { - margin-left: 0; -} - -/* -- Main Sidebar Nav ----------------------------------------------------- */ - -#main-sidebar nav { - display: flex; - flex-direction: column; - justify-self: flex-end; - flex: none; - margin-top: auto; - padding-top: 1.5rem; -} - -#main-sidebar nav a { - height: 1.5rem; - display: flex; - align-items: center; - transition: all 0.2s; - padding-left: 0.5rem; - margin-left: -0.5rem; - border-radius: var(--border-radius-base); - white-space: nowrap; -} - -#main-sidebar nav a, -#main-sidebar nav .icon { - color: var(--color-sidebar-subtle-fg); -} - -#main-sidebar nav a:hover, -#main-sidebar nav a:hover .icon { - color: var(--color-sidebar-fg); - text-decoration: none; -} - -#main-sidebar .show-keyboard-shortcuts { - position: relative; - line-height: 1; - display: flex; - align-items: center; - font-weight: bold; - color: var(--color-sidebar-fg); - cursor: pointer; - height: 1.875rem; - margin-top: 1.5rem; -} - -#main-sidebar .show-keyboard-shortcuts:hover { - text-decoration: none; -} - -#main-sidebar .show-keyboard-shortcuts .keyboard-shortcut { - justify-self: flex-end; - margin-left: auto; - margin-right: 0.1875rem; -} - -#main-sidebar .keyboard-shortcut .key { - color: var(--color-sidebar-keyboard-shortcut-key-fg); - background: var(--color-sidebar-keyboard-shortcut-key-bg); - font-weight: normal; -} - -#main-sidebar .show-keyboard-shortcuts:hover { - background: var(--color-sidebar-focus-bg); -} -#main-sidebar .show-keyboard-shortcuts:hover .key { - color: var(--color-sidebar-keyboard-shortcut-hover-key-fg); -} - -/* -- Responsive ----------------------------------------------------------- */ - -@media only screen and (min-width: 1025px) { - .page.sidebar-toggled { - --main-sidebar-width: var(--main-sidebar-collapsed-width); - } - - .page.sidebar-toggled .collapse-sidebar-button { - animation: collapse-sidebar-button var(--transition-sidebar-collapse-time) - ease-in-out; - transition: none; - animation-fill-mode: forwards; - } - - .page.sidebar-toggled #main-sidebar { - overflow: visible; - } - - .page.sidebar-toggled #main-sidebar .collapsed-content { - opacity: 1; - transform: translateX(0); - } - - .page.sidebar-toggled #main-sidebar .expanded-content { - transition: opacity; - transition-duration: var(--transition-sidebar-collapse-time) * 1.1; - transition-delay: 0; - opacity: 0; - overflow: hidden; - } - - .page.sidebar-toggled .sidebar-scroll-area { - overflow: hidden; - } -} - -@media only screen and (max-width: 1024px) { - #app { - grid-template-rows: 3.5rem auto; - grid-template-columns: auto auto; - grid-template-areas: - "app-header app-header" - "page-content page-content"; - } - - #main-sidebar { - display: none; - } - - .page-content { - width: 100vw; - } - - .page.sidebar-toggled { - grid-template-rows: 3.5rem auto; - grid-template-columns: auto auto; - grid-template-areas: - "app-header app-header" - "main-sidebar main-sidebar"; - } - - .page.sidebar-toggled #main-sidebar { - display: flex; - width: 100vw; - } - - .page.sidebar-toggled .collapse-sidebar-button, - .page.sidebar-toggled .page-content { - display: none; - } -} - -/* collapse animations */ -@keyframes collapse-sidebar-button { - 0% { - opacity: 0; - transform: translateX(-17rem); - } - 50% { - opacity: 0.5; - } - 100% { - opacity: 1; - transform: translateX(-3.15rem); - } -} - @import "./help-modal.css"; @import "./publish-modal.css"; @import "./report-bug-modal.css"; diff --git a/src/css/ui/page-layout.css b/src/css/ui/page-layout.css index 16385cd..657118d 100644 --- a/src/css/ui/page-layout.css +++ b/src/css/ui/page-layout.css @@ -1,30 +1,26 @@ .page { + grid-area: page-layout; display: grid; transition: grid-template-columns var(--transition-sidebar-collapse-time); --transition-sidebar-collapse-time: 0.3s; } .page.sidebar-layout { - grid-template-rows: var(--app-header-height) auto; + grid-template-rows: auto; grid-template-columns: var(--main-sidebar-width) auto; - grid-template-areas: - "app-header app-header" - "main-sidebar page-content"; + grid-template-areas: "main-sidebar page-content"; } .page.full-layout { - grid-template-rows: var(--app-header-height) auto; + grid-template-rows: auto; grid-template-columns: auto; - grid-template-areas: - "app-header" - "page-content"; + grid-template-areas: "page-content"; } .page.hero-layout { - grid-template-rows: var(--app-header-height) var(page-hero-height) auto; + grid-template-rows: var(--page-hero-height) auto; grid-template-columns: auto; grid-template-areas: - "app-header" "page-hero" "page-content"; } @@ -58,6 +54,326 @@ overflow: auto; } +#main-sidebar { + grid-area: main-sidebar; + display: flex; + flex-direction: column; + position: relative; + overflow: visible; + height: calc(100vh - var(--app-header-height)); + background: var(--color-sidebar-bg); + color: var(--color-sidebar-fg); + border-right: 1px solid var(--color-sidebar-border); + box-shadow: 0 -1px 0 var(--color-sidebar-border); + font-size: var(--font-size-medium); + z-index: var(--layer-popover); + + --color-main-fg: var(--color-sidebar-fg); + --color-main-subtle-fg: var(--color-sidebar-subtle-fg); + + --color-button-default-fg: var(--color-sidebar-button-default-fg); + --color-button-default-bg: var(--color-sidebar-button-default-bg); + --color-button-default-hover-fg: var(--color-sidebar-button-default-hover-fg); + --color-button-default-hover-bg: var(--color-sidebar-button-default-hover-bg); + + --color-tooltip-fg: var(--color-sidebar-tooltip-fg); + --color-tooltip-bg: var(--color-sidebar-tooltip-bg); + --color-tooltip-border: var(--color-sidebar-tooltip-border); + + --color-main-divider: var(--color-sidebar-divider); +} + +#main-sidebar a:hover { + text-decoration: none; +} + +#main-sidebar .expanded-content { + display: flex; + flex-direction: column; + height: calc(100vh - var(--app-header-height)); + opacity: 1; + transition: all var(--transition-sidebar-collapse-time); + transition-delay: var(--transition-sidebar-collapse-time); +} + +#main-sidebar .sidebar-scroll-area { + display: flex; + flex-direction: column; + overflow-y: auto; + overflow-x: hidden; + height: calc(100vh - var(--app-header-height)); + scrollbar-width: thin; + scrollbar-color: var(--color-sidebar-subtle-fg) var(--color-sidebar-bg); + padding: 0rem 1rem 1rem 1.5rem; +} + +#main-sidebar .sidebar-scroll-area::-webkit-scrollbar { + width: 0.4rem; + height: 0.4rem; +} + +#main-sidebar .sidebar-scroll-area::-webkit-scrollbar-track { + background: var(--color-sidebar-bg); +} + +#main-sidebar .sidebar-scroll-area::-webkit-scrollbar-thumb { + background-color: var(--color-sidebar-subtle-fg); + border-radius: 0.2rem; +} + +#main-sidebar .loading-placeholder { + background: var(--color-sidebar-subtle-fg); + opacity: 0.5; +} + +#main-sidebar .sidebar-section { + margin-top: 1.5rem; +} + +#main-sidebar .sidebar-section:first-child { + margin-top: 1rem; +} + +#main-sidebar .sidebar-section-title { + font-size: var(--font-size-medium); + font-weight: normal; + color: var(--color-sidebar-subtle-fg); + text-transform: uppercase; + font-size: 0.75rem; + height: 1.875rem; + display: flex; + align-items: center; + white-space: nowrap; +} + +/* TODO: Consolidate with codebase-tree .node */ + +#main-sidebar .sidebar-item { + margin-left: -0.5rem; + display: flex; + user-select: none; + align-items: center; + border-radius: var(--border-radius-base); + padding-left: 0.5rem; + margin-bottom: 0.125rem; + height: 1.875rem; +} + +#main-sidebar .sidebar-item > label { + color: var(--color-sidebar-fg); + transition: all 0.2s; + cursor: pointer; + overflow: hidden; + text-overflow: ellipsis; + line-height: 1.875; +} + +#main-sidebar .sidebar-item:hover { + background: var(--color-sidebar-focus-bg); + text-decoration: none; +} + +#main-sidebar .divider { + margin: 0; +} + +/* -- Collapsing ----------------------------------------------------------- */ + +#main-sidebar .collapse-sidebar-button { + position: absolute; + left: var(--main-sidebar-width); + top: 1rem; + z-index: var(--layer-popover); + opacity: 0; + transition: opacity 0.2s !important; +} + +#app:not(.sidebar-toggled) .collapse-sidebar-button .button { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + padding-left: 0.2rem; + background: var(--color-sidebar-bg); +} + +#main-sidebar:not(.sidebar-toggled):hover .collapse-sidebar-button { + opacity: 1; +} + +#main-sidebar .collapsed-content { + transform: translateX(-17rem); + width: var(--main-sidebar-collapsed-width); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + opacity: 0; + transition: all var(--transition-sidebar-collapse-time) ease-in-out; + position: absolute; + bottom: 1rem; + gap: 1.5rem; +} + +#main-sidebar .collapsed-content .keyboard-shortcut:hover .key { + color: var(--color-sidebar-keyboard-shortcut-hover-key-fg); +} + +#main-sidebar .sidebar-header { + padding: 1rem 1.5rem 0 1.5rem; + display: flex; + flex-direction: column; + position: relative; + gap: 1.5rem; + margin-bottom: 0.5rem; +} + +#main-sidebar .sidebar-header:after { + position: absolute; + left: 1.5rem; + right: 1.5rem; + bottom: -2rem; + height: 1.75rem; + content: ""; + background: linear-gradient( + var(--color-sidebar-bg), + var(--color-sidebar-bg), + var(--color-sidebar-bg-transparent) + ); +} + +#main-sidebar .sidebar-header .namespace-slug { + position: relative; +} + +#main-sidebar .sidebar-header .is-overflowing .namespace-slug:after { + position: absolute; + top: 0; + right: -1.5rem; + bottom: 0; + content: ""; + width: 1.5rem; + background: linear-gradient( + 90deg, + var(--color-sidebar-bg), + var(--color-sidebar-bg), + var(--color-sidebar-bg-transparent) + ); +} + +#main-sidebar .sidebar-header .namespace { + display: inline-flex; + color: var(--color-sidebar-fg-em); + font-size: 1rem; + font-weight: 500; + height: 1.5rem; + overflow: hidden; + white-space: nowrap; + text-align: right; + flex-direction: row-reverse; +} + +#main-sidebar .sidebar-header-item { + display: flex; + flex: 1; + flex-direction: row; + user-select: none; + align-items: center; + border-radius: var(--border-radius-base); + height: 1.875rem; + gap: 0.75rem; +} + +#main-sidebar .sidebar-header-item .button { + width: 100%; +} + +#main-sidebar . { + margin-bottom: 0; +} + +/* -- Main Sidebar Unison Submenu ----------------------------------------------------- */ + +#main-sidebar .sidebar-unison-submenu { + color: var(--color-sidebar-subtle-fg); + width: 1.5rem; + height: 1.5rem; +} + +#main-sidebar .sidebar-unison-submenu:hover { + color: var(--color-sidebar-focus-fg); +} + +#main-sidebar nav a.tooltip-menu-item { + margin-left: 0; +} + +/* -- Main Sidebar Nav ----------------------------------------------------- */ + +#main-sidebar nav { + display: flex; + flex-direction: column; + justify-self: flex-end; + flex: none; + margin-top: auto; + padding-top: 1.5rem; +} + +#main-sidebar nav a { + height: 1.5rem; + display: flex; + align-items: center; + transition: all 0.2s; + padding-left: 0.5rem; + margin-left: -0.5rem; + border-radius: var(--border-radius-base); + white-space: nowrap; +} + +#main-sidebar nav a, +#main-sidebar nav .icon { + color: var(--color-sidebar-subtle-fg); +} + +#main-sidebar nav a:hover, +#main-sidebar nav a:hover .icon { + color: var(--color-sidebar-fg); + text-decoration: none; +} + +#main-sidebar .show-keyboard-shortcuts { + position: relative; + line-height: 1; + display: flex; + align-items: center; + font-weight: bold; + color: var(--color-sidebar-fg); + cursor: pointer; + height: 1.875rem; + margin-top: 1.5rem; +} + +#main-sidebar .show-keyboard-shortcuts:hover { + text-decoration: none; +} + +#main-sidebar .show-keyboard-shortcuts .keyboard-shortcut { + justify-self: flex-end; + margin-left: auto; + margin-right: 0.1875rem; +} + +#main-sidebar .keyboard-shortcut .key { + color: var(--color-sidebar-keyboard-shortcut-key-fg); + background: var(--color-sidebar-keyboard-shortcut-key-bg); + font-weight: normal; +} + +#main-sidebar .show-keyboard-shortcuts:hover { + background: var(--color-sidebar-focus-bg); +} +#main-sidebar .show-keyboard-shortcuts:hover .key { + color: var(--color-sidebar-keyboard-shortcut-hover-key-fg); +} + /* -- Hero ----------------------------------------------------------------- */ .page.hero-layout .page-hero { @@ -74,3 +390,86 @@ width: 62.5rem; margin: auto; } + +/* -- Responsive ----------------------------------------------------------- */ + +@media only screen and (min-width: 1025px) { + .page.sidebar-toggled { + --main-sidebar-width: var(--main-sidebar-collapsed-width); + } + + .page.sidebar-toggled .collapse-sidebar-button { + animation: collapse-sidebar-button var(--transition-sidebar-collapse-time) + ease-in-out; + transition: none; + animation-fill-mode: forwards; + } + + .page.sidebar-toggled #main-sidebar { + overflow: visible; + } + + .page.sidebar-toggled #main-sidebar .collapsed-content { + opacity: 1; + transform: translateX(0); + } + + .page.sidebar-toggled #main-sidebar .expanded-content { + transition: opacity; + transition-duration: var(--transition-sidebar-collapse-time) * 1.1; + transition-delay: 0; + opacity: 0; + overflow: hidden; + } + + .page.sidebar-toggled .sidebar-scroll-area { + overflow: hidden; + } +} + +/* collapse animations */ +@keyframes collapse-sidebar-button { + 0% { + opacity: 0; + transform: translateX(-17rem); + } + 50% { + opacity: 0.5; + } + 100% { + opacity: 1; + transform: translateX(-3.15rem); + } +} + +@media only screen and (max-width: 1024px) { + .page.sidebar-layout { + grid-template-rows: auto; + grid-template-columns: auto; + grid-template-areas: "page-content"; + } + + #main-sidebar { + display: none; + } + + .page-content { + width: 100vw; + } + + .page.sidebar-layout.sidebar-toggled { + grid-template-rows: auto; + grid-template-columns: auto auto; + grid-template-areas: "main-sidebar main-sidebar"; + } + + .page.sidebar-toggled #main-sidebar { + display: flex; + width: 100vw; + } + + .page.sidebar-toggled .collapse-sidebar-button, + .page.sidebar-toggled .page-content { + display: none; + } +} From adac694d19668c36e1d0f49d5b1999961c079f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Thu, 16 Dec 2021 10:53:00 -0500 Subject: [PATCH 29/54] Catalog: Fetch catalog doc and projects and render Fetch the full catalog for the Catalog page by fetching the special catalog doc and parsing it into a CatalogMask which is used to categorize projects which are now also fetched. With the data in hand the page renders a series of `UI.Card` components for each category with their projects. Note that the categories are not currently sorted, but we'll want them to be in the future. --- src/Api.elm | 52 ++++++- src/Hash.elm | 9 ++ src/Project.elm | 35 ++++- src/UnisonShare/App.elm | 5 +- src/UnisonShare/Catalog.elm | 84 +++++++++++ src/UnisonShare/Catalog/CatalogMask.elm | 132 +++++++++++++++++ src/UnisonShare/Page/Catalog.elm | 113 +++++--------- tests/ProjectTests.elm | 30 ++++ .../UnisonShare/Catalog/CatalogMaskTests.elm | 114 ++++++++++++++ tests/UnisonShare/CatalogTests.elm | 140 ++++++++++++++++++ 10 files changed, 627 insertions(+), 87 deletions(-) create mode 100644 src/UnisonShare/Catalog.elm create mode 100644 src/UnisonShare/Catalog/CatalogMask.elm create mode 100644 tests/ProjectTests.elm create mode 100644 tests/UnisonShare/Catalog/CatalogMaskTests.elm create mode 100644 tests/UnisonShare/CatalogTests.elm diff --git a/src/Api.elm b/src/Api.elm index 1ec8794..e0e435e 100644 --- a/src/Api.elm +++ b/src/Api.elm @@ -10,6 +10,7 @@ module Api exposing , perform , projects , toRequest + , toTask , toUrl ) @@ -20,6 +21,7 @@ import Json.Decode as Decode import Perspective exposing (Perspective(..)) import Regex import Syntax +import Task exposing (Task) import Url.Builder exposing (QueryParameter, absolute, int, string) @@ -31,6 +33,11 @@ type Endpoint = Endpoint (List String) (List QueryParameter) +toUrl : ApiBasePath -> Endpoint -> String +toUrl (ApiBasePath basePath) (Endpoint paths queryParams) = + absolute (basePath ++ paths) queryParams + + codebaseHash : Endpoint codebaseHash = Endpoint [ "list" ] [ string "namespace" "." ] @@ -107,11 +114,6 @@ type ApiRequest a msg = ApiRequest Endpoint (Decode.Decoder a) (Result Http.Error a -> msg) -toUrl : ApiBasePath -> Endpoint -> String -toUrl (ApiBasePath basePath) (Endpoint paths queryParams) = - absolute (basePath ++ paths) queryParams - - toRequest : Decode.Decoder a -> (Result Http.Error a -> msg) -> Endpoint -> ApiRequest a msg toRequest decoder toMsg endpoint = ApiRequest endpoint decoder toMsg @@ -126,6 +128,46 @@ perform basePath (ApiRequest endpoint decoder toMsg) = +--- TASK ---------------------------------------------------------------------- + + +{-| TODO Perhaps this API should be merged into ApiRequest fully?? | +-} +toTask : ApiBasePath -> Decode.Decoder a -> Endpoint -> Task Http.Error a +toTask basePath decoder endpoint = + Http.task + { method = "GET" + , headers = [] + , url = toUrl basePath endpoint + , body = Http.emptyBody + , resolver = Http.stringResolver (httpJsonBodyResolver decoder) + , timeout = Nothing + } + + +httpJsonBodyResolver : Decode.Decoder a -> Http.Response String -> Result Http.Error a +httpJsonBodyResolver decoder resp = + case resp of + Http.GoodStatus_ _ s -> + Decode.decodeString decoder s + |> Result.mapError (Decode.errorToString >> Http.BadBody) + + Http.BadUrl_ s -> + Err (Http.BadUrl s) + + Http.Timeout_ -> + Err Http.Timeout + + Http.NetworkError_ -> + Err Http.NetworkError + + Http.BadStatus_ m s -> + Decode.decodeString decoder s + -- just trying; if our decoder understands the response body, great + |> Result.mapError (\_ -> Http.BadStatus m.statusCode) + + + -- ERROR ---------------------------------------------------------------------- diff --git a/src/Hash.elm b/src/Hash.elm index f79bbbd..61de779 100644 --- a/src/Hash.elm +++ b/src/Hash.elm @@ -13,6 +13,7 @@ module Hash exposing , toShortString , toString , toUrlString + , unsafeFromString , urlParser , urlPrefix ) @@ -94,6 +95,14 @@ fromString raw = Nothing +{-| !! Don't use this function outside of testing. It provides no guarantees +for the correctness of the Hash. +-} +unsafeFromString : String -> Hash +unsafeFromString raw = + Hash raw + + isRawHash : String -> Bool isRawHash str = String.startsWith prefix str || String.startsWith urlPrefix str diff --git a/src/Project.elm b/src/Project.elm index 1ede6e5..69368aa 100644 --- a/src/Project.elm +++ b/src/Project.elm @@ -1,7 +1,8 @@ module Project exposing (..) -import FullyQualifiedName exposing (FQN) -import Json.Decode as Decode +import FullyQualifiedName as FQN exposing (FQN) +import Hash exposing (Hash) +import Json.Decode as Decode exposing (field, string) type Owner @@ -9,18 +10,40 @@ type Owner type alias Project a = - { a | owner : Owner, name : FQN } + { a | owner : Owner, name : FQN, hash : Hash } type alias ProjectListing = Project {} +slug : Project a -> FQN +slug project = + FQN.cons (ownerToString project.owner) project.name + + ownerToString : Owner -> String ownerToString (Owner o) = o -decodeList : Decode.Decoder (List ProjectListing) -decodeList = - Decode.succeed [] + +-- Decode + + +decodeListing : Decode.Decoder ProjectListing +decodeListing = + let + mk owner name hash = + { owner = owner, name = name, hash = hash } + in + Decode.map3 + mk + (field "owner" (Decode.map Owner string)) + (field "name" FQN.decode) + (field "hash" Hash.decode) + + +decodeListings : Decode.Decoder (List ProjectListing) +decodeListings = + Decode.list decodeListing diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index f8794a0..b2d1dc1 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -76,7 +76,7 @@ init env route navKey = |> Maybe.map (Api.perform env.apiBasePath) |> Maybe.withDefault Cmd.none - ( catalog, _ ) = + ( catalog, catalogCmd ) = Catalog.init env model = @@ -96,6 +96,7 @@ init env route navKey = , Cmd.batch [ Cmd.map CodebaseTreeMsg codebaseTreeCmd , Cmd.map WorkspaceMsg workspaceCmd + , Cmd.map CatalogMsg catalogCmd , fetchNamespaceDetailsCmd ] ) @@ -153,7 +154,7 @@ update msg ({ env } as model) = ( catalog, cmd ) = Catalog.init model.env in - ( { model | catalog = catalog }, Cmd.map CatalogMsg cmd ) + ( { model2 | catalog = catalog }, Cmd.map CatalogMsg cmd ) Route.Definition params ref -> let diff --git a/src/UnisonShare/Catalog.elm b/src/UnisonShare/Catalog.elm new file mode 100644 index 0000000..05fcbed --- /dev/null +++ b/src/UnisonShare/Catalog.elm @@ -0,0 +1,84 @@ +module UnisonShare.Catalog exposing (..) + +import Dict exposing (Dict) +import FullyQualifiedName as FQN +import Json.Decode as Decode +import Project exposing (ProjectListing) +import UnisonShare.Catalog.CatalogMask as CatalogMask exposing (CatalogMask) + + +type Catalog + = Catalog (Dict String (List ProjectListing)) + + + +-- CREATE + + +empty : Catalog +empty = + Catalog Dict.empty + + +catalog : CatalogMask -> List ProjectListing -> Catalog +catalog mask projectListings_ = + let + catalog_ project ((Catalog dict) as acc) = + let + projectName = + project + |> Project.slug + |> FQN.toString + + categoryName = + CatalogMask.categoryOf projectName mask + + set old = + case old of + Just ps -> + Just (ps ++ [ project ]) + + Nothing -> + Just [ project ] + in + case categoryName of + Just c -> + Catalog (Dict.update c set dict) + + Nothing -> + acc + in + List.foldl catalog_ empty projectListings_ + + + +-- HELPERS + + +isEmpty : Catalog -> Bool +isEmpty (Catalog dict) = + Dict.isEmpty dict + + +categories : Catalog -> List String +categories (Catalog dict) = + Dict.keys dict + + +projectListings : Catalog -> List ProjectListing +projectListings (Catalog dict) = + List.concat (Dict.values dict) + + +toList : Catalog -> List ( String, List ProjectListing ) +toList (Catalog dict) = + Dict.toList dict + + + +-- DECODE + + +decodeCatalogMask : Decode.Decoder CatalogMask +decodeCatalogMask = + CatalogMask.decode diff --git a/src/UnisonShare/Catalog/CatalogMask.elm b/src/UnisonShare/Catalog/CatalogMask.elm new file mode 100644 index 0000000..9decc2a --- /dev/null +++ b/src/UnisonShare/Catalog/CatalogMask.elm @@ -0,0 +1,132 @@ +module UnisonShare.Catalog.CatalogMask exposing + ( CatalogMask + , categories + , categoryOf + , decode + , empty + , fromDoc + , fromList + , isEmpty + , projectNames + , toList + ) + +import Definition.Doc as Doc exposing (Doc) +import Dict exposing (Dict) +import Json.Decode as Decode exposing (field, index) +import List.Extra as ListE + + +{-| CatalogMask is used to create the Catalog and map ProjectListings to their +categories. + +Indexed by project to category "unison.http" -> "Web & Networking" + +-} +type CatalogMask + = CatalogMask (Dict String String) + + + +-- Create + + +empty : CatalogMask +empty = + CatalogMask Dict.empty + + +fromList : List ( String, String ) -> CatalogMask +fromList categories_ = + CatalogMask (Dict.fromList categories_) + + +{-| For right now, a CatalogMask is fetched as a Doc from the server and as +such we can parse that into a CatalogMask if it has the right shape (if not, +its an empty mask) +-} +fromDoc : Doc -> CatalogMask +fromDoc doc = + let + category_ d = + case d of + Doc.Section category content -> + case content of + [ Doc.BulletedList projects ] -> + List.map (\p -> ( Doc.toString "" p, Doc.toString " " category )) projects + + _ -> + [] + + _ -> + [] + + categories_ = + case doc of + Doc.UntitledSection ds -> + List.concatMap category_ ds + + _ -> + [] + in + fromList categories_ + + + +-- Helpers + + +isEmpty : CatalogMask -> Bool +isEmpty (CatalogMask mask) = + Dict.isEmpty mask + + +categoryOf : String -> CatalogMask -> Maybe String +categoryOf projectName (CatalogMask mask) = + Dict.get projectName mask + + +categories : CatalogMask -> List String +categories (CatalogMask mask) = + Dict.values mask |> ListE.unique + + +projectNames : CatalogMask -> List String +projectNames (CatalogMask mask) = + Dict.keys mask + + +toList : CatalogMask -> List ( String, String ) +toList (CatalogMask mask) = + Dict.toList mask + + + +-- Decode + + +decode : Decode.Decoder CatalogMask +decode = + Decode.map fromDoc decodeCatalogDoc + + +decodeCatalogDoc : Decode.Decoder Doc +decodeCatalogDoc = + let + decodeDoc : Decode.Decoder Doc + decodeDoc = + field "termDocs" (index 0 (index 2 Doc.decode)) + + decodeTermDocs : Decode.Decoder (List Doc) + decodeTermDocs = + Decode.keyValuePairs decodeDoc |> Decode.map (List.map Tuple.second) + + decodeList : Decode.Decoder (List Doc) + decodeList = + field "termDefinitions" decodeTermDocs + in + Decode.map List.head decodeList + |> Decode.andThen + (Maybe.map Decode.succeed + >> Maybe.withDefault (Decode.fail "Empty list") + ) diff --git a/src/UnisonShare/Page/Catalog.elm b/src/UnisonShare/Page/Catalog.elm index 0b9ea8b..b163e59 100644 --- a/src/UnisonShare/Page/Catalog.elm +++ b/src/UnisonShare/Page/Catalog.elm @@ -1,20 +1,21 @@ module UnisonShare.Page.Catalog exposing (..) --- import Api - +import Api import Env exposing (Env) import FullyQualifiedName as FQN import Html exposing (Html, a, div, h1, input, strong, text) -import Html.Attributes exposing (class, href, placeholder) +import Html.Attributes exposing (autofocus, class, href, placeholder) import Html.Events exposing (onInput) import Http +import Perspective import Project exposing (ProjectListing) import RemoteData exposing (RemoteData(..), WebData) -import Set exposing (Set) +import Task import UI import UI.Card as Card import UI.Icon as Icon import UI.PageLayout as PageLayout exposing (PageLayout) +import UnisonShare.Catalog as Catalog exposing (Catalog) import UnisonShare.Route as Route @@ -22,14 +23,6 @@ import UnisonShare.Route as Route -- MODEL -type Category - = Category String (List ProjectListing) - - -type Catalog - = Catalog (Set Category) - - type alias LoadedModel = { query : String , catalog : Catalog @@ -41,27 +34,29 @@ type alias Model = init : Env -> ( Model, Cmd Msg ) -init _ = - {- - let - fetchCmd = - Api.projects - |> Api.toRequest Project.decodeList FetchProjectsFinished - |> Api.perform env.apiBasePath - in - ( Loading, fetchCmd ) - -} - let - categories = - Set.empty +init env = + ( Loading, fetchCatalog env ) + - model = - Success - { catalog = Catalog categories - , query = "" - } +{-| Fetch the Catalog in sequence by first fetching the doc, then the +projectListings and finally merging them into a Catalog +-} +fetchCatalog : Env -> Cmd Msg +fetchCatalog env = + let + perspective = + Perspective.toCodebasePerspective env.perspective in - ( model, Cmd.none ) + Api.getDefinition perspective [ "_catalog" ] + |> Api.toTask env.apiBasePath Catalog.decodeCatalogMask + |> Task.andThen + (\catalog -> + Api.projects + |> Api.toTask env.apiBasePath Project.decodeListings + |> Task.map (\projects -> ( catalog, projects )) + ) + |> Task.map (\( cm, ps ) -> Catalog.catalog cm ps) + |> Task.attempt FetchCatalogFinished @@ -71,22 +66,19 @@ init _ = type Msg = UpdateQuery String | ClearQuery - | FetchProjectsFinished (Result Http.Error (List ProjectListing)) + | NoOp + | FetchCatalogFinished (Result Http.Error Catalog) update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case ( msg, model ) of - ( FetchProjectsFinished projectsResult, _ ) -> - case projectsResult of + ( FetchCatalogFinished catalogResult, _ ) -> + case catalogResult of Err e -> ( Failure e, Cmd.none ) - Ok projects -> - let - catalog = - projectsToCatalog projects - in + Ok catalog -> ( Success { query = "", catalog = catalog }, Cmd.none ) ( UpdateQuery query, Success m ) -> @@ -99,11 +91,6 @@ update msg model = ( model, Cmd.none ) -projectsToCatalog : List ProjectListing -> Catalog -projectsToCatalog _ = - Catalog Set.empty - - -- VIEW @@ -122,8 +109,8 @@ viewProjectListing project = a [ class "project-listing", href url ] [ FQN.view slug ] -viewCategory : Category -> Html msg -viewCategory (Category category projects) = +viewCategory : ( String, List ProjectListing ) -> Html msg +viewCategory ( category, projects ) = let projectLinks = projects @@ -136,33 +123,10 @@ viewCategory (Category category projects) = viewLoaded : LoadedModel -> PageLayout Msg viewLoaded model = let - content = - [ div [ class "categories" ] - [ viewCategory - (Category "Featured" - [ { owner = Project.Owner "unison", name = FQN.fromString "base" } - , { owner = Project.Owner "unison", name = FQN.fromString "distributed" } - ] - ) - , viewCategory - (Category "Parsers & Text Manipulation" - [ { owner = Project.Owner "rlmark", name = FQN.fromString "parsing" } - , { owner = Project.Owner "stew", name = FQN.fromString "json" } - ] - ) - , viewCategory (Category "Databases" []) - , viewCategory (Category "Datatypes" []) - , viewCategory (Category "Math" []) - , viewCategory (Category "Instrumentation" []) - , viewCategory (Category "Multimedia" []) - , viewCategory - (Category "Networking" - [ { owner = Project.Owner "unison", name = FQN.fromString "http" } - ] - ) - , viewCategory (Category "Utilities" []) - ] - ] + categories = + model.catalog + |> Catalog.toList + |> List.map viewCategory in PageLayout.HeroLayout { hero = @@ -184,12 +148,13 @@ viewLoaded model = , input [ placeholder "Search for projects" , onInput UpdateQuery + , autofocus True ] [] ] ] ) - , content = PageLayout.PageContent content + , content = PageLayout.PageContent [ div [ class "categories" ] categories ] } diff --git a/tests/ProjectTests.elm b/tests/ProjectTests.elm new file mode 100644 index 0000000..eb186c8 --- /dev/null +++ b/tests/ProjectTests.elm @@ -0,0 +1,30 @@ +module ProjectTests exposing (..) + +import Expect +import FullyQualifiedName as FQN +import Hash +import Project +import Test exposing (..) + + +slug : Test +slug = + describe "Project.slug" + [ test "Returns the slug of a project by owner and name" <| + \_ -> + Expect.equal + "unison.http" + (Project.slug project |> FQN.toString) + ] + + + +-- Helpers + + +project : Project.ProjectListing +project = + { owner = Project.Owner "unison" + , name = FQN.fromString "http" + , hash = Hash.unsafeFromString "##unison.http" + } diff --git a/tests/UnisonShare/Catalog/CatalogMaskTests.elm b/tests/UnisonShare/Catalog/CatalogMaskTests.elm new file mode 100644 index 0000000..849148a --- /dev/null +++ b/tests/UnisonShare/Catalog/CatalogMaskTests.elm @@ -0,0 +1,114 @@ +module UnisonShare.Catalog.CatalogMaskTests exposing (..) + +import Definition.Doc as Doc +import Expect +import Test exposing (..) +import UnisonShare.Catalog.CatalogMask as CatalogMask + + +fromDoc : Test +fromDoc = + describe "CatalogMask.fromDoc" + [ test "Creates a CatalogMask from a Doc" <| + \_ -> + Expect.equal (List.sort rawMask) + (CatalogMask.fromDoc doc |> CatalogMask.toList |> List.sort) + ] + + +fromList : Test +fromList = + describe "CatalogMask.fromList" + [ test "Creates a CatalogMask from a List" <| + \_ -> + let + catalogMask = + CatalogMask.fromList rawMask + in + Expect.equal (List.sort rawMask) + (CatalogMask.toList catalogMask |> List.sort) + ] + + +categoryOf : Test +categoryOf = + describe "CatalogMask.categoryOf" + [ test "given a project, returns its category " <| + \_ -> + let + catalogMask = + CatalogMask.fromList rawMask + in + Expect.equal (Just "Featured") (CatalogMask.categoryOf "unison.base" catalogMask) + ] + + +categories : Test +categories = + describe "CatalogMask.categories" + [ test "Returns all categories in a Mask" <| + \_ -> + let + catalogMask = + CatalogMask.fromList rawMask + in + Expect.equal + (List.sort + [ "Featured" + , "Web & Networking" + , "Parsers & Text Manipulation" + , "Datatypes" + ] + ) + (CatalogMask.categories catalogMask |> List.sort) + ] + + +projectNames : Test +projectNames = + describe "CatalogMask.projectNames" + [ test "Returns all project names in a Mask" <| + \_ -> + let + catalogMask = + CatalogMask.fromList rawMask + in + Expect.equal + (List.sort + [ "unison.base" + , "unison.distributed" + , "unison.http" + , "hojberg.textExtra" + , "hojberg.nanoid" + ] + ) + (CatalogMask.projectNames catalogMask |> List.sort) + ] + + + +-- HELPERS + + +rawMask : List ( String, String ) +rawMask = + [ ( "unison.base", "Featured" ) + , ( "unison.distributed", "Featured" ) + , ( "unison.http", "Web & Networking" ) + , ( "hojberg.textExtra", "Parsers & Text Manipulation" ) + , ( "hojberg.nanoid", "Datatypes" ) + ] + + +doc : Doc.Doc +doc = + Doc.UntitledSection + [ Doc.Section (Doc.Word "Featured") + [ Doc.BulletedList [ Doc.Word "unison.base", Doc.Word "unison.distributed" ] ] + , Doc.Section (Doc.Word "Web & Networking") + [ Doc.BulletedList [ Doc.Word "unison.http" ] ] + , Doc.Section (Doc.Word "Parsers & Text Manipulation") + [ Doc.BulletedList [ Doc.Word "hojberg.textExtra" ] ] + , Doc.Section (Doc.Word "Datatypes") + [ Doc.BulletedList [ Doc.Word "hojberg.nanoid" ] ] + ] diff --git a/tests/UnisonShare/CatalogTests.elm b/tests/UnisonShare/CatalogTests.elm new file mode 100644 index 0000000..e048900 --- /dev/null +++ b/tests/UnisonShare/CatalogTests.elm @@ -0,0 +1,140 @@ +module UnisonShare.CatalogTests exposing (..) + +import Expect +import FullyQualifiedName as FQN +import Hash +import Project +import Test exposing (..) +import UnisonShare.Catalog as Catalog +import UnisonShare.Catalog.CatalogMask as CatalogMask + + +catalog : Test +catalog = + describe "Catalog.catalog" + [ describe "Create a Catalog from a CatalogMask and ProjectListings" + [ test "It retains only the overlapping items between the mask and the listings" <| + \_ -> + let + projectListings_ = + [ baseListing, distributedListing, textExtraListing, nanoidListing ] + + catalog_ = + Catalog.catalog catalogMask projectListings_ + in + Expect.equal + [ ( "Featured", [ baseListing, distributedListing ] ) + , ( "Parsers & Text Manipulation", [ textExtraListing ] ) + ] + (Catalog.toList catalog_) + ] + ] + + +categories : Test +categories = + describe "Catalog.categories" + [ test "Extracts the categories of a Catalog that exist in both the mask and the project listings" <| + \_ -> + let + projectListings_ = + [ baseListing, distributedListing, textExtraListing ] + + catalog_ = + Catalog.catalog catalogMask projectListings_ + in + Expect.equal [ "Featured", "Parsers & Text Manipulation" ] (Catalog.categories catalog_) + ] + + +projectListings : Test +projectListings = + describe "Catalog.projectListings" + [ test "Extracts the projectListings of a Catalog that exist in both the mask and the project listings" <| + \_ -> + let + projectListings_ = + [ baseListing, textExtraListing, nanoidListing ] + + catalog_ = + Catalog.catalog catalogMask projectListings_ + in + Expect.equal [ baseListing, textExtraListing ] (Catalog.projectListings catalog_) + ] + + +toList : Test +toList = + describe "Catalog.toList" + [ test "Returns the Catalog as a List" <| + \_ -> + let + projectListings_ = + [ baseListing, distributedListing, textExtraListing, nanoidListing ] + + catalog_ = + Catalog.catalog catalogMask projectListings_ + in + Expect.equal + [ ( "Featured", [ baseListing, distributedListing ] ) + , ( "Parsers & Text Manipulation", [ textExtraListing ] ) + ] + (Catalog.toList catalog_) + ] + + + +-- helpers + + +catalogMask : CatalogMask.CatalogMask +catalogMask = + CatalogMask.fromList + [ ( "unison.base", "Featured" ) + , ( "unison.distributed", "Featured" ) + , ( "unison.http", "Web & Networking" ) + , ( "hojberg.textExtra", "Parsers & Text Manipulation" ) + , ( "hojberg.money", "Datatypes" ) + ] + + +baseListing : Project.ProjectListing +baseListing = + { owner = Project.Owner "unison" + , name = FQN.fromString "base" + , hash = Hash.unsafeFromString "##unison.base" + } + + +distributedListing : Project.ProjectListing +distributedListing = + { owner = Project.Owner "unison" + , name = FQN.fromString "distributed" + , hash = Hash.unsafeFromString "##unison.distributed" + } + + +httpListing : Project.ProjectListing +httpListing = + { owner = Project.Owner "unison" + , name = FQN.fromString "http" + , hash = Hash.unsafeFromString "##unison.http" + } + + +textExtraListing : Project.ProjectListing +textExtraListing = + { owner = Project.Owner "hojberg" + , name = FQN.fromString "textExtra" + , hash = Hash.unsafeFromString "##hojberg.textExtra" + } + + +{-| Note: does purposely not exist in mask +-} +nanoidListing : Project.ProjectListing +nanoidListing = + { owner = Project.Owner "hojberg" + , name = FQN.fromString "nanoid" + , hash = Hash.unsafeFromString "##hojberg.nanoid" + } From a9a8c9fa53fa71705314af427e59bd32dd2469f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Fri, 17 Dec 2021 14:57:21 -0500 Subject: [PATCH 30/54] Catalog: ensure the catalog is sorted by category Using the categories CategoryMask (and OrderedDict), sort the Catalog such that it matches the original order in the catalog Doc. --- elm.json | 1 + src/UnisonShare/Catalog.elm | 63 +++++++++++++++---- src/UnisonShare/Catalog/CatalogMask.elm | 18 +++--- .../UnisonShare/Catalog/CatalogMaskTests.elm | 38 +++++------ 4 files changed, 77 insertions(+), 43 deletions(-) diff --git a/elm.json b/elm.json index b60f497..2f4bf47 100644 --- a/elm.json +++ b/elm.json @@ -20,6 +20,7 @@ "elm-community/list-extra": "8.2.4", "elm-community/maybe-extra": "5.2.0", "elm-community/string-extra": "4.0.1", + "j-maas/elm-ordered-containers": "1.0.0", "krisajenkins/remotedata": "6.0.1", "mgold/elm-nonempty-list": "4.1.0", "stoeffel/set-extra": "1.2.3", diff --git a/src/UnisonShare/Catalog.elm b/src/UnisonShare/Catalog.elm index 05fcbed..7c3daf8 100644 --- a/src/UnisonShare/Catalog.elm +++ b/src/UnisonShare/Catalog.elm @@ -1,34 +1,38 @@ module UnisonShare.Catalog exposing (..) -import Dict exposing (Dict) +import Dict import FullyQualifiedName as FQN import Json.Decode as Decode +import OrderedDict exposing (OrderedDict) import Project exposing (ProjectListing) import UnisonShare.Catalog.CatalogMask as CatalogMask exposing (CatalogMask) type Catalog - = Catalog (Dict String (List ProjectListing)) + = Catalog (OrderedDict String (List ProjectListing)) -- CREATE +{-| Create the empty Catalog +-} empty : Catalog empty = - Catalog Dict.empty + Catalog OrderedDict.empty +{-| Create a Catalog using a mask and listings. The mask is used to get the +categories and to ensure the category sort order is preserved +-} catalog : CatalogMask -> List ProjectListing -> Catalog catalog mask projectListings_ = let - catalog_ project ((Catalog dict) as acc) = + group project acc = let projectName = - project - |> Project.slug - |> FQN.toString + project |> Project.slug |> FQN.toString categoryName = CatalogMask.categoryOf projectName mask @@ -43,12 +47,39 @@ catalog mask projectListings_ = in case categoryName of Just c -> - Catalog (Dict.update c set dict) + Dict.update c set acc + + Nothing -> + acc + + grouped = + List.foldl group Dict.empty projectListings_ + + sortedCategories category acc = + case Dict.get category grouped of + Just ps -> + insert category ps acc Nothing -> acc in - List.foldl catalog_ empty projectListings_ + List.foldl sortedCategories empty (CatalogMask.categories mask) + + +{-| Insert a category and projects within into a Catalog +-} +insert : String -> List ProjectListing -> Catalog -> Catalog +insert categoryName projectListings_ (Catalog dict) = + Catalog (OrderedDict.insert categoryName projectListings_ dict) + + +{-| Create a Catalog given a list of projects grouped by category +-} +fromList : List ( String, List ProjectListing ) -> Catalog +fromList items = + items + |> OrderedDict.fromList + |> Catalog @@ -57,22 +88,28 @@ catalog mask projectListings_ = isEmpty : Catalog -> Bool isEmpty (Catalog dict) = - Dict.isEmpty dict + OrderedDict.isEmpty dict +{-| Extract all categories from a Catalog +-} categories : Catalog -> List String categories (Catalog dict) = - Dict.keys dict + OrderedDict.keys dict +{-| Extract all project listings from a Catalog +-} projectListings : Catalog -> List ProjectListing projectListings (Catalog dict) = - List.concat (Dict.values dict) + List.concat (OrderedDict.values dict) +{-| Convert a Catalog to a list of project listings grouped by category +-} toList : Catalog -> List ( String, List ProjectListing ) toList (Catalog dict) = - Dict.toList dict + OrderedDict.toList dict diff --git a/src/UnisonShare/Catalog/CatalogMask.elm b/src/UnisonShare/Catalog/CatalogMask.elm index 9decc2a..d27ec59 100644 --- a/src/UnisonShare/Catalog/CatalogMask.elm +++ b/src/UnisonShare/Catalog/CatalogMask.elm @@ -12,9 +12,9 @@ module UnisonShare.Catalog.CatalogMask exposing ) import Definition.Doc as Doc exposing (Doc) -import Dict exposing (Dict) import Json.Decode as Decode exposing (field, index) import List.Extra as ListE +import OrderedDict exposing (OrderedDict) {-| CatalogMask is used to create the Catalog and map ProjectListings to their @@ -24,7 +24,7 @@ Indexed by project to category "unison.http" -> "Web & Networking" -} type CatalogMask - = CatalogMask (Dict String String) + = CatalogMask (OrderedDict String String) @@ -33,12 +33,12 @@ type CatalogMask empty : CatalogMask empty = - CatalogMask Dict.empty + CatalogMask OrderedDict.empty fromList : List ( String, String ) -> CatalogMask fromList categories_ = - CatalogMask (Dict.fromList categories_) + CatalogMask (OrderedDict.fromList categories_) {-| For right now, a CatalogMask is fetched as a Doc from the server and as @@ -78,27 +78,27 @@ fromDoc doc = isEmpty : CatalogMask -> Bool isEmpty (CatalogMask mask) = - Dict.isEmpty mask + OrderedDict.isEmpty mask categoryOf : String -> CatalogMask -> Maybe String categoryOf projectName (CatalogMask mask) = - Dict.get projectName mask + OrderedDict.get projectName mask categories : CatalogMask -> List String categories (CatalogMask mask) = - Dict.values mask |> ListE.unique + OrderedDict.values mask |> ListE.unique projectNames : CatalogMask -> List String projectNames (CatalogMask mask) = - Dict.keys mask + OrderedDict.keys mask toList : CatalogMask -> List ( String, String ) toList (CatalogMask mask) = - Dict.toList mask + OrderedDict.toList mask diff --git a/tests/UnisonShare/Catalog/CatalogMaskTests.elm b/tests/UnisonShare/Catalog/CatalogMaskTests.elm index 849148a..ee7c64f 100644 --- a/tests/UnisonShare/Catalog/CatalogMaskTests.elm +++ b/tests/UnisonShare/Catalog/CatalogMaskTests.elm @@ -11,8 +11,8 @@ fromDoc = describe "CatalogMask.fromDoc" [ test "Creates a CatalogMask from a Doc" <| \_ -> - Expect.equal (List.sort rawMask) - (CatalogMask.fromDoc doc |> CatalogMask.toList |> List.sort) + Expect.equal rawMask + (CatalogMask.fromDoc doc |> CatalogMask.toList) ] @@ -25,8 +25,8 @@ fromList = catalogMask = CatalogMask.fromList rawMask in - Expect.equal (List.sort rawMask) - (CatalogMask.toList catalogMask |> List.sort) + Expect.equal rawMask + (CatalogMask.toList catalogMask) ] @@ -53,14 +53,12 @@ categories = CatalogMask.fromList rawMask in Expect.equal - (List.sort - [ "Featured" - , "Web & Networking" - , "Parsers & Text Manipulation" - , "Datatypes" - ] - ) - (CatalogMask.categories catalogMask |> List.sort) + [ "Featured" + , "Web & Networking" + , "Parsers & Text Manipulation" + , "Datatypes" + ] + (CatalogMask.categories catalogMask) ] @@ -74,15 +72,13 @@ projectNames = CatalogMask.fromList rawMask in Expect.equal - (List.sort - [ "unison.base" - , "unison.distributed" - , "unison.http" - , "hojberg.textExtra" - , "hojberg.nanoid" - ] - ) - (CatalogMask.projectNames catalogMask |> List.sort) + [ "unison.base" + , "unison.distributed" + , "unison.http" + , "hojberg.textExtra" + , "hojberg.nanoid" + ] + (CatalogMask.projectNames catalogMask) ] From c2b462741b24219817a4468851f38641a55f5dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 3 Jan 2022 13:02:38 -0500 Subject: [PATCH 31/54] Catalog: add search support Add support for fuzzy searching the catalog --- elm.json | 1 + src/Project.elm | 5 ++ src/UnisonShare/Catalog.elm | 20 +++++ src/UnisonShare/Page/Catalog.elm | 80 ++++++++++++++++---- src/css/unison-share/page/catalog.css | 101 ++++++++++++++++++++++---- tests/UnisonShare/CatalogTests.elm | 33 +++++++++ 6 files changed, 211 insertions(+), 29 deletions(-) diff --git a/elm.json b/elm.json index 2f4bf47..d330c2c 100644 --- a/elm.json +++ b/elm.json @@ -6,6 +6,7 @@ "elm-version": "0.19.1", "dependencies": { "direct": { + "NoRedInk/elm-simple-fuzzy": "1.0.3", "elm/browser": "1.0.2", "elm/core": "1.0.5", "elm/html": "1.0.0", diff --git a/src/Project.elm b/src/Project.elm index 69368aa..79c7db6 100644 --- a/src/Project.elm +++ b/src/Project.elm @@ -22,6 +22,11 @@ slug project = FQN.cons (ownerToString project.owner) project.name +slugString : Project a -> String +slugString project = + project |> slug |> FQN.toString + + ownerToString : Owner -> String ownerToString (Owner o) = o diff --git a/src/UnisonShare/Catalog.elm b/src/UnisonShare/Catalog.elm index 7c3daf8..d2de85e 100644 --- a/src/UnisonShare/Catalog.elm +++ b/src/UnisonShare/Catalog.elm @@ -5,6 +5,7 @@ import FullyQualifiedName as FQN import Json.Decode as Decode import OrderedDict exposing (OrderedDict) import Project exposing (ProjectListing) +import Simple.Fuzzy as Fuzzy import UnisonShare.Catalog.CatalogMask as CatalogMask exposing (CatalogMask) @@ -86,6 +87,25 @@ fromList items = -- HELPERS +{-| Fuzzy search through a flattened catalog by project name and category +-} +search : Catalog -> String -> List ( ProjectListing, String ) +search catalog_ query = + let + flat ( category, projects ) acc = + acc ++ List.map (\p -> ( p, category )) projects + + normalize ( p, c ) = + p + |> Project.slugString + |> (++) c + in + catalog_ + |> toList + |> List.foldl flat [] + |> Fuzzy.filter normalize query + + isEmpty : Catalog -> Bool isEmpty (Catalog dict) = OrderedDict.isEmpty dict diff --git a/src/UnisonShare/Page/Catalog.elm b/src/UnisonShare/Page/Catalog.elm index b163e59..bf77240 100644 --- a/src/UnisonShare/Page/Catalog.elm +++ b/src/UnisonShare/Page/Catalog.elm @@ -3,9 +3,9 @@ module UnisonShare.Page.Catalog exposing (..) import Api import Env exposing (Env) import FullyQualifiedName as FQN -import Html exposing (Html, a, div, h1, input, strong, text) +import Html exposing (Html, a, div, h1, input, span, strong, text) import Html.Attributes exposing (autofocus, class, href, placeholder) -import Html.Events exposing (onInput) +import Html.Events exposing (onBlur, onFocus, onInput) import Http import Perspective import Project exposing (ProjectListing) @@ -25,6 +25,7 @@ import UnisonShare.Route as Route type alias LoadedModel = { query : String + , hasFocus : Bool , catalog : Catalog } @@ -65,6 +66,7 @@ fetchCatalog env = type Msg = UpdateQuery String + | UpdateFocus Bool | ClearQuery | NoOp | FetchCatalogFinished (Result Http.Error Catalog) @@ -79,7 +81,10 @@ update msg model = ( Failure e, Cmd.none ) Ok catalog -> - ( Success { query = "", catalog = catalog }, Cmd.none ) + ( Success { query = "", hasFocus = True, catalog = catalog }, Cmd.none ) + + ( UpdateFocus hasFocus, Success m ) -> + ( Success { m | hasFocus = hasFocus }, Cmd.none ) ( UpdateQuery query, Success m ) -> ( Success { m | query = query }, Cmd.none ) @@ -95,18 +100,18 @@ update msg model = -- VIEW +projectUrl : ProjectListing -> String +projectUrl = + Route.forProject >> Route.toUrlString + + viewProjectListing : ProjectListing -> Html msg viewProjectListing project = let slug = - FQN.cons (Project.ownerToString project.owner) project.name - - url = - project - |> Route.forProject - |> Route.toUrlString + Project.slug project in - a [ class "project-listing", href url ] [ FQN.view slug ] + a [ class "project-listing", href (projectUrl project) ] [ FQN.view slug ] viewCategory : ( String, List ProjectListing ) -> Html msg @@ -120,6 +125,37 @@ viewCategory ( category, projects ) = |> Card.view +viewSearchResult : ( ProjectListing, String ) -> Html msg +viewSearchResult ( project, category ) = + a + [ class "search-result", href (projectUrl project) ] + [ project |> Project.slug |> FQN.view + , span [ class "category" ] [ text category ] + ] + + +viewSearchResults : LoadedModel -> Html msg +viewSearchResults model = + if String.length model.query > 3 then + let + results = + model.query + |> Catalog.search model.catalog + |> List.map viewSearchResult + + resultsPane = + if List.isEmpty results then + [ div [ class "empty-state" ] [ text ("No matching projects found for \"" ++ model.query ++ "\"") ] ] + + else + results + in + div [ class "search-results" ] resultsPane + + else + UI.nothing + + viewLoaded : LoadedModel -> PageLayout Msg viewLoaded model = let @@ -127,6 +163,13 @@ viewLoaded model = model.catalog |> Catalog.toList |> List.map viewCategory + + searchResults = + if model.hasFocus then + viewSearchResults model + + else + UI.nothing in PageLayout.HeroLayout { hero = @@ -144,13 +187,18 @@ viewLoaded model = , div [] [ text "Projects, libraries, documention, terms, and types" ] ] , div [ class "catalog-search" ] - [ Icon.view Icon.search - , input - [ placeholder "Search for projects" - , onInput UpdateQuery - , autofocus True + [ div [ class "search-field" ] + [ Icon.view Icon.search + , input + [ placeholder "Search for projects" + , onInput UpdateQuery + , autofocus True + , onBlur (UpdateFocus False) + , onFocus (UpdateFocus True) + ] + [] ] - [] + , searchResults ] ] ) diff --git a/src/css/unison-share/page/catalog.css b/src/css/unison-share/page/catalog.css index 5d7c84a..26db3a5 100644 --- a/src/css/unison-share/page/catalog.css +++ b/src/css/unison-share/page/catalog.css @@ -56,41 +56,100 @@ .catalog-hero .catalog-search { width: 50rem; - height: 3.5rem; background: var(--color-main-bg); color: var(--color-main-fg); border-radius: var(--border-radius-base); position: absolute; - bottom: -1.75rem; - border: 2px solid var(--color-catalog-hero-bg); + top: calc(var(--page-hero-height) - 1.75rem); + border: 2px solid transparent; box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.1); display: flex; - flex-direction: row; - align-items: center; - padding: 1rem 0 1rem 1rem; + flex-direction: column; left: 50%; transform: translateX(-50%); z-index: var(--layer-base); transition: all 0.2s; + background: var(--color-gray-lighten-55); +} + +.catalog-hero .catalog-search:focus-within { + border-color: var(--color-main-focus-fg); + box-shadow: 0 0 0 2px var(--color-main-focus-outline); +} + +.catalog-hero .catalog-search .search-field { + display: flex; + flex-direction: row; + align-items: center; + height: 3.5rem; + padding: 1rem 0 1rem 1rem; + border-radius: var(--border-radius-base); +} + +.catalog-hero .catalog-search .search-field:focus-within { + background: var(--color-gray-lighten-60); +} + +.catalog-hero .catalog-search .search-field .icon { + font-size: 1.5rem; + color: var(--color-main-subtle-fg); } -.catalog-hero .catalog-search input { + +.catalog-hero .catalog-search .search-field input { width: 100%; height: calc(3.5rem - 4px); margin-left: 0.75rem; font-size: 1.125rem; border-radius: var(--border-radius-base); + font-weight: bold; + background: transparent; } -.catalog-hero .catalog-search input:focus { + +.catalog-hero .catalog-search .search-field input::placeholder { + font-weight: normal; +} + +.catalog-hero .catalog-search .search-field input:focus { outline: none; } -.catalog-hero .catalog-search:focus-within { - border-color: var(--color-main-focus-fg); - box-shadow: 0 0 0 2px var(--color-main-focus-outline); + +.catalog-hero .catalog-search .search-results { + background: var(--color-main-bg); + border-top: 1px solid var(--color-gray-lighten-50); + border-radius: 0 0 var(--border-radius-base) var(--border-radius-base); + display: flex; + flex-direction: column; + gap: 0.75rem; + padding: 0.75rem; } -.catalog-hero .catalog-search .icon { - font-size: 1.5rem; +.catalog-hero .catalog-search .search-results .search-result { + display: flex; + flex-direction: row; + padding: 0.5rem 1rem; + align-items: center; + height: 3rem; + border-radius: var(--border-radius-base); + font-size: 1rem; +} + +.catalog-hero .catalog-search .search-results .search-result:hover { + background: var(--color-gray-lighten-55); + text-decoration: none; +} + +.catalog-hero .catalog-search .search-results .search-result .category { color: var(--color-main-subtle-fg); + font-size: var(--font-size-medium); + margin-left: auto; + justify-self: flex-end; +} + +.catalog-hero .catalog-search .search-results .empty-state { + font-size: var(--font-size-base); + color: var(--color-main-subtle-fg); + text-align: center; + padding: 1rem 0; } .categories { @@ -103,4 +162,20 @@ .categories .card { width: 18.75rem; + gap: 0.5rem; +} + +.categories .card .project-listing { + display: flex; + flex-direction: row; + align-items: center; + height: 2rem; + padding: 0 0.25rem; + margin-left: -0.25rem; + border-radius: var(--border-radius-base); +} + +.categories .card .project-listing:hover { + text-decoration: none; + background: var(--color-gray-lighten-55); } diff --git a/tests/UnisonShare/CatalogTests.elm b/tests/UnisonShare/CatalogTests.elm index e048900..4e03b3d 100644 --- a/tests/UnisonShare/CatalogTests.elm +++ b/tests/UnisonShare/CatalogTests.elm @@ -83,6 +83,39 @@ toList = ] +search : Test +search = + describe "Catalog.search" + [ test "Fuzzy finds projects in the catalog by project name " <| + \_ -> + let + projectListings_ = + [ baseListing, distributedListing, textExtraListing, nanoidListing ] + + catalog_ = + Catalog.catalog catalogMask projectListings_ + in + Expect.equal + [ ( baseListing, "Featured" ) + , ( distributedListing, "Featured" ) + ] + (Catalog.search catalog_ "unison") + , test "Fuzzy finds projects in the catalog by category" <| + \_ -> + let + projectListings_ = + [ baseListing, distributedListing, textExtraListing, nanoidListing ] + + catalog_ = + Catalog.catalog catalogMask projectListings_ + in + Expect.equal + [ ( textExtraListing, "Parsers & Text Manipulation" ) + ] + (Catalog.search catalog_ "parsers") + ] + + -- helpers From 3423d1ea34a9cc7a7eedbd70a2f866920ce7a0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Wed, 5 Jan 2022 09:27:11 -0500 Subject: [PATCH 32/54] Temporarily hide the _catalog doc Unison Share will have a new front page that features a catalog which is derived from a Unison Doc. This page will mean that users will never see a tree view of the top of the Unison Share like they do now. While the Catalog page is being worked on, but not switched over to, we want to hide the _catalog doc from the top level codebase tree to avoid any confusion. This should be removed later when the Catalog page is in place. --- src/CodebaseTree.elm | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/CodebaseTree.elm b/src/CodebaseTree.elm index 6b164e4..2103eca 100644 --- a/src/CodebaseTree.elm +++ b/src/CodebaseTree.elm @@ -194,11 +194,17 @@ viewListingRow clickMsg label_ category icon = |> Maybe.map (\msg -> a [ containerClass, onClick msg ]) |> Maybe.withDefault (span [ containerClass ]) in - container - [ Icon.view icon - , viewListingLabel label_ - , span [ class "definition-category" ] [ text category ] - ] + -- TODO: Temporary work around to avoid the hidden catalog definition to + -- show up on Share while the catalog page is being worked on + if label_ == "_catalog" then + UI.nothing + + else + container + [ Icon.view icon + , viewListingLabel label_ + , span [ class "definition-category" ] [ text category ] + ] viewListingLabel : String -> Html msg From 92d7ad6638ab35e88adf8efb23dcabd3fcf7e796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Wed, 5 Jan 2022 10:04:45 -0500 Subject: [PATCH 33/54] Move route specific keyboardshortcuts to pages Ensure that when we are on the Catalog say, we don't trigger Workspace keyboardshortcuts. Notably this also means that things like the Finder are no longer an App concern, by a Workspace shortcut. There's a few other things that should be moved down a layer like toggling the sidebar, but this should only happen for Share and not for Local (this is not tackled in this effort). --- src/UnisonLocal/App.elm | 13 -------- src/UnisonShare/App.elm | 71 ++++++++++++++++++++--------------------- src/Workspace.elm | 19 +++++++++-- 3 files changed, 50 insertions(+), 53 deletions(-) diff --git a/src/UnisonLocal/App.elm b/src/UnisonLocal/App.elm index 0a3188d..503fa9d 100644 --- a/src/UnisonLocal/App.elm +++ b/src/UnisonLocal/App.elm @@ -384,25 +384,12 @@ keydown model keyboardEvent = ( { model | sidebarToggled = not model.sidebarToggled }, Cmd.none ) in case shortcut of - KeyboardShortcut.Chord Ctrl (K _) -> - showFinder model Nothing - - KeyboardShortcut.Chord Meta (K _) -> - if model.env.operatingSystem == Env.MacOS then - showFinder model Nothing - - else - noOp - KeyboardShortcut.Chord Ctrl (B _) -> toggleSidebar KeyboardShortcut.Chord Meta (B _) -> toggleSidebar - KeyboardShortcut.Sequence _ ForwardSlash -> - showFinder model Nothing - KeyboardShortcut.Chord Shift QuestionMark -> ( { model | modal = HelpModal }, Cmd.none ) diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index b2d1dc1..682d56f 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -126,8 +126,8 @@ type Msg update : Msg -> Model -> ( Model, Cmd Msg ) update msg ({ env } as model) = - case msg of - LinkClicked urlRequest -> + case ( model.route, msg ) of + ( _, LinkClicked urlRequest ) -> case urlRequest of Browser.Internal url -> ( model, Nav.pushUrl model.navKey (Url.toString url) ) @@ -137,7 +137,7 @@ update msg ({ env } as model) = Browser.External _ -> ( model, Cmd.none ) - UrlChanged url -> + ( _, UrlChanged url ) -> let route = Route.fromUrl env.basePath url @@ -172,10 +172,10 @@ update msg ({ env } as model) = Route.Perspective params -> fetchPerspectiveAndCodebaseTree env.perspective { model2 | env = newEnv params } - ChangePerspective perspective -> + ( _, ChangePerspective perspective ) -> navigateToPerspective model perspective - FetchPerspectiveNamespaceDetailsFinished fqn details -> + ( _, FetchPerspectiveNamespaceDetailsFinished fqn details ) -> let perspective = case env.perspective of @@ -194,24 +194,24 @@ update msg ({ env } as model) = in ( { model | env = nextEnv }, Cmd.none ) - Keydown event -> + ( _, Keydown event ) -> keydown model event - OpenDefinition ref -> + ( _, OpenDefinition ref ) -> navigateToDefinition model ref - ShowModal modal -> + ( _, ShowModal modal ) -> let ( appModal, cmd ) = AppModal.show modal in ( { model | appModal = appModal }, Cmd.map AppModalMsg cmd ) - ToggleSidebar -> + ( _, ToggleSidebar ) -> ( { model | sidebarToggled = not model.sidebarToggled }, Cmd.none ) -- Sub msgs - AppModalMsg amMsg -> + ( _, AppModalMsg amMsg ) -> let ( am, amCmd, out ) = AppModal.update env amMsg model.appModal @@ -226,27 +226,34 @@ update msg ({ env } as model) = in ( newModel, Cmd.batch [ Cmd.map AppModalMsg amCmd, cmd ] ) - CatalogMsg cMsg -> + ( Route.Catalog, CatalogMsg cMsg ) -> let ( catalog, cmd ) = Catalog.update cMsg model.catalog in ( { model | catalog = catalog }, Cmd.map CatalogMsg cmd ) - WorkspaceMsg wMsg -> - let - ( workspace, wCmd, outMsg ) = - Workspace.update env wMsg model.workspace + ( _, WorkspaceMsg wMsg ) -> + -- TODO: Clean this up, there should be a top level Project route + -- instead of 2 separate workspace routes (perspective and + -- definition) + if model.route /= Route.Catalog then + let + ( workspace, wCmd, outMsg ) = + Workspace.update env wMsg model.workspace - model2 = - { model | workspace = workspace } + model2 = + { model | workspace = workspace } - ( model3, cmd ) = - handleWorkspaceOutMsg model2 outMsg - in - ( model3, Cmd.batch [ cmd, Cmd.map WorkspaceMsg wCmd ] ) + ( model3, cmd ) = + handleWorkspaceOutMsg model2 outMsg + in + ( model3, Cmd.batch [ cmd, Cmd.map WorkspaceMsg wCmd ] ) + + else + ( model, Cmd.none ) - PerspectiveLandingMsg rMsg -> + ( _, PerspectiveLandingMsg rMsg ) -> let ( perspectiveLanding, outMsg ) = PerspectiveLanding.update rMsg model.perspectiveLanding @@ -264,7 +271,7 @@ update msg ({ env } as model) = PerspectiveLanding.None -> ( model2, Cmd.none ) - CodebaseTreeMsg cMsg -> + ( _, CodebaseTreeMsg cMsg ) -> let ( codebaseTree, cCmd, outMsg ) = CodebaseTree.update env cMsg model.codebaseTree @@ -292,13 +299,16 @@ update msg ({ env } as model) = in ( model3, Cmd.batch [ cmd, Cmd.map CodebaseTreeMsg cCmd ] ) - KeyboardShortcutMsg kMsg -> + ( _, KeyboardShortcutMsg kMsg ) -> let ( keyboardShortcut, cmd ) = KeyboardShortcut.update kMsg model.keyboardShortcut in ( { model | keyboardShortcut = keyboardShortcut }, Cmd.map KeyboardShortcutMsg cmd ) + _ -> + ( model, Cmd.none ) + -- UPDATE HELPERS @@ -392,25 +402,12 @@ keydown model keyboardEvent = ( { model | sidebarToggled = not model.sidebarToggled }, Cmd.none ) in case shortcut of - KeyboardShortcut.Chord Ctrl (K _) -> - showFinder model Nothing - - KeyboardShortcut.Chord Meta (K _) -> - if model.env.operatingSystem == Env.MacOS then - showFinder model Nothing - - else - noOp - KeyboardShortcut.Chord Ctrl (B _) -> toggleSidebar KeyboardShortcut.Chord Meta (B _) -> toggleSidebar - KeyboardShortcut.Sequence _ ForwardSlash -> - showFinder model Nothing - KeyboardShortcut.Chord Shift QuestionMark -> let ( am, amCmd ) = diff --git a/src/Workspace.elm b/src/Workspace.elm index c57a156..9b59e66 100644 --- a/src/Workspace.elm +++ b/src/Workspace.elm @@ -175,7 +175,7 @@ update env msg ({ workspaceItems } as model) = KeyboardShortcut.fromKeyboardEvent model.keyboardShortcut event ( nextModel, cmd, out ) = - handleKeyboardShortcut { model | keyboardShortcut = keyboardShortcut } shortcut + handleKeyboardShortcut env { model | keyboardShortcut = keyboardShortcut } shortcut in ( nextModel, Cmd.batch [ cmd, Cmd.map KeyboardShortcutMsg kCmd ], out ) @@ -313,8 +313,8 @@ openDefinitionsFocusToOutMsg openDefs = |> Maybe.withDefault Emptied -handleKeyboardShortcut : Model -> KeyboardShortcut -> ( Model, Cmd Msg, OutMsg ) -handleKeyboardShortcut ({ workspaceItems } as model) shortcut = +handleKeyboardShortcut : Env -> Model -> KeyboardShortcut -> ( Model, Cmd Msg, OutMsg ) +handleKeyboardShortcut env ({ workspaceItems } as model) shortcut = let scrollToCmd = WorkspaceItems.focus @@ -351,6 +351,19 @@ handleKeyboardShortcut ({ workspaceItems } as model) shortcut = ( { model | workspaceItems = next }, scrollToCmd next, openDefinitionsFocusToOutMsg next ) in case shortcut of + KeyboardShortcut.Chord Ctrl (K _) -> + ( model, Cmd.none, ShowFinderRequest Nothing ) + + KeyboardShortcut.Chord Meta (K _) -> + if env.operatingSystem == Env.MacOS then + ( model, Cmd.none, ShowFinderRequest Nothing ) + + else + ( model, Cmd.none, None ) + + Sequence _ ForwardSlash -> + ( model, Cmd.none, ShowFinderRequest Nothing ) + Chord Alt ArrowDown -> moveDown From abebe9d6e5a58f4b86dcde2d707d6fa8e70e49c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Thu, 6 Jan 2022 11:02:11 -0500 Subject: [PATCH 34/54] Add Hashvatars Add Hashvatars to display a unique avatar from a Unison Hash for Projects using the Unison hexgrid and the design system colors. Each color is picked by harmonizing the first picked color (for the background) via the full gamut colors and then approximating those colors to colors confined in the design system by using rgb difference. --- elm.json | 6 +- src/Color/Harmony.elm | 186 ++++++++++++++ src/Hashvatar.elm | 117 +++++++++ src/Hashvatar/HexGrid.elm | 112 +++++++++ src/Project.elm | 22 ++ src/UI/Click.elm | 8 + src/UI/Color.elm | 338 ++++++++++++++++++++++++++ src/UnisonShare/Page/Catalog.elm | 10 +- src/css/app.css | 1 + src/css/elements.css | 1 + src/css/elements/hashvatar.css | 3 + src/css/project-listing.css | 13 + src/css/themes/unison/colors.css | 13 +- src/css/unison-share/page/catalog.css | 4 +- tests/Color/HarmonyTests.elm | 91 +++++++ 15 files changed, 905 insertions(+), 20 deletions(-) create mode 100644 src/Color/Harmony.elm create mode 100644 src/Hashvatar.elm create mode 100644 src/Hashvatar/HexGrid.elm create mode 100644 src/UI/Color.elm create mode 100644 src/css/elements/hashvatar.css create mode 100644 src/css/project-listing.css create mode 100644 tests/Color/HarmonyTests.elm diff --git a/elm.json b/elm.json index d330c2c..0f49d3f 100644 --- a/elm.json +++ b/elm.json @@ -7,6 +7,7 @@ "dependencies": { "direct": { "NoRedInk/elm-simple-fuzzy": "1.0.3", + "avh4/elm-color": "1.0.0", "elm/browser": "1.0.2", "elm/core": "1.0.5", "elm/html": "1.0.0", @@ -24,6 +25,7 @@ "j-maas/elm-ordered-containers": "1.0.0", "krisajenkins/remotedata": "6.0.1", "mgold/elm-nonempty-list": "4.1.0", + "noahzgordon/elm-color-extra": "1.0.2", "stoeffel/set-extra": "1.2.3", "wernerdegroot/listzipper": "4.0.0" }, @@ -33,12 +35,14 @@ "elm/random": "1.0.0", "elm/time": "1.0.0", "elm/virtual-dom": "1.0.2", + "fredcy/elm-parseint": "2.0.1", "rtfeldman/elm-iso8601-date-strings": "1.1.3" } }, "test-dependencies": { "direct": { - "elm-explorations/test": "1.2.2" + "elm-explorations/test": "1.2.2", + "TSFoster/elm-tuple-extra": "2.0.0" }, "indirect": {} } diff --git a/src/Color/Harmony.elm b/src/Color/Harmony.elm new file mode 100644 index 0000000..556dfde --- /dev/null +++ b/src/Color/Harmony.elm @@ -0,0 +1,186 @@ +module Color.Harmony exposing (..) + +import Color exposing (Color) +import List.Extra as ListE + + +harmonizesWith : Color -> List Color +harmonizesWith color = + let + complementary_ = + complementary color + + ( analogousA, analogousB ) = + analogous color + + ( triadicA, triadicB ) = + triadic color + + ( splitA, splitB ) = + splitComplementary color + + ( squareA, squareB, squareC ) = + square color + + ( tetridicA, tetridicB, tetridicC ) = + tetridic color + + harmonizesWith_ = + [ complementary_ + , analogousA + , analogousB + , triadicA + , triadicB + , splitA + , splitB + , squareA + , squareB + , squareC + , tetridicA + , tetridicB + , tetridicC + ] + in + ListE.uniqueBy Color.toCssString harmonizesWith_ + + +{-| RGB Difference - alpha is disregarded +-} +difference : Color -> Color -> Float +difference a b = + let + a_ = + toRgb255 a + + b_ = + toRgb255 b + + sum = + toFloat (((a_.red - b_.red) ^ 2) + ((a_.blue - b_.blue) ^ 2) + ((a_.green - b_.green) ^ 2)) + in + sqrt sum + + +toRgb255 : Color -> { red : Int, green : Int, blue : Int } +toRgb255 c = + let + rgba = + Color.toRgba c + in + { red = floor (rgba.red * 255) + , green = floor (rgba.red * 255) + , blue = floor (rgba.blue * 255) + } + + +{-| Opposites on the color wheel +-} +complementary : Color -> Color +complementary color = + hueAdd 180 color + + +{-| Adjacent colors on the color wheel +-} +analogous : Color -> ( Color, Color ) +analogous color = + ( hueAdd 30 color + , hueSubtract 30 color + ) + + +triadic : Color -> ( Color, Color ) +triadic color = + ( hueAdd 120 color + , hueAdd 240 color + ) + + +splitComplementary : Color -> ( Color, Color ) +splitComplementary color = + ( hueAdd 150 color + , hueAdd 210 color + ) + + +square : Color -> ( Color, Color, Color ) +square color = + ( hueAdd 90 color + , hueAdd 180 color + , hueAdd 270 color + ) + + +tetridic : Color -> ( Color, Color, Color ) +tetridic color = + ( hueAdd 60 color + , hueAdd 180 color + , hueAdd 240 color + ) + + + +-- Internal Helpers + + +hueAdd : Int -> Color -> Color +hueAdd num color = + let + hsla = + Color.toHsla color + + hue = + hsla.hue + |> toAngle + |> (+) num + |> wrap360 + |> toPt + in + Color.fromHsla { hsla | hue = hue } + + +hueSubtract : Int -> Color -> Color +hueSubtract num color = + let + hsla = + Color.toHsla color + + hue = + hsla.hue + |> toAngle + |> (-) num + |> wrap360 + |> toPt + in + Color.fromHsla { hsla | hue = hue } + + +toAngle : Float -> Int +toAngle pt = + let + a = + floor (pt * 360) + in + if a > 360 then + 360 - (360 - a) + + else + a + + +toPt : Int -> Float +toPt ang = + toFloat ang / 360 + + +wrap360 : Int -> Int +wrap360 h = + let + x = + modBy 360 h + in + if x < 0 then + x + 360 + + else + x diff --git a/src/Hashvatar.elm b/src/Hashvatar.elm new file mode 100644 index 0000000..fab9547 --- /dev/null +++ b/src/Hashvatar.elm @@ -0,0 +1,117 @@ +module Hashvatar exposing (..) + +import Hash exposing (Hash) +import Hashvatar.HexGrid as HexGrid +import Html exposing (Html, div) +import Html.Attributes exposing (class) +import List.Extra as ListE +import String.Extra exposing (break) +import UI.Color as Color exposing (Color) + + +view : Hash -> Html msg +view hash = + let + raw = + hash |> Hash.toString |> String.replace "#" "" + + numSlots = + 5 + + partLength = + String.length raw // numSlots + + parts = + break partLength raw + + toCharCodeSum str = + str + |> String.toList + |> List.foldl (\c acc -> acc + Char.toCode c) 0 + + grid = + parts + |> List.map toCharCodeSum + |> toGrid + |> Maybe.withDefault HexGrid.empty + in + div [ class "hashvatar" ] + [ HexGrid.view grid + ] + + + +-- Helpers + + +getIn : Int -> List Color -> Maybe Color +getIn unmoddedIdx colors_ = + ListE.getAt (modBy (List.length colors_) unmoddedIdx) colors_ + + +toGrid : List Int -> Maybe HexGrid.HexGrid +toGrid slots = + let + selectBg grid_ = + let + background = + getIn grid_.background Color.darkNonGrays + in + Maybe.map + (\bg -> + { background = bg + , tendrils = grid_.tendrils + , cell1 = grid_.cell1 + , cell2 = grid_.cell2 + , cell3 = grid_.cell3 + } + ) + background + + selectTendrils grid_ = + let + tendrils = + getIn grid_.tendrils (Color.harmonizesWith grid_.background) + in + Maybe.map + (\tr -> + { background = grid_.background + , tendrils = tr + , cell1 = grid_.cell1 + , cell2 = grid_.cell2 + , cell3 = grid_.cell3 + } + ) + tendrils + + selectCells grid_ = + Maybe.map3 + (\cell1 cell2 cell3 -> + { background = grid_.background + , tendrils = grid_.tendrils + , cell1 = cell1 + , cell2 = cell2 + , cell3 = cell3 + } + ) + (getIn grid_.cell1 (Color.harmonizesWith grid_.background)) + (getIn grid_.cell2 (Color.harmonizesWith grid_.background)) + (getIn grid_.cell3 (Color.harmonizesWith grid_.background)) + in + Maybe.map5 + (\background tendrils cell1 cell2 cell3 -> + { background = background + , tendrils = tendrils + , cell1 = cell1 + , cell2 = cell2 + , cell3 = cell3 + } + ) + (ListE.getAt 0 slots) + (ListE.getAt 1 slots) + (ListE.getAt 2 slots) + (ListE.getAt 3 slots) + (ListE.getAt 4 slots) + |> Maybe.andThen selectBg + |> Maybe.andThen selectTendrils + |> Maybe.andThen selectCells diff --git a/src/Hashvatar/HexGrid.elm b/src/Hashvatar/HexGrid.elm new file mode 100644 index 0000000..9fe4d8f --- /dev/null +++ b/src/Hashvatar/HexGrid.elm @@ -0,0 +1,112 @@ +{- TODO: Add Design Tokens!!! -} + + +module Hashvatar.HexGrid exposing (..) + +import Html exposing (Html) +import Svg exposing (g, mask, path, rect, svg) +import Svg.Attributes as Attrs + exposing + ( d + , fill + , height + , id + , maskUnits + , rx + , style + , viewBox + , width + , x + , y + ) +import UI.Color as Color exposing (Color) + + +type alias HexGrid = + { background : Color + , tendrils : Color + , cell1 : Color + , cell2 : Color + , cell3 : Color + } + + +empty : HexGrid +empty = + { background = Color.grayDarken20 + , tendrils = Color.grayBase + , cell1 = Color.grayLighten30 + , cell2 = Color.grayLighten30 + , cell3 = Color.grayLighten30 + } + + +view : HexGrid -> Html msg +view cfg_ = + let + cfg = + { background = Color.toCssString cfg_.background + , tendrils = Color.toCssString cfg_.tendrils + , cell1 = Color.toCssString cfg_.cell1 + , cell2 = Color.toCssString cfg_.cell2 + , cell3 = Color.toCssString cfg_.cell3 + } + + tendrils = + g [] + [ path [ fill cfg.tendrils, d "M7.65541 2.93911L10.3665 1.37384C8.12966 -2.50053 3.99576 -4.88724 -0.47798 -4.88724V-1.75671C2.87732 -1.75671 5.97777 0.0333302 7.65541 2.93911ZM10.3665 1.37384C9.9343 0.62518 8.977 0.368677 8.22835 0.80092C7.47969 1.23315 7.22317 2.19045 7.65541 2.93911C8.08764 3.68777 9.04497 3.94429 9.79362 3.51205C10.5423 3.07982 10.7988 2.12249 10.3665 1.37384ZM-0.47798 -4.88724C-1.34246 -4.88724 -2.04324 -4.18644 -2.04324 -3.32197C-2.04324 -2.45749 -1.34246 -1.75671 -0.47798 -1.75671C0.386494 -1.75671 1.08731 -2.45749 1.08731 -3.32197C1.08731 -4.18644 0.386494 -4.88724 -0.47798 -4.88724Z" ] [] + , path [ fill cfg.tendrils, d "M10.3665 1.37384L7.65541 2.93911C9.89228 6.81348 14.0262 9.2002 18.5 9.2002V6.06965C15.1447 6.06965 12.0442 4.27962 10.3665 1.37384ZM7.65541 2.93911C8.08764 3.68777 9.04497 3.94429 9.79362 3.51205C10.5423 3.07982 10.7988 2.12249 10.3665 1.37384C9.9343 0.62518 8.977 0.368677 8.22835 0.80092C7.47969 1.23315 7.22317 2.19045 7.65541 2.93911ZM18.5 9.2002C19.3645 9.2002 20.0652 8.49938 20.0652 7.63491C20.0652 6.77043 19.3645 6.06965 18.5 6.06965C17.6355 6.06965 16.9347 6.77043 16.9347 7.63491C16.9347 8.49938 17.6355 9.2002 18.5 9.2002Z" ] [] + , path [ fill cfg.tendrils, d "M18.5 9.2002V6.06965C14.0262 6.06965 9.89233 8.45638 7.65546 12.3307L10.3666 13.896C12.0442 10.9902 15.1447 9.2002 18.5 9.2002ZM18.5 6.06965C17.6355 6.06965 16.9347 6.77043 16.9347 7.63491C16.9347 8.49938 17.6355 9.2002 18.5 9.2002C19.3645 9.2002 20.0652 8.49938 20.0652 7.63491C20.0652 6.77043 19.3645 6.06965 18.5 6.06965ZM7.65546 12.3307C7.22322 13.0794 7.47973 14.0367 8.22838 14.469C8.97704 14.9012 9.93435 14.6447 10.3666 13.896C10.7988 13.1474 10.5423 12.1901 9.79366 11.7578C9.045 11.3256 8.08769 11.5821 7.65546 12.3307Z" ] [] + , path [ fill cfg.tendrils, d "M7.65546 12.3307L10.3666 13.896C12.6034 10.0217 12.6034 5.24822 10.3665 1.37384L7.65541 2.93911C9.33307 5.84489 9.33311 9.42498 7.65546 12.3307ZM10.3666 13.896C10.7988 13.1474 10.5423 12.1901 9.79366 11.7578C9.045 11.3256 8.08769 11.5821 7.65546 12.3307C7.22322 13.0794 7.47973 14.0367 8.22838 14.469C8.97704 14.9012 9.93435 14.6447 10.3666 13.896ZM10.3665 1.37384C9.9343 0.62518 8.977 0.368677 8.22835 0.80092C7.47969 1.23315 7.22317 2.19045 7.65541 2.93911C8.08764 3.68777 9.04497 3.94429 9.79362 3.51205C10.5423 3.07982 10.7988 2.12249 10.3665 1.37384Z" ] [] + , path [ fill cfg.tendrils, d "M10.3666 13.896L7.65546 12.3307C5.41859 16.2051 5.41859 20.9786 7.65546 24.853L10.3666 23.2877C8.68892 20.3819 8.68892 16.8018 10.3666 13.896ZM7.65546 12.3307C7.22322 13.0794 7.47973 14.0367 8.22838 14.469C8.97704 14.9012 9.93435 14.6447 10.3666 13.896C10.7988 13.1474 10.5423 12.1901 9.79366 11.7578C9.045 11.3256 8.08769 11.5821 7.65546 12.3307ZM7.65546 24.853C8.08769 25.6016 9.04499 25.8581 9.79364 25.4259C10.5423 24.9936 10.7988 24.0363 10.3666 23.2877C9.93435 22.539 8.97703 22.2825 8.22837 22.7148C7.47971 23.147 7.22321 24.1043 7.65546 24.853Z" ] [] + , path [ fill cfg.tendrils, d "M7.65546 24.853L10.3666 23.2877C8.12971 19.4133 3.99576 17.0265 -0.47798 17.0265V20.1571C2.87732 20.1571 5.97782 21.9472 7.65546 24.853ZM10.3666 23.2877C9.93435 22.539 8.97703 22.2825 8.22837 22.7148C7.47971 23.147 7.22321 24.1043 7.65546 24.853C8.08769 25.6016 9.04499 25.8581 9.79364 25.4259C10.5423 24.9936 10.7988 24.0363 10.3666 23.2877ZM-0.47798 17.0265C-1.34246 17.0265 -2.04324 17.7273 -2.04324 18.5918C-2.04324 19.4563 -1.34246 20.1571 -0.47798 20.1571C0.386494 20.1571 1.08731 19.4563 1.08731 18.5918C1.08731 17.7273 0.386494 17.0265 -0.47798 17.0265Z" ] [] + , path [ fill cfg.tendrils, d "M-0.47798 17.0265V20.1571C3.99576 20.1571 8.12971 17.7704 10.3666 13.896L7.65546 12.3307C5.9778 15.2365 2.87732 17.0265 -0.47798 17.0265ZM-0.47798 20.1571C0.386494 20.1571 1.08731 19.4563 1.08731 18.5918C1.08731 17.7273 0.386494 17.0265 -0.47798 17.0265C-1.34246 17.0265 -2.04324 17.7273 -2.04324 18.5918C-2.04324 19.4563 -1.34246 20.1571 -0.47798 20.1571ZM10.3666 13.896C10.7988 13.1474 10.5423 12.1901 9.79366 11.7578C9.045 11.3256 8.08769 11.5821 7.65546 12.3307C7.22322 13.0794 7.47973 14.0367 8.22838 14.469C8.97704 14.9012 9.93435 14.6447 10.3666 13.896Z" ] [] + , path [ fill cfg.tendrils, d "M6.56578 19.835C7.31443 19.4028 7.57095 18.4455 7.1387 17.6968C6.70647 16.9482 5.74916 16.6917 5.0005 17.1239C4.25185 17.5561 3.99534 18.5135 4.42758 19.2621C4.85981 20.0108 5.81712 20.2673 6.56578 19.835Z" ] [] + , path [ fill cfg.tendrils, d "M26.6334 13.896L29.3445 12.3307C27.1077 8.45638 22.9737 6.06965 18.5 6.06965V9.2002C21.8553 9.2002 24.9558 10.9902 26.6334 13.896ZM29.3445 12.3307C28.9123 11.5821 27.955 11.3256 27.2063 11.7578C26.4577 12.1901 26.2012 13.1474 26.6334 13.896C27.0656 14.6447 28.023 14.9012 28.7716 14.469C29.5203 14.0367 29.7768 13.0794 29.3445 12.3307ZM18.5 6.06965C17.6355 6.06965 16.9347 6.77043 16.9347 7.63491C16.9347 8.49938 17.6355 9.2002 18.5 9.2002C19.3645 9.2002 20.0652 8.49938 20.0652 7.63491C20.0652 6.77043 19.3645 6.06965 18.5 6.06965Z" ] [] + , path [ fill cfg.tendrils, d "M18.5 6.06965V9.2002C22.9737 9.2002 27.1077 6.81347 29.3445 2.9391L26.6334 1.37383C24.9558 4.27961 21.8553 6.06965 18.5 6.06965ZM18.5 9.2002C19.3645 9.2002 20.0652 8.49938 20.0652 7.63491C20.0652 6.77043 19.3645 6.06965 18.5 6.06965C17.6355 6.06965 16.9347 6.77043 16.9347 7.63491C16.9347 8.49938 17.6355 9.2002 18.5 9.2002ZM29.3445 2.9391C29.7768 2.19045 29.5203 1.23314 28.7716 0.800907C28.0229 0.368676 27.0656 0.625174 26.6334 1.37383C26.2012 2.12249 26.4577 3.0798 27.2063 3.51203C27.955 3.94426 28.9123 3.68776 29.3445 2.9391Z" ] [] + , path [ fill cfg.tendrils, d "M7.65546 24.853L10.3666 23.2877C8.12971 19.4133 3.99576 17.0265 -0.47798 17.0265V20.1571C2.87732 20.1571 5.97782 21.9472 7.65546 24.853ZM10.3666 23.2877C9.93435 22.539 8.97703 22.2825 8.22837 22.7148C7.47971 23.147 7.22321 24.1043 7.65546 24.853C8.08769 25.6016 9.04499 25.8581 9.79364 25.4259C10.5423 24.9936 10.7988 24.0363 10.3666 23.2877ZM-0.47798 17.0265C-1.34246 17.0265 -2.04324 17.7273 -2.04324 18.5918C-2.04324 19.4563 -1.34246 20.1571 -0.47798 20.1571C0.386494 20.1571 1.08731 19.4563 1.08731 18.5918C1.08731 17.7273 0.386494 17.0265 -0.47798 17.0265Z" ] [] + , path [ fill cfg.tendrils, d "M11.4563 6.39175C10.7076 6.82398 10.4511 7.78129 10.8834 8.52995C11.3156 9.2786 12.2729 9.53511 13.0216 9.10287C13.7702 8.67064 14.0267 7.71333 13.5945 6.96467C13.1623 6.21602 12.2049 5.95951 11.4563 6.39175Z" ] [] + , path [ fill cfg.tendrils, d "M10.3666 23.2877L7.65546 24.853C9.89233 28.7273 14.0262 31.114 18.5 31.114V27.9835C15.1447 27.9835 12.0442 26.1935 10.3666 23.2877ZM7.65546 24.853C8.08769 25.6016 9.04499 25.8581 9.79364 25.4259C10.5423 24.9936 10.7988 24.0363 10.3666 23.2877C9.93435 22.539 8.97703 22.2825 8.22837 22.7148C7.47971 23.147 7.22321 24.1043 7.65546 24.853ZM18.5 31.114C19.3645 31.114 20.0652 30.4132 20.0652 29.5488C20.0652 28.6843 19.3645 27.9835 18.5 27.9835C17.6355 27.9835 16.9347 28.6843 16.9347 29.5488C16.9347 30.4132 17.6355 31.114 18.5 31.114Z" ] [] + , path [ fill cfg.tendrils, d "M10.3666 -8.01774L7.65544 -9.58302C5.41857 -5.70865 5.41854 -0.935271 7.65541 2.93911L10.3665 1.37384C8.68888 -1.53194 8.68891 -5.11197 10.3666 -8.01774ZM7.65544 -9.58302C7.22321 -8.83436 7.4797 -7.87703 8.22835 -7.4448C8.97701 -7.01257 9.93433 -7.26909 10.3666 -8.01774C10.7988 -8.7664 10.5423 -9.7237 9.79363 -10.1559C9.04497 -10.5882 8.08767 -10.3317 7.65544 -9.58302ZM7.65541 2.93911C8.08764 3.68777 9.04497 3.94429 9.79362 3.51205C10.5423 3.07982 10.7988 2.12249 10.3665 1.37384C9.9343 0.62518 8.977 0.368677 8.22835 0.80092C7.47969 1.23315 7.22317 2.19045 7.65541 2.93911Z" ] [] + ] + + cell2 = + g [] + [ path [ fill cfg.cell2, d "M18.5 -0.191418V2.93912C20.7369 2.93912 22.8038 1.74576 23.9223 -0.191418L21.2111 -1.75669C20.6519 -0.788103 19.6184 -0.191418 18.5 -0.191418ZM18.5 2.93912C19.3645 2.93912 20.0653 2.23832 20.0653 1.37385C20.0653 0.509374 19.3645 -0.191418 18.5 -0.191418C17.6355 -0.191418 16.9347 0.509374 16.9347 1.37385C16.9347 2.23832 17.6355 2.93912 18.5 2.93912ZM23.9223 -0.191418C24.3545 -0.940073 24.098 -1.89738 23.3493 -2.32962C22.6007 -2.76185 21.6434 -2.50535 21.2111 -1.75669C20.7789 -1.00804 21.0354 -0.0507262 21.7841 0.381505C22.5327 0.813736 23.49 0.557238 23.9223 -0.191418Z" ] [] + , path [ fill cfg.cell2, d "M15.789 -1.75669L13.0778 -0.191412C14.1963 1.74578 16.2631 2.93912 18.5 2.93912V-0.191418C17.3816 -0.191418 16.3482 -0.788085 15.789 -1.75669ZM13.0778 -0.191412C13.5101 0.557243 14.4674 0.813754 15.216 0.38151C15.9647 -0.0507211 16.2212 -1.00803 15.789 -1.75669C15.3567 -2.50534 14.3994 -2.76185 13.6507 -2.32961C12.9021 -1.89738 12.6456 -0.940068 13.0778 -0.191412ZM18.5 2.93912C19.3645 2.93912 20.0653 2.23832 20.0653 1.37385C20.0653 0.509374 19.3645 -0.191418 18.5 -0.191418C17.6355 -0.191418 16.9347 0.509374 16.9347 1.37385C16.9347 2.23832 17.6355 2.93912 18.5 2.93912Z" ] [] + ] + + cell1 = + g + [] + [ path [ fill cfg.cell1, d "M2.23312 6.0697L4.94425 4.50443C3.82582 2.56724 1.75886 1.37389 -0.478011 1.37389V4.50443C0.640418 4.50443 1.67391 5.1011 2.23312 6.0697ZM4.94425 4.50443C4.51202 3.75577 3.55472 3.49927 2.80606 3.93151C2.05741 4.36374 1.80088 5.32105 2.23312 6.0697C2.66536 6.81836 3.62268 7.07488 4.37134 6.64263C5.11999 6.2104 5.37649 5.25308 4.94425 4.50443ZM-0.478011 1.37389C-1.34249 1.37389 -2.04327 2.0747 -2.04327 2.93918C-2.04327 3.80365 -1.34249 4.50443 -0.478011 4.50443C0.386464 4.50443 1.08728 3.80365 1.08728 2.93918C1.08728 2.0747 0.386464 1.37389 -0.478011 1.37389Z" ] [] + , path [ fill cfg.cell1, d "M2.23307 9.20018L4.9442 10.7655C6.06262 8.82827 6.06268 6.44161 4.94425 4.50443L2.23312 6.0697C2.79234 7.03829 2.79229 8.23159 2.23307 9.20018ZM4.9442 10.7655C5.37643 10.0168 5.11994 9.05949 4.37128 8.62726C3.62263 8.19503 2.6653 8.45152 2.23307 9.20018C1.80084 9.94884 2.05735 10.9062 2.80601 11.3384C3.55467 11.7706 4.51196 11.5141 4.9442 10.7655ZM4.94425 4.50443C4.51202 3.75577 3.55472 3.49927 2.80606 3.93151C2.05741 4.36374 1.80088 5.32105 2.23312 6.0697C2.66536 6.81836 3.62268 7.07488 4.37134 6.64263C5.11999 6.2104 5.37649 5.25308 4.94425 4.50443Z" ] [] + , path [ fill cfg.cell1, d "M-0.477996 10.7655V13.896C1.75887 13.896 3.82575 12.7026 4.9442 10.7655L2.23307 9.20018C1.67386 10.1688 0.640433 10.7655 -0.477996 10.7655ZM-0.477996 13.896C0.386479 13.896 1.08728 13.1952 1.08728 12.3308C1.08728 11.4663 0.386479 10.7655 -0.477996 10.7655C-1.34247 10.7655 -2.04327 11.4663 -2.04327 12.3308C-2.04327 13.1952 -1.34247 13.896 -0.477996 13.896ZM4.9442 10.7655C5.37643 10.0168 5.11994 9.05949 4.37128 8.62726C3.62263 8.19503 2.6653 8.45152 2.23307 9.20018C1.80084 9.94884 2.05735 10.9062 2.80601 11.3384C3.55467 11.7706 4.51196 11.5141 4.9442 10.7655Z" ] [] + ] + + cell3 = + g + [] + [ path [ fill cfg.cell3, d "M21.2111 17.0265L23.9222 15.4613C22.8038 13.5241 20.7369 12.3307 18.5 12.3307V15.4613C19.6184 15.4613 20.6519 16.0579 21.2111 17.0265ZM23.9222 15.4613C23.49 14.7126 22.5327 14.4561 21.7841 14.8884C21.0354 15.3206 20.7789 16.2779 21.2111 17.0265C21.6433 17.7752 22.6007 18.0317 23.3493 17.5995C24.098 17.1672 24.3545 16.2099 23.9222 15.4613ZM18.5 12.3307C17.6355 12.3307 16.9347 13.0315 16.9347 13.896C16.9347 14.7605 17.6355 15.4613 18.5 15.4613C19.3645 15.4613 20.0653 14.7605 20.0653 13.896C20.0653 13.0315 19.3645 12.3307 18.5 12.3307Z" ] [] + , path [ fill cfg.cell3, d "M21.2111 20.1571L23.9222 21.7224C25.0407 19.7852 25.0407 17.3984 23.9222 15.4613L21.2111 17.0265C21.7703 17.9951 21.7703 19.1885 21.2111 20.1571ZM23.9222 21.7224C24.3545 20.9737 24.098 20.0164 23.3493 19.5842C22.6007 19.1519 21.6433 19.4085 21.2111 20.1571C20.7789 20.9058 21.0354 21.8631 21.784 22.2953C22.5327 22.7275 23.49 22.471 23.9222 21.7224ZM23.9222 15.4613C23.49 14.7126 22.5327 14.4561 21.7841 14.8884C21.0354 15.3206 20.7789 16.2779 21.2111 17.0265C21.6433 17.7752 22.6007 18.0317 23.3493 17.5995C24.098 17.1672 24.3545 16.2099 23.9222 15.4613Z" ] [] + , path [ fill cfg.cell3, d "M18.5 21.7224V24.8529C20.7369 24.8529 22.8038 23.6596 23.9222 21.7224L21.2111 20.1571C20.6519 21.1257 19.6184 21.7224 18.5 21.7224ZM18.5 24.8529C19.3645 24.8529 20.0653 24.1521 20.0653 23.2877C20.0653 22.4232 19.3645 21.7224 18.5 21.7224C17.6355 21.7224 16.9347 22.4232 16.9347 23.2877C16.9347 24.1521 17.6355 24.8529 18.5 24.8529ZM23.9222 21.7224C24.3545 20.9737 24.098 20.0164 23.3493 19.5842C22.6007 19.1519 21.6433 19.4085 21.2111 20.1571C20.7789 20.9058 21.0354 21.8631 21.784 22.2953C22.5327 22.7275 23.49 22.471 23.9222 21.7224Z" ] [] + , path [ fill cfg.cell3, d "M15.7889 20.1571L13.0778 21.7224C14.1963 23.6596 16.2631 24.8529 18.5 24.8529V21.7224C17.3816 21.7224 16.3482 21.1257 15.7889 20.1571ZM13.0778 21.7224C13.5101 22.4711 14.4674 22.7276 15.216 22.2953C15.9647 21.8631 16.2212 20.9058 15.7889 20.1571C15.3567 19.4085 14.3994 19.152 13.6507 19.5842C12.9021 20.0164 12.6456 20.9738 13.0778 21.7224ZM18.5 24.8529C19.3645 24.8529 20.0653 24.1521 20.0653 23.2877C20.0653 22.4232 19.3645 21.7224 18.5 21.7224C17.6355 21.7224 16.9347 22.4232 16.9347 23.2877C16.9347 24.1521 17.6355 24.8529 18.5 24.8529Z" ] [] + , path [ fill cfg.cell3, d "M15.7888 17.0266L13.0777 15.4613C11.9592 17.3985 11.9594 19.7852 13.0778 21.7224L15.7889 20.1571C15.2297 19.1886 15.2296 17.9951 15.7888 17.0266ZM13.0777 15.4613C12.6454 16.2099 12.9019 17.1673 13.6506 17.5995C14.3992 18.0317 15.3565 17.7752 15.7888 17.0266C16.221 16.2779 15.9645 15.3206 15.2158 14.8884C14.4672 14.4561 13.5099 14.7126 13.0777 15.4613ZM13.0778 21.7224C13.5101 22.4711 14.4674 22.7276 15.216 22.2953C15.9647 21.8631 16.2212 20.9058 15.7889 20.1571C15.3567 19.4085 14.3994 19.152 13.6507 19.5842C12.9021 20.0164 12.6456 20.9738 13.0778 21.7224Z" ] [] + , path [ fill cfg.cell3, d "M18.5 15.4613V12.3307C16.2631 12.3307 14.1961 13.5241 13.0777 15.4613L15.7888 17.0266C16.348 16.058 17.3816 15.4613 18.5 15.4613ZM18.5 12.3307C17.6355 12.3307 16.9347 13.0315 16.9347 13.896C16.9347 14.7605 17.6355 15.4613 18.5 15.4613C19.3645 15.4613 20.0653 14.7605 20.0653 13.896C20.0653 13.0315 19.3645 12.3307 18.5 12.3307ZM13.0777 15.4613C12.6454 16.2099 12.9019 17.1673 13.6506 17.5995C14.3992 18.0317 15.3565 17.7752 15.7888 17.0266C16.221 16.2779 15.9645 15.3206 15.2158 14.8884C14.4672 14.4561 13.5099 14.7126 13.0777 15.4613Z" ] [] + ] + + background = + rect [ width "24", height "24", rx "4", fill cfg.background ] [] + in + -- the background rect provides both background color and boundary in the form of the mask hence its double usage + svg [ width "24", height "24", viewBox "0 0 24 24", fill "none" ] + [ mask [ id "hashvatar-mask", style "mask-type: alpha", maskUnits "userSpaceOnUse", x "0", y "0", width "24", height "24" ] + [ background ] + , g [ Attrs.mask "url(#hashvatar-mask)" ] + [ background + , tendrils + , cell1 + , cell2 + , cell3 + ] + ] diff --git a/src/Project.elm b/src/Project.elm index 79c7db6..f39a5a8 100644 --- a/src/Project.elm +++ b/src/Project.elm @@ -2,7 +2,11 @@ module Project exposing (..) import FullyQualifiedName as FQN exposing (FQN) import Hash exposing (Hash) +import Hashvatar +import Html exposing (Html) +import Html.Attributes exposing (class) import Json.Decode as Decode exposing (field, string) +import UI.Click as Click type Owner @@ -33,6 +37,24 @@ ownerToString (Owner o) = +-- View + + +viewSlug : Project a -> Html msg +viewSlug project = + project |> slug |> FQN.view + + +viewProjectListing : Click.Click msg -> Project a -> Html msg +viewProjectListing click project = + Click.view [ class "project-listing" ] + [ Hashvatar.view project.hash + , viewSlug project + ] + click + + + -- Decode diff --git a/src/UI/Click.elm b/src/UI/Click.elm index edd599c..83398ba 100644 --- a/src/UI/Click.elm +++ b/src/UI/Click.elm @@ -7,7 +7,9 @@ import Html.Events exposing (onClick) type Click msg = ExternalHref String + | Href String -- Internal route | OnClick msg + | Disabled attrs : Click msg -> List (Attribute msg) @@ -16,9 +18,15 @@ attrs click = ExternalHref href_ -> [ href href_, rel "noopener", target "_blank" ] + Href href_ -> + [ href href_ ] + OnClick msg -> [ onClick msg ] + Disabled -> + [] + view : List (Attribute msg) -> List (Html msg) -> Click msg -> Html msg view attrs_ content click = diff --git a/src/UI/Color.elm b/src/UI/Color.elm new file mode 100644 index 0000000..794f993 --- /dev/null +++ b/src/UI/Color.elm @@ -0,0 +1,338 @@ +{- TODO: Autogenerate from a JSON file -} + + +module UI.Color exposing (..) + +import Color as C +import Color.Accessibility exposing (luminance) +import Color.Harmony as Harmony +import List.Extra as ListE + + +{-| Wraps a more fundamental Color library (avh4/elm-color— a very standard +one from the Elm community). This should never be exposed as we only want +colors from the Unison Design System to be used. +-} +type Color + = Color C.Color + + + +-- Helpers + + +toCssString : Color -> String +toCssString (Color color) = + C.toCssString color + + +harmonizesWith : Color -> List Color +harmonizesWith ((Color color_) as color) = + let + harmonizesWith_ = + color_ + |> Harmony.harmonizesWith + |> List.map fromColor_ + |> ListE.uniqueBy toCssString + |> List.filter (\c -> c /= color) + in + harmonizesWith_ + + +{-| Find the closests color in the design system to a full gamut color +-} +fromColor_ : C.Color -> Color +fromColor_ full = + let + differences = + nonGrays + |> List.map + (\((Color c) as color) -> + ( Harmony.difference full c, color ) + ) + |> List.sortBy Tuple.first + in + differences + |> List.head + |> Maybe.map Tuple.second + |> Maybe.withDefault grayBase + + + +-- Colors + + +colors : List Color +colors = + grays ++ pinks ++ greens ++ blues ++ oranges ++ purples + + +nonGrays : List Color +nonGrays = + pinks ++ greens ++ blues ++ oranges ++ purples + + +dark : List Color +dark = + colors |> filterLuminance ((>) 0.5) + + +darkNonGrays : List Color +darkNonGrays = + nonGrays |> filterLuminance ((>) 0.5) + + +light : List Color +light = + colors |> filterLuminance ((<=) 0.5) + + +lightNonGrays : List Color +lightNonGrays = + nonGrays |> filterLuminance ((<=) 0.5) + + +filterLuminance : (Float -> Bool) -> List Color -> List Color +filterLuminance pred colors_ = + let + filter_ (Color fullGamut) = + pred (luminance fullGamut) + in + List.filter filter_ colors_ + + + +-- Grays + + +grays : List Color +grays = + [ grayDarken30 + , grayDarken25 + , grayDarken20 + , grayDarken10 + , grayBase + , grayLighten20 + , grayLighten30 + , grayLighten40 + , grayLighten45 + , grayLighten50 + , grayLighten55 + , grayLighten60 + , grayLighten100 + ] + + +grayDarken30 : Color +grayDarken30 = + Color (C.rgb255 24 24 28) + + +grayDarken25 : Color +grayDarken25 = + Color (C.rgb255 34 35 42) + + +grayDarken20 : Color +grayDarken20 = + Color (C.rgb255 45 46 53) + + +grayDarken10 : Color +grayDarken10 = + Color (C.rgb255 65 66 75) + + +grayBase : Color +grayBase = + Color (C.rgb255 81 82 88) + + +grayLighten20 : Color +grayLighten20 = + Color (C.rgb255 129 130 134) + + +grayLighten30 : Color +grayLighten30 = + Color (C.rgb255 189 191 198) + + +grayLighten40 : Color +grayLighten40 = + Color (C.rgb255 209 213 220) + + +grayLighten45 : Color +grayLighten45 = + Color (C.rgb255 217 224 231) + + +grayLighten50 : Color +grayLighten50 = + Color (C.rgb255 228 234 243) + + +grayLighten55 : Color +grayLighten55 = + Color (C.rgb255 241 243 245) + + +grayLighten60 : Color +grayLighten60 = + Color (C.rgb255 250 250 251) + + +grayLighten100 : Color +grayLighten100 = + Color (C.rgb255 255 255 255) + + + +-- Pinks + + +pinks : List Color +pinks = + [ pink1, pink2, pink3 ] + + +pink1 : Color +pink1 = + Color (C.rgb255 255 71 86) + + +pink2 : Color +pink2 = + Color (C.rgb255 255 108 120) + + +pink3 : Color +pink3 = + Color (C.rgb255 255 155 163) + + + +-- Greens + + +greens : List Color +greens = + [ green1, green2, green3, green4 ] + + +green1 : Color +green1 = + Color (C.rgb255 39 174 96) + + +green2 : Color +green2 = + Color (C.rgb255 82 209 136) + + +green3 : Color +green3 = + Color (C.rgb255 136 243 181) + + +green4 : Color +green4 = + Color (C.rgb255 198 255 222) + + + +-- Blues + + +blues : List Color +blues = + [ blue1, blue2, blue3, blue4, blue5 ] + + +blue1 : Color +blue1 = + Color (C.rgb255 34 94 190) + + +blue2 : Color +blue2 = + Color (C.rgb255 86 149 244) + + +blue3 : Color +blue3 = + Color (C.rgb255 158 197 255) + + +blue4 : Color +blue4 = + Color (C.rgb255 203 224 255) + + +blue5 : Color +blue5 = + Color (C.rgb255 236 242 250) + + + +-- Oranges + + +oranges : List Color +oranges = + [ orange1, orange2, orange3, orange4, orange5 ] + + +orange1 : Color +orange1 = + Color (C.rgb255 255 136 0) + + +orange2 : Color +orange2 = + Color (C.rgb255 255 196 31) + + +orange3 : Color +orange3 = + Color (C.rgb255 255 224 139) + + +orange4 : Color +orange4 = + Color (C.rgb255 255 238 190) + + +orange5 : Color +orange5 = + Color (C.rgb255 255 247 223) + + + +-- Purples + + +purples : List Color +purples = + [ purple1, purple2, purple3, purple4 ] + + +purple1 : Color +purple1 = + Color (C.rgb255 85 55 123) + + +purple2 : Color +purple2 = + Color (C.rgb255 115 77 163) + + +purple3 : Color +purple3 = + Color (C.rgb255 154 118 200) + + +purple4 : Color +purple4 = + Color (C.rgb255 198 168 236) diff --git a/src/UnisonShare/Page/Catalog.elm b/src/UnisonShare/Page/Catalog.elm index bf77240..f1bdf2e 100644 --- a/src/UnisonShare/Page/Catalog.elm +++ b/src/UnisonShare/Page/Catalog.elm @@ -2,7 +2,6 @@ module UnisonShare.Page.Catalog exposing (..) import Api import Env exposing (Env) -import FullyQualifiedName as FQN import Html exposing (Html, a, div, h1, input, span, strong, text) import Html.Attributes exposing (autofocus, class, href, placeholder) import Html.Events exposing (onBlur, onFocus, onInput) @@ -13,6 +12,7 @@ import RemoteData exposing (RemoteData(..), WebData) import Task import UI import UI.Card as Card +import UI.Click as Click import UI.Icon as Icon import UI.PageLayout as PageLayout exposing (PageLayout) import UnisonShare.Catalog as Catalog exposing (Catalog) @@ -107,11 +107,7 @@ projectUrl = viewProjectListing : ProjectListing -> Html msg viewProjectListing project = - let - slug = - Project.slug project - in - a [ class "project-listing", href (projectUrl project) ] [ FQN.view slug ] + Project.viewProjectListing (Click.Href (projectUrl project)) project viewCategory : ( String, List ProjectListing ) -> Html msg @@ -129,7 +125,7 @@ viewSearchResult : ( ProjectListing, String ) -> Html msg viewSearchResult ( project, category ) = a [ class "search-result", href (projectUrl project) ] - [ project |> Project.slug |> FQN.view + [ Project.viewProjectListing Click.Disabled project , span [ class "category" ] [ text category ] ] diff --git a/src/css/app.css b/src/css/app.css index f1b1aaa..bfb5b03 100644 --- a/src/css/app.css +++ b/src/css/app.css @@ -33,3 +33,4 @@ @import "./perspective-landing.css"; @import "./workspace.css"; @import "./definition-doc.css"; +@import "./project-listing.css"; diff --git a/src/css/elements.css b/src/css/elements.css index 3ec1006..a47e17b 100644 --- a/src/css/elements.css +++ b/src/css/elements.css @@ -187,3 +187,4 @@ p { @import "./elements/fold-toggle.css"; @import "./elements/fully-qualified-name.css"; @import "./elements/card.css"; +@import "./elements/hashvatar.css"; diff --git a/src/css/elements/hashvatar.css b/src/css/elements/hashvatar.css new file mode 100644 index 0000000..a7859b9 --- /dev/null +++ b/src/css/elements/hashvatar.css @@ -0,0 +1,3 @@ +.hashvatar { + display: inline-flex; +} diff --git a/src/css/project-listing.css b/src/css/project-listing.css new file mode 100644 index 0000000..69d54fd --- /dev/null +++ b/src/css/project-listing.css @@ -0,0 +1,13 @@ +.project-listing { + display: flex; + flex-direction: row; + align-items: center; +} + +.project-listing .hashvatar { + margin-right: 0.5rem; +} + +.project-listing:hover { + text-decoration: none; +} diff --git a/src/css/themes/unison/colors.css b/src/css/themes/unison/colors.css index 592ee8a..36fbf95 100644 --- a/src/css/themes/unison/colors.css +++ b/src/css/themes/unison/colors.css @@ -12,18 +12,11 @@ --color-gray-darken-20-transparent: rgba(45, 46, 53, 0); --color-gray-darken-10-transparent: rgba(65, 66, 75, 0); - /* Brand colors */ - --color-brand-bright-red: #ff4756; - --color-brand-orange: #ff8800; - --color-brand-yellow: #ffc41f; - --color-brand-purple: #8f228f; - --color-brand-dark-purple: #520066; - /* Grays */ - --color-gray-darken-10: #41424b; - --color-gray-darken-20: #2d2e35; - --color-gray-darken-25: #22232a; --color-gray-darken-30: #18181c; + --color-gray-darken-25: #22232a; + --color-gray-darken-20: #2d2e35; + --color-gray-darken-10: #41424b; --color-gray-base: #515258; diff --git a/src/css/unison-share/page/catalog.css b/src/css/unison-share/page/catalog.css index 26db3a5..1fa9468 100644 --- a/src/css/unison-share/page/catalog.css +++ b/src/css/unison-share/page/catalog.css @@ -61,7 +61,7 @@ border-radius: var(--border-radius-base); position: absolute; top: calc(var(--page-hero-height) - 1.75rem); - border: 2px solid transparent; + border: 2px solid var(--color-catalog-hero-bg); box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.1); display: flex; flex-direction: column; @@ -69,7 +69,6 @@ transform: translateX(-50%); z-index: var(--layer-base); transition: all 0.2s; - background: var(--color-gray-lighten-55); } .catalog-hero .catalog-search:focus-within { @@ -143,6 +142,7 @@ font-size: var(--font-size-medium); margin-left: auto; justify-self: flex-end; + text-transform: uppercase; } .catalog-hero .catalog-search .search-results .empty-state { diff --git a/tests/Color/HarmonyTests.elm b/tests/Color/HarmonyTests.elm new file mode 100644 index 0000000..15f8573 --- /dev/null +++ b/tests/Color/HarmonyTests.elm @@ -0,0 +1,91 @@ +module Color.HarmonyTests exposing (..) + +import Color exposing (Color, toCssString) +import Color.Harmony as Harmony +import Expect +import Test exposing (..) +import Tuple3 exposing (mapAllThree) + + +complementary : Test +complementary = + describe "Color.Harmony.complementary" + [ test "Returns the complementary color" <| + \_ -> + Expect.equal (toCssString (Harmony.complementary mainColor)) + "rgba(0%,100%,100%,1)" + ] + + +analogous : Test +analogous = + describe "Color.Harmony.analogous" + [ test "Returns the analogous colors" <| + \_ -> + let + result = + Tuple.mapBoth toCssString toCssString (Harmony.analogous mainColor) + in + Expect.equal result ( "rgba(100%,50%,0%,1)", "rgba(100%,50%,0%,1)" ) + ] + + +triadic : Test +triadic = + describe "Color.Harmony.triadic" + [ test "Returns the triadic colors" <| + \_ -> + let + result = + Tuple.mapBoth toCssString toCssString (Harmony.triadic mainColor) + in + Expect.equal result ( "rgba(0%,100%,0%,1)", "rgba(0%,0%,100%,1)" ) + ] + + +splitComplementary : Test +splitComplementary = + describe "Color.Harmony.splitComplementary" + [ test "Returns the split complementary colors" <| + \_ -> + let + result = + Tuple.mapBoth toCssString toCssString (Harmony.splitComplementary mainColor) + in + Expect.equal result ( "rgba(0%,100%,50%,1)", "rgba(0%,50%,100%,1)" ) + ] + + +square : Test +square = + describe "Color.Harmony.square" + [ test "Returns the square colors" <| + \_ -> + let + result = + mapAllThree toCssString toCssString toCssString (Harmony.square mainColor) + in + Expect.equal result ( "rgba(50%,100%,0%,1)", "rgba(0%,100%,100%,1)", "rgba(50%,0%,100%,1)" ) + ] + + +tetridic : Test +tetridic = + describe "Color.Harmony.tetridic" + [ test "Returns the tetridic colors" <| + \_ -> + let + result = + mapAllThree toCssString toCssString toCssString (Harmony.tetridic mainColor) + in + Expect.equal result ( "rgba(100%,100%,0%,1)", "rgba(0%,100%,100%,1)", "rgba(0%,0%,100%,1)" ) + ] + + + +-- Helpers + + +mainColor : Color +mainColor = + Color.rgb255 255 0 0 From 35b36b3276b4f0c29850fc3e13064321582e5896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Thu, 6 Jan 2022 12:38:25 -0500 Subject: [PATCH 35/54] Add a project route for Unison Share Move the perspective routes into 1 route with 2 subroutes: Project with ProjectRoute. --- src/UnisonShare/App.elm | 42 ++++++++----------- src/UnisonShare/Route.elm | 58 +++++++++++++++----------- tests/UnisonShare/RouteTests.elm | 71 +++++++++++++++++++------------- 3 files changed, 95 insertions(+), 76 deletions(-) diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index 682d56f..fc0a45b 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -59,9 +59,10 @@ type alias Model = init : Env -> Route -> Nav.Key -> ( Model, Cmd Msg ) init env route navKey = let + -- TODO: This whole thing should be route driven ( workspace, workspaceCmd ) = case route of - Route.Definition _ ref -> + Route.Project _ (Route.ProjectDefinition ref) -> Workspace.init env (Just ref) _ -> @@ -156,7 +157,7 @@ update msg ({ env } as model) = in ( { model2 | catalog = catalog }, Cmd.map CatalogMsg cmd ) - Route.Definition params ref -> + Route.Project params (Route.ProjectDefinition ref) -> let ( workspace, cmd ) = Workspace.open (newEnv params) model.workspace ref @@ -169,7 +170,7 @@ update msg ({ env } as model) = in ( model4, Cmd.batch [ Cmd.map WorkspaceMsg cmd, fetchPerspectiveCmd ] ) - Route.Perspective params -> + Route.Project params Route.ProjectRoot -> fetchPerspectiveAndCodebaseTree env.perspective { model2 | env = newEnv params } ( _, ChangePerspective perspective ) -> @@ -233,27 +234,20 @@ update msg ({ env } as model) = in ( { model | catalog = catalog }, Cmd.map CatalogMsg cmd ) - ( _, WorkspaceMsg wMsg ) -> - -- TODO: Clean this up, there should be a top level Project route - -- instead of 2 separate workspace routes (perspective and - -- definition) - if model.route /= Route.Catalog then - let - ( workspace, wCmd, outMsg ) = - Workspace.update env wMsg model.workspace - - model2 = - { model | workspace = workspace } + ( Route.Project _ _, WorkspaceMsg wMsg ) -> + let + ( workspace, wCmd, outMsg ) = + Workspace.update env wMsg model.workspace - ( model3, cmd ) = - handleWorkspaceOutMsg model2 outMsg - in - ( model3, Cmd.batch [ cmd, Cmd.map WorkspaceMsg wCmd ] ) + model2 = + { model | workspace = workspace } - else - ( model, Cmd.none ) + ( model3, cmd ) = + handleWorkspaceOutMsg model2 outMsg + in + ( model3, Cmd.batch [ cmd, Cmd.map WorkspaceMsg wCmd ] ) - ( _, PerspectiveLandingMsg rMsg ) -> + ( Route.Project _ Route.ProjectRoot, PerspectiveLandingMsg rMsg ) -> let ( perspectiveLanding, outMsg ) = PerspectiveLanding.update rMsg model.perspectiveLanding @@ -271,7 +265,7 @@ update msg ({ env } as model) = PerspectiveLanding.None -> ( model2, Cmd.none ) - ( _, CodebaseTreeMsg cMsg ) -> + ( Route.Project _ _, CodebaseTreeMsg cMsg ) -> let ( codebaseTree, cCmd, outMsg ) = CodebaseTree.update env cMsg model.codebaseTree @@ -690,7 +684,7 @@ view model = Route.Catalog -> Html.map CatalogMsg (Catalog.view model.catalog) - Route.Perspective _ -> + Route.Project _ Route.ProjectRoot -> Html.map PerspectiveLandingMsg (PerspectiveLanding.view model.env @@ -699,7 +693,7 @@ view model = |> withSidebar |> PageLayout.view - Route.Definition _ _ -> + Route.Project _ (Route.ProjectDefinition _) -> Html.map WorkspaceMsg (Workspace.view model.workspace) |> withSidebar |> PageLayout.view diff --git a/src/UnisonShare/Route.elm b/src/UnisonShare/Route.elm index ec7df46..d927750 100644 --- a/src/UnisonShare/Route.elm +++ b/src/UnisonShare/Route.elm @@ -1,5 +1,6 @@ module UnisonShare.Route exposing - ( Route(..) + ( ProjectRoute(..) + , Route(..) , forProject , fromUrl , navigate @@ -73,10 +74,14 @@ import Url.Builder exposing (relative) -} +type ProjectRoute + = ProjectRoot + | ProjectDefinition Reference + + type Route = Catalog - | Perspective PerspectiveParams - | Definition PerspectiveParams Reference + | Project PerspectiveParams ProjectRoute updatePerspectiveParams : Route -> PerspectiveParams -> Route @@ -85,11 +90,11 @@ updatePerspectiveParams route params = Catalog -> Catalog - Perspective _ -> - Perspective params + Project _ ProjectRoot -> + Project params ProjectRoot - Definition _ ref -> - Definition params ref + Project _ (ProjectDefinition ref) -> + Project params (ProjectDefinition ref) @@ -103,12 +108,12 @@ catalog = perspective : Parser Route perspective = - succeed Perspective |. slash |= RP.perspectiveParams |. end + succeed (\pp -> Project pp ProjectRoot) |. slash |= RP.perspectiveParams |. end definition : Parser Route definition = - succeed Definition |. slash |= RP.perspectiveParams |. slash |= reference |. end + succeed (\pp r -> Project pp (ProjectDefinition r)) |. slash |= RP.perspectiveParams |. slash |= reference |. end toRoute : Parser Route @@ -149,7 +154,7 @@ fromUrl basePath url = "/" ++ path parse url_ = - Result.withDefault (Perspective (ByCodebase Relative)) (Parser.run toRoute url_) + Result.withDefault (Project (ByCodebase Relative) ProjectRoot) (Parser.run toRoute url_) in url |> .path @@ -168,11 +173,8 @@ perspectiveParams route = Catalog -> Nothing - Perspective nsRef -> - Just nsRef - - Definition nsRef _ -> - Just nsRef + Project pp _ -> + Just pp @@ -185,7 +187,7 @@ forProject project_ = fqn = FQN.cons (Project.ownerToString project_.owner) project_.name in - Perspective (Perspective.ByNamespace Relative fqn) + Project (Perspective.ByNamespace Relative fqn) ProjectRoot @@ -200,7 +202,7 @@ toDefinition oldRoute ref = |> perspectiveParams |> Maybe.withDefault (ByCodebase Relative) in - Definition params ref + Project params (ProjectDefinition ref) toUrlString : Route -> String @@ -253,10 +255,10 @@ toUrlString route = Catalog -> [ "catalog" ] - Perspective pp -> + Project pp ProjectRoot -> perspectiveParamsToPath pp False - Definition pp ref -> + Project pp (ProjectDefinition ref) -> case ref of TypeReference hq -> perspectiveParamsToPath pp True ++ ("types" :: hqToPath hq) @@ -284,9 +286,13 @@ navigate navKey route = |> Nav.pushUrl navKey + +-- TODO: this should go away in UnisonShare + + navigateToPerspective : Nav.Key -> PerspectiveParams -> Cmd msg navigateToPerspective navKey perspectiveParams_ = - navigate navKey (Perspective perspectiveParams_) + navigate navKey (Project perspectiveParams_ ProjectRoot) navigateToCurrentPerspective : Nav.Key -> Route -> Cmd msg @@ -310,6 +316,10 @@ navigateToDefinition navKey currentRoute reference = navigate navKey (toDefinition currentRoute reference) + +-- TODO: This should go away in UnisonShare + + replacePerspective : Nav.Key -> PerspectiveParams -> Route -> Cmd msg replacePerspective navKey perspectiveParams_ oldRoute = let @@ -318,10 +328,10 @@ replacePerspective navKey perspectiveParams_ oldRoute = Catalog -> Catalog - Perspective _ -> - Perspective perspectiveParams_ + Project _ ProjectRoot -> + Project perspectiveParams_ ProjectRoot - Definition _ ref -> - Definition perspectiveParams_ ref + Project _ (ProjectDefinition ref) -> + Project perspectiveParams_ (ProjectDefinition ref) in navigate navKey newRoute diff --git a/tests/UnisonShare/RouteTests.elm b/tests/UnisonShare/RouteTests.elm index 9794295..138c580 100644 --- a/tests/UnisonShare/RouteTests.elm +++ b/tests/UnisonShare/RouteTests.elm @@ -7,31 +7,44 @@ import Hash exposing (Hash) import HashQualified as HQ import Perspective exposing (CodebasePerspectiveParam(..), PerspectiveParams(..)) import Test exposing (..) -import UnisonShare.Route as Route +import UnisonShare.Route as Route exposing (ProjectRoute(..), Route(..)) import Url exposing (Url) -perspectiveRoute : Test -perspectiveRoute = - describe "Route.fromUrl : perspective route" - [ test "Matches root to relative codease perspective" <| +catalogRoute : Test +catalogRoute = + describe "Route.fromUrl : catalog route" + [ test "Matches /catalog to Catalog" <| \_ -> let url = - mkUrl "/" - - expected = - Route.Perspective (ByCodebase Relative) + mkUrl "/catalog" in - Expect.equal expected (Route.fromUrl "" url) - , test "Matches a codebase relative perspective" <| + Expect.equal Catalog (Route.fromUrl "" url) + + {- TODO enable when cutting over to catalog + , test "Matches root to Catalog" <| + \_ -> + let + url = + mkUrl "/" + in + Expect.equal Catalog (Route.fromUrl "" url) + -} + ] + + +projectRootRoute : Test +projectRootRoute = + describe "Route.fromUrl : project root route" + [ test "Matches a codebase relative perspective" <| \_ -> let url = mkUrl "/latest" expected = - Route.Perspective (ByCodebase Relative) + Project (ByCodebase Relative) ProjectRoot in Expect.equal expected (Route.fromUrl "" url) , test "Matches a codebase relative perspective with namespace" <| @@ -41,7 +54,7 @@ perspectiveRoute = mkUrl "/latest/namespaces/base/List" expected = - Route.Perspective (ByNamespace Relative (fqn "base.List")) + Project (ByNamespace Relative (fqn "base.List")) ProjectRoot in Expect.equal expected (Route.fromUrl "" url) , test "Matches a codebase absolute perspective" <| @@ -52,7 +65,7 @@ perspectiveRoute = expected = hash "@codebasehash" - |> Maybe.map (\h -> Route.Perspective (ByCodebase (Absolute h))) + |> Maybe.map (\h -> Project (ByCodebase (Absolute h)) ProjectRoot) in Expect.equal expected (Just (Route.fromUrl "" url)) , test "Matches a codebase absolute perspective with namespace" <| @@ -63,7 +76,7 @@ perspectiveRoute = expected = hash "@codebasehash" - |> Maybe.map (\h -> Route.Perspective (ByNamespace (Absolute h) (fqn "base.List"))) + |> Maybe.map (\h -> Project (ByNamespace (Absolute h) (fqn "base.List")) ProjectRoot) in Expect.equal expected (Just (Route.fromUrl "" url)) , test "Matches a namespace with special characters" <| @@ -73,7 +86,7 @@ perspectiveRoute = mkUrl "/latest/namespaces/base/List/;./%2F/docs" expected = - Route.Perspective (ByNamespace Relative (segments [ "base", "List", ".", "/", "docs" ])) + Route.Project (ByNamespace Relative (segments [ "base", "List", ".", "/", "docs" ])) ProjectRoot in Expect.equal expected (Route.fromUrl "" url) ] @@ -89,7 +102,7 @@ definitionRoute = mkUrl "/latest/terms/base/List/map" expected = - Route.Definition (ByCodebase Relative) (Reference.fromString TermReference "base.List.map") + Project (ByCodebase Relative) (ProjectDefinition (Reference.fromString TermReference "base.List.map")) in Expect.equal expected (Route.fromUrl "" url) , test "Matches a codebase relative and relative definition within a namespace" <| @@ -99,7 +112,7 @@ definitionRoute = mkUrl "/latest/namespaces/base/List/;/terms/map" expected = - Route.Definition (ByNamespace Relative (fqn "base.List")) (Reference.fromString TermReference "map") + Project (ByNamespace Relative (fqn "base.List")) (ProjectDefinition (Reference.fromString TermReference "map")) in Expect.equal expected (Route.fromUrl "" url) , test "Matches a codebase relative and absolute definition" <| @@ -109,7 +122,7 @@ definitionRoute = mkUrl "/latest/terms/@definitionhash" expected = - Route.Definition (ByCodebase Relative) (Reference.fromString TermReference "#definitionhash") + Project (ByCodebase Relative) (ProjectDefinition (Reference.fromString TermReference "#definitionhash")) in Expect.equal expected (Route.fromUrl "" url) , test "Matches a codebase relative and absolute definition within a namespace" <| @@ -119,7 +132,7 @@ definitionRoute = mkUrl "/latest/namespaces/base/List/;/terms/@definitionhash" expected = - Route.Definition (ByNamespace Relative (fqn "base.List")) (Reference.fromString TermReference "#definitionhash") + Project (ByNamespace Relative (fqn "base.List")) (ProjectDefinition (Reference.fromString TermReference "#definitionhash")) in Expect.equal expected (Route.fromUrl "" url) , test "Matches a codebase absolute and relative definition " <| @@ -130,7 +143,7 @@ definitionRoute = expected = hash "@codebasehash" - |> Maybe.map (\h -> Route.Definition (ByCodebase (Absolute h)) (Reference.fromString TermReference "base.List.map")) + |> Maybe.map (\h -> Project (ByCodebase (Absolute h)) (ProjectDefinition (Reference.fromString TermReference "base.List.map"))) in Expect.equal expected (Just (Route.fromUrl "" url)) , test "Matches a codebase absolute and relative definition within a namespace" <| @@ -141,7 +154,7 @@ definitionRoute = expected = hash "@codebasehash" - |> Maybe.map (\h -> Route.Definition (ByNamespace (Absolute h) (fqn "base.List")) (Reference.fromString TermReference "map")) + |> Maybe.map (\h -> Project (ByNamespace (Absolute h) (fqn "base.List")) (ProjectDefinition (Reference.fromString TermReference "map"))) in Expect.equal expected (Just (Route.fromUrl "" url)) , test "Matches a codebase absolute and absolute definition " <| @@ -152,7 +165,7 @@ definitionRoute = expected = hash "@codebasehash" - |> Maybe.map (\h -> Route.Definition (ByCodebase (Absolute h)) (Reference.fromString TermReference "#definitionhash")) + |> Maybe.map (\h -> Project (ByCodebase (Absolute h)) (ProjectDefinition (Reference.fromString TermReference "#definitionhash"))) in Expect.equal expected (Just (Route.fromUrl "" url)) , test "Matches a codebase absolute and absolute definition within a namespace" <| @@ -163,7 +176,7 @@ definitionRoute = expected = hash "@codebasehash" - |> Maybe.map (\h -> Route.Definition (ByNamespace (Absolute h) (fqn "base.List")) (Reference.fromString TermReference "map")) + |> Maybe.map (\h -> Project (ByNamespace (Absolute h) (fqn "base.List")) (ProjectDefinition (Reference.fromString TermReference "map"))) in Expect.equal expected (Just (Route.fromUrl "" url)) , test "Matches a namespace and definition with special characters" <| @@ -173,9 +186,11 @@ definitionRoute = mkUrl "/latest/namespaces/base/List/;./%2F/docs/;/terms/docs/about/;./and/%2F/doc" expected = - Route.Definition + Project (ByNamespace Relative (segments [ "base", "List", ".", "/", "docs" ])) - (TermReference (HQ.NameOnly (segments [ "docs", "about", ".", "and", "/", "doc" ]))) + (ProjectDefinition + (TermReference (HQ.NameOnly (segments [ "docs", "about", ".", "and", "/", "doc" ]))) + ) in Expect.equal expected (Route.fromUrl "" url) ] @@ -194,7 +209,7 @@ fromUrlBasePath = "/some-token/ui/" expected = - Route.Definition (ByCodebase Relative) (Reference.fromString TermReference "#abc123") + Project (ByCodebase Relative) (ProjectDefinition (Reference.fromString TermReference "#abc123")) in Expect.equal expected (Route.fromUrl basePath url) , test "Matches with a root basePath prefix" <| @@ -207,7 +222,7 @@ fromUrlBasePath = "/" expected = - Route.Definition (ByCodebase Relative) (Reference.fromString TermReference "#abc123") + Project (ByCodebase Relative) (ProjectDefinition (Reference.fromString TermReference "#abc123")) in Expect.equal expected (Route.fromUrl basePath url) ] From 719f7b81fc409cb2f96492f55ad73e39e25f19d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Fri, 7 Jan 2022 12:40:09 -0500 Subject: [PATCH 36/54] Show Hashvatar in the sidebar perspective When any namespace is set as the perspective, show a hashvatar, defaulting to a blank one. --- src/Hashvatar.elm | 14 ++- src/UI.elm | 5 - src/UnisonLocal/App.elm | 10 +- src/UnisonShare/App.elm | 10 +- src/css/elements.css | 9 -- src/css/ui/page-layout.css | 4 +- src/img/namespace-slug-untitled.svg | 161 ---------------------------- 7 files changed, 29 insertions(+), 184 deletions(-) delete mode 100644 src/img/namespace-slug-untitled.svg diff --git a/src/Hashvatar.elm b/src/Hashvatar.elm index fab9547..6e04512 100644 --- a/src/Hashvatar.elm +++ b/src/Hashvatar.elm @@ -9,6 +9,11 @@ import String.Extra exposing (break) import UI.Color as Color exposing (Color) +empty : Html msg +empty = + view_ HexGrid.empty + + view : Hash -> Html msg view hash = let @@ -35,9 +40,12 @@ view hash = |> toGrid |> Maybe.withDefault HexGrid.empty in - div [ class "hashvatar" ] - [ HexGrid.view grid - ] + view_ grid + + +view_ : HexGrid.HexGrid -> Html msg +view_ grid = + div [ class "hashvatar" ] [ HexGrid.view grid ] diff --git a/src/UI.elm b/src/UI.elm index f726681..d18e4fe 100644 --- a/src/UI.elm +++ b/src/UI.elm @@ -71,8 +71,3 @@ divider = charWidth : Int -> String charWidth numChars = String.fromInt numChars ++ "ch" - - -namespaceSlug : Html msg -namespaceSlug = - div [ class "namespace-slug" ] [] diff --git a/src/UnisonLocal/App.elm b/src/UnisonLocal/App.elm index 503fa9d..8ead521 100644 --- a/src/UnisonLocal/App.elm +++ b/src/UnisonLocal/App.elm @@ -9,6 +9,7 @@ import Env exposing (Env, OperatingSystem(..)) import Finder import Finder.SearchOptions as SearchOptions import FullyQualifiedName as FQN exposing (FQN) +import Hashvatar import Html exposing (Html, a, div, h1, h2, h3, nav, p, section, span, strong, text) import Html.Attributes exposing (class, classList, href, id, rel, target, title) import Html.Events exposing (onClick) @@ -493,7 +494,7 @@ viewSidebarHeader env = Codebase _ -> UI.nothing - Namespace { fqn } -> + Namespace { fqn, details } -> let -- Imprecise, but close enough, approximation of overflowing, -- which results in a slight faded left edge A better way would @@ -501,11 +502,16 @@ viewSidebarHeader env = -- thats quite involved... isOverflowing = fqn |> FQN.toString |> String.length |> (\l -> l > 20) + + hashvatar = + details + |> RemoteData.map (Namespace.hash >> Hashvatar.view) + |> RemoteData.withDefault Hashvatar.empty in Sidebar.header [ Sidebar.headerItem [ classList [ ( "is-overflowing", isOverflowing ) ] ] - [ UI.namespaceSlug + [ hashvatar , h2 [ class "namespace" ] [ FQN.view fqn ] ] , UI.divider diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index fc0a45b..b36fb1a 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -7,6 +7,7 @@ import CodebaseTree import Definition.Reference exposing (Reference) import Env exposing (Env) import FullyQualifiedName as FQN exposing (FQN) +import Hashvatar import Html exposing (Html, a, div, h1, h2, nav, p, span, text) import Html.Attributes exposing (class, classList, id, title) import Html.Events exposing (onClick) @@ -512,7 +513,7 @@ viewSidebarHeader env = Codebase _ -> UI.nothing - Namespace { fqn } -> + Namespace { fqn, details } -> let -- Imprecise, but close enough, approximation of overflowing, -- which results in a slight faded left edge A better way would @@ -527,11 +528,16 @@ viewSidebarHeader env = |> Button.view |> List.singleton |> Sidebar.headerItem [] + + hashvatar = + details + |> RemoteData.map (Namespace.hash >> Hashvatar.view) + |> RemoteData.withDefault Hashvatar.empty in Sidebar.header [ Sidebar.headerItem [ classList [ ( "is-overflowing", isOverflowing ) ] ] - [ UI.namespaceSlug + [ hashvatar , h2 [ class "namespace" ] [ FQN.view fqn ] ] , download diff --git a/src/css/elements.css b/src/css/elements.css index a47e17b..4e396a2 100644 --- a/src/css/elements.css +++ b/src/css/elements.css @@ -171,15 +171,6 @@ p { align-items: center; } -/* Namespace Slug */ -.namespace-slug { - display: inline-flex; - flex: 0 0 1.5rem; - width: 1.5rem; - height: 1.5rem; - background: url(../img/namespace-slug-untitled.svg); -} - @import "./elements/icon.css"; @import "./elements/banner.css"; @import "./elements/button.css"; diff --git a/src/css/ui/page-layout.css b/src/css/ui/page-layout.css index 657118d..a965ce9 100644 --- a/src/css/ui/page-layout.css +++ b/src/css/ui/page-layout.css @@ -240,11 +240,11 @@ ); } -#main-sidebar .sidebar-header .namespace-slug { +#main-sidebar .sidebar-header .hasvatar { position: relative; } -#main-sidebar .sidebar-header .is-overflowing .namespace-slug:after { +#main-sidebar .sidebar-header .is-overflowing .hashvatar:after { position: absolute; top: 0; right: -1.5rem; diff --git a/src/img/namespace-slug-untitled.svg b/src/img/namespace-slug-untitled.svg deleted file mode 100644 index 763c133..0000000 --- a/src/img/namespace-slug-untitled.svg +++ /dev/null @@ -1,161 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 755c41c7bee3d87e9e71d19d8d762429c9ed5ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Tue, 11 Jan 2022 15:28:05 -0500 Subject: [PATCH 37/54] Add keyboard support to catalog search When searching through the catalog support keyboard navigation with up and down arrows as well as selection with enter and indexed selection with ; followed by an index number. This mirrors the functionality of the Finder. --- src/Env.elm | 7 +- src/SearchResults.elm | 6 + src/UnisonLocal/App.elm | 22 ++- src/UnisonLocal/PreApp.elm | 8 +- src/UnisonShare/App.elm | 26 ++- src/UnisonShare/Page/Catalog.elm | 223 ++++++++++++++++++++++---- src/UnisonShare/PreApp.elm | 8 +- src/UnisonShare/Route.elm | 6 + src/css/elements/card.css | 2 +- src/css/unison-share/page/catalog.css | 66 ++++++-- 10 files changed, 290 insertions(+), 84 deletions(-) diff --git a/src/Env.elm b/src/Env.elm index 15bc6dc..0901b46 100644 --- a/src/Env.elm +++ b/src/Env.elm @@ -1,6 +1,7 @@ module Env exposing (..) import Api exposing (ApiBasePath(..)) +import Browser.Navigation as Nav import Env.AppContext as AppContext exposing (AppContext) import Perspective exposing (Perspective) @@ -19,6 +20,7 @@ type alias Env = , basePath : String , apiBasePath : ApiBasePath , appContext : AppContext + , navKey : Nav.Key , perspective : Perspective } @@ -31,12 +33,13 @@ type alias Flags = } -init : Flags -> Perspective -> Env -init flags perspective = +init : Flags -> Nav.Key -> Perspective -> Env +init flags navKey perspective = { operatingSystem = operatingSystemFromString flags.operatingSystem , basePath = flags.basePath , apiBasePath = ApiBasePath flags.apiBasePath , appContext = AppContext.fromString flags.appContext + , navKey = navKey , perspective = perspective } diff --git a/src/SearchResults.elm b/src/SearchResults.elm index 7c00580..1eeb7e5 100644 --- a/src/SearchResults.elm +++ b/src/SearchResults.elm @@ -1,6 +1,7 @@ module SearchResults exposing ( Matches , SearchResults(..) + , empty , focus , focusOn , from @@ -32,6 +33,11 @@ type SearchResults a | SearchResults (Matches a) +empty : SearchResults a +empty = + Empty + + isEmpty : SearchResults a -> Bool isEmpty results = case results of diff --git a/src/UnisonLocal/App.elm b/src/UnisonLocal/App.elm index 503fa9d..da828c3 100644 --- a/src/UnisonLocal/App.elm +++ b/src/UnisonLocal/App.elm @@ -48,8 +48,7 @@ type Modal type alias Model = - { navKey : Nav.Key - , route : Route + { route : Route , codebaseTree : CodebaseTree.Model , workspace : Workspace.Model , perspectiveLanding : PerspectiveLanding.Model @@ -63,8 +62,8 @@ type alias Model = } -init : Env -> Route -> Nav.Key -> ( Model, Cmd Msg ) -init env route navKey = +init : Env -> Route -> ( Model, Cmd Msg ) +init env route = let ( workspace, workspaceCmd ) = case route of @@ -84,8 +83,7 @@ init env route navKey = |> Maybe.withDefault Cmd.none model = - { navKey = navKey - , route = route + { route = route , workspace = workspace , perspectiveLanding = PerspectiveLanding.init , codebaseTree = codebaseTree @@ -132,7 +130,7 @@ update msg ({ env } as model) = LinkClicked urlRequest -> case urlRequest of Browser.Internal url -> - ( model, Nav.pushUrl model.navKey (Url.toString url) ) + ( model, Nav.pushUrl env.navKey (Url.toString url) ) -- External links are handled via target blank and never end up -- here @@ -298,7 +296,7 @@ update msg ({ env } as model) = navigateToDefinition : Model -> Reference -> ( Model, Cmd Msg ) navigateToDefinition model ref = - ( model, Route.navigateToDefinition model.navKey model.route ref ) + ( model, Route.navigateToDefinition model.env.navKey model.route ref ) navigateToPerspective : Model -> Perspective -> ( Model, Cmd Msg ) @@ -318,7 +316,7 @@ navigateToPerspective model perspective = |> Maybe.withDefault model.route changeRouteCmd = - Route.replacePerspective model.navKey (Perspective.toParams perspective) focusedReferenceRoute + Route.replacePerspective model.env.navKey (Perspective.toParams perspective) focusedReferenceRoute in ( { model | workspace = workspace }, changeRouteCmd ) @@ -351,7 +349,7 @@ fetchPerspectiveAndCodebaseTree oldPerspective ({ env } as model) = handleWorkspaceOutMsg : Model -> Workspace.OutMsg -> ( Model, Cmd Msg ) -handleWorkspaceOutMsg model out = +handleWorkspaceOutMsg ({ env } as model) out = case out of Workspace.None -> ( model, Cmd.none ) @@ -360,10 +358,10 @@ handleWorkspaceOutMsg model out = showFinder model withinNamespace Workspace.Focused ref -> - ( model, Route.navigateToDefinition model.navKey model.route ref ) + ( model, Route.navigateToDefinition env.navKey model.route ref ) Workspace.Emptied -> - ( model, Route.navigateToCurrentPerspective model.navKey model.route ) + ( model, Route.navigateToCurrentPerspective env.navKey model.route ) Workspace.ChangePerspectiveToNamespace fqn -> fqn diff --git a/src/UnisonLocal/PreApp.elm b/src/UnisonLocal/PreApp.elm index 9c2d798..d7c415e 100644 --- a/src/UnisonLocal/PreApp.elm +++ b/src/UnisonLocal/PreApp.elm @@ -42,10 +42,10 @@ init flags url navKey = perspectiveToAppInit perspective = let env = - Env.init preEnv.flags perspective + Env.init preEnv.flags preEnv.navKey perspective ( app, cmd ) = - App.init env preEnv.route preEnv.navKey + App.init env preEnv.route in ( Initialized app, Cmd.map AppMsg cmd ) @@ -79,7 +79,7 @@ update msg model = Ok perspective -> let env = - Env.init preEnv.flags perspective + Env.init preEnv.flags preEnv.navKey perspective newRoute = perspective @@ -87,7 +87,7 @@ update msg model = |> Route.updatePerspectiveParams preEnv.route ( app, cmd ) = - App.init env newRoute preEnv.navKey + App.init env newRoute in ( Initialized app, Cmd.map AppMsg cmd ) diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index fc0a45b..11e8328 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -40,8 +40,7 @@ import Workspace.WorkspaceItems as WorkspaceItems type alias Model = - { navKey : Nav.Key - , route : Route + { route : Route , codebaseTree : CodebaseTree.Model , workspace : Workspace.Model , perspectiveLanding : PerspectiveLanding.Model @@ -56,8 +55,8 @@ type alias Model = } -init : Env -> Route -> Nav.Key -> ( Model, Cmd Msg ) -init env route navKey = +init : Env -> Route -> ( Model, Cmd Msg ) +init env route = let -- TODO: This whole thing should be route driven ( workspace, workspaceCmd ) = @@ -81,8 +80,7 @@ init env route navKey = Catalog.init env model = - { navKey = navKey - , route = route + { route = route , workspace = workspace , perspectiveLanding = PerspectiveLanding.init , codebaseTree = codebaseTree @@ -131,7 +129,7 @@ update msg ({ env } as model) = ( _, LinkClicked urlRequest ) -> case urlRequest of Browser.Internal url -> - ( model, Nav.pushUrl model.navKey (Url.toString url) ) + ( model, Nav.pushUrl env.navKey (Url.toString url) ) -- External links are handled via target blank and never end up -- here @@ -230,7 +228,7 @@ update msg ({ env } as model) = ( Route.Catalog, CatalogMsg cMsg ) -> let ( catalog, cmd ) = - Catalog.update cMsg model.catalog + Catalog.update env cMsg model.catalog in ( { model | catalog = catalog }, Cmd.map CatalogMsg cmd ) @@ -310,7 +308,7 @@ update msg ({ env } as model) = navigateToDefinition : Model -> Reference -> ( Model, Cmd Msg ) navigateToDefinition model ref = - ( model, Route.navigateToDefinition model.navKey model.route ref ) + ( model, Route.navigateToDefinition model.env.navKey model.route ref ) navigateToPerspective : Model -> Perspective -> ( Model, Cmd Msg ) @@ -330,7 +328,7 @@ navigateToPerspective model perspective = |> Maybe.withDefault model.route changeRouteCmd = - Route.replacePerspective model.navKey (Perspective.toParams perspective) focusedReferenceRoute + Route.replacePerspective model.env.navKey (Perspective.toParams perspective) focusedReferenceRoute in ( { model | workspace = workspace }, changeRouteCmd ) @@ -363,7 +361,7 @@ fetchPerspectiveAndCodebaseTree oldPerspective ({ env } as model) = handleWorkspaceOutMsg : Model -> Workspace.OutMsg -> ( Model, Cmd Msg ) -handleWorkspaceOutMsg model out = +handleWorkspaceOutMsg ({ env } as model) out = case out of Workspace.None -> ( model, Cmd.none ) @@ -372,14 +370,14 @@ handleWorkspaceOutMsg model out = showFinder model withinNamespace Workspace.Focused ref -> - ( model, Route.navigateToDefinition model.navKey model.route ref ) + ( model, Route.navigateToDefinition env.navKey model.route ref ) Workspace.Emptied -> - ( model, Route.navigateToCurrentPerspective model.navKey model.route ) + ( model, Route.navigateToCurrentPerspective env.navKey model.route ) Workspace.ChangePerspectiveToNamespace fqn -> fqn - |> Perspective.toNamespacePerspective model.env.perspective + |> Perspective.toNamespacePerspective env.perspective |> navigateToPerspective model diff --git a/src/UnisonShare/Page/Catalog.elm b/src/UnisonShare/Page/Catalog.elm index f1bdf2e..33cec9f 100644 --- a/src/UnisonShare/Page/Catalog.elm +++ b/src/UnisonShare/Page/Catalog.elm @@ -2,13 +2,17 @@ module UnisonShare.Page.Catalog exposing (..) import Api import Env exposing (Env) -import Html exposing (Html, a, div, h1, input, span, strong, text) -import Html.Attributes exposing (autofocus, class, href, placeholder) -import Html.Events exposing (onBlur, onFocus, onInput) +import Html exposing (Html, div, h1, input, strong, table, tbody, td, text, tr) +import Html.Attributes exposing (autofocus, class, classList, placeholder) +import Html.Events exposing (onBlur, onClick, onFocus, onInput) import Http +import KeyboardShortcut exposing (KeyboardShortcut(..)) +import KeyboardShortcut.Key as Key exposing (Key(..)) +import KeyboardShortcut.KeyboardEvent as KeyboardEvent exposing (KeyboardEvent) import Perspective import Project exposing (ProjectListing) import RemoteData exposing (RemoteData(..), WebData) +import SearchResults exposing (SearchResults(..)) import Task import UI import UI.Card as Card @@ -23,10 +27,23 @@ import UnisonShare.Route as Route -- MODEL +type alias SearchResult = + ( ProjectListing, String ) + + +type alias CatalogSearchResults = + SearchResults SearchResult + + +type alias CatalogSearch = + { query : String, results : CatalogSearchResults } + + type alias LoadedModel = - { query : String + { search : CatalogSearch , hasFocus : Bool , catalog : Catalog + , keyboardShortcut : KeyboardShortcut.Model } @@ -68,12 +85,14 @@ type Msg = UpdateQuery String | UpdateFocus Bool | ClearQuery - | NoOp + | SelectProject ProjectListing | FetchCatalogFinished (Result Http.Error Catalog) + | Keydown KeyboardEvent + | KeyboardShortcutMsg KeyboardShortcut.Msg -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = +update : Env -> Msg -> Model -> ( Model, Cmd Msg ) +update env msg model = case ( msg, model ) of ( FetchCatalogFinished catalogResult, _ ) -> case catalogResult of @@ -81,21 +100,119 @@ update msg model = ( Failure e, Cmd.none ) Ok catalog -> - ( Success { query = "", hasFocus = True, catalog = catalog }, Cmd.none ) + let + initModel = + { search = { query = "", results = SearchResults.empty } + , hasFocus = True + , catalog = catalog + , keyboardShortcut = KeyboardShortcut.init env.operatingSystem + } + in + ( Success initModel, Cmd.none ) ( UpdateFocus hasFocus, Success m ) -> ( Success { m | hasFocus = hasFocus }, Cmd.none ) ( UpdateQuery query, Success m ) -> - ( Success { m | query = query }, Cmd.none ) + let + searchResults = + if String.length query < 3 then + SearchResults.empty + + else + query + |> Catalog.search m.catalog + |> SearchResults.fromList + in + ( Success { m | search = { query = query, results = searchResults } }, Cmd.none ) ( ClearQuery, Success m ) -> - ( Success { m | query = "" }, Cmd.none ) + ( Success { m | search = { query = "", results = SearchResults.empty } }, Cmd.none ) + + ( SelectProject project, Success m ) -> + ( Success m, Route.navigateToProject env.navKey project ) + + ( Keydown event, Success m ) -> + let + ( keyboardShortcut, kCmd ) = + KeyboardShortcut.collect m.keyboardShortcut event.key + + cmd = + Cmd.map KeyboardShortcutMsg kCmd + + newModel = + { m | keyboardShortcut = keyboardShortcut } + + shortcut = + KeyboardShortcut.fromKeyboardEvent m.keyboardShortcut event + in + case shortcut of + Sequence _ Escape -> + ( Success { newModel | search = { query = "", results = SearchResults.empty } }, cmd ) + + Sequence _ ArrowUp -> + let + newSearch = + mapSearch SearchResults.prev m.search + in + ( Success { newModel | search = newSearch }, cmd ) + + Sequence _ ArrowDown -> + let + newSearch = + mapSearch SearchResults.next m.search + in + ( Success { newModel | search = newSearch }, cmd ) + + Sequence _ Enter -> + case m.search.results of + Empty -> + ( Success newModel, cmd ) + + SearchResults matches -> + let + navigate = + matches + |> SearchResults.focus + |> Tuple.first + |> Route.navigateToProject env.navKey + in + ( Success newModel, Cmd.batch [ cmd, navigate ] ) + + Sequence (Just Semicolon) k -> + case Key.toNumber k of + Just n -> + let + navigate = + SearchResults.getAt (n - 1) m.search.results + |> Maybe.map Tuple.first + |> Maybe.map (Route.navigateToProject env.navKey) + |> Maybe.withDefault Cmd.none + in + ( Success newModel, Cmd.batch [ cmd, navigate ] ) + + Nothing -> + ( Success newModel, cmd ) + + _ -> + ( Success newModel, cmd ) + + ( KeyboardShortcutMsg kMsg, Success m ) -> + let + ( keyboardShortcut, cmd ) = + KeyboardShortcut.update kMsg m.keyboardShortcut + in + ( Success { m | keyboardShortcut = keyboardShortcut }, Cmd.map KeyboardShortcutMsg cmd ) _ -> ( model, Cmd.none ) +mapSearch : (CatalogSearchResults -> CatalogSearchResults) -> CatalogSearch -> CatalogSearch +mapSearch f search = + { search | results = f search.results } + + -- VIEW @@ -121,32 +238,69 @@ viewCategory ( category, projects ) = |> Card.view -viewSearchResult : ( ProjectListing, String ) -> Html msg -viewSearchResult ( project, category ) = - a - [ class "search-result", href (projectUrl project) ] - [ Project.viewProjectListing Click.Disabled project - , span [ class "category" ] [ text category ] +viewMatch : KeyboardShortcut.Model -> SearchResult -> Bool -> Maybe Key -> Html Msg +viewMatch keyboardShortcut ( project, category ) isFocused shortcut = + let + shortcutIndicator = + if isFocused then + KeyboardShortcut.view keyboardShortcut (Sequence Nothing Key.Enter) + + else + case shortcut of + Nothing -> + UI.nothing + + Just key -> + KeyboardShortcut.view keyboardShortcut (Sequence (Just Key.Semicolon) key) + in + tr + [ classList [ ( "search-result", True ), ( "focused", isFocused ) ] + , onClick (SelectProject project) + ] + [ td [ class "project-name" ] [ Project.viewProjectListing Click.Disabled project ] + , td [ class "category" ] [ text category ] + , td [] [ div [ class "shortcut" ] [ shortcutIndicator ] ] ] -viewSearchResults : LoadedModel -> Html msg -viewSearchResults model = - if String.length model.query > 3 then - let - results = - model.query - |> Catalog.search model.catalog - |> List.map viewSearchResult +indexToShortcut : Int -> Maybe Key +indexToShortcut index = + let + n = + index + 1 + in + if n > 9 then + Nothing + + else + n |> String.fromInt |> Key.fromString |> Just + +viewMatches : KeyboardShortcut.Model -> SearchResults.Matches SearchResult -> Html Msg +viewMatches keyboardShortcut matches = + let + matchItems = + matches + |> SearchResults.mapMatchesToList (\d f -> ( d, f )) + |> List.indexedMap (\i ( d, f ) -> ( d, f, indexToShortcut i )) + |> List.map (\( d, f, s ) -> viewMatch keyboardShortcut d f s) + in + table [] [ tbody [] matchItems ] + + +viewSearchResults : KeyboardShortcut.Model -> CatalogSearch -> Html Msg +viewSearchResults keyboardShortcut { query, results } = + if String.length query > 2 then + let resultsPane = - if List.isEmpty results then - [ div [ class "empty-state" ] [ text ("No matching projects found for \"" ++ model.query ++ "\"") ] ] + case results of + Empty -> + div [ class "empty-state" ] [ text ("No matching projects found for \"" ++ query ++ "\"") ] - else - results + SearchResults matches -> + viewMatches keyboardShortcut matches in - div [ class "search-results" ] resultsPane + div [ class "search-results" ] [ resultsPane ] else UI.nothing @@ -162,10 +316,17 @@ viewLoaded model = searchResults = if model.hasFocus then - viewSearchResults model + viewSearchResults model.keyboardShortcut model.search else UI.nothing + + keyboardEvent = + KeyboardEvent.on KeyboardEvent.Keydown Keydown + |> KeyboardEvent.stopPropagation + |> KeyboardEvent.preventDefaultWhen + (\evt -> List.member evt.key [ ArrowUp, ArrowDown, Semicolon ]) + |> KeyboardEvent.attach in PageLayout.HeroLayout { hero = @@ -182,7 +343,7 @@ viewLoaded model = ] , div [] [ text "Projects, libraries, documention, terms, and types" ] ] - , div [ class "catalog-search" ] + , div [ class "catalog-search", keyboardEvent ] [ div [ class "search-field" ] [ Icon.view Icon.search , input diff --git a/src/UnisonShare/PreApp.elm b/src/UnisonShare/PreApp.elm index c58d988..789354c 100644 --- a/src/UnisonShare/PreApp.elm +++ b/src/UnisonShare/PreApp.elm @@ -47,10 +47,10 @@ init flags url navKey = perspectiveToAppInit perspective = let env = - Env.init preEnv.flags perspective + Env.init preEnv.flags preEnv.navKey perspective ( app, cmd ) = - App.init env preEnv.route preEnv.navKey + App.init env preEnv.route in ( Initialized app, Cmd.map AppMsg cmd ) @@ -84,7 +84,7 @@ update msg model = Ok perspective -> let env = - Env.init preEnv.flags perspective + Env.init preEnv.flags preEnv.navKey perspective newRoute = perspective @@ -92,7 +92,7 @@ update msg model = |> Route.updatePerspectiveParams preEnv.route ( app, cmd ) = - App.init env newRoute preEnv.navKey + App.init env newRoute in ( Initialized app, Cmd.map AppMsg cmd ) diff --git a/src/UnisonShare/Route.elm b/src/UnisonShare/Route.elm index d927750..2ea2955 100644 --- a/src/UnisonShare/Route.elm +++ b/src/UnisonShare/Route.elm @@ -8,6 +8,7 @@ module UnisonShare.Route exposing , navigateToDefinition , navigateToLatestCodebase , navigateToPerspective + , navigateToProject , perspectiveParams , replacePerspective , toDefinition @@ -286,6 +287,11 @@ navigate navKey route = |> Nav.pushUrl navKey +navigateToProject : Nav.Key -> Project a -> Cmd msg +navigateToProject navKey project = + navigate navKey (forProject project) + + -- TODO: this should go away in UnisonShare diff --git a/src/css/elements/card.css b/src/css/elements/card.css index 35c6998..b9ce301 100644 --- a/src/css/elements/card.css +++ b/src/css/elements/card.css @@ -11,6 +11,6 @@ .card .card-title { color: var(--color-card-title); text-transform: uppercase; - font-size: var(--font-size-medium); + font-size: var(--font-size-small); font-weight: normal; } diff --git a/src/css/unison-share/page/catalog.css b/src/css/unison-share/page/catalog.css index 1fa9468..5e0fe35 100644 --- a/src/css/unison-share/page/catalog.css +++ b/src/css/unison-share/page/catalog.css @@ -116,35 +116,69 @@ background: var(--color-main-bg); border-top: 1px solid var(--color-gray-lighten-50); border-radius: 0 0 var(--border-radius-base) var(--border-radius-base); - display: flex; - flex-direction: column; - gap: 0.75rem; padding: 0.75rem; } -.catalog-hero .catalog-search .search-results .search-result { - display: flex; - flex-direction: row; - padding: 0.5rem 1rem; - align-items: center; +.catalog-hero .catalog-search .search-results table { + width: 100%; +} + +.catalog-hero .catalog-search .search-results .search-result td { + padding: 0.5rem 0.75rem; height: 3rem; - border-radius: var(--border-radius-base); font-size: 1rem; } -.catalog-hero .catalog-search .search-results .search-result:hover { - background: var(--color-gray-lighten-55); - text-decoration: none; +.catalog-hero .catalog-search .search-results td:first-child { + border-radius: var(--border-radius-base) 0 0 var(--border-radius-base); +} + +.catalog-hero .catalog-search .search-results td:last { + border-radius: 0 var(--border-radius-base) var(--border-radius-base) 0; +} + +.catalog-hero .catalog-search .search-results .search-result td.project-name { + width: 20em; + text-overflow: ellipsis; + overflow: hidden; } -.catalog-hero .catalog-search .search-results .search-result .category { +.catalog-hero .catalog-search .search-results .search-result td.category { color: var(--color-main-subtle-fg); - font-size: var(--font-size-medium); - margin-left: auto; - justify-self: flex-end; + font-size: var(--font-size-small); text-transform: uppercase; } +.catalog-hero .catalog-search .search-results .search-result .shortcut { + display: flex; + align-items: center; + justify-content: flex-end; +} + +.catalog-hero .catalog-search .search-results .search-result .key { + color: var(--color-modal-subtle-fg-em); + background: var(--color-modal-subtle-bg); +} + +.catalog-hero .catalog-search .search-results .search-result .key.active { + color: var(--color-modal-focus-subtle-fg); + background: var(--color-modal-focus-subtle-bg); +} + +.catalog-hero .catalog-search .search-results .search-result.focused { + background: var(--color-gray-lighten-55); +} + +.catalog-hero .catalog-search .search-results .search-result.focused .key { + color: var(--color-modal-focus-subtle-fg); + background: var(--color-modal-focus-subtle-bg); +} + +.catalog-hero .catalog-search .search-results .search-result:hover { + background: var(--color-gray-lighten-60); + text-decoration: none; +} + .catalog-hero .catalog-search .search-results .empty-state { font-size: var(--font-size-base); color: var(--color-main-subtle-fg); From f12590643eb86dbcec043abd385c60ebda306a5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Wed, 12 Jan 2022 10:27:55 -0500 Subject: [PATCH 38/54] Hashvatar: use background hue for tendrils When picking the tendril color for the `Hashvatar`, pick one in the same hue as the background color. To support this add a `Hue` type to `Color` to help determine what hue a `Color` is in. --- src/Hashvatar.elm | 2 +- src/UI/Color.elm | 92 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/src/Hashvatar.elm b/src/Hashvatar.elm index fab9547..b09774e 100644 --- a/src/Hashvatar.elm +++ b/src/Hashvatar.elm @@ -71,7 +71,7 @@ toGrid slots = selectTendrils grid_ = let tendrils = - getIn grid_.tendrils (Color.harmonizesWith grid_.background) + getIn grid_.tendrils (Color.inSameHue grid_.background) in Maybe.map (\tr -> diff --git a/src/UI/Color.elm b/src/UI/Color.elm index 794f993..2000e32 100644 --- a/src/UI/Color.elm +++ b/src/UI/Color.elm @@ -102,6 +102,98 @@ filterLuminance pred colors_ = +-- Hue + + +type Hue + = Gray + | Orange + | Pink + | Purple + | Blue + | Green + + +hueOf : Color -> Hue +hueOf color = + if isGray color then + Gray + + else if isOrange color then + Orange + + else if isPink color then + Pink + + else if isPurple color then + Purple + + else if isBlue color then + Blue + + else + Green + + +{-| Get all the other colors in the same hue as the given color +-} +inSameHue : Color -> List Color +inSameHue color = + let + sameHue = + case hueOf color of + Gray -> + grays + + Orange -> + oranges + + Pink -> + pinks + + Purple -> + purples + + Blue -> + blues + + Green -> + greens + in + List.filter (\c -> c /= color) sameHue + + +isGray : Color -> Bool +isGray color = + List.member color grays + + +isOrange : Color -> Bool +isOrange color = + List.member color oranges + + +isPink : Color -> Bool +isPink color = + List.member color pinks + + +isPurple : Color -> Bool +isPurple color = + List.member color purples + + +isBlue : Color -> Bool +isBlue color = + List.member color blues + + +isGreen : Color -> Bool +isGreen color = + List.member color greens + + + -- Grays From 706d0564d431e996e77808437d94dd6bf57e9379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Thu, 20 Jan 2022 09:24:08 -0500 Subject: [PATCH 39/54] css reset --- src/css/base.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/css/base.css b/src/css/base.css index 2e6b10f..ac68636 100644 --- a/src/css/base.css +++ b/src/css/base.css @@ -30,6 +30,9 @@ html { /* TODO: Make configurable by users */ /* https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant-ligatures */ font-variant-ligatures: none; /* normal */ + -moz-text-size-adjust: none; + -webkit-text-size-adjust: none; + text-size-adjust: none; } body { @@ -37,5 +40,4 @@ body { line-height: 1.4; background: var(--color-main-bg); color: var(--color-main-fg); - -webkit-text-size-adjust: 100%; } From 95923331f73a8deedb4ad7396d39e4fcf48025e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Thu, 20 Jan 2022 09:25:45 -0500 Subject: [PATCH 40/54] package-lock.json --- package-lock.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package-lock.json b/package-lock.json index 1f1ce69..1a628d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "codebase-ui", "version": "1.0.0", "devDependencies": { "archiver": "^5.3.0", From 32beb1632ec789840b43dd32ac1426f1b404b9f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Fri, 21 Jan 2022 09:37:55 -0500 Subject: [PATCH 41/54] upgrade webpack --- package-lock.json | 6037 ++++++++++++++++++++++----------------------- package.json | 6 +- 2 files changed, 2998 insertions(+), 3045 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1a628d4..3d39d82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,18 +20,18 @@ "filemanager-webpack-plugin": "^4.0.0", "html-webpack-plugin": "^5.3.1", "style-loader": "^2.0.0", - "webpack": "^5.35.0", - "webpack-cli": "^4.6.0", - "webpack-dev-server": "^4.0.0-beta.3" + "webpack": "^5.66.0", + "webpack-cli": "^4.9.1", + "webpack-dev-server": "^4.7.3" }, "engines": { "node": ">=12.0.0" } }, "node_modules/@discoveryjs/json-ext": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz", - "integrity": "sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", + "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==", "dev": true, "engines": { "node": ">=10.0.0" @@ -57,43 +57,34 @@ "dev": true }, "node_modules/@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.4", + "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" }, "engines": { "node": ">= 8" } }, - "node_modules/@nodelib/fs.scandir/node_modules/@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/@nodelib/fs.stat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", - "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "engines": { - "node": ">= 6" + "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.4", + "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" }, "engines": { @@ -124,6 +115,25 @@ "node": ">=10" } }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/cacheable-request": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", @@ -136,10 +146,29 @@ "@types/responselike": "*" } }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, "node_modules/@types/eslint": { - "version": "7.2.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.10.tgz", - "integrity": "sha512-kUEPnMKrqbtpCq/KTaGFFKAcz6Ethm2EjCoKIDaCmfRBWLbFuTcOJfTlorwbnboXBzahqWLgUp1BQeKHiJzPUQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.0.tgz", + "integrity": "sha512-JUYa/5JwoqikCy7O7jKtuNe9Z4ZZt615G+1EKfaDGSNEpzaA2OwbV/G1v08Oa7fd1XzlFoSCvt9ePl9/6FyAug==", "dev": true, "dependencies": { "@types/estree": "*", @@ -147,9 +176,9 @@ } }, "node_modules/@types/eslint-scope": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz", - "integrity": "sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", "dev": true, "dependencies": { "@types/eslint": "*", @@ -157,15 +186,38 @@ } }, "node_modules/@types/estree": { - "version": "0.0.47", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.47.tgz", - "integrity": "sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==", + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", "dev": true }, + "node_modules/@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "node_modules/@types/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", "dev": true, "dependencies": { "@types/minimatch": "*", @@ -173,9 +225,9 @@ } }, "node_modules/@types/html-minifier-terser": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", - "integrity": "sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", "dev": true }, "node_modules/@types/http-cache-semantics": { @@ -185,39 +237,57 @@ "dev": true }, "node_modules/@types/http-proxy": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.6.tgz", - "integrity": "sha512-+qsjqR75S/ib0ig0R9WN+CDoZeOBU6F2XLewgC4KVgdXiNHiKKHFEMRHOrs5PbYE97D5vataw5wPj4KLYfUkuQ==", + "version": "1.17.8", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz", + "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, "node_modules/@types/keyv": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.2.tgz", - "integrity": "sha512-/FvAK2p4jQOaJ6CGDHJTqZcUtbZe820qIeTg7o0Shg7drB4JHeL+V/dhSaly7NXx6u8eSee+r7coT+yuJEvDLg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", + "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", "dev": true, "dependencies": { "@types/node": "*" } }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, "node_modules/@types/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", "dev": true }, "node_modules/@types/node": { - "version": "14.14.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz", - "integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==", + "version": "17.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz", + "integrity": "sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, "node_modules/@types/responselike": { @@ -230,161 +300,198 @@ } }, "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", "dev": true }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", + "integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.0.tgz", - "integrity": "sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", "dev": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0" + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz", - "integrity": "sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", "dev": true }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz", - "integrity": "sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz", - "integrity": "sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz", - "integrity": "sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", "dev": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.0", - "@webassemblyjs/helper-api-error": "1.11.0", + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz", - "integrity": "sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz", - "integrity": "sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-buffer": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/wasm-gen": "1.11.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz", - "integrity": "sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", "dev": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.0.tgz", - "integrity": "sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", "dev": true, "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.0.tgz", - "integrity": "sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz", - "integrity": "sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-buffer": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/helper-wasm-section": "1.11.0", - "@webassemblyjs/wasm-gen": "1.11.0", - "@webassemblyjs/wasm-opt": "1.11.0", - "@webassemblyjs/wasm-parser": "1.11.0", - "@webassemblyjs/wast-printer": "1.11.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz", - "integrity": "sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/ieee754": "1.11.0", - "@webassemblyjs/leb128": "1.11.0", - "@webassemblyjs/utf8": "1.11.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz", - "integrity": "sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-buffer": "1.11.0", - "@webassemblyjs/wasm-gen": "1.11.0", - "@webassemblyjs/wasm-parser": "1.11.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz", - "integrity": "sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-api-error": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/ieee754": "1.11.0", - "@webassemblyjs/leb128": "1.11.0", - "@webassemblyjs/utf8": "1.11.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz", - "integrity": "sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/ast": "1.11.1", "@xtuc/long": "4.2.2" } }, "node_modules/@webpack-cli/configtest": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.0.2.tgz", - "integrity": "sha512-3OBzV2fBGZ5TBfdW50cha1lHDVf9vlvRXnjpVbJBa20pSZQaSkMJZiwA8V2vD9ogyeXn8nU5s5A6mHyf5jhMzA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz", + "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==", "dev": true, "peerDependencies": { "webpack": "4.x.x || 5.x.x", @@ -392,9 +499,9 @@ } }, "node_modules/@webpack-cli/info": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.2.3.tgz", - "integrity": "sha512-lLek3/T7u40lTqzCGpC6CAbY6+vXhdhmwFRxZLMnRm6/sIF/7qMpT8MocXCRQfz0JAh63wpbXLMnsQ5162WS7Q==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.0.tgz", + "integrity": "sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw==", "dev": true, "dependencies": { "envinfo": "^7.7.3" @@ -404,9 +511,9 @@ } }, "node_modules/@webpack-cli/serve": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.3.1.tgz", - "integrity": "sha512-0qXvpeYO6vaNoRBI52/UsbcaBydJCggoBBnIo/ovQQdn6fug0BgwsjorV1hVS7fMqGVTZGcVxv8334gjmbj5hw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz", + "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==", "dev": true, "peerDependencies": { "webpack-cli": "4.x.x" @@ -443,9 +550,9 @@ } }, "node_modules/acorn": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.1.1.tgz", - "integrity": "sha512-xYiIVjNuqtKXMxlRMDc6mZUhXehod4a3gbZ1qRlM7icK4EbxUFNLhWoPblCvFtB2Y9CIqHP3CF/rdxLItaQv8g==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -454,6 +561,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -483,6 +599,45 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -492,15 +647,6 @@ "ajv": "^6.9.1" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -528,10 +674,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", "dev": true, "engines": [ "node >= 0.8.0" @@ -541,12 +687,12 @@ } }, "node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/ansi-styles": { @@ -565,9 +711,9 @@ } }, "node_modules/anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", @@ -646,12 +792,6 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/archiver/node_modules/async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", - "dev": true - }, "node_modules/arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -686,15 +826,12 @@ "dev": true }, "node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "dependencies": { - "array-uniq": "^1.0.1" - }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/array-uniq": { @@ -725,9 +862,9 @@ } }, "node_modules/asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, "dependencies": { "safer-buffer": "~2.1.0" @@ -752,13 +889,10 @@ } }, "node_modules/async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "dev": true }, "node_modules/asynckit": { "version": "0.4.0", @@ -803,9 +937,9 @@ "dev": true }, "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "node_modules/base": { @@ -932,64 +1066,60 @@ } }, "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", "dev": true, "dependencies": { - "bytes": "3.1.0", + "bytes": "3.1.1", "content-type": "~1.0.4", "debug": "2.6.9", "depd": "~1.1.2", - "http-errors": "1.7.2", + "http-errors": "1.8.1", "iconv-lite": "0.4.24", "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" }, "engines": { "node": ">= 0.8" } }, "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", "dev": true, "engines": { "node": ">= 0.8" } }, - "node_modules/body-parser/node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" + "ms": "2.0.0" } }, - "node_modules/body-parser/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, "node_modules/body-parser/node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", "dev": true, "engines": { "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/bonjour": { @@ -1035,16 +1165,16 @@ } }, "node_modules/browserslist": { - "version": "4.16.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.5.tgz", - "integrity": "sha512-C2HAjrM1AI/djrpAUU/tr4pml1DqLIzJKSLDBXBrNErl9ZCCTXdhwxdJjYc16953+mBWf7Lw+uUJgpgb8cN71A==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001214", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.719", + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", "escalade": "^3.1.1", - "node-releases": "^1.1.71" + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" }, "bin": { "browserslist": "cli.js" @@ -1091,9 +1221,9 @@ } }, "node_modules/buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, "node_modules/buffer-indexof": { @@ -1171,21 +1301,6 @@ "node": ">=8" } }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -1216,10 +1331,14 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001214", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001214.tgz", - "integrity": "sha512-O2/SCpuaU3eASWVaesQirZv1MSjUNOvmugaD8zNSJqw6Vv5SGwoOpA9LJs3pNPfM745nxqPvfZY3MQKY4AKHYg==", - "dev": true + "version": "1.0.30001300", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001300.tgz", + "integrity": "sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } }, "node_modules/caseless": { "version": "0.12.0", @@ -1256,24 +1375,30 @@ } }, "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "glob-parent": "~5.1.0", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" }, "engines": { "node": ">= 8.10.0" }, "optionalDependencies": { - "fsevents": "~2.3.1" + "fsevents": "~2.3.2" } }, "node_modules/chownr": { @@ -1393,15 +1518,15 @@ } }, "node_modules/clean-css": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", - "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.2.tgz", + "integrity": "sha512-/eR8ru5zyxKzpBLv9YZvMXgTSSQn7AdkMItMYynsFgGwTveCRVam9IUPFloE85B4vAIj05IuKmmEoV7/AQjT0w==", "dev": true, "dependencies": { "source-map": "~0.6.0" }, "engines": { - "node": ">= 4.0" + "node": ">= 10.0" } }, "node_modules/clean-stack": { @@ -1426,9 +1551,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", - "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", "dev": true, "engines": { "node": ">=6" @@ -1510,9 +1635,9 @@ "dev": true }, "node_modules/colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", "dev": true }, "node_modules/combined-stream": { @@ -1528,20 +1653,14 @@ } }, "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, "engines": { - "node": ">= 6" + "node": ">= 10" } }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, "node_modules/component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -1549,13 +1668,13 @@ "dev": true }, "node_modules/compress-commons": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.0.tgz", - "integrity": "sha512-ofaaLqfraD1YRTkrRKPCrGJ1pFeDG/MVCkVVV2FNGeWquSlqw5wOrwOfPQ1xF2u+blpeWASie5EubHz+vsNIgA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz", + "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==", "dev": true, "dependencies": { "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.1", + "crc32-stream": "^4.0.2", "normalize-path": "^3.0.0", "readable-stream": "^3.6.0" }, @@ -1593,6 +1712,21 @@ "node": ">= 0.8.0" } }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "node_modules/compression/node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -1615,23 +1749,17 @@ } }, "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, "dependencies": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" }, "engines": { "node": ">= 0.6" } }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, "node_modules/content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -1642,9 +1770,9 @@ } }, "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", "dev": true, "engines": { "node": ">= 0.6" @@ -1690,45 +1818,6 @@ "webpack": "^5.1.0" } }, - "node_modules/copy-webpack-plugin/node_modules/@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/copy-webpack-plugin/node_modules/fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/copy-webpack-plugin/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -1773,6 +1862,138 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cpy/node_modules/@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cpy/node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "dependencies": { + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cpy/node_modules/fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dev": true, + "dependencies": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/cpy/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/cpy/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/cpy/node_modules/globby": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", @@ -1792,16 +2013,113 @@ "node": ">=6" } }, - "node_modules/cpy/node_modules/p-map": { + "node_modules/cpy/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/cpy/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/is-number": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "dependencies": { - "aggregate-error": "^3.0.0" + "kind-of": "^3.0.2" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cpy/node_modules/path-type/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cpy/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cpy/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" } }, "node_modules/crc-32": { @@ -1847,61 +2165,15 @@ "node": ">= 8" } }, - "node_modules/cross-spawn/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cross-spawn/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cross-spawn/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/css-loader": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.4.tgz", - "integrity": "sha512-OFYGyINCKkdQsTrSYxzGSFnGS4gNjcXkKkQgWxK138jgnPt+lepxdjSZNc8sHAl5vP3DhsJUxufWIjOwI8PMMw==", + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz", + "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==", "dev": true, "dependencies": { - "camelcase": "^6.2.0", "icss-utils": "^5.1.0", "loader-utils": "^2.0.0", - "postcss": "^8.2.10", + "postcss": "^8.2.15", "postcss-modules-extract-imports": "^3.0.0", "postcss-modules-local-by-default": "^4.0.0", "postcss-modules-scope": "^3.0.0", @@ -1921,53 +2193,26 @@ "webpack": "^4.27.0 || ^5.0.0" } }, - "node_modules/css-loader/node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/css-loader/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/css-select": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz", - "integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", + "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", "dev": true, "dependencies": { "boolbase": "^1.0.0", - "css-what": "^5.0.0", - "domhandler": "^4.2.0", - "domutils": "^2.6.0", - "nth-check": "^2.0.0" + "css-what": "^5.1.0", + "domhandler": "^4.3.0", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" }, "funding": { "url": "https://github.com/sponsors/fb55" } }, "node_modules/css-what": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.0.1.tgz", - "integrity": "sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", + "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", "dev": true, "engines": { "node": ">= 6" @@ -2001,12 +2246,20 @@ } }, "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "dependencies": { - "ms": "2.0.0" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/decode-uri-component": { @@ -2077,6 +2330,15 @@ "node": ">=10" } }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -2139,15 +2401,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/del/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2173,21 +2426,21 @@ "dev": true }, "node_modules/detect-node": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.5.tgz", - "integrity": "sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, "node_modules/dir-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", - "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "dependencies": { - "path-type": "^3.0.0" + "path-type": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/dns-equal": { @@ -2251,9 +2504,9 @@ ] }, "node_modules/domhandler": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", - "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", + "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", "dev": true, "dependencies": { "domelementtype": "^2.2.0" @@ -2266,9 +2519,9 @@ } }, "node_modules/domutils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz", - "integrity": "sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", "dev": true, "dependencies": { "dom-serializer": "^1.0.1", @@ -2312,9 +2565,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.3.720", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.720.tgz", - "integrity": "sha512-B6zLTxxaOFP4WZm6DrvgRk8kLFYWNhQ5TrHMC0l5WtkMXhU5UbnvWoTfeEwqOruUSlNMhVLfYak7REX6oC5Yfw==", + "version": "1.4.49", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.49.tgz", + "integrity": "sha512-k/0t1TRfonHIp8TJKfjBu2cKj8MqYTiEpOhci+q7CVEE5xnCQnx1pTa+V8b/sdhe4S3PR4p4iceEQWhGrKQORQ==", "dev": true }, "node_modules/elm": { @@ -2334,9 +2587,9 @@ } }, "node_modules/elm-asset-webpack-loader": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/elm-asset-webpack-loader/-/elm-asset-webpack-loader-1.1.2.tgz", - "integrity": "sha512-jrXYtxk13LXtbxCiT23+RuNhUgbJgGBRW2sCqkMGojTZTFKttL1E8mSUvsIuomUiKLJOSXUZb3HjvwXNkJjTNA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/elm-asset-webpack-loader/-/elm-asset-webpack-loader-1.1.3.tgz", + "integrity": "sha512-PED3ISseHh3ZKnMir2t9L5HOZOJFcodplKgSPIR+tDN9YzSRXbmyZ8E5qFehykqrmsg1H7zj1wL1/0J90judgQ==", "dev": true }, "node_modules/elm-format": { @@ -2353,15 +2606,15 @@ } }, "node_modules/elm-review": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/elm-review/-/elm-review-2.5.5.tgz", - "integrity": "sha512-VOPeOgeE16RjLich8Gt1qJlXyASG2qS5Or4Q3vO1OlAYLPxEAlzUlEiloa7RFtm6dnEYk38axnpx8WexKy7VjA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/elm-review/-/elm-review-2.7.0.tgz", + "integrity": "sha512-PvZj6M6rHYpyAGp2MKF/TzeDawioQacBTkVxzlBBGuMoO4LXw2PMIm/NmhkxcD38l5SvDIwpWmyDKvEsMaWNhQ==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "chokidar": "^3.4.0", + "chokidar": "^3.5.2", "cross-spawn": "^7.0.3", - "elm-tooling": "^1.2.0", + "elm-tooling": "^1.6.0", "fast-levenshtein": "^3.0.0", "find-up": "^4.1.0", "folder-hash": "^3.3.0", @@ -2388,62 +2641,17 @@ "url": "https://github.com/sponsors/jfmengels" } }, - "node_modules/elm-review/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/elm-review/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/elm-review/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/elm-review/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/elm-test": { - "version": "0.19.1-revision6", - "resolved": "https://registry.npmjs.org/elm-test/-/elm-test-0.19.1-revision6.tgz", - "integrity": "sha512-4VbIyCRlCUm/py0E0AjMT3/mwd6DR4Y5Z5gEox6z5JII6ZdKIJmcQzjgWRI5qo5ERJiw9M/Nxhk7SGXFUbZsxQ==", + "version": "0.19.1-revision7", + "resolved": "https://registry.npmjs.org/elm-test/-/elm-test-0.19.1-revision7.tgz", + "integrity": "sha512-sd3nCQMeYMaY84Sz41bVJ30ZvQN1/4ZcD8uYMOuUbM39FDh58NY9/AcImVJ7Z+gjCFdcSU6VscZzhUoPW8jp6Q==", "dev": true, "dependencies": { "chalk": "^4.1.0", "chokidar": "^3.5.1", - "commander": "^7.0.0", + "commander": "^7.1.0", "cross-spawn": "^7.0.3", - "elm-tooling": "^1.1.0", + "elm-tooling": "^1.2.0", "glob": "^7.1.6", "graceful-fs": "^4.2.4", "rimraf": "^3.0.2", @@ -2458,34 +2666,10 @@ "node": ">=10.13.0" } }, - "node_modules/elm-test/node_modules/commander": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.0.0.tgz", - "integrity": "sha512-ovx/7NkTrnPuIV8sqk/GjUIIM1+iUQeqA3ye2VNpq9sVoiZsooObWlQy+OPWGI17GDaEoybuAGJm6U8yC077BA==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/elm-test/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/elm-tooling": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/elm-tooling/-/elm-tooling-1.4.1.tgz", - "integrity": "sha512-5Wj1G9KCJHRxnfFmAa1YSgpP3chblcifpw5Yg8hCDAnYg5DZuMvO4m22Py15TvwlN7fKD7nghnuHoSUiz0vdnw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/elm-tooling/-/elm-tooling-1.7.0.tgz", + "integrity": "sha512-EHZ54voWrG3BhUONbH/wFw5U95H6N7R4QFgXHDrPIaDBDdeyNkpFu4QWArSWkhzxyCF7hqT8ya2yy7SferDsgg==", "dev": true, "bin": { "elm-tooling": "index.js" @@ -2541,9 +2725,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.0.tgz", - "integrity": "sha512-Sl3KRpJA8OpprrtaIswVki3cWPiPKxXuFxJXBp+zNb6s6VwNWwFRUdtmzd2ReUut8n+sCPx7QCtQ7w5wfJhSgQ==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", + "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -2553,18 +2737,6 @@ "node": ">=10.13.0" } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", @@ -2587,9 +2759,9 @@ } }, "node_modules/es-module-lexer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.1.tgz", - "integrity": "sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", "dev": true }, "node_modules/escalade": { @@ -2633,9 +2805,9 @@ } }, "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" @@ -2694,7 +2866,19 @@ "node": ">=10" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/exit-on-epipe": { @@ -2724,6 +2908,15 @@ "node": ">=0.10.0" } }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, "node_modules/expand-brackets/node_modules/define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -2828,18 +3021,24 @@ "node": ">=0.10.0" } }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", + "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", "dev": true, "dependencies": { "accepts": "~1.3.7", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.19.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.4.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "~1.1.2", @@ -2853,13 +3052,13 @@ "on-finished": "~2.3.0", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.9.6", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", "statuses": "~1.5.0", "type-is": "~1.6.18", "utils-merge": "1.0.1", @@ -2875,21 +3074,33 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "node_modules/express/node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", "dev": true, "engines": { "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -2977,42 +3188,19 @@ "dev": true }, "node_modules/fast-glob": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", - "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", - "dev": true, - "dependencies": { - "@mrmlnc/readdir-enhanced": "^2.2.1", - "@nodelib/fs.stat": "^1.1.2", - "glob-parent": "^3.1.0", - "is-glob": "^4.0.0", - "merge2": "^1.2.3", - "micromatch": "^3.1.10" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "dependencies": { - "is-extglob": "^2.1.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6.0" } }, "node_modules/fast-json-stable-stringify": { @@ -3037,18 +3225,18 @@ "dev": true }, "node_modules/fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "dependencies": { "reusify": "^1.0.4" } }, "node_modules/faye-websocket": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", - "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", "dev": true, "dependencies": { "websocket-driver": ">=0.5.1" @@ -3107,23 +3295,21 @@ "node": ">= 0.8" } }, - "node_modules/find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + "ms": "2.0.0" } }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "node_modules/find-elm-dependencies": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/find-elm-dependencies/-/find-elm-dependencies-2.0.4.tgz", @@ -3179,33 +3365,10 @@ "node": ">=6.0.0" } }, - "node_modules/folder-hash/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/folder-hash/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", "dev": true, "funding": [ { @@ -3255,9 +3418,9 @@ } }, "node_modules/forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true, "engines": { "node": ">= 0.6" @@ -3330,9 +3493,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz", - "integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, "optional": true, @@ -3364,12 +3527,15 @@ } }, "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3394,9 +3560,9 @@ } }, "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -3432,16 +3598,16 @@ "dev": true }, "node_modules/globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -3451,93 +3617,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby/node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/globby/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/globby/node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/globby/node_modules/fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/globby/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/globby/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/globby/node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/globby/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/got": { "version": "10.7.0", "resolved": "https://registry.npmjs.org/got/-/got-10.7.0.tgz", @@ -3567,25 +3646,10 @@ "url": "https://github.com/sindresorhus/got?sponsor=1" } }, - "node_modules/got/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, "node_modules/handle-thing": { @@ -3674,6 +3738,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -3795,59 +3874,45 @@ "dev": true }, "node_modules/html-minifier-terser": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", - "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", "dev": true, "dependencies": { - "camel-case": "^4.1.1", - "clean-css": "^4.2.3", - "commander": "^4.1.1", + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", "he": "^1.2.0", - "param-case": "^3.0.3", + "param-case": "^3.0.4", "relateurl": "^0.2.7", - "terser": "^4.6.3" + "terser": "^5.10.0" }, "bin": { "html-minifier-terser": "cli.js" }, "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/html-minifier-terser/node_modules/terser": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", - "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "node_modules/html-minifier-terser/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "dev": true, - "dependencies": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - }, - "bin": { - "terser": "bin/terser" - }, "engines": { - "node": ">=6.0.0" + "node": ">= 12" } }, - "node_modules/html-minifier-terser/node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, "node_modules/html-webpack-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.3.1.tgz", - "integrity": "sha512-rZsVvPXUYFyME0cuGkyOHfx9hmkFa4pWfxY/mdY38PsBEaVNsRoA+Id+8z6DBDgyv3zaw6XQszdF8HLwfQvcdQ==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", "dev": true, "dependencies": { - "@types/html-minifier-terser": "^5.0.0", - "html-minifier-terser": "^5.0.1", - "lodash": "^4.17.20", - "pretty-error": "^2.1.1", + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", "tapable": "^2.0.0" }, "engines": { @@ -3893,25 +3958,25 @@ "dev": true }, "node_modules/http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "dev": true, "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", - "setprototypeof": "1.1.1", + "setprototypeof": "1.2.0", "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "toidentifier": "1.0.1" }, "engines": { "node": ">= 0.6" } }, "node_modules/http-parser-js": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", - "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", + "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==", "dev": true }, "node_modules/http-proxy": { @@ -3922,39 +3987,26 @@ "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-middleware": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz", - "integrity": "sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==", - "dev": true, - "dependencies": { - "@types/http-proxy": "^1.17.5", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" + "requires-port": "^1.0.0" }, "engines": { "node": ">=8.0.0" } }, - "node_modules/http-proxy-middleware/node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "node_modules/http-proxy-middleware": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz", + "integrity": "sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg==", "dev": true, "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "@types/http-proxy": "^1.17.5", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" }, "engines": { - "node": ">=8.6" + "node": ">=12.0.0" } }, "node_modules/http-signature": { @@ -4026,18 +4078,18 @@ ] }, "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true, "engines": { "node": ">= 4" } }, "node_modules/import-local": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", - "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, "dependencies": { "pkg-dir": "^4.2.0", @@ -4048,6 +4100,9 @@ }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/indent-string": { @@ -4075,24 +4130,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "node_modules/internal-ip": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-6.2.0.tgz", - "integrity": "sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg==", - "dev": true, - "dependencies": { - "default-gateway": "^6.0.0", - "ipaddr.js": "^1.9.1", - "is-ip": "^3.1.0", - "p-event": "^4.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/internal-ip?sponsor=1" - } - }, "node_modules/interpret": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", @@ -4108,31 +4145,13 @@ "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", "dev": true }, - "node_modules/ip-regex": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", - "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-absolute-url": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", - "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", "dev": true, "engines": { - "node": ">=8" + "node": ">= 10" } }, "node_modules/is-accessor-descriptor": { @@ -4148,12 +4167,13 @@ } }, "node_modules/is-arguments": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", - "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dev": true, "dependencies": { - "call-bind": "^1.0.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -4181,9 +4201,9 @@ "dev": true }, "node_modules/is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -4205,10 +4225,13 @@ } }, "node_modules/is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -4276,9 +4299,9 @@ } }, "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { "is-extglob": "^2.1.1" @@ -4296,18 +4319,6 @@ "node": ">=8" } }, - "node_modules/is-ip": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", - "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", - "dev": true, - "dependencies": { - "ip-regex": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4360,13 +4371,13 @@ } }, "node_modules/is-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", - "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "has-symbols": "^1.0.1" + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -4376,12 +4387,15 @@ } }, "node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-typedarray": { @@ -4451,19 +4465,34 @@ "dev": true }, "node_modules/jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "version": "27.4.6", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.6.tgz", + "integrity": "sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw==", "dev": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" + "supports-color": "^8.0.0" }, "engines": { "node": ">= 10.13.0" } }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -4483,9 +4512,9 @@ "dev": true }, "node_modules/json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "dev": true }, "node_modules/json-schema-traverse": { @@ -4528,18 +4557,18 @@ } }, "node_modules/jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", "dev": true, - "engines": [ - "node >=0.6.0" - ], "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", - "json-schema": "0.2.3", + "json-schema": "0.4.0", "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" } }, "node_modules/junk": { @@ -4552,20 +4581,14 @@ } }, "node_modules/keyv": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", - "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.5.tgz", + "integrity": "sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA==", "dev": true, "dependencies": { "json-buffer": "3.0.1" } }, - "node_modules/killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", - "dev": true - }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -4585,9 +4608,9 @@ } }, "node_modules/lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", "dev": true, "dependencies": { "readable-stream": "^2.0.5" @@ -4636,9 +4659,9 @@ } }, "node_modules/loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -4767,18 +4790,6 @@ "semver": "bin/semver.js" } }, - "node_modules/map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "dependencies": { - "p-defer": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -4809,35 +4820,10 @@ "node": ">= 0.6" } }, - "node_modules/mem": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/mem/-/mem-8.1.1.tgz", - "integrity": "sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==", - "dev": true, - "dependencies": { - "map-age-cleaner": "^0.1.3", - "mimic-fn": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/mem?sponsor=1" - } - }, - "node_modules/mem/node_modules/mimic-fn": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", - "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/memfs": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.2.tgz", - "integrity": "sha512-RE0CwmIM3CEvpcdK3rZ19BC4E6hv9kADkMN5rPduRak58cNArWLi/9jFLsa4rhsjfVxMP3v0jO7FHXq7SvFY5Q==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", + "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", "dev": true, "dependencies": { "fs-monkey": "1.0.3" @@ -4877,151 +4863,46 @@ } }, "node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/micromatch/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "dependencies": { - "is-buffer": "^1.1.5" + "braces": "^3.0.1", + "picomatch": "^2.2.3" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, - "node_modules/micromatch/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "bin": { + "mime": "cli.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, "node_modules/mime-db": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", - "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", "dev": true, "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.31", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", - "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", "dev": true, "dependencies": { - "mime-db": "1.48.0" + "mime-db": "1.51.0" }, "engines": { "node": ">= 0.6" @@ -5073,9 +4954,9 @@ "dev": true }, "node_modules/minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", "dev": true, "dependencies": { "yallist": "^4.0.0" @@ -5123,9 +5004,9 @@ } }, "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "node_modules/multicast-dns": { @@ -5148,9 +5029,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.1.22", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz", - "integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", + "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -5249,19 +5130,70 @@ "node": ">=4.8" } }, + "node_modules/node-elm-compiler/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/node-elm-compiler/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/node-elm-compiler/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-elm-compiler/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-elm-compiler/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz", + "integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==", "dev": true, "engines": { - "node": ">= 6.0.0" + "node": ">= 6.13.0" } }, "node_modules/node-releases": { - "version": "1.1.71", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", - "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", "dev": true }, "node_modules/normalize-path": { @@ -5297,19 +5229,10 @@ "node": ">=8" } }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/nth-check": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", - "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", "dev": true, "dependencies": { "boolbase": "^1.0.0" @@ -5513,16 +5436,17 @@ } }, "node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", "dev": true, "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5551,27 +5475,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/p-all": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-all/-/p-all-2.1.0.tgz", @@ -5584,6 +5487,15 @@ "node": ">=6" } }, + "node_modules/p-all/node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/p-cancelable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", @@ -5593,15 +5505,6 @@ "node": ">=8" } }, - "node_modules/p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/p-event": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", @@ -5629,6 +5532,15 @@ "node": ">=8" } }, + "node_modules/p-filter/node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -5681,22 +5593,25 @@ } }, "node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/p-retry": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.5.0.tgz", - "integrity": "sha512-5Hwh4aVQSu6BEP+w2zKlVXtFAaYQe1qWuVADSgoeVlLjwe/Q/AMSoRR4MDeaAfu8llT+YNbEijWu/YF3m6avkg==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", + "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", "dev": true, "dependencies": { "@types/retry": "^0.12.0", - "retry": "^0.12.0" + "retry": "^0.13.1" }, "engines": { "node": ">=8" @@ -5786,12 +5701,12 @@ } }, "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/path-parse": { @@ -5807,24 +5722,12 @@ "dev": true }, "node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-type/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/performance-now": { @@ -5833,10 +5736,16 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "node_modules/picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -5880,6 +5789,15 @@ "node": ">= 0.12.0" } }, + "node_modules/portfinder/node_modules/async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, "node_modules/portfinder/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -5901,12 +5819,6 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/portfinder/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -5917,14 +5829,14 @@ } }, "node_modules/postcss": { - "version": "8.2.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.12.tgz", - "integrity": "sha512-BJnGT5+0q2tzvs6oQfnY2NpEJ7rIXNfBnZtQOKCIsweeWXBXeDd5k31UgTdS3d/c02ouspufn37mTaHWkJyzMQ==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", + "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", "dev": true, "dependencies": { - "colorette": "^1.2.2", - "nanoid": "^3.1.22", - "source-map": "^0.6.1" + "nanoid": "^3.1.30", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -5994,9 +5906,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.5.tgz", - "integrity": "sha512-aFYPoYmXbZ1V6HZaSvat08M97A8HqO6Pjz+PiNpw/DhuRrC72XWAdp3hL6wusDCN31sSmcZyMGa2hZEuX+Xfhg==", + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz", + "integrity": "sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -6007,19 +5919,19 @@ } }, "node_modules/postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, "node_modules/pretty-error": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", - "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", "dev": true, "dependencies": { "lodash": "^4.17.20", - "renderkid": "^2.0.4" + "renderkid": "^3.0.0" } }, "node_modules/printj": { @@ -6041,9 +5953,9 @@ "dev": true }, "node_modules/prompts": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", - "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, "dependencies": { "kleur": "^3.0.3", @@ -6054,18 +5966,27 @@ } }, "node_modules/proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, "dependencies": { - "forwarded": "~0.1.2", + "forwarded": "0.2.0", "ipaddr.js": "1.9.1" }, "engines": { "node": ">= 0.10" } }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -6092,23 +6013,14 @@ } }, "node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true, "engines": { "node": ">=0.6" } }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true, - "engines": { - "node": ">=0.4.x" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -6148,13 +6060,13 @@ } }, "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", "dev": true, "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.1", + "http-errors": "1.8.1", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -6163,36 +6075,14 @@ } }, "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", "dev": true, "engines": { "node": ">= 0.8" } }, - "node_modules/raw-body/node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dev": true, - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -6217,9 +6107,9 @@ } }, "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "dependencies": { "picomatch": "^2.2.1" @@ -6229,9 +6119,9 @@ } }, "node_modules/rechoir": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", - "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", "dev": true, "dependencies": { "resolve": "^1.9.0" @@ -6254,9 +6144,9 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", + "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", @@ -6279,16 +6169,16 @@ } }, "node_modules/renderkid": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz", - "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", "dev": true, "dependencies": { "css-select": "^4.1.3", "dom-converter": "^0.2.0", "htmlparser2": "^6.1.0", "lodash": "^4.17.21", - "strip-ansi": "^3.0.1" + "strip-ansi": "^6.0.1" } }, "node_modules/repeat-element": { @@ -6341,6 +6231,15 @@ "node": ">= 6" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -6348,13 +6247,17 @@ "dev": true }, "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", + "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", "dev": true, "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.8.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6420,9 +6323,9 @@ } }, "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true, "engines": { "node": ">= 4" @@ -6512,12 +6415,12 @@ "dev": true }, "node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.6", + "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" }, @@ -6536,27 +6439,36 @@ "dev": true }, "node_modules/selfsigned": { - "version": "1.10.11", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", - "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", + "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", "dev": true, "dependencies": { - "node-forge": "^0.10.0" + "node-forge": "^1.2.0" + }, + "engines": { + "node": ">=10" } }, "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { - "semver": "bin/semver" + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", "dev": true, "dependencies": { "debug": "2.6.9", @@ -6566,9 +6478,9 @@ "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "1.8.1", "mime": "1.6.0", - "ms": "2.1.1", + "ms": "2.1.3", "on-finished": "~2.3.0", "range-parser": "~1.2.1", "statuses": "~1.5.0" @@ -6577,22 +6489,25 @@ "node": ">= 0.8.0" } }, - "node_modules/send/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" + "dependencies": { + "ms": "2.0.0" } }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "node_modules/serialize-javascript": { @@ -6622,6 +6537,15 @@ "node": ">= 0.8.0" } }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, "node_modules/serve-index/node_modules/http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", @@ -6643,6 +6567,12 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "node_modules/serve-index/node_modules/setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", @@ -6650,15 +6580,15 @@ "dev": true }, "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", "dev": true, "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.17.2" }, "engines": { "node": ">= 0.8.0" @@ -6701,9 +6631,9 @@ } }, "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, "node_modules/shallow-clone": { @@ -6719,30 +6649,30 @@ } }, "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "dependencies": { - "shebang-regex": "^1.0.0" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", "dev": true }, "node_modules/sisteransi": { @@ -6752,12 +6682,12 @@ "dev": true }, "node_modules/slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/snapdragon": { @@ -6829,6 +6759,15 @@ "node": ">=0.10.0" } }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, "node_modules/snapdragon/node_modules/define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -6933,6 +6872,12 @@ "node": ">=0.10.0" } }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "node_modules/snapdragon/node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -6943,21 +6888,24 @@ } }, "node_modules/sockjs": { - "version": "0.3.21", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", - "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==", + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", "dev": true, "dependencies": { "faye-websocket": "^0.11.3", - "uuid": "^3.4.0", + "uuid": "^8.3.2", "websocket-driver": "^0.7.4" } }, - "node_modules/source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } }, "node_modules/source-map": { "version": "0.6.1", @@ -6968,10 +6916,20 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", "dev": true, "dependencies": { "atob": "^2.1.2", @@ -6982,9 +6940,9 @@ } }, "node_modules/source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "dependencies": { "buffer-from": "^1.0.0", @@ -6995,6 +6953,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", "dev": true }, "node_modules/spdy": { @@ -7027,52 +6986,6 @@ "wbuf": "^1.7.3" } }, - "node_modules/spdy-transport/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/spdy-transport/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/spdy/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/spdy/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/split": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", @@ -7098,9 +7011,9 @@ } }, "node_modules/sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "dev": true, "dependencies": { "asn1": "~0.2.3", @@ -7237,50 +7150,29 @@ } }, "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/strip-final-newline": { @@ -7337,19 +7229,31 @@ "node": ">=8" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tapable": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", - "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/tar": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.10.tgz", - "integrity": "sha512-kvvfiVvjGMxeUNB6MyYv5z7vhfFRwbwCXJAeL0/lnbrttBVqcMOnpHUf0X42LrPMR8mMpgapkJMchFH4FSHzNA==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, "dependencies": { "chownr": "^2.0.0", @@ -7433,34 +7337,41 @@ } }, "node_modules/terser": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.6.1.tgz", - "integrity": "sha512-yv9YLFQQ+3ZqgWCUk+pvNJwgUTdlIxUk1WTN+RnaFJe2L7ipG2csPT0ra2XRm7Cs8cxN7QXmK1rFzEwYEQkzXw==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", + "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", "dev": true, "dependencies": { "commander": "^2.20.0", "source-map": "~0.7.2", - "source-map-support": "~0.5.19" + "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" }, "engines": { "node": ">=10" + }, + "peerDependencies": { + "acorn": "^8.5.0" + }, + "peerDependenciesMeta": { + "acorn": { + "optional": true + } } }, "node_modules/terser-webpack-plugin": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.1.tgz", - "integrity": "sha512-5XNNXZiR8YO6X6KhSGXfY0QrGrCRlSwAEjIIrlRQR4W8nP69TaJUlh3bkuac6zzgspiGPfKEHcY295MMVExl5Q==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz", + "integrity": "sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ==", "dev": true, "dependencies": { - "jest-worker": "^26.6.2", - "p-limit": "^3.1.0", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1", + "jest-worker": "^27.4.1", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", "source-map": "^0.6.1", - "terser": "^5.5.1" + "terser": "^5.7.2" }, "engines": { "node": ">= 10.13.0" @@ -7471,6 +7382,26 @@ }, "peerDependencies": { "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" } }, "node_modules/terser/node_modules/commander": { @@ -7561,9 +7492,9 @@ } }, "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true, "engines": { "node": ">=0.6" @@ -7592,9 +7523,9 @@ } }, "node_modules/tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true }, "node_modules/tunnel-agent": { @@ -7768,22 +7699,6 @@ "deprecated": "Please see https://github.com/lydell/urix#deprecated", "dev": true }, - "node_modules/url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - }, "node_modules/use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -7818,17 +7733,12 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", "dev": true, "bin": { "uuid": "bin/uuid" } }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -7853,9 +7763,9 @@ } }, "node_modules/watchpack": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.1.1.tgz", - "integrity": "sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", "dev": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -7884,34 +7794,35 @@ } }, "node_modules/webpack": { - "version": "5.35.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.35.0.tgz", - "integrity": "sha512-au3gu55yYF/h6NXFr0KZPZAYxS6Nlc595BzYPke8n0CSff5WXcoixtjh5LC/8mXunkRKxhymhXmBY0+kEbR6jg==", + "version": "5.66.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.66.0.tgz", + "integrity": "sha512-NJNtGT7IKpGzdW7Iwpn/09OXz9inIkeIQ/ibY6B+MdV1x6+uReqz/5z1L89ezWnpPDWpXF0TY5PCYKQdWVn8Vg==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.47", - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/wasm-edit": "1.11.0", - "@webassemblyjs/wasm-parser": "1.11.0", - "acorn": "^8.0.4", + "@types/estree": "^0.0.50", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.0", - "es-module-lexer": "^0.4.0", - "eslint-scope": "^5.1.1", + "enhanced-resolve": "^5.8.3", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.4", + "graceful-fs": "^4.2.9", "json-parse-better-errors": "^1.0.2", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.0.0", + "schema-utils": "^3.1.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.1", - "watchpack": "^2.0.0", - "webpack-sources": "^2.1.1" + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.2" }, "bin": { "webpack": "bin/webpack.js" @@ -7930,24 +7841,22 @@ } }, "node_modules/webpack-cli": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.6.0.tgz", - "integrity": "sha512-9YV+qTcGMjQFiY7Nb1kmnupvb1x40lfpj8pwdO/bom+sQiP4OBMKjHq29YQrlDWDPZO9r/qWaRRywKaRDKqBTA==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.1.tgz", + "integrity": "sha512-JYRFVuyFpzDxMDB+v/nanUdQYcZtqFPGzmlW4s+UkPMFhSpfRNmf1z4AwYcHJVdvEFAM7FFCQdNTpsBYhDLusQ==", "dev": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.0.2", - "@webpack-cli/info": "^1.2.3", - "@webpack-cli/serve": "^1.3.1", - "colorette": "^1.2.1", + "@webpack-cli/configtest": "^1.1.0", + "@webpack-cli/info": "^1.4.0", + "@webpack-cli/serve": "^1.6.0", + "colorette": "^2.0.14", "commander": "^7.0.0", - "enquirer": "^2.3.6", "execa": "^5.0.0", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", "interpret": "^2.2.0", "rechoir": "^0.7.0", - "v8-compile-cache": "^2.2.0", "webpack-merge": "^5.7.3" }, "bin": { @@ -7974,30 +7883,20 @@ } } }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, "node_modules/webpack-dev-middleware": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-4.3.0.tgz", - "integrity": "sha512-PjwyVY95/bhBh6VUqt6z4THplYcsvQ8YNNBTBM873xLVmw8FLeALn0qurHbs9EmcfhzQis/eoqypSnZeuUz26w==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.0.tgz", + "integrity": "sha512-MouJz+rXAm9B1OTOYaJnn6rtD/lWZPy2ufQCH3BPs8Rloh/Du6Jze4p7AeLYHkVi0giJnYLaSGDC7S+GM9arhg==", "dev": true, "dependencies": { - "colorette": "^1.2.2", - "mem": "^8.1.1", + "colorette": "^2.0.10", "memfs": "^3.2.2", - "mime-types": "^2.1.30", + "mime-types": "^2.1.31", "range-parser": "^1.2.1", - "schema-utils": "^3.0.0" + "schema-utils": "^4.0.0" }, "engines": { - "node": ">= v10.23.3" + "node": ">= 12.13.0" }, "funding": { "type": "opencollective", @@ -8007,39 +7906,94 @@ "webpack": "^4.0.0 || ^5.0.0" } }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/webpack-dev-server": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.0.0-beta.3.tgz", - "integrity": "sha512-Ud7ieH15No/KiSdRuzk+2k+S4gSCR/N7m4hJhesDbKQEZy3P+NPXTXfsimNOZvbVX2TRuIEFB+VdLZFn8DwGwg==", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz", + "integrity": "sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q==", "dev": true, "dependencies": { - "ansi-html": "^0.0.7", + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/serve-index": "^1.9.1", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.2.2", + "ansi-html-community": "^0.0.8", "bonjour": "^3.5.0", - "chokidar": "^3.5.1", + "chokidar": "^3.5.2", + "colorette": "^2.0.10", "compression": "^1.7.4", "connect-history-api-fallback": "^1.6.0", + "default-gateway": "^6.0.3", "del": "^6.0.0", "express": "^4.17.1", - "find-cache-dir": "^3.3.1", "graceful-fs": "^4.2.6", "html-entities": "^2.3.2", - "http-proxy-middleware": "^1.3.1", - "internal-ip": "^6.2.0", - "ipaddr.js": "^2.0.0", - "is-absolute-url": "^3.0.3", - "killable": "^1.0.1", - "open": "^7.4.2", + "http-proxy-middleware": "^2.0.0", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", "p-retry": "^4.5.0", "portfinder": "^1.0.28", - "schema-utils": "^3.0.0", - "selfsigned": "^1.10.11", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.0", "serve-index": "^1.9.1", "sockjs": "^0.3.21", "spdy": "^4.0.2", - "strip-ansi": "^6.0.0", - "url": "^0.11.0", - "webpack-dev-middleware": "^4.1.0", - "ws": "^7.4.5" + "strip-ansi": "^7.0.0", + "webpack-dev-middleware": "^5.3.0", + "ws": "^8.1.0" }, "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" @@ -8048,7 +8002,7 @@ "node": ">= 12.13.0" }, "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" + "webpack": "^4.37.0 || ^5.0.0" }, "peerDependenciesMeta": { "webpack-cli": { @@ -8056,40 +8010,90 @@ } } }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, "node_modules/webpack-dev-server/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/webpack-dev-server/node_modules/ipaddr.js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.0.tgz", - "integrity": "sha512-S54H9mIj0rbxRIyrDMEuuER86LdlgUg9FSeZ8duQb6CUG2iRrA36MYVQBSprTF/ZeAwvyQ5mDGuNvIPM0BIl3w==", + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, "engines": { - "node": ">= 10" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/webpack-dev-server/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/webpack-merge": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.7.3.tgz", - "integrity": "sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", "dev": true, "dependencies": { "clone-deep": "^4.0.1", @@ -8100,14 +8104,10 @@ } }, "node_modules/webpack-sources": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.2.0.tgz", - "integrity": "sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, - "dependencies": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" - }, "engines": { "node": ">=10.13.0" } @@ -8136,15 +8136,18 @@ } }, "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "dependencies": { "isexe": "^2.0.0" }, "bin": { - "which": "bin/which" + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, "node_modules/wildcard": { @@ -8167,27 +8170,6 @@ "node": ">=8" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -8195,12 +8177,12 @@ "dev": true }, "node_modules/ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz", + "integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==", "dev": true, "engines": { - "node": ">=8.3.0" + "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", @@ -8259,9 +8241,9 @@ }, "dependencies": { "@discoveryjs/json-ext": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz", - "integrity": "sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", + "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==", "dev": true }, "@mrmlnc/readdir-enhanced": { @@ -8283,36 +8265,28 @@ } }, "@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "requires": { - "@nodelib/fs.stat": "2.0.4", + "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" - }, - "dependencies": { - "@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", - "dev": true - } } }, "@nodelib/fs.stat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", - "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, "@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { - "@nodelib/fs.scandir": "2.1.4", + "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, @@ -8331,6 +8305,25 @@ "defer-to-connect": "^2.0.0" } }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/cacheable-request": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", @@ -8343,10 +8336,29 @@ "@types/responselike": "*" } }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, "@types/eslint": { - "version": "7.2.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.10.tgz", - "integrity": "sha512-kUEPnMKrqbtpCq/KTaGFFKAcz6Ethm2EjCoKIDaCmfRBWLbFuTcOJfTlorwbnboXBzahqWLgUp1BQeKHiJzPUQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.0.tgz", + "integrity": "sha512-JUYa/5JwoqikCy7O7jKtuNe9Z4ZZt615G+1EKfaDGSNEpzaA2OwbV/G1v08Oa7fd1XzlFoSCvt9ePl9/6FyAug==", "dev": true, "requires": { "@types/estree": "*", @@ -8354,9 +8366,9 @@ } }, "@types/eslint-scope": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz", - "integrity": "sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", "dev": true, "requires": { "@types/eslint": "*", @@ -8364,15 +8376,38 @@ } }, "@types/estree": { - "version": "0.0.47", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.47.tgz", - "integrity": "sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==", + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", "dev": true }, + "@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "@types/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", "dev": true, "requires": { "@types/minimatch": "*", @@ -8380,9 +8415,9 @@ } }, "@types/html-minifier-terser": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", - "integrity": "sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", "dev": true }, "@types/http-cache-semantics": { @@ -8392,39 +8427,57 @@ "dev": true }, "@types/http-proxy": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.6.tgz", - "integrity": "sha512-+qsjqR75S/ib0ig0R9WN+CDoZeOBU6F2XLewgC4KVgdXiNHiKKHFEMRHOrs5PbYE97D5vataw5wPj4KLYfUkuQ==", + "version": "1.17.8", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz", + "integrity": "sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA==", "dev": true, "requires": { "@types/node": "*" } }, "@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, "@types/keyv": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.2.tgz", - "integrity": "sha512-/FvAK2p4jQOaJ6CGDHJTqZcUtbZe820qIeTg7o0Shg7drB4JHeL+V/dhSaly7NXx6u8eSee+r7coT+yuJEvDLg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", + "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", "dev": true, "requires": { "@types/node": "*" } }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, "@types/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", "dev": true }, "@types/node": { - "version": "14.14.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz", - "integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==", + "version": "17.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz", + "integrity": "sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==", + "dev": true + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, "@types/responselike": { @@ -8437,177 +8490,214 @@ } }, "@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", + "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==", "dev": true }, + "@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/ws": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.2.tgz", + "integrity": "sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@webassemblyjs/ast": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.0.tgz", - "integrity": "sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", "dev": true, "requires": { - "@webassemblyjs/helper-numbers": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0" + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz", - "integrity": "sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", "dev": true }, "@webassemblyjs/helper-api-error": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz", - "integrity": "sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", "dev": true }, "@webassemblyjs/helper-buffer": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz", - "integrity": "sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", "dev": true }, "@webassemblyjs/helper-numbers": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz", - "integrity": "sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", "dev": true, "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.0", - "@webassemblyjs/helper-api-error": "1.11.0", + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", "@xtuc/long": "4.2.2" } }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz", - "integrity": "sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", "dev": true }, "@webassemblyjs/helper-wasm-section": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz", - "integrity": "sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-buffer": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/wasm-gen": "1.11.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" } }, "@webassemblyjs/ieee754": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz", - "integrity": "sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", "dev": true, "requires": { "@xtuc/ieee754": "^1.2.0" } }, "@webassemblyjs/leb128": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.0.tgz", - "integrity": "sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", "dev": true, "requires": { "@xtuc/long": "4.2.2" } }, "@webassemblyjs/utf8": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.0.tgz", - "integrity": "sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", "dev": true }, "@webassemblyjs/wasm-edit": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz", - "integrity": "sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-buffer": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/helper-wasm-section": "1.11.0", - "@webassemblyjs/wasm-gen": "1.11.0", - "@webassemblyjs/wasm-opt": "1.11.0", - "@webassemblyjs/wasm-parser": "1.11.0", - "@webassemblyjs/wast-printer": "1.11.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" } }, "@webassemblyjs/wasm-gen": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz", - "integrity": "sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/ieee754": "1.11.0", - "@webassemblyjs/leb128": "1.11.0", - "@webassemblyjs/utf8": "1.11.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" } }, "@webassemblyjs/wasm-opt": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz", - "integrity": "sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-buffer": "1.11.0", - "@webassemblyjs/wasm-gen": "1.11.0", - "@webassemblyjs/wasm-parser": "1.11.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" } }, "@webassemblyjs/wasm-parser": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz", - "integrity": "sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-api-error": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/ieee754": "1.11.0", - "@webassemblyjs/leb128": "1.11.0", - "@webassemblyjs/utf8": "1.11.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" } }, "@webassemblyjs/wast-printer": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz", - "integrity": "sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/ast": "1.11.1", "@xtuc/long": "4.2.2" } }, "@webpack-cli/configtest": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.0.2.tgz", - "integrity": "sha512-3OBzV2fBGZ5TBfdW50cha1lHDVf9vlvRXnjpVbJBa20pSZQaSkMJZiwA8V2vD9ogyeXn8nU5s5A6mHyf5jhMzA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz", + "integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==", "dev": true, "requires": {} }, "@webpack-cli/info": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.2.3.tgz", - "integrity": "sha512-lLek3/T7u40lTqzCGpC6CAbY6+vXhdhmwFRxZLMnRm6/sIF/7qMpT8MocXCRQfz0JAh63wpbXLMnsQ5162WS7Q==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.0.tgz", + "integrity": "sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw==", "dev": true, "requires": { "envinfo": "^7.7.3" } }, "@webpack-cli/serve": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.3.1.tgz", - "integrity": "sha512-0qXvpeYO6vaNoRBI52/UsbcaBydJCggoBBnIo/ovQQdn6fug0BgwsjorV1hVS7fMqGVTZGcVxv8334gjmbj5hw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz", + "integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==", "dev": true, "requires": {} }, @@ -8634,11 +8724,18 @@ } }, "acorn": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.1.1.tgz", - "integrity": "sha512-xYiIVjNuqtKXMxlRMDc6mZUhXehod4a3gbZ1qRlM7icK4EbxUFNLhWoPblCvFtB2Y9CIqHP3CF/rdxLItaQv8g==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", "dev": true }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "requires": {} + }, "aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -8661,6 +8758,35 @@ "uri-js": "^4.2.2" } }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -8668,12 +8794,6 @@ "dev": true, "requires": {} }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -8691,16 +8811,16 @@ } } }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", "dev": true }, "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { @@ -8713,9 +8833,9 @@ } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -8735,14 +8855,6 @@ "readdir-glob": "^1.0.0", "tar-stream": "^2.2.0", "zip-stream": "^4.1.0" - }, - "dependencies": { - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", - "dev": true - } } }, "archiver-utils": { @@ -8820,13 +8932,10 @@ "dev": true }, "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true }, "array-uniq": { "version": "1.0.3", @@ -8847,9 +8956,9 @@ "dev": true }, "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, "requires": { "safer-buffer": "~2.1.0" @@ -8868,13 +8977,10 @@ "dev": true }, "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "dev": true }, "asynckit": { "version": "0.4.0", @@ -8907,9 +9013,9 @@ "dev": true }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "base": { @@ -9004,52 +9110,48 @@ } }, "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", "dev": true, "requires": { - "bytes": "3.1.0", + "bytes": "3.1.1", "content-type": "~1.0.4", "debug": "2.6.9", "depd": "~1.1.2", - "http-errors": "1.7.2", + "http-errors": "1.8.1", "iconv-lite": "0.4.24", "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" }, "dependencies": { "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", "dev": true }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "ms": "2.0.0" } }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", "dev": true } } @@ -9094,16 +9196,16 @@ } }, "browserslist": { - "version": "4.16.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.5.tgz", - "integrity": "sha512-C2HAjrM1AI/djrpAUU/tr4pml1DqLIzJKSLDBXBrNErl9ZCCTXdhwxdJjYc16953+mBWf7Lw+uUJgpgb8cN71A==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001214", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.719", + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", "escalade": "^3.1.1", - "node-releases": "^1.1.71" + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" } }, "buffer": { @@ -9123,9 +9225,9 @@ "dev": true }, "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, "buffer-indexof": { @@ -9186,17 +9288,6 @@ "lowercase-keys": "^2.0.0", "normalize-url": "^6.0.1", "responselike": "^2.0.0" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - } } }, "call-bind": { @@ -9226,9 +9317,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001214", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001214.tgz", - "integrity": "sha512-O2/SCpuaU3eASWVaesQirZv1MSjUNOvmugaD8zNSJqw6Vv5SGwoOpA9LJs3pNPfM745nxqPvfZY3MQKY4AKHYg==", + "version": "1.0.30001300", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001300.tgz", + "integrity": "sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA==", "dev": true }, "caseless": { @@ -9257,19 +9348,19 @@ } }, "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" } }, "chownr": { @@ -9365,9 +9456,9 @@ } }, "clean-css": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", - "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.2.tgz", + "integrity": "sha512-/eR8ru5zyxKzpBLv9YZvMXgTSSQn7AdkMItMYynsFgGwTveCRVam9IUPFloE85B4vAIj05IuKmmEoV7/AQjT0w==", "dev": true, "requires": { "source-map": "~0.6.0" @@ -9389,9 +9480,9 @@ } }, "cli-spinners": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", - "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", "dev": true }, "clone": { @@ -9454,9 +9545,9 @@ "dev": true }, "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", "dev": true }, "combined-stream": { @@ -9469,15 +9560,9 @@ } }, "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true }, "component-emitter": { @@ -9487,13 +9572,13 @@ "dev": true }, "compress-commons": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.0.tgz", - "integrity": "sha512-ofaaLqfraD1YRTkrRKPCrGJ1pFeDG/MVCkVVV2FNGeWquSlqw5wOrwOfPQ1xF2u+blpeWASie5EubHz+vsNIgA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz", + "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==", "dev": true, "requires": { "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.1", + "crc32-stream": "^4.0.2", "normalize-path": "^3.0.0", "readable-stream": "^3.6.0" } @@ -9522,6 +9607,21 @@ "vary": "~1.1.2" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -9543,20 +9643,12 @@ "dev": true }, "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, "requires": { - "safe-buffer": "5.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } + "safe-buffer": "5.2.1" } }, "content-type": { @@ -9566,9 +9658,9 @@ "dev": true }, "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", "dev": true }, "cookie-signature": { @@ -9591,43 +9683,11 @@ "requires": { "fast-glob": "^3.2.5", "glob-parent": "^5.1.1", - "globby": "^11.0.3", - "normalize-path": "^3.0.0", - "p-limit": "^3.1.0", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1" - }, - "dependencies": { - "@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", - "dev": true - }, - "fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - } - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - } + "globby": "^11.0.3", + "normalize-path": "^3.0.0", + "p-limit": "^3.1.0", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1" } }, "core-util-is": { @@ -9665,6 +9725,117 @@ "p-map": "^3.0.0" }, "dependencies": { + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + } + }, + "fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dev": true, + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, "globby": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", @@ -9681,13 +9852,90 @@ "slash": "^2.0.0" } }, - "p-map": { + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-number": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "aggregate-error": "^3.0.0" + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" } } } @@ -9721,50 +9969,17 @@ "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" - }, - "dependencies": { - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } } }, "css-loader": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.4.tgz", - "integrity": "sha512-OFYGyINCKkdQsTrSYxzGSFnGS4gNjcXkKkQgWxK138jgnPt+lepxdjSZNc8sHAl5vP3DhsJUxufWIjOwI8PMMw==", + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz", + "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==", "dev": true, "requires": { - "camelcase": "^6.2.0", "icss-utils": "^5.1.0", "loader-utils": "^2.0.0", - "postcss": "^8.2.10", + "postcss": "^8.2.15", "postcss-modules-extract-imports": "^3.0.0", "postcss-modules-local-by-default": "^4.0.0", "postcss-modules-scope": "^3.0.0", @@ -9772,42 +9987,25 @@ "postcss-value-parser": "^4.1.0", "schema-utils": "^3.0.0", "semver": "^7.3.5" - }, - "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } } }, "css-select": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz", - "integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", + "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", "dev": true, "requires": { "boolbase": "^1.0.0", - "css-what": "^5.0.0", - "domhandler": "^4.2.0", - "domutils": "^2.6.0", - "nth-check": "^2.0.0" + "css-what": "^5.1.0", + "domhandler": "^4.3.0", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" } }, "css-what": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.0.1.tgz", - "integrity": "sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", + "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", "dev": true }, "cssesc": { @@ -9826,12 +10024,12 @@ } }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.2" } }, "decode-uri-component": { @@ -9887,6 +10085,12 @@ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -9930,12 +10134,6 @@ "requires": { "aggregate-error": "^3.0.0" } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true } } }, @@ -9958,18 +10156,18 @@ "dev": true }, "detect-node": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.5.tgz", - "integrity": "sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, "dir-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", - "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "requires": { - "path-type": "^3.0.0" + "path-type": "^4.0.0" } }, "dns-equal": { @@ -10024,18 +10222,18 @@ "dev": true }, "domhandler": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", - "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", + "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", "dev": true, "requires": { "domelementtype": "^2.2.0" } }, "domutils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz", - "integrity": "sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", "dev": true, "requires": { "dom-serializer": "^1.0.1", @@ -10076,9 +10274,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.720", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.720.tgz", - "integrity": "sha512-B6zLTxxaOFP4WZm6DrvgRk8kLFYWNhQ5TrHMC0l5WtkMXhU5UbnvWoTfeEwqOruUSlNMhVLfYak7REX6oC5Yfw==", + "version": "1.4.49", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.49.tgz", + "integrity": "sha512-k/0t1TRfonHIp8TJKfjBu2cKj8MqYTiEpOhci+q7CVEE5xnCQnx1pTa+V8b/sdhe4S3PR4p4iceEQWhGrKQORQ==", "dev": true }, "elm": { @@ -10091,9 +10289,9 @@ } }, "elm-asset-webpack-loader": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/elm-asset-webpack-loader/-/elm-asset-webpack-loader-1.1.2.tgz", - "integrity": "sha512-jrXYtxk13LXtbxCiT23+RuNhUgbJgGBRW2sCqkMGojTZTFKttL1E8mSUvsIuomUiKLJOSXUZb3HjvwXNkJjTNA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/elm-asset-webpack-loader/-/elm-asset-webpack-loader-1.1.3.tgz", + "integrity": "sha512-PED3ISseHh3ZKnMir2t9L5HOZOJFcodplKgSPIR+tDN9YzSRXbmyZ8E5qFehykqrmsg1H7zj1wL1/0J90judgQ==", "dev": true }, "elm-format": { @@ -10106,15 +10304,15 @@ } }, "elm-review": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/elm-review/-/elm-review-2.5.5.tgz", - "integrity": "sha512-VOPeOgeE16RjLich8Gt1qJlXyASG2qS5Or4Q3vO1OlAYLPxEAlzUlEiloa7RFtm6dnEYk38axnpx8WexKy7VjA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/elm-review/-/elm-review-2.7.0.tgz", + "integrity": "sha512-PvZj6M6rHYpyAGp2MKF/TzeDawioQacBTkVxzlBBGuMoO4LXw2PMIm/NmhkxcD38l5SvDIwpWmyDKvEsMaWNhQ==", "dev": true, "requires": { "chalk": "^4.0.0", - "chokidar": "^3.4.0", + "chokidar": "^3.5.2", "cross-spawn": "^7.0.3", - "elm-tooling": "^1.2.0", + "elm-tooling": "^1.6.0", "fast-levenshtein": "^3.0.0", "find-up": "^4.1.0", "folder-hash": "^3.3.0", @@ -10130,80 +10328,31 @@ "terminal-link": "^2.1.1", "which": "^2.0.2", "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } } }, "elm-test": { - "version": "0.19.1-revision6", - "resolved": "https://registry.npmjs.org/elm-test/-/elm-test-0.19.1-revision6.tgz", - "integrity": "sha512-4VbIyCRlCUm/py0E0AjMT3/mwd6DR4Y5Z5gEox6z5JII6ZdKIJmcQzjgWRI5qo5ERJiw9M/Nxhk7SGXFUbZsxQ==", + "version": "0.19.1-revision7", + "resolved": "https://registry.npmjs.org/elm-test/-/elm-test-0.19.1-revision7.tgz", + "integrity": "sha512-sd3nCQMeYMaY84Sz41bVJ30ZvQN1/4ZcD8uYMOuUbM39FDh58NY9/AcImVJ7Z+gjCFdcSU6VscZzhUoPW8jp6Q==", "dev": true, "requires": { "chalk": "^4.1.0", "chokidar": "^3.5.1", - "commander": "^7.0.0", - "cross-spawn": "^7.0.3", - "elm-tooling": "^1.1.0", - "glob": "^7.1.6", - "graceful-fs": "^4.2.4", - "rimraf": "^3.0.2", - "split": "^1.0.1", - "which": "^2.0.2", - "xmlbuilder": "^15.1.0" - }, - "dependencies": { - "commander": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.0.0.tgz", - "integrity": "sha512-ovx/7NkTrnPuIV8sqk/GjUIIM1+iUQeqA3ye2VNpq9sVoiZsooObWlQy+OPWGI17GDaEoybuAGJm6U8yC077BA==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } + "commander": "^7.1.0", + "cross-spawn": "^7.0.3", + "elm-tooling": "^1.2.0", + "glob": "^7.1.6", + "graceful-fs": "^4.2.4", + "rimraf": "^3.0.2", + "split": "^1.0.1", + "which": "^2.0.2", + "xmlbuilder": "^15.1.0" } }, "elm-tooling": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/elm-tooling/-/elm-tooling-1.4.1.tgz", - "integrity": "sha512-5Wj1G9KCJHRxnfFmAa1YSgpP3chblcifpw5Yg8hCDAnYg5DZuMvO4m22Py15TvwlN7fKD7nghnuHoSUiz0vdnw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/elm-tooling/-/elm-tooling-1.7.0.tgz", + "integrity": "sha512-EHZ54voWrG3BhUONbH/wFw5U95H6N7R4QFgXHDrPIaDBDdeyNkpFu4QWArSWkhzxyCF7hqT8ya2yy7SferDsgg==", "dev": true }, "elm-webpack-loader": { @@ -10244,24 +10393,15 @@ } }, "enhanced-resolve": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.0.tgz", - "integrity": "sha512-Sl3KRpJA8OpprrtaIswVki3cWPiPKxXuFxJXBp+zNb6s6VwNWwFRUdtmzd2ReUut8n+sCPx7QCtQ7w5wfJhSgQ==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz", + "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==", "dev": true, "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, "entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", @@ -10275,9 +10415,9 @@ "dev": true }, "es-module-lexer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.1.tgz", - "integrity": "sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", "dev": true }, "escalade": { @@ -10312,9 +10452,9 @@ }, "dependencies": { "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true } } @@ -10358,6 +10498,14 @@ "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + } } }, "exit-on-epipe": { @@ -10381,6 +10529,15 @@ "to-regex": "^3.0.1" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -10461,21 +10618,27 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", + "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", "dev": true, "requires": { "accepts": "~1.3.7", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.19.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.4.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "~1.1.2", @@ -10489,13 +10652,13 @@ "on-finished": "~2.3.0", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.9.6", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", "statuses": "~1.5.0", "type-is": "~1.6.18", "utils-merge": "1.0.1", @@ -10508,16 +10671,25 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", "dev": true } } @@ -10593,40 +10765,16 @@ "dev": true }, "fast-glob": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", - "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", "dev": true, "requires": { - "@mrmlnc/readdir-enhanced": "^2.2.1", - "@nodelib/fs.stat": "^1.1.2", - "glob-parent": "^3.1.0", - "is-glob": "^4.0.0", - "merge2": "^1.2.3", - "micromatch": "^3.1.10" - }, - "dependencies": { - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - } + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" } }, "fast-json-stable-stringify": { @@ -10651,18 +10799,18 @@ "dev": true }, "fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dev": true, "requires": { "reusify": "^1.0.4" } }, "faye-websocket": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", - "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", "dev": true, "requires": { "websocket-driver": ">=0.5.1" @@ -10704,17 +10852,23 @@ "parseurl": "~1.3.3", "statuses": "~1.5.0", "unpipe": "~1.0.0" - } - }, - "find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "find-elm-dependencies": { @@ -10752,29 +10906,12 @@ "debug": "^4.1.1", "graceful-fs": "~4.2.0", "minimatch": "~3.0.4" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", "dev": true }, "for-in": { @@ -10801,9 +10938,9 @@ } }, "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true }, "fragment-cache": { @@ -10861,9 +10998,9 @@ "dev": true }, "fsevents": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz", - "integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "optional": true }, @@ -10885,10 +11022,13 @@ } }, "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } }, "get-value": { "version": "2.0.6", @@ -10906,9 +11046,9 @@ } }, "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -10935,82 +11075,17 @@ "dev": true }, "globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" - }, - "dependencies": { - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - } - }, - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } } }, "got": { @@ -11034,23 +11109,12 @@ "responselike": "^2.0.0", "to-readable-stream": "^2.0.0", "type-fest": "^0.10.0" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - } } }, "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, "handle-thing": { @@ -11116,6 +11180,15 @@ "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", "dev": true }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -11225,51 +11298,38 @@ "dev": true }, "html-minifier-terser": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", - "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", "dev": true, "requires": { - "camel-case": "^4.1.1", - "clean-css": "^4.2.3", - "commander": "^4.1.1", + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", "he": "^1.2.0", - "param-case": "^3.0.3", + "param-case": "^3.0.4", "relateurl": "^0.2.7", - "terser": "^4.6.3" + "terser": "^5.10.0" }, "dependencies": { - "terser": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", - "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - } - } + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true } } }, "html-webpack-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.3.1.tgz", - "integrity": "sha512-rZsVvPXUYFyME0cuGkyOHfx9hmkFa4pWfxY/mdY38PsBEaVNsRoA+Id+8z6DBDgyv3zaw6XQszdF8HLwfQvcdQ==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", "dev": true, "requires": { - "@types/html-minifier-terser": "^5.0.0", - "html-minifier-terser": "^5.0.1", - "lodash": "^4.17.20", - "pretty-error": "^2.1.1", + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", "tapable": "^2.0.0" } }, @@ -11298,22 +11358,22 @@ "dev": true }, "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "dev": true, "requires": { "depd": "~1.1.2", "inherits": "2.0.4", - "setprototypeof": "1.1.1", + "setprototypeof": "1.2.0", "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "toidentifier": "1.0.1" } }, "http-parser-js": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", - "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", + "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==", "dev": true }, "http-proxy": { @@ -11328,9 +11388,9 @@ } }, "http-proxy-middleware": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz", - "integrity": "sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.1.tgz", + "integrity": "sha512-cfaXRVoZxSed/BmkA7SwBVNI9Kj7HFltaE5rqYOub5kWzWZ+gofV2koVN1j2rMW7pEfSSlCHGJ31xmuyFyfLOg==", "dev": true, "requires": { "@types/http-proxy": "^1.17.5", @@ -11338,18 +11398,6 @@ "is-glob": "^4.0.1", "is-plain-obj": "^3.0.0", "micromatch": "^4.0.2" - }, - "dependencies": { - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - } } }, "http-signature": { @@ -11392,15 +11440,15 @@ "dev": true }, "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "import-local": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", - "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, "requires": { "pkg-dir": "^4.2.0", @@ -11429,18 +11477,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "internal-ip": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-6.2.0.tgz", - "integrity": "sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg==", - "dev": true, - "requires": { - "default-gateway": "^6.0.0", - "ipaddr.js": "^1.9.1", - "is-ip": "^3.1.0", - "p-event": "^4.2.0" - } - }, "interpret": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", @@ -11453,22 +11489,10 @@ "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", "dev": true }, - "ip-regex": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", - "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", - "dev": true - }, "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true - }, - "is-absolute-url": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", - "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", "dev": true }, "is-accessor-descriptor": { @@ -11481,12 +11505,13 @@ } }, "is-arguments": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", - "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dev": true, "requires": { - "call-bind": "^1.0.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" } }, "is-binary-path": { @@ -11505,9 +11530,9 @@ "dev": true }, "is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", "dev": true, "requires": { "has": "^1.0.3" @@ -11523,10 +11548,13 @@ } }, "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } }, "is-descriptor": { "version": "1.0.2", @@ -11567,9 +11595,9 @@ "dev": true }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -11581,15 +11609,6 @@ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true }, - "is-ip": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", - "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", - "dev": true, - "requires": { - "ip-regex": "^4.0.0" - } - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -11624,19 +11643,19 @@ } }, "is-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", - "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "has-symbols": "^1.0.1" + "has-tostringtag": "^1.0.0" } }, "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, "is-typedarray": { @@ -11691,14 +11710,25 @@ "dev": true }, "jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "version": "27.4.6", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.6.tgz", + "integrity": "sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw==", "dev": true, "requires": { "@types/node": "*", "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "jsbn": { @@ -11720,9 +11750,9 @@ "dev": true }, "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "dev": true }, "json-schema-traverse": { @@ -11757,14 +11787,14 @@ } }, "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", - "json-schema": "0.2.3", + "json-schema": "0.4.0", "verror": "1.10.0" } }, @@ -11775,20 +11805,14 @@ "dev": true }, "keyv": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", - "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.5.tgz", + "integrity": "sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA==", "dev": true, "requires": { "json-buffer": "3.0.1" } }, - "killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", - "dev": true - }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -11802,9 +11826,9 @@ "dev": true }, "lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", "dev": true, "requires": { "readable-stream": "^2.0.5" @@ -11849,9 +11873,9 @@ "dev": true }, "loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -11955,15 +11979,6 @@ } } }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -11985,28 +12000,10 @@ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, - "mem": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/mem/-/mem-8.1.1.tgz", - "integrity": "sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.3", - "mimic-fn": "^3.1.0" - }, - "dependencies": { - "mimic-fn": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", - "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", - "dev": true - } - } - }, "memfs": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.2.tgz", - "integrity": "sha512-RE0CwmIM3CEvpcdK3rZ19BC4E6hv9kADkMN5rPduRak58cNArWLi/9jFLsa4rhsjfVxMP3v0jO7FHXq7SvFY5Q==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", + "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", "dev": true, "requires": { "fs-monkey": "1.0.3" @@ -12037,129 +12034,34 @@ "dev": true }, "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } + "braces": "^3.0.1", + "picomatch": "^2.2.3" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, "mime-db": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", - "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", "dev": true }, "mime-types": { - "version": "2.1.31", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", - "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", "dev": true, "requires": { - "mime-db": "1.48.0" + "mime-db": "1.51.0" } }, "mimic-fn": { @@ -12196,9 +12098,9 @@ "dev": true }, "minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", "dev": true, "requires": { "yallist": "^4.0.0" @@ -12231,9 +12133,9 @@ "dev": true }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "multicast-dns": { @@ -12253,9 +12155,9 @@ "dev": true }, "nanoid": { - "version": "3.1.22", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz", - "integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", + "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", "dev": true }, "nanomatch": { @@ -12335,19 +12237,55 @@ "shebang-command": "^1.2.0", "which": "^1.2.9" } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } } } }, "node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.2.1.tgz", + "integrity": "sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==", "dev": true }, "node-releases": { - "version": "1.1.71", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", - "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", "dev": true }, "normalize-path": { @@ -12369,20 +12307,12 @@ "dev": true, "requires": { "path-key": "^3.0.0" - }, - "dependencies": { - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - } } }, "nth-check": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", - "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", "dev": true, "requires": { "boolbase": "^1.0.0" @@ -12536,13 +12466,14 @@ } }, "open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", "dev": true, "requires": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" } }, "ora": { @@ -12560,23 +12491,6 @@ "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } } }, "p-all": { @@ -12586,6 +12500,14 @@ "dev": true, "requires": { "p-map": "^2.0.0" + }, + "dependencies": { + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + } } }, "p-cancelable": { @@ -12594,12 +12516,6 @@ "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", "dev": true }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, "p-event": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", @@ -12616,6 +12532,14 @@ "dev": true, "requires": { "p-map": "^2.0.0" + }, + "dependencies": { + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + } } }, "p-finally": { @@ -12654,19 +12578,22 @@ } }, "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } }, "p-retry": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.5.0.tgz", - "integrity": "sha512-5Hwh4aVQSu6BEP+w2zKlVXtFAaYQe1qWuVADSgoeVlLjwe/Q/AMSoRR4MDeaAfu8llT+YNbEijWu/YF3m6avkg==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.1.tgz", + "integrity": "sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==", "dev": true, "requires": { "@types/retry": "^0.12.0", - "retry": "^0.12.0" + "retry": "^0.13.1" } }, "p-timeout": { @@ -12735,9 +12662,9 @@ "dev": true }, "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-parse": { @@ -12753,21 +12680,10 @@ "dev": true }, "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true }, "performance-now": { "version": "2.1.0", @@ -12775,10 +12691,16 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, "pify": { @@ -12807,6 +12729,15 @@ "mkdirp": "^0.5.5" }, "dependencies": { + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, "debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -12824,12 +12755,6 @@ "requires": { "minimist": "^1.2.5" } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true } } }, @@ -12840,14 +12765,14 @@ "dev": true }, "postcss": { - "version": "8.2.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.12.tgz", - "integrity": "sha512-BJnGT5+0q2tzvs6oQfnY2NpEJ7rIXNfBnZtQOKCIsweeWXBXeDd5k31UgTdS3d/c02ouspufn37mTaHWkJyzMQ==", + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", + "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", "dev": true, "requires": { - "colorette": "^1.2.2", - "nanoid": "^3.1.22", - "source-map": "^0.6.1" + "nanoid": "^3.1.30", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.1" } }, "postcss-modules-extract-imports": { @@ -12887,9 +12812,9 @@ } }, "postcss-selector-parser": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.5.tgz", - "integrity": "sha512-aFYPoYmXbZ1V6HZaSvat08M97A8HqO6Pjz+PiNpw/DhuRrC72XWAdp3hL6wusDCN31sSmcZyMGa2hZEuX+Xfhg==", + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz", + "integrity": "sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ==", "dev": true, "requires": { "cssesc": "^3.0.0", @@ -12897,19 +12822,19 @@ } }, "postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, "pretty-error": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", - "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", "dev": true, "requires": { "lodash": "^4.17.20", - "renderkid": "^2.0.4" + "renderkid": "^3.0.0" } }, "printj": { @@ -12925,9 +12850,9 @@ "dev": true }, "prompts": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", - "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, "requires": { "kleur": "^3.0.3", @@ -12935,13 +12860,21 @@ } }, "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, "requires": { - "forwarded": "~0.1.2", + "forwarded": "0.2.0", "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + } } }, "psl": { @@ -12967,15 +12900,9 @@ "dev": true }, "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true }, "queue-microtask": { @@ -13000,40 +12927,21 @@ "dev": true }, "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", "dev": true, "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.1", + "http-errors": "1.8.1", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, "dependencies": { "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "dev": true - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", "dev": true } } @@ -13059,18 +12967,18 @@ } }, "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { "picomatch": "^2.2.1" } }, "rechoir": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", - "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", "dev": true, "requires": { "resolve": "^1.9.0" @@ -13087,9 +12995,9 @@ } }, "regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", + "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -13103,16 +13011,16 @@ "dev": true }, "renderkid": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz", - "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", "dev": true, "requires": { "css-select": "^4.1.3", "dom-converter": "^0.2.0", "htmlparser2": "^6.1.0", "lodash": "^4.17.21", - "strip-ansi": "^3.0.1" + "strip-ansi": "^6.0.1" } }, "repeat-element": { @@ -13155,6 +13063,12 @@ "uuid": "^3.3.2" } }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -13162,13 +13076,14 @@ "dev": true }, "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", + "integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==", "dev": true, "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.8.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "resolve-cwd": { @@ -13218,9 +13133,9 @@ "dev": true }, "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true }, "reusify": { @@ -13269,12 +13184,12 @@ "dev": true }, "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dev": true, "requires": { - "@types/json-schema": "^7.0.6", + "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } @@ -13286,24 +13201,27 @@ "dev": true }, "selfsigned": { - "version": "1.10.11", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", - "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.0.tgz", + "integrity": "sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ==", "dev": true, "requires": { - "node-forge": "^0.10.0" + "node-forge": "^1.2.0" } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } }, "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", "dev": true, "requires": { "debug": "2.6.9", @@ -13313,24 +13231,35 @@ "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "1.8.1", "mime": "1.6.0", - "ms": "2.1.1", + "ms": "2.1.3", "on-finished": "~2.3.0", "range-parser": "~1.2.1", "statuses": "~1.5.0" }, "dependencies": { - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } }, "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true } } @@ -13359,6 +13288,15 @@ "parseurl": "~1.3.2" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", @@ -13377,6 +13315,12 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", @@ -13386,15 +13330,15 @@ } }, "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", "dev": true, "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.17.2" } }, "set-value": { @@ -13427,9 +13371,9 @@ } }, "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, "shallow-clone": { @@ -13442,24 +13386,24 @@ } }, "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "^3.0.0" } }, "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", "dev": true }, "sisteransi": { @@ -13469,9 +13413,9 @@ "dev": true }, "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, "snapdragon": { @@ -13490,6 +13434,15 @@ "use": "^3.1.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -13571,6 +13524,12 @@ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -13622,28 +13581,36 @@ } }, "sockjs": { - "version": "0.3.21", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", - "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==", + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", "dev": true, "requires": { "faye-websocket": "^0.11.3", - "uuid": "^3.4.0", + "uuid": "^8.3.2", "websocket-driver": "^0.7.4" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + } } }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, "source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -13658,9 +13625,9 @@ } }, "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -13684,23 +13651,6 @@ "http-deceiver": "^1.2.7", "select-hose": "^2.0.0", "spdy-transport": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "spdy-transport": { @@ -13715,23 +13665,6 @@ "obuf": "^1.1.2", "readable-stream": "^3.0.6", "wbuf": "^1.7.3" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "split": { @@ -13753,9 +13686,9 @@ } }, "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", "dev": true, "requires": { "asn1": "~0.2.3", @@ -13863,40 +13796,23 @@ } }, "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^5.0.1" } }, "strip-final-newline": { @@ -13934,16 +13850,22 @@ "supports-color": "^7.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, "tapable": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", - "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true }, "tar": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.10.tgz", - "integrity": "sha512-kvvfiVvjGMxeUNB6MyYv5z7vhfFRwbwCXJAeL0/lnbrttBVqcMOnpHUf0X42LrPMR8mMpgapkJMchFH4FSHzNA==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, "requires": { "chownr": "^2.0.0", @@ -14008,14 +13930,14 @@ } }, "terser": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.6.1.tgz", - "integrity": "sha512-yv9YLFQQ+3ZqgWCUk+pvNJwgUTdlIxUk1WTN+RnaFJe2L7ipG2csPT0ra2XRm7Cs8cxN7QXmK1rFzEwYEQkzXw==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", + "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", "dev": true, "requires": { "commander": "^2.20.0", "source-map": "~0.7.2", - "source-map-support": "~0.5.19" + "source-map-support": "~0.5.20" }, "dependencies": { "commander": { @@ -14033,17 +13955,27 @@ } }, "terser-webpack-plugin": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.1.tgz", - "integrity": "sha512-5XNNXZiR8YO6X6KhSGXfY0QrGrCRlSwAEjIIrlRQR4W8nP69TaJUlh3bkuac6zzgspiGPfKEHcY295MMVExl5Q==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz", + "integrity": "sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ==", "dev": true, "requires": { - "jest-worker": "^26.6.2", - "p-limit": "^3.1.0", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1", + "jest-worker": "^27.4.1", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", "source-map": "^0.6.1", - "terser": "^5.5.1" + "terser": "^5.7.2" + }, + "dependencies": { + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + } } }, "through": { @@ -14106,9 +14038,9 @@ } }, "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true }, "tough-cookie": { @@ -14128,9 +14060,9 @@ "dev": true }, "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true }, "tunnel-agent": { @@ -14272,24 +14204,6 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -14320,12 +14234,6 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -14344,9 +14252,9 @@ } }, "watchpack": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.1.1.tgz", - "integrity": "sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", "dev": true, "requires": { "glob-to-regexp": "^0.4.1", @@ -14372,142 +14280,208 @@ } }, "webpack": { - "version": "5.35.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.35.0.tgz", - "integrity": "sha512-au3gu55yYF/h6NXFr0KZPZAYxS6Nlc595BzYPke8n0CSff5WXcoixtjh5LC/8mXunkRKxhymhXmBY0+kEbR6jg==", + "version": "5.66.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.66.0.tgz", + "integrity": "sha512-NJNtGT7IKpGzdW7Iwpn/09OXz9inIkeIQ/ibY6B+MdV1x6+uReqz/5z1L89ezWnpPDWpXF0TY5PCYKQdWVn8Vg==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.47", - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/wasm-edit": "1.11.0", - "@webassemblyjs/wasm-parser": "1.11.0", - "acorn": "^8.0.4", + "@types/estree": "^0.0.50", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.0", - "es-module-lexer": "^0.4.0", - "eslint-scope": "^5.1.1", + "enhanced-resolve": "^5.8.3", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.4", + "graceful-fs": "^4.2.9", "json-parse-better-errors": "^1.0.2", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.0.0", + "schema-utils": "^3.1.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.1", - "watchpack": "^2.0.0", - "webpack-sources": "^2.1.1" + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.2" } }, "webpack-cli": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.6.0.tgz", - "integrity": "sha512-9YV+qTcGMjQFiY7Nb1kmnupvb1x40lfpj8pwdO/bom+sQiP4OBMKjHq29YQrlDWDPZO9r/qWaRRywKaRDKqBTA==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.1.tgz", + "integrity": "sha512-JYRFVuyFpzDxMDB+v/nanUdQYcZtqFPGzmlW4s+UkPMFhSpfRNmf1z4AwYcHJVdvEFAM7FFCQdNTpsBYhDLusQ==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.0.2", - "@webpack-cli/info": "^1.2.3", - "@webpack-cli/serve": "^1.3.1", - "colorette": "^1.2.1", + "@webpack-cli/configtest": "^1.1.0", + "@webpack-cli/info": "^1.4.0", + "@webpack-cli/serve": "^1.6.0", + "colorette": "^2.0.14", "commander": "^7.0.0", - "enquirer": "^2.3.6", "execa": "^5.0.0", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", "interpret": "^2.2.0", "rechoir": "^0.7.0", - "v8-compile-cache": "^2.2.0", "webpack-merge": "^5.7.3" - }, - "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true - } } }, "webpack-dev-middleware": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-4.3.0.tgz", - "integrity": "sha512-PjwyVY95/bhBh6VUqt6z4THplYcsvQ8YNNBTBM873xLVmw8FLeALn0qurHbs9EmcfhzQis/eoqypSnZeuUz26w==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.0.tgz", + "integrity": "sha512-MouJz+rXAm9B1OTOYaJnn6rtD/lWZPy2ufQCH3BPs8Rloh/Du6Jze4p7AeLYHkVi0giJnYLaSGDC7S+GM9arhg==", "dev": true, "requires": { - "colorette": "^1.2.2", - "mem": "^8.1.1", + "colorette": "^2.0.10", "memfs": "^3.2.2", - "mime-types": "^2.1.30", + "mime-types": "^2.1.31", "range-parser": "^1.2.1", - "schema-utils": "^3.0.0" + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } } }, "webpack-dev-server": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.0.0-beta.3.tgz", - "integrity": "sha512-Ud7ieH15No/KiSdRuzk+2k+S4gSCR/N7m4hJhesDbKQEZy3P+NPXTXfsimNOZvbVX2TRuIEFB+VdLZFn8DwGwg==", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz", + "integrity": "sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q==", "dev": true, "requires": { - "ansi-html": "^0.0.7", + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/serve-index": "^1.9.1", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.2.2", + "ansi-html-community": "^0.0.8", "bonjour": "^3.5.0", - "chokidar": "^3.5.1", + "chokidar": "^3.5.2", + "colorette": "^2.0.10", "compression": "^1.7.4", "connect-history-api-fallback": "^1.6.0", + "default-gateway": "^6.0.3", "del": "^6.0.0", "express": "^4.17.1", - "find-cache-dir": "^3.3.1", "graceful-fs": "^4.2.6", "html-entities": "^2.3.2", - "http-proxy-middleware": "^1.3.1", - "internal-ip": "^6.2.0", - "ipaddr.js": "^2.0.0", - "is-absolute-url": "^3.0.3", - "killable": "^1.0.1", - "open": "^7.4.2", + "http-proxy-middleware": "^2.0.0", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", "p-retry": "^4.5.0", "portfinder": "^1.0.28", - "schema-utils": "^3.0.0", - "selfsigned": "^1.10.11", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.0", "serve-index": "^1.9.1", "sockjs": "^0.3.21", "spdy": "^4.0.2", - "strip-ansi": "^6.0.0", - "url": "^0.11.0", - "webpack-dev-middleware": "^4.1.0", - "ws": "^7.4.5" + "strip-ansi": "^7.0.0", + "webpack-dev-middleware": "^5.3.0", + "ws": "^8.1.0" }, "dependencies": { + "ajv": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true }, - "ipaddr.js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.0.tgz", - "integrity": "sha512-S54H9mIj0rbxRIyrDMEuuER86LdlgUg9FSeZ8duQb6CUG2iRrA36MYVQBSprTF/ZeAwvyQ5mDGuNvIPM0BIl3w==", + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^6.0.1" } } } }, "webpack-merge": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.7.3.tgz", - "integrity": "sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", "dev": true, "requires": { "clone-deep": "^4.0.1", @@ -14515,14 +14489,10 @@ } }, "webpack-sources": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.2.0.tgz", - "integrity": "sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==", - "dev": true, - "requires": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" - } + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true }, "websocket-driver": { "version": "0.7.4", @@ -14542,9 +14512,9 @@ "dev": true }, "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -14565,23 +14535,6 @@ "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } } }, "wrappy": { @@ -14591,9 +14544,9 @@ "dev": true }, "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz", + "integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==", "dev": true, "requires": {} }, diff --git a/package.json b/package.json index e7dd117..d8a8c5e 100644 --- a/package.json +++ b/package.json @@ -32,9 +32,9 @@ "filemanager-webpack-plugin": "^4.0.0", "html-webpack-plugin": "^5.3.1", "style-loader": "^2.0.0", - "webpack": "^5.35.0", - "webpack-cli": "^4.6.0", - "webpack-dev-server": "^4.0.0-beta.3" + "webpack": "^5.66.0", + "webpack-cli": "^4.9.1", + "webpack-dev-server": "^4.7.3" }, "engines": { "node": ">=12.0.0" From 6d04b8a86a8f34cedb927ffd9e4b31d35da77631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 24 Jan 2022 12:45:07 -0500 Subject: [PATCH 42/54] Add foundations for the User page Add a new route `/users/:username` that displays a users page with their README and their projects (via a new query param to the `/projects` API). --- src/Api.elm | 15 ++- src/Definition/Readme.elm | 7 +- src/PerspectiveLanding.elm | 7 +- src/UI/Card.elm | 34 ++++- src/UnisonShare/App.elm | 73 +++++++--- src/UnisonShare/Page/Catalog.elm | 2 +- src/UnisonShare/Page/UserPage.elm | 170 ++++++++++++++++++++++++ src/UnisonShare/Route.elm | 50 ++++++- src/UnisonShare/User.elm | 55 ++++++++ src/css/composites.css | 2 +- src/css/composites/project-listing.css | 15 +++ src/css/composites/readme.css | 23 ---- src/css/elements/card.css | 4 + src/css/perspective-landing.css | 22 +++ src/css/themes/unison/light.css | 2 + src/css/ui/page-layout.css | 2 +- src/css/unison-share.css | 1 + src/css/unison-share/page/catalog.css | 11 -- src/css/unison-share/page/user-page.css | 27 ++++ 19 files changed, 450 insertions(+), 72 deletions(-) create mode 100644 src/UnisonShare/Page/UserPage.elm create mode 100644 src/UnisonShare/User.elm create mode 100644 src/css/composites/project-listing.css delete mode 100644 src/css/composites/readme.css create mode 100644 src/css/unison-share/page/user-page.css diff --git a/src/Api.elm b/src/Api.elm index e0e435e..5cf6a1d 100644 --- a/src/Api.elm +++ b/src/Api.elm @@ -61,9 +61,18 @@ namespace perspective fqn = Endpoint [ "namespaces", FQN.toString fqn ] queryParams -projects : Endpoint -projects = - Endpoint [ "projects" ] [] +projects : Maybe String -> Endpoint +projects owner = + let + queryParams = + case owner of + Just owner_ -> + [ string "owner" owner_ ] + + Nothing -> + [] + in + Endpoint [ "projects" ] queryParams getDefinition : Perspective -> List String -> Endpoint diff --git a/src/Definition/Readme.elm b/src/Definition/Readme.elm index ce71fc8..d4855f4 100644 --- a/src/Definition/Readme.elm +++ b/src/Definition/Readme.elm @@ -2,10 +2,9 @@ module Definition.Readme exposing (..) import Definition.Doc as Doc exposing (Doc, DocFoldToggles, FoldId) import Definition.Reference exposing (Reference) -import Html exposing (Html, div, header, text) +import Html exposing (Html, div) import Html.Attributes exposing (class) import Json.Decode as Decode -import UI.Icon as Icon {-| Represent the Readme Doc definition of a namespace. This is typically @@ -28,9 +27,7 @@ view : -> Html msg view refToMsg toggleFoldMsg docFoldToggles (Readme doc) = div [ class "readme" ] - [ header [] [ Icon.view Icon.doc, text "README" ] - , Doc.view refToMsg toggleFoldMsg docFoldToggles doc - ] + [ Doc.view refToMsg toggleFoldMsg docFoldToggles doc ] diff --git a/src/PerspectiveLanding.elm b/src/PerspectiveLanding.elm index fcb2e99..90fb66b 100644 --- a/src/PerspectiveLanding.elm +++ b/src/PerspectiveLanding.elm @@ -170,7 +170,12 @@ view env model = Success (Namespace _ _ { readme }) -> case readme of Just r -> - container [ Readme.view OpenReference ToggleDocFold model r ] + container + [ div [ class "perspective-landing-readme" ] + [ header [] [ Icon.view Icon.doc, text "README" ] + , Readme.view OpenReference ToggleDocFold model r + ] + ] Nothing -> viewEmptyStateNamespace fqn diff --git a/src/UI/Card.elm b/src/UI/Card.elm index edbbc7f..1761449 100644 --- a/src/UI/Card.elm +++ b/src/UI/Card.elm @@ -4,18 +4,36 @@ import Html exposing (Html, div, h3, text) import Html.Attributes exposing (class) +type CardType + = Contained + | Uncontained + + type alias Card msg = - { title : Maybe String, items : List (Html msg) } + { type_ : CardType + , title : Maybe String + , items : List (Html msg) + } card : List (Html msg) -> Card msg card items = - { title = Nothing, items = items } + { type_ = Uncontained, title = Nothing, items = items } titled : String -> List (Html msg) -> Card msg titled title items = - { title = Just title, items = items } + { type_ = Uncontained, title = Just title, items = items } + + +withType : CardType -> Card msg -> Card msg +withType type_ card_ = + { card_ | type_ = type_ } + + +asContained : Card msg -> Card msg +asContained card_ = + { card_ | type_ = Contained } withTitle : String -> Card msg -> Card msg @@ -43,5 +61,13 @@ view card_ = Nothing -> card_.items + + typeClass = + case card_.type_ of + Contained -> + "contained" + + Uncontained -> + "uncontained" in - div [ class "card" ] items + div [ class "card", class typeClass ] items diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index 030101c..3eb4829 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -18,7 +18,7 @@ import KeyboardShortcut.KeyboardEvent as KeyboardEvent exposing (KeyboardEvent) import Namespace exposing (NamespaceDetails) import Perspective exposing (Perspective(..)) import PerspectiveLanding -import RemoteData +import RemoteData exposing (RemoteData(..)) import UI import UI.AppHeader as AppHeader import UI.Banner as Banner @@ -30,6 +30,7 @@ import UI.Sidebar as Sidebar import UI.Tooltip as Tooltip import UnisonShare.AppModal as AppModal import UnisonShare.Page.Catalog as Catalog +import UnisonShare.Page.UserPage as UserPage import UnisonShare.Route as Route exposing (Route) import Url exposing (Url) import Workspace @@ -46,6 +47,7 @@ type alias Model = , workspace : Workspace.Model , perspectiveLanding : PerspectiveLanding.Model , catalog : Catalog.Model + , userPage : UserPage.Model , appModal : AppModal.Model , keyboardShortcut : KeyboardShortcut.Model , env : Env @@ -78,7 +80,20 @@ init env route = |> Maybe.withDefault Cmd.none ( catalog, catalogCmd ) = - Catalog.init env + case route of + Route.Catalog -> + Catalog.init env + + _ -> + ( NotAsked, Cmd.none ) + + ( userPage, userPageCmd ) = + case route of + Route.User username -> + UserPage.init env username + + _ -> + ( NotAsked, Cmd.none ) model = { route = route @@ -90,6 +105,7 @@ init env route = , env = env , sidebarToggled = False , catalog = catalog + , userPage = userPage } in ( model @@ -97,6 +113,7 @@ init env route = [ Cmd.map CodebaseTreeMsg codebaseTreeCmd , Cmd.map WorkspaceMsg workspaceCmd , Cmd.map CatalogMsg catalogCmd + , Cmd.map UserPageMsg userPageCmd , fetchNamespaceDetailsCmd ] ) @@ -118,6 +135,7 @@ type Msg -- sub msgs | AppModalMsg AppModal.Msg | CatalogMsg Catalog.Msg + | UserPageMsg UserPage.Msg | WorkspaceMsg Workspace.Msg | PerspectiveLandingMsg PerspectiveLanding.Msg | CodebaseTreeMsg CodebaseTree.Msg @@ -156,6 +174,13 @@ update msg ({ env } as model) = in ( { model2 | catalog = catalog }, Cmd.map CatalogMsg cmd ) + Route.User username -> + let + ( userPage, cmd ) = + UserPage.init model.env username + in + ( { model2 | userPage = userPage }, Cmd.map UserPageMsg cmd ) + Route.Project params (Route.ProjectDefinition ref) -> let ( workspace, cmd ) = @@ -233,6 +258,13 @@ update msg ({ env } as model) = in ( { model | catalog = catalog }, Cmd.map CatalogMsg cmd ) + ( Route.User _, UserPageMsg uMsg ) -> + let + ( userPage, cmd ) = + UserPage.update env uMsg model.userPage + in + ( { model | userPage = userPage }, Cmd.map UserPageMsg cmd ) + ( Route.Project _ _, WorkspaceMsg wMsg ) -> let ( workspace, wCmd, outMsg ) = @@ -683,28 +715,39 @@ view model = , content = PageLayout.PageContent [ pageContent ] } - page = + ( pageId, page ) = case model.route of Route.Catalog -> - Html.map CatalogMsg (Catalog.view model.catalog) + ( "catalog-page", Html.map CatalogMsg (Catalog.view model.catalog) ) + + Route.User _ -> + ( "user-page", Html.map UserPageMsg (UserPage.view model.userPage) ) Route.Project _ Route.ProjectRoot -> - Html.map PerspectiveLandingMsg - (PerspectiveLanding.view - model.env - model.perspectiveLanding - ) - |> withSidebar - |> PageLayout.view + let + page_ = + Html.map PerspectiveLandingMsg + (PerspectiveLanding.view + model.env + model.perspectiveLanding + ) + |> withSidebar + |> PageLayout.view + in + ( "project-page", page_ ) Route.Project _ (Route.ProjectDefinition _) -> - Html.map WorkspaceMsg (Workspace.view model.workspace) - |> withSidebar - |> PageLayout.view + let + page_ = + Html.map WorkspaceMsg (Workspace.view model.workspace) + |> withSidebar + |> PageLayout.view + in + ( "project-page", page_ ) in { title = "Unison Share" , body = - [ div [ id "app" ] + [ div [ id "app", class pageId ] [ appHeader , page , Html.map AppModalMsg (AppModal.view model.env model.appModal) diff --git a/src/UnisonShare/Page/Catalog.elm b/src/UnisonShare/Page/Catalog.elm index 33cec9f..22ed5e7 100644 --- a/src/UnisonShare/Page/Catalog.elm +++ b/src/UnisonShare/Page/Catalog.elm @@ -69,7 +69,7 @@ fetchCatalog env = |> Api.toTask env.apiBasePath Catalog.decodeCatalogMask |> Task.andThen (\catalog -> - Api.projects + Api.projects Nothing |> Api.toTask env.apiBasePath Project.decodeListings |> Task.map (\projects -> ( catalog, projects )) ) diff --git a/src/UnisonShare/Page/UserPage.elm b/src/UnisonShare/Page/UserPage.elm new file mode 100644 index 0000000..581f738 --- /dev/null +++ b/src/UnisonShare/Page/UserPage.elm @@ -0,0 +1,170 @@ +module UnisonShare.Page.UserPage exposing (..) + +import Api +import Definition.Doc as Doc +import Definition.Readme as Readme +import Definition.Reference exposing (Reference) +import Env exposing (Env) +import FullyQualifiedName as FQN +import Html exposing (Html, div, h1, text) +import Html.Attributes exposing (class) +import Http +import Perspective +import Project exposing (ProjectListing) +import RemoteData exposing (RemoteData(..), WebData) +import Task +import UI +import UI.Card as Card +import UI.Click as Click +import UI.PageLayout as PageLayout exposing (PageLayout) +import UnisonShare.Route as Route +import UnisonShare.User as User exposing (UserDetails, Username) + + + +-- MODEL + + +type alias LoadedModel = + { user : UserDetails + , docFoldToggles : Doc.DocFoldToggles + , projects : List ProjectListing + } + + +type alias Model = + WebData LoadedModel + + +init : Env -> Username -> ( Model, Cmd Msg ) +init env username = + ( Loading, fetchUser env username ) + + +{-| Fetch the Catalog in sequence by first fetching the doc, then the +projectListings and finally merging them into a Catalog +-} +fetchUser : Env -> Username -> Cmd Msg +fetchUser env username = + let + perspective = + Perspective.toCodebasePerspective env.perspective + + usernameFqn = + username |> User.usernameToString |> FQN.fromString + in + Api.namespace perspective usernameFqn + |> Api.toTask env.apiBasePath User.decodeDetails + |> Task.andThen + (\userDetails -> + Api.projects (username |> User.usernameToString |> Just) + |> Api.toTask env.apiBasePath Project.decodeListings + |> Task.map (\projects -> ( userDetails, projects )) + ) + |> Task.attempt FetchUserProfileFinished + + + +-- UPDATE + + +type Msg + = OpenReference Reference + | ToggleDocFold Doc.FoldId + | FetchUserProfileFinished (Result Http.Error ( UserDetails, List ProjectListing )) + + +update : Env -> Msg -> Model -> ( Model, Cmd Msg ) +update _ msg model = + case ( model, msg ) of + ( _, FetchUserProfileFinished res ) -> + case res of + Err e -> + ( Failure e, Cmd.none ) + + Ok ( userDetails, projects ) -> + let + m = + { docFoldToggles = Doc.emptyDocFoldToggles + , user = userDetails + , projects = projects + } + in + ( Success m, Cmd.none ) + + ( Success _, OpenReference _ ) -> + ( model, Cmd.none ) + + ( Success m, ToggleDocFold fid ) -> + ( Success { m | docFoldToggles = Doc.toggleFold m.docFoldToggles fid }, Cmd.none ) + + _ -> + ( model, Cmd.none ) + + + +-- VIEW + + +projectUrl : ProjectListing -> String +projectUrl = + Route.forProject >> Route.toUrlString + + +viewProjectListing : ProjectListing -> Html msg +viewProjectListing project = + Project.viewProjectListing (Click.Href (projectUrl project)) project + + +viewLoadedModel : LoadedModel -> PageLayout Msg +viewLoadedModel { user, projects, docFoldToggles } = + let + readmeCard = + user.readme + |> Maybe.map (Readme.view OpenReference ToggleDocFold docFoldToggles) + |> Maybe.map (\r -> Card.titled "readme" [ r ]) + |> Maybe.map Card.asContained + |> Maybe.map Card.view + |> Maybe.withDefault UI.nothing + + projectsCard = + projects + |> List.map viewProjectListing + |> (\ps -> [ div [ class "projects" ] ps ]) + |> Card.titled "projects" + |> Card.view + in + PageLayout.HeroLayout + { hero = + PageLayout.PageHero + (div [ class "user-hero" ] [ h1 [] [ text (User.usernameToString user.username) ] ]) + , content = PageLayout.PageContent [ readmeCard, projectsCard ] + } + + +disabledPage : Html Msg -> PageLayout Msg +disabledPage content = + PageLayout.HeroLayout + { hero = PageLayout.PageHero UI.nothing + , content = PageLayout.PageContent [ content ] + } + + +view : Model -> Html Msg +view model = + let + page = + case model of + NotAsked -> + disabledPage (div [] []) + + Loading -> + disabledPage (div [] []) + + Failure _ -> + disabledPage (div [] []) + + Success m -> + viewLoadedModel m + in + PageLayout.view page diff --git a/src/UnisonShare/Route.elm b/src/UnisonShare/Route.elm index 2ea2955..b61e3cb 100644 --- a/src/UnisonShare/Route.elm +++ b/src/UnisonShare/Route.elm @@ -2,6 +2,7 @@ module UnisonShare.Route exposing ( ProjectRoute(..) , Route(..) , forProject + , forUser , fromUrl , navigate , navigateToCurrentPerspective @@ -27,6 +28,7 @@ import Parser exposing ((|.), (|=), Parser, end, oneOf, succeed) import Perspective exposing (CodebasePerspectiveParam(..), PerspectiveParams(..)) import Project exposing (Project) import Route.Parsers as RP exposing (b, reference, s, slash) +import UnisonShare.User as User exposing (User) import Url exposing (Url) import Url.Builder exposing (relative) @@ -82,6 +84,7 @@ type ProjectRoute type Route = Catalog + | User User.Username | Project PerspectiveParams ProjectRoute @@ -91,6 +94,9 @@ updatePerspectiveParams route params = Catalog -> Catalog + User username_ -> + User username_ + Project _ ProjectRoot -> Project params ProjectRoot @@ -107,6 +113,28 @@ catalog = succeed Catalog |. slash |. s "catalog" +user : Parser Route +user = + succeed User |. slash |. s "users" |. slash |= username |. end + + +username : Parser User.Username +username = + let + handleMaybe mUsername = + case mUsername of + Just u -> + Parser.succeed u + + Nothing -> + Parser.problem "Invalid username" + in + Parser.chompUntilEndOr "/" + |> Parser.getChompedString + |> Parser.map User.usernameFromString + |> Parser.andThen handleMaybe + + perspective : Parser Route perspective = succeed (\pp -> Project pp ProjectRoot) |. slash |= RP.perspectiveParams |. end @@ -119,7 +147,7 @@ definition = toRoute : Parser Route toRoute = - oneOf [ b catalog, b perspective, b definition ] + oneOf [ b catalog, b user, b perspective, b definition ] {-| In environments like Unison Local, the UI is served with a base path @@ -171,12 +199,12 @@ fromUrl basePath url = perspectiveParams : Route -> Maybe PerspectiveParams perspectiveParams route = case route of - Catalog -> - Nothing - Project pp _ -> Just pp + _ -> + Nothing + -- Create @@ -191,6 +219,11 @@ forProject project_ = Project (Perspective.ByNamespace Relative fqn) ProjectRoot +forUser : User a -> Route +forUser user_ = + User user_.username + + -- TRANSFORM @@ -256,6 +289,9 @@ toUrlString route = Catalog -> [ "catalog" ] + User username_ -> + [ "users", User.usernameToString username_ ] + Project pp ProjectRoot -> perspectiveParamsToPath pp False @@ -331,13 +367,13 @@ replacePerspective navKey perspectiveParams_ oldRoute = let newRoute = case oldRoute of - Catalog -> - Catalog - Project _ ProjectRoot -> Project perspectiveParams_ ProjectRoot Project _ (ProjectDefinition ref) -> Project perspectiveParams_ (ProjectDefinition ref) + + _ -> + oldRoute in navigate navKey newRoute diff --git a/src/UnisonShare/User.elm b/src/UnisonShare/User.elm new file mode 100644 index 0000000..0f2369a --- /dev/null +++ b/src/UnisonShare/User.elm @@ -0,0 +1,55 @@ +module UnisonShare.User exposing + ( User + , UserDetails + , Username + , decodeDetails + , usernameFromString + , usernameToString + ) + +import Definition.Readme as Readme exposing (Readme) +import Hash exposing (Hash) +import Json.Decode as Decode exposing (field, maybe, string) +import Project exposing (ProjectListing) + + +type Username + = Username String + + +type alias User u = + { u | hash : Hash, username : Username } + + +type alias UserDetails = + User { readme : Maybe Readme, projects : List ProjectListing } + + + +-- HELPERS + + +usernameFromString : String -> Maybe Username +usernameFromString raw = + Just (Username raw) + + +usernameToString : Username -> String +usernameToString (Username raw) = + raw + + + +-- DECODE + + +decodeDetails : Decode.Decoder UserDetails +decodeDetails = + let + makeDetails hash username readme = + { hash = hash, username = username, readme = readme, projects = [] } + in + Decode.map3 makeDetails + (field "hash" Hash.decode) + (field "fqn" (Decode.map Username string)) + (maybe (field "readme" Readme.decode)) diff --git a/src/css/composites.css b/src/css/composites.css index a87d89d..ed2b1af 100644 --- a/src/css/composites.css +++ b/src/css/composites.css @@ -1,5 +1,5 @@ @import "./composites/app-header.css"; @import "./composites/modal.css"; @import "./composites/codebase-tree.css"; -@import "./composites/readme.css"; @import "./composites/copy-field.css"; +@import "./composites/project-listing.css"; diff --git a/src/css/composites/project-listing.css b/src/css/composites/project-listing.css new file mode 100644 index 0000000..9279a50 --- /dev/null +++ b/src/css/composites/project-listing.css @@ -0,0 +1,15 @@ +/* A combination of a hashvatar and a project name */ + +.project-listing { + display: flex; + flex-direction: row; + align-items: center; + height: 2rem; + padding: 0 0.25rem; + border-radius: var(--border-radius-base); +} + +.project-listing:hover { + text-decoration: none; + background: var(--color-main-hover-bg); +} diff --git a/src/css/composites/readme.css b/src/css/composites/readme.css deleted file mode 100644 index 9eff6e1..0000000 --- a/src/css/composites/readme.css +++ /dev/null @@ -1,23 +0,0 @@ -.readme header { - font-size: var(--font-size-medium); - font-weight: bold; - height: 1.5rem; - line-height: 1; - display: flex; - flex-direction: row; -} - -.readme header .icon { - margin-right: 0.375rem; -} - -.readme .definition-doc { - margin: 1.5rem 0; - padding-left: 1.25rem; -} - -@media only screen and (max-width: 1024px) { - .readme .definition-doc { - padding-left: 0; - } -} diff --git a/src/css/elements/card.css b/src/css/elements/card.css index b9ce301..c246112 100644 --- a/src/css/elements/card.css +++ b/src/css/elements/card.css @@ -8,6 +8,10 @@ background: var(--color-card-bg); } +.card.contained { + border: 1px solid var(--color-card-border); +} + .card .card-title { color: var(--color-card-title); text-transform: uppercase; diff --git a/src/css/perspective-landing.css b/src/css/perspective-landing.css index 2e508c8..4662883 100644 --- a/src/css/perspective-landing.css +++ b/src/css/perspective-landing.css @@ -146,6 +146,24 @@ justify-content: flex-end; } +.perspective-landing-readme header { + font-size: var(--font-size-medium); + font-weight: bold; + height: 1.5rem; + line-height: 1; + display: flex; + flex-direction: row; +} + +.perspective-landing-readme header .icon { + margin-right: 0.375rem; +} + +.perspective-landing-readme .definition-doc { + margin: 1.5rem 0; + padding-left: 1.25rem; +} + @media only screen and (max-width: 1024px) { #perspective-landing-content { width: calc(100vw - 2rem); @@ -155,6 +173,10 @@ align-self: center; width: calc(100vw - 4rem); } + + .perspective-landing-readme .definition-doc { + padding-left: 0; + } } @media only screen and (max-width: 414px) { diff --git a/src/css/themes/unison/light.css b/src/css/themes/unison/light.css index da85c42..86fc66d 100644 --- a/src/css/themes/unison/light.css +++ b/src/css/themes/unison/light.css @@ -8,6 +8,7 @@ --color-main-subtle-fg: var(--color-gray-lighten-30); --color-main-subtle-fg-em: var(--color-gray-lighten-20); --color-main-subtle-bg: var(--color-gray-lighten-60); + --color-main-hover-bg: var(--color-gray-lighten-55); --color-main-border: var(--color-gray-lighten-40); --color-main-subtle-border: var(--color-gray-lighten-50); --color-main-divider: var(--color-gray-lighten-55); @@ -213,6 +214,7 @@ --color-card-bg: var(--color-gray-lighten-100); --color-card-fg: var(--color-gray-darken-30); --color-card-title: var(--color-gray-lighten-30); + --color-card-border: var(--color-gray-lighten-50); /* Badge */ --color-badge-fg: var(--color-gray-base); diff --git a/src/css/ui/page-layout.css b/src/css/ui/page-layout.css index a965ce9..536e159 100644 --- a/src/css/ui/page-layout.css +++ b/src/css/ui/page-layout.css @@ -383,7 +383,7 @@ justify-content: center; background: var(--color-gray-darken-10); color: var(--color-gray-lighten-100); - height: 16rem; + height: var(--page-hero-height); } .page.hero-layout .page-content { diff --git a/src/css/unison-share.css b/src/css/unison-share.css index 95b3ef1..3aee449 100644 --- a/src/css/unison-share.css +++ b/src/css/unison-share.css @@ -1 +1,2 @@ @import "./unison-share/page/catalog.css"; +@import "./unison-share/page/user-page.css"; diff --git a/src/css/unison-share/page/catalog.css b/src/css/unison-share/page/catalog.css index 5e0fe35..45a5b40 100644 --- a/src/css/unison-share/page/catalog.css +++ b/src/css/unison-share/page/catalog.css @@ -200,16 +200,5 @@ } .categories .card .project-listing { - display: flex; - flex-direction: row; - align-items: center; - height: 2rem; - padding: 0 0.25rem; margin-left: -0.25rem; - border-radius: var(--border-radius-base); -} - -.categories .card .project-listing:hover { - text-decoration: none; - background: var(--color-gray-lighten-55); } diff --git a/src/css/unison-share/page/user-page.css b/src/css/unison-share/page/user-page.css new file mode 100644 index 0000000..8156c48 --- /dev/null +++ b/src/css/unison-share/page/user-page.css @@ -0,0 +1,27 @@ +.user-page { + --page-hero-height: 8rem; +} + +.user-page .page-content { + display: flex; + align-items: flex-start; + flex-direction: column; + padding: 4rem 0; + gap: 2rem; +} + +.user-page .page-content .card { + width: 100%; +} + +.user-page .page-content .projects { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 0.5rem; +} + +.user-page .page-content .projects .project-listing { + width: calc(33%); + margin-left: -0.25rem; +} From 695e6424c7f02beb51ac23bf18e6e63e91ba7c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 24 Jan 2022 13:50:24 -0500 Subject: [PATCH 43/54] Use normal hrefs for the App title click When clicking the title of the app ("Unison Share" / "Unison Local"), simply use a `Click.Href` of `"/"` to navigate to the root page of the application instead of a complicated perspective change. --- src/UI/AppHeader.elm | 15 +++++--------- src/UnisonLocal/App.elm | 44 +++++++++++++-------------------------- src/UnisonShare/App.elm | 46 +++++++++++++---------------------------- 3 files changed, 33 insertions(+), 72 deletions(-) diff --git a/src/UI/AppHeader.elm b/src/UI/AppHeader.elm index 69f7a66..09fe89e 100644 --- a/src/UI/AppHeader.elm +++ b/src/UI/AppHeader.elm @@ -1,17 +1,17 @@ module UI.AppHeader exposing (..) -import Html exposing (Html, a, header, section, span) +import Html exposing (Html, a, header, section) import Html.Attributes exposing (class, id) import Html.Events exposing (onClick) import UI import UI.Banner as Banner exposing (Banner) import UI.Button as Button exposing (Button) +import UI.Click as Click exposing (Click) import UI.Icon as Icon type AppTitle msg - = Clickable msg (Html msg) - | Disabled (Html msg) + = AppTitle (Click msg) (Html msg) type alias MenuToggle msg = @@ -70,13 +70,8 @@ view appHeader_ = viewAppTitle : AppTitle msg -> Html msg -viewAppTitle title = - case title of - Clickable clickMsg content -> - a [ class "app-title", onClick clickMsg ] [ content ] - - Disabled content -> - span [ class "app-title" ] [ content ] +viewAppTitle (AppTitle click content) = + Click.view [ class "app-title" ] [ content ] click view_ : List (Html msg) -> Html msg diff --git a/src/UnisonLocal/App.elm b/src/UnisonLocal/App.elm index 52b81d2..db198c2 100644 --- a/src/UnisonLocal/App.elm +++ b/src/UnisonLocal/App.elm @@ -451,36 +451,20 @@ subscriptions model = -- VIEW -appTitle : Maybe msg -> AppHeader.AppTitle msg -appTitle clickMsg = - let - appTitle_ = - case clickMsg of - Nothing -> - AppHeader.Disabled - - Just msg -> - AppHeader.Clickable msg - in - appTitle_ (h1 [] [ text "Unison", span [ class "context unison-local" ] [ text "Local" ] ]) - - -viewAppHeader : Model -> AppHeader.AppHeader Msg -viewAppHeader model = - let - changePerspectiveMsg = - case model.env.perspective of - Codebase codebaseHash -> - ChangePerspective (Codebase codebaseHash) +appTitle : Click msg -> AppHeader.AppTitle msg +appTitle click = + AppHeader.AppTitle click + (h1 [] + [ text "Unison" + , span [ class "context unison-local" ] [ text "Local" ] + ] + ) - Namespace { codebaseHash } -> - ChangePerspective (Codebase codebaseHash) - appTitle_ = - appTitle (Just changePerspectiveMsg) - in +appHeader : AppHeader.AppHeader Msg +appHeader = { menuToggle = Just ToggleSidebar - , appTitle = appTitle_ + , appTitle = appTitle (Click.Href "/") , banner = Nothing , rightButton = Just (Button.button (ShowModal PublishModal) "Publish on Unison Share" |> Button.share) } @@ -746,7 +730,7 @@ viewModal model = viewAppLoading : Html msg viewAppLoading = div [ id "app" ] - [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) + [ AppHeader.view (AppHeader.appHeader (appTitle Click.Disabled)) , PageLayout.view (PageLayout.SidebarLayout { sidebar = [] @@ -760,7 +744,7 @@ viewAppLoading = viewAppError : Http.Error -> Html msg viewAppError error = div [ id "app" ] - [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) + [ AppHeader.view (AppHeader.appHeader (appTitle Click.Disabled)) , PageLayout.view (PageLayout.SidebarLayout { sidebar = [] @@ -801,5 +785,5 @@ view model = } in { title = "Unison Local" - , body = [ div [ id "app" ] [ AppHeader.view (viewAppHeader model), PageLayout.view page, viewModal model ] ] + , body = [ div [ id "app" ] [ AppHeader.view appHeader, PageLayout.view page, viewModal model ] ] } diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index 3eb4829..a4cfeab 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -494,34 +494,19 @@ subscriptions model = -- VIEW -appTitle : Maybe msg -> AppHeader.AppTitle msg -appTitle clickMsg = - let - appTitle_ = - case clickMsg of - Nothing -> - AppHeader.Disabled - - Just msg -> - AppHeader.Clickable msg - in - appTitle_ (h1 [] [ text "Unison", span [ class "context unison-share" ] [ text "Share" ] ]) +appTitle : Click msg -> AppHeader.AppTitle msg +appTitle click = + AppHeader.AppTitle click + (h1 [] + [ text "Unison" + , span [ class "context unison-share" ] [ text "Share" ] + ] + ) -viewAppHeader : Model -> AppHeader.AppHeader Msg -viewAppHeader model = +appHeader : AppHeader.AppHeader Msg +appHeader = let - changePerspectiveMsg = - case model.env.perspective of - Codebase codebaseHash -> - ChangePerspective (Codebase codebaseHash) - - Namespace { codebaseHash } -> - ChangePerspective (Codebase codebaseHash) - - appTitle_ = - appTitle (Just changePerspectiveMsg) - banner = Just (Banner.promotion "article" @@ -531,7 +516,7 @@ viewAppHeader model = ) in { menuToggle = Just ToggleSidebar - , appTitle = appTitle_ + , appTitle = appTitle (Click.Href "/") , banner = banner , rightButton = Just (Button.button (ShowModal AppModal.PublishModal) "Publish on Unison Share" |> Button.share) } @@ -675,7 +660,7 @@ viewMainSidebar model = viewAppLoading : Html msg viewAppLoading = div [ id "app" ] - [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) + [ AppHeader.view (AppHeader.appHeader (appTitle Click.Disabled)) , PageLayout.view (PageLayout.FullLayout { content = PageLayout.PageContent [] } @@ -686,7 +671,7 @@ viewAppLoading = viewAppError : Http.Error -> Html msg viewAppError error = div [ id "app" ] - [ AppHeader.view (AppHeader.appHeader (appTitle Nothing)) + [ AppHeader.view (AppHeader.appHeader (appTitle Click.Disabled)) , PageLayout.view (PageLayout.FullLayout { content = @@ -705,9 +690,6 @@ viewAppError error = view : Model -> Browser.Document Msg view model = let - appHeader = - AppHeader.view (viewAppHeader model) - withSidebar pageContent = PageLayout.SidebarLayout { sidebar = viewMainSidebar model @@ -748,7 +730,7 @@ view model = { title = "Unison Share" , body = [ div [ id "app", class pageId ] - [ appHeader + [ AppHeader.view appHeader , page , Html.map AppModalMsg (AppModal.view model.env model.appModal) ] From cb4ce4e5a04b07db92fadaeb74651a75af8cad5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Fri, 7 Jan 2022 10:57:04 -0500 Subject: [PATCH 44/54] Enable the Catalog as the front page of Share --- src/UnisonLocal/App.elm | 8 +++++++- src/UnisonShare/Route.elm | 13 +++++++------ tests/UnisonShare/RouteTests.elm | 17 +++++++---------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/UnisonLocal/App.elm b/src/UnisonLocal/App.elm index db198c2..9a17f3f 100644 --- a/src/UnisonLocal/App.elm +++ b/src/UnisonLocal/App.elm @@ -785,5 +785,11 @@ view model = } in { title = "Unison Local" - , body = [ div [ id "app" ] [ AppHeader.view appHeader, PageLayout.view page, viewModal model ] ] + , body = + [ div [ id "app" ] + [ AppHeader.view appHeader + , PageLayout.view page + , viewModal model + ] + ] } diff --git a/src/UnisonShare/Route.elm b/src/UnisonShare/Route.elm index b61e3cb..88a6578 100644 --- a/src/UnisonShare/Route.elm +++ b/src/UnisonShare/Route.elm @@ -36,11 +36,9 @@ import Url.Builder exposing (relative) {- - Routing - ======= + URL Scheme for Projects + ======================= - URL Scheme - ---------- Directly on the codebase /[latest|:codebase-hash]/[namespaces|types|terms]/[:namespace-name|:definition-name|:definition-hash] @@ -49,11 +47,14 @@ import Url.Builder exposing (relative) Within a namespace /[latest|:codebase-hash]/[namespaces]/[:namespace-name]/-/[types|terms]/[:definition-name|:definition-hash] + TODO: + /projects/[:project-name]/-/[namespaces|types|terms]/[:definition-name|:definition-hash] + /projects/[:project-name]/-/[namespaces]/[:namespace-name]/-/[types|terms]/[:definition-name|:definition-hash] Relative examples ----------------- - Top level of a Codebase: / + Top level of a Codebase: / Top level of a Codebase: /latest With namespace context: /latest/namespaces/base/List Definitions: /latest/[types|terms]/base/List/map @@ -183,7 +184,7 @@ fromUrl basePath url = "/" ++ path parse url_ = - Result.withDefault (Project (ByCodebase Relative) ProjectRoot) (Parser.run toRoute url_) + Result.withDefault Catalog (Parser.run toRoute url_) in url |> .path diff --git a/tests/UnisonShare/RouteTests.elm b/tests/UnisonShare/RouteTests.elm index 138c580..4e64664 100644 --- a/tests/UnisonShare/RouteTests.elm +++ b/tests/UnisonShare/RouteTests.elm @@ -21,16 +21,13 @@ catalogRoute = mkUrl "/catalog" in Expect.equal Catalog (Route.fromUrl "" url) - - {- TODO enable when cutting over to catalog - , test "Matches root to Catalog" <| - \_ -> - let - url = - mkUrl "/" - in - Expect.equal Catalog (Route.fromUrl "" url) - -} + , test "Matches root to Catalog" <| + \_ -> + let + url = + mkUrl "/" + in + Expect.equal Catalog (Route.fromUrl "" url) ] From fc08712ff813762f1b9272c15ca6af9acce66cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Thu, 27 Jan 2022 14:35:50 -0500 Subject: [PATCH 45/54] Catalog: Support searching for users Add user search support to the catalog and a user icon --- src/UI/Icon.elm | 9 ++ src/UnisonShare/Catalog.elm | 20 ---- src/UnisonShare/Page/Catalog.elm | 149 ++++++++++++++++++++------ src/UnisonShare/Route.elm | 12 +++ src/css/unison-share/page/catalog.css | 48 ++++++++- tests/UnisonShare/CatalogTests.elm | 33 ------ 6 files changed, 183 insertions(+), 88 deletions(-) diff --git a/src/UI/Icon.elm b/src/UI/Icon.elm index e381d63..ac20cb9 100644 --- a/src/UI/Icon.elm +++ b/src/UI/Icon.elm @@ -379,3 +379,12 @@ clipboard = [ path [ fill "currentColor", fillRule "evenodd", d "M8 2.25C8 2.11193 7.88807 2 7.75 2H6.25C6.11193 2 6 2.11193 6 2.25V2.75C6 2.88807 6.11193 3 6.25 3H7.75C7.88807 3 8 2.88807 8 2.75V2.25ZM6 1C5.44772 1 5 1.44772 5 2V3C5 3.55228 5.44772 4 6 4H8C8.55228 4 9 3.55228 9 3V2C9 1.44772 8.55228 1 8 1H6Z" ] [] , path [ fill "currentColor", fillRule "evenodd", d "M3 2.5C3 2.22386 3.22386 2 3.5 2C3.77614 2 4 2.22386 4 2.5V10.5C4 10.7761 4.22386 11 4.5 11H9.5C9.77614 11 10 10.7761 10 10.5V2.5C10 2.22386 10.2239 2 10.5 2C10.7761 2 11 2.22386 11 2.5V11C11 11.5523 10.5523 12 10 12H4C3.44772 12 3 11.5523 3 11V2.5Z" ] [] ] + + +user : Icon msg +user = + Icon "user" + [] + [ path [ fill "currentColor", d "M7 7C3.97669 7 1.47565 9.60877 1.06051 13.0021C0.99344 13.5503 1.44772 14 2 14H12C12.5523 14 13.0066 13.5503 12.9395 13.0021C12.5243 9.60877 10.0233 7 7 7Z" ] [] + , circle [ fill "currentColor", cx "7", cy "3", r "3" ] [] + ] diff --git a/src/UnisonShare/Catalog.elm b/src/UnisonShare/Catalog.elm index d2de85e..7c3daf8 100644 --- a/src/UnisonShare/Catalog.elm +++ b/src/UnisonShare/Catalog.elm @@ -5,7 +5,6 @@ import FullyQualifiedName as FQN import Json.Decode as Decode import OrderedDict exposing (OrderedDict) import Project exposing (ProjectListing) -import Simple.Fuzzy as Fuzzy import UnisonShare.Catalog.CatalogMask as CatalogMask exposing (CatalogMask) @@ -87,25 +86,6 @@ fromList items = -- HELPERS -{-| Fuzzy search through a flattened catalog by project name and category --} -search : Catalog -> String -> List ( ProjectListing, String ) -search catalog_ query = - let - flat ( category, projects ) acc = - acc ++ List.map (\p -> ( p, category )) projects - - normalize ( p, c ) = - p - |> Project.slugString - |> (++) c - in - catalog_ - |> toList - |> List.foldl flat [] - |> Fuzzy.filter normalize query - - isEmpty : Catalog -> Bool isEmpty (Catalog dict) = OrderedDict.isEmpty dict diff --git a/src/UnisonShare/Page/Catalog.elm b/src/UnisonShare/Page/Catalog.elm index 22ed5e7..8152df7 100644 --- a/src/UnisonShare/Page/Catalog.elm +++ b/src/UnisonShare/Page/Catalog.elm @@ -4,15 +4,18 @@ import Api import Env exposing (Env) import Html exposing (Html, div, h1, input, strong, table, tbody, td, text, tr) import Html.Attributes exposing (autofocus, class, classList, placeholder) -import Html.Events exposing (onBlur, onClick, onFocus, onInput) +import Html.Events exposing (onBlur, onFocus, onInput, onMouseDown) import Http import KeyboardShortcut exposing (KeyboardShortcut(..)) import KeyboardShortcut.Key as Key exposing (Key(..)) import KeyboardShortcut.KeyboardEvent as KeyboardEvent exposing (KeyboardEvent) +import List.Extra as ListE +import Maybe.Extra as MaybeE import Perspective import Project exposing (ProjectListing) import RemoteData exposing (RemoteData(..), WebData) import SearchResults exposing (SearchResults(..)) +import Simple.Fuzzy as Fuzzy import Task import UI import UI.Card as Card @@ -20,19 +23,26 @@ import UI.Click as Click import UI.Icon as Icon import UI.PageLayout as PageLayout exposing (PageLayout) import UnisonShare.Catalog as Catalog exposing (Catalog) +import UnisonShare.Catalog.CatalogMask exposing (CatalogMask) import UnisonShare.Route as Route +import UnisonShare.User as User exposing (Username) -- MODEL -type alias SearchResult = - ( ProjectListing, String ) +type Match + = UserMatch Username + | ProjectMatch ProjectListing String type alias CatalogSearchResults = - SearchResults SearchResult + SearchResults Match + + + +-- TODO: Rename type alias CatalogSearch = @@ -43,6 +53,7 @@ type alias LoadedModel = { search : CatalogSearch , hasFocus : Bool , catalog : Catalog + , usernames : List Username , keyboardShortcut : KeyboardShortcut.Model } @@ -53,14 +64,14 @@ type alias Model = init : Env -> ( Model, Cmd Msg ) init env = - ( Loading, fetchCatalog env ) + ( Loading, fetch env ) {-| Fetch the Catalog in sequence by first fetching the doc, then the projectListings and finally merging them into a Catalog -} -fetchCatalog : Env -> Cmd Msg -fetchCatalog env = +fetch : Env -> Cmd Msg +fetch env = let perspective = Perspective.toCodebasePerspective env.perspective @@ -73,8 +84,7 @@ fetchCatalog env = |> Api.toTask env.apiBasePath Project.decodeListings |> Task.map (\projects -> ( catalog, projects )) ) - |> Task.map (\( cm, ps ) -> Catalog.catalog cm ps) - |> Task.attempt FetchCatalogFinished + |> Task.attempt FetchFinished @@ -86,7 +96,8 @@ type Msg | UpdateFocus Bool | ClearQuery | SelectProject ProjectListing - | FetchCatalogFinished (Result Http.Error Catalog) + | SelectUser Username + | FetchFinished (Result Http.Error ( CatalogMask, List ProjectListing )) | Keydown KeyboardEvent | KeyboardShortcutMsg KeyboardShortcut.Msg @@ -94,17 +105,25 @@ type Msg update : Env -> Msg -> Model -> ( Model, Cmd Msg ) update env msg model = case ( msg, model ) of - ( FetchCatalogFinished catalogResult, _ ) -> - case catalogResult of + ( FetchFinished result, _ ) -> + case result of Err e -> ( Failure e, Cmd.none ) - Ok catalog -> + Ok ( mask, listings ) -> let + usernames = + listings + |> List.map (.owner >> Project.ownerToString) + |> ListE.unique + |> List.map User.usernameFromString + |> MaybeE.values + initModel = { search = { query = "", results = SearchResults.empty } , hasFocus = True - , catalog = catalog + , catalog = Catalog.catalog mask listings + , usernames = usernames , keyboardShortcut = KeyboardShortcut.init env.operatingSystem } in @@ -121,7 +140,7 @@ update env msg model = else query - |> Catalog.search m.catalog + |> search m.catalog m.usernames |> SearchResults.fromList in ( Success { m | search = { query = query, results = searchResults } }, Cmd.none ) @@ -132,6 +151,9 @@ update env msg model = ( SelectProject project, Success m ) -> ( Success m, Route.navigateToProject env.navKey project ) + ( SelectUser username, Success m ) -> + ( Success m, Route.navigateToUsername env.navKey username ) + ( Keydown event, Success m ) -> let ( keyboardShortcut, kCmd ) = @@ -174,8 +196,7 @@ update env msg model = navigate = matches |> SearchResults.focus - |> Tuple.first - |> Route.navigateToProject env.navKey + |> matchToNavigate env in ( Success newModel, Cmd.batch [ cmd, navigate ] ) @@ -185,8 +206,7 @@ update env msg model = let navigate = SearchResults.getAt (n - 1) m.search.results - |> Maybe.map Tuple.first - |> Maybe.map (Route.navigateToProject env.navKey) + |> Maybe.map (matchToNavigate env) |> Maybe.withDefault Cmd.none in ( Success newModel, Cmd.batch [ cmd, navigate ] ) @@ -209,8 +229,49 @@ update env msg model = mapSearch : (CatalogSearchResults -> CatalogSearchResults) -> CatalogSearch -> CatalogSearch -mapSearch f search = - { search | results = f search.results } +mapSearch f search_ = + { search_ | results = f search_.results } + + +matchToNavigate : Env -> Match -> Cmd Msg +matchToNavigate env match = + case match of + UserMatch username -> + Route.navigateToUsername env.navKey username + + ProjectMatch project _ -> + Route.navigateToProject env.navKey project + + +toMatches : Catalog -> List Username -> List Match +toMatches catalog users = + let + flat ( category, projects ) acc = + acc ++ List.map (\p -> ProjectMatch p category) projects + + projectMatches = + catalog + |> Catalog.toList + |> List.foldl flat [] + + userMatches = + List.map UserMatch users + in + userMatches ++ projectMatches + + +search : Catalog -> List Username -> String -> List Match +search catalog users query = + let + normalize m = + case m of + UserMatch u -> + User.usernameToString u + + ProjectMatch p _ -> + Project.slugString p + in + Fuzzy.filter normalize query (toMatches catalog users) @@ -238,8 +299,11 @@ viewCategory ( category, projects ) = |> Card.view -viewMatch : KeyboardShortcut.Model -> SearchResult -> Bool -> Maybe Key -> Html Msg -viewMatch keyboardShortcut ( project, category ) isFocused shortcut = +{-| View a match in the dropdown list. Use `onMouseDown` instead of `onClick` +to avoid competing with `onBlur` on the input +-} +viewMatch : KeyboardShortcut.Model -> Match -> Bool -> Maybe Key -> Html Msg +viewMatch keyboardShortcut match isFocused shortcut = let shortcutIndicator = if isFocused then @@ -253,14 +317,31 @@ viewMatch keyboardShortcut ( project, category ) isFocused shortcut = Just key -> KeyboardShortcut.view keyboardShortcut (Sequence (Just Key.Semicolon) key) in - tr - [ classList [ ( "search-result", True ), ( "focused", isFocused ) ] - , onClick (SelectProject project) - ] - [ td [ class "project-name" ] [ Project.viewProjectListing Click.Disabled project ] - , td [ class "category" ] [ text category ] - , td [] [ div [ class "shortcut" ] [ shortcutIndicator ] ] - ] + case match of + UserMatch username -> + tr + [ classList [ ( "search-result", True ), ( "focused", isFocused ) ] + , onMouseDown (SelectUser username) + ] + [ td [ class "match-name" ] + [ div [ class "user-listing" ] + [ div [ class "avatar" ] [ Icon.view Icon.user ] + , text (User.usernameToString username) + ] + ] + , td [ class "category" ] [ text "User" ] + , td [] [ div [ class "shortcut" ] [ shortcutIndicator ] ] + ] + + ProjectMatch project category -> + tr + [ classList [ ( "search-result", True ), ( "focused", isFocused ) ] + , onMouseDown (SelectProject project) + ] + [ td [ class "match-name" ] [ Project.viewProjectListing Click.Disabled project ] + , td [ class "category" ] [ text category ] + , td [] [ div [ class "shortcut" ] [ shortcutIndicator ] ] + ] indexToShortcut : Int -> Maybe Key @@ -276,7 +357,7 @@ indexToShortcut index = n |> String.fromInt |> Key.fromString |> Just -viewMatches : KeyboardShortcut.Model -> SearchResults.Matches SearchResult -> Html Msg +viewMatches : KeyboardShortcut.Model -> SearchResults.Matches Match -> Html Msg viewMatches keyboardShortcut matches = let matchItems = @@ -295,7 +376,7 @@ viewSearchResults keyboardShortcut { query, results } = resultsPane = case results of Empty -> - div [ class "empty-state" ] [ text ("No matching projects found for \"" ++ query ++ "\"") ] + div [ class "empty-state" ] [ text ("No matches found for \"" ++ query ++ "\"") ] SearchResults matches -> viewMatches keyboardShortcut matches @@ -347,7 +428,7 @@ viewLoaded model = [ div [ class "search-field" ] [ Icon.view Icon.search , input - [ placeholder "Search for projects" + [ placeholder "Search for projects and users" , onInput UpdateQuery , autofocus True , onBlur (UpdateFocus False) diff --git a/src/UnisonShare/Route.elm b/src/UnisonShare/Route.elm index b61e3cb..e6a9e30 100644 --- a/src/UnisonShare/Route.elm +++ b/src/UnisonShare/Route.elm @@ -10,6 +10,8 @@ module UnisonShare.Route exposing , navigateToLatestCodebase , navigateToPerspective , navigateToProject + , navigateToUser + , navigateToUsername , perspectiveParams , replacePerspective , toDefinition @@ -328,6 +330,16 @@ navigateToProject navKey project = navigate navKey (forProject project) +navigateToUser : Nav.Key -> User.User a -> Cmd msg +navigateToUser navKey user_ = + navigate navKey (forUser user_) + + +navigateToUsername : Nav.Key -> User.Username -> Cmd msg +navigateToUsername navKey username_ = + navigate navKey (User username_) + + -- TODO: this should go away in UnisonShare diff --git a/src/css/unison-share/page/catalog.css b/src/css/unison-share/page/catalog.css index 45a5b40..2c4aa7b 100644 --- a/src/css/unison-share/page/catalog.css +++ b/src/css/unison-share/page/catalog.css @@ -127,6 +127,7 @@ padding: 0.5rem 0.75rem; height: 3rem; font-size: 1rem; + cursor: pointer; } .catalog-hero .catalog-search .search-results td:first-child { @@ -137,7 +138,7 @@ border-radius: 0 var(--border-radius-base) var(--border-radius-base) 0; } -.catalog-hero .catalog-search .search-results .search-result td.project-name { +.catalog-hero .catalog-search .search-results .search-result td.match-name { width: 20em; text-overflow: ellipsis; overflow: hidden; @@ -179,6 +180,51 @@ text-decoration: none; } +.catalog-hero + .catalog-search + .search-results + .search-result + .project-listing:hover { + background: none; +} + +.catalog-hero .catalog-search .search-results .search-result .user-listing { + display: flex; + flex-direction: row; + align-items: center; + font-weight: bold; + padding-left: 0.25rem; +} + +.catalog-hero + .catalog-search + .search-results + .search-result + .user-listing + .avatar { + width: 1.5rem; + height: 1.5rem; + margin-right: 0.5rem; + background: var(--color-main-subtle-bg); + border: 1px solid var(--color-main-border); + border-radius: 0.75rem; + display: flex; + flex-direction: row; + align-items: flex-end; + justify-content: center; + overflow: hidden; +} +.catalog-hero + .catalog-search + .search-results + .search-result + .user-listing + .avatar + .icon { + font-size: 1rem; + color: var(--color-main-border); +} + .catalog-hero .catalog-search .search-results .empty-state { font-size: var(--font-size-base); color: var(--color-main-subtle-fg); diff --git a/tests/UnisonShare/CatalogTests.elm b/tests/UnisonShare/CatalogTests.elm index 4e03b3d..e048900 100644 --- a/tests/UnisonShare/CatalogTests.elm +++ b/tests/UnisonShare/CatalogTests.elm @@ -83,39 +83,6 @@ toList = ] -search : Test -search = - describe "Catalog.search" - [ test "Fuzzy finds projects in the catalog by project name " <| - \_ -> - let - projectListings_ = - [ baseListing, distributedListing, textExtraListing, nanoidListing ] - - catalog_ = - Catalog.catalog catalogMask projectListings_ - in - Expect.equal - [ ( baseListing, "Featured" ) - , ( distributedListing, "Featured" ) - ] - (Catalog.search catalog_ "unison") - , test "Fuzzy finds projects in the catalog by category" <| - \_ -> - let - projectListings_ = - [ baseListing, distributedListing, textExtraListing, nanoidListing ] - - catalog_ = - Catalog.catalog catalogMask projectListings_ - in - Expect.equal - [ ( textExtraListing, "Parsers & Text Manipulation" ) - ] - (Catalog.search catalog_ "parsers") - ] - - -- helpers From 6bf714323448b264b5f0289ae32a5bfbcd3ed017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Fri, 28 Jan 2022 16:35:18 -0500 Subject: [PATCH 46/54] Add search for all projects, not just catalog --- src/Project.elm | 5 +++++ src/UnisonShare/Page/Catalog.elm | 36 ++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/Project.elm b/src/Project.elm index f39a5a8..0636f89 100644 --- a/src/Project.elm +++ b/src/Project.elm @@ -36,6 +36,11 @@ ownerToString (Owner o) = o +equals : Project a -> Project b -> Bool +equals a b = + Hash.equals a.hash b.hash + + -- View diff --git a/src/UnisonShare/Page/Catalog.elm b/src/UnisonShare/Page/Catalog.elm index 8152df7..b7b5c61 100644 --- a/src/UnisonShare/Page/Catalog.elm +++ b/src/UnisonShare/Page/Catalog.elm @@ -54,6 +54,7 @@ type alias LoadedModel = , hasFocus : Bool , catalog : Catalog , usernames : List Username + , projectListings : List ProjectListing , keyboardShortcut : KeyboardShortcut.Model } @@ -124,6 +125,7 @@ update env msg model = , hasFocus = True , catalog = Catalog.catalog mask listings , usernames = usernames + , projectListings = listings , keyboardShortcut = KeyboardShortcut.init env.operatingSystem } in @@ -140,7 +142,8 @@ update env msg model = else query - |> search m.catalog m.usernames + |> search m.catalog m.projectListings m.usernames + |> List.take 9 |> SearchResults.fromList in ( Success { m | search = { query = query, results = searchResults } }, Cmd.none ) @@ -243,16 +246,27 @@ matchToNavigate env match = Route.navigateToProject env.navKey project -toMatches : Catalog -> List Username -> List Match -toMatches catalog users = +toMatches : Catalog -> List ProjectListing -> List Username -> List Match +toMatches catalog projects users = let - flat ( category, projects ) acc = - acc ++ List.map (\p -> ProjectMatch p category) projects + catalogProjects = + catalog |> Catalog.toList |> List.foldl flat [] + + flat ( category, projects_ ) acc = + acc ++ List.map (\p -> ( p, category )) projects_ + + categoryOf project = + catalogProjects + |> ListE.find (\( p, _ ) -> Project.equals project p) + |> Maybe.map Tuple.second projectMatches = - catalog - |> Catalog.toList - |> List.foldl flat [] + projects + |> List.map + (\p -> + ProjectMatch p + (Maybe.withDefault "" (categoryOf p)) + ) userMatches = List.map UserMatch users @@ -260,8 +274,8 @@ toMatches catalog users = userMatches ++ projectMatches -search : Catalog -> List Username -> String -> List Match -search catalog users query = +search : Catalog -> List ProjectListing -> List Username -> String -> List Match +search catalog projects users query = let normalize m = case m of @@ -271,7 +285,7 @@ search catalog users query = ProjectMatch p _ -> Project.slugString p in - Fuzzy.filter normalize query (toMatches catalog users) + Fuzzy.filter normalize query (toMatches catalog projects users) From 08d3f0970bb43e953d427ecbd102c48109039187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 31 Jan 2022 13:47:04 -0500 Subject: [PATCH 47/54] Revert "Enable the Catalog as the front page of Share" --- src/UnisonLocal/App.elm | 8 +------- src/UnisonShare/Route.elm | 13 ++++++------- tests/UnisonShare/RouteTests.elm | 17 ++++++++++------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/UnisonLocal/App.elm b/src/UnisonLocal/App.elm index 9a17f3f..db198c2 100644 --- a/src/UnisonLocal/App.elm +++ b/src/UnisonLocal/App.elm @@ -785,11 +785,5 @@ view model = } in { title = "Unison Local" - , body = - [ div [ id "app" ] - [ AppHeader.view appHeader - , PageLayout.view page - , viewModal model - ] - ] + , body = [ div [ id "app" ] [ AppHeader.view appHeader, PageLayout.view page, viewModal model ] ] } diff --git a/src/UnisonShare/Route.elm b/src/UnisonShare/Route.elm index 80435d2..e6a9e30 100644 --- a/src/UnisonShare/Route.elm +++ b/src/UnisonShare/Route.elm @@ -38,9 +38,11 @@ import Url.Builder exposing (relative) {- - URL Scheme for Projects - ======================= + Routing + ======= + URL Scheme + ---------- Directly on the codebase /[latest|:codebase-hash]/[namespaces|types|terms]/[:namespace-name|:definition-name|:definition-hash] @@ -49,14 +51,11 @@ import Url.Builder exposing (relative) Within a namespace /[latest|:codebase-hash]/[namespaces]/[:namespace-name]/-/[types|terms]/[:definition-name|:definition-hash] - TODO: - /projects/[:project-name]/-/[namespaces|types|terms]/[:definition-name|:definition-hash] - /projects/[:project-name]/-/[namespaces]/[:namespace-name]/-/[types|terms]/[:definition-name|:definition-hash] Relative examples ----------------- - Top level of a Codebase: / + Top level of a Codebase: / Top level of a Codebase: /latest With namespace context: /latest/namespaces/base/List Definitions: /latest/[types|terms]/base/List/map @@ -186,7 +185,7 @@ fromUrl basePath url = "/" ++ path parse url_ = - Result.withDefault Catalog (Parser.run toRoute url_) + Result.withDefault (Project (ByCodebase Relative) ProjectRoot) (Parser.run toRoute url_) in url |> .path diff --git a/tests/UnisonShare/RouteTests.elm b/tests/UnisonShare/RouteTests.elm index 4e64664..138c580 100644 --- a/tests/UnisonShare/RouteTests.elm +++ b/tests/UnisonShare/RouteTests.elm @@ -21,13 +21,16 @@ catalogRoute = mkUrl "/catalog" in Expect.equal Catalog (Route.fromUrl "" url) - , test "Matches root to Catalog" <| - \_ -> - let - url = - mkUrl "/" - in - Expect.equal Catalog (Route.fromUrl "" url) + + {- TODO enable when cutting over to catalog + , test "Matches root to Catalog" <| + \_ -> + let + url = + mkUrl "/" + in + Expect.equal Catalog (Route.fromUrl "" url) + -} ] From 928c801ea08fefab34cbef29de61c11e0d5fcd84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 31 Jan 2022 15:20:47 -0500 Subject: [PATCH 48/54] add better support for mobile on the catalog --- src/css/ui/page-layout.css | 4 ++- src/css/unison-share/page/catalog.css | 40 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/css/ui/page-layout.css b/src/css/ui/page-layout.css index 536e159..4e171f8 100644 --- a/src/css/ui/page-layout.css +++ b/src/css/ui/page-layout.css @@ -453,7 +453,9 @@ display: none; } - .page-content { + .page.sidebar-layout .page-content, + .page.hero-layout .page-content, + .page.full-layout .page-content { width: 100vw; } diff --git a/src/css/unison-share/page/catalog.css b/src/css/unison-share/page/catalog.css index 2c4aa7b..2e45441 100644 --- a/src/css/unison-share/page/catalog.css +++ b/src/css/unison-share/page/catalog.css @@ -117,6 +117,7 @@ border-top: 1px solid var(--color-gray-lighten-50); border-radius: 0 0 var(--border-radius-base) var(--border-radius-base); padding: 0.75rem; + overflow: auto; } .catalog-hero .catalog-search .search-results table { @@ -248,3 +249,42 @@ .categories .card .project-listing { margin-left: -0.25rem; } + +@media only screen and (max-width: 1025px) { + .catalog-page { + --page-hero-height: 18rem; + } + + .page.hero-layout .page-hero { + padding: 0 1rem; + } + + .catalog-hero h1 { + font-size: 1.9rem; + } + + .catalog-hero .catalog-search { + width: calc(100vw - 1rem); + } + + .categories { + flex-direction: column; + margin-top: 4rem; + gap: 1rem; + } + + .categories .card { + width: auto; + } + + .catalog-hero .catalog-search .search-results .search-result td.category { + display: none; + } + .catalog-hero + .catalog-search + .search-results + .search-result + .keyboard-shortcut { + display: none; + } +} From 244cc58c2e847a4ea9a709938060e0dbe4e785f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 31 Jan 2022 15:35:29 -0500 Subject: [PATCH 49/54] add better support for mobile on the user page --- src/css/unison-share/page/user-page.css | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/css/unison-share/page/user-page.css b/src/css/unison-share/page/user-page.css index 8156c48..274afa1 100644 --- a/src/css/unison-share/page/user-page.css +++ b/src/css/unison-share/page/user-page.css @@ -25,3 +25,21 @@ width: calc(33%); margin-left: -0.25rem; } + +@media only screen and (max-width: 1025px) { + .user-page { + --page-hero-height: 4rem; + } + + .user-page .page-content { + padding: 2rem 0; + } + + .user-page .page-content .card.contained { + border: none; + } + + .user-page .page-content .projects { + flex-direction: column; + } +} From e7a5c1e80711e9424ce93178e2d5740c2aa564df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 31 Jan 2022 15:36:31 -0500 Subject: [PATCH 50/54] Revert "Revert "Enable the Catalog as the front page of Share"" --- src/UnisonLocal/App.elm | 8 +++++++- src/UnisonShare/Route.elm | 13 +++++++------ tests/UnisonShare/RouteTests.elm | 17 +++++++---------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/UnisonLocal/App.elm b/src/UnisonLocal/App.elm index db198c2..9a17f3f 100644 --- a/src/UnisonLocal/App.elm +++ b/src/UnisonLocal/App.elm @@ -785,5 +785,11 @@ view model = } in { title = "Unison Local" - , body = [ div [ id "app" ] [ AppHeader.view appHeader, PageLayout.view page, viewModal model ] ] + , body = + [ div [ id "app" ] + [ AppHeader.view appHeader + , PageLayout.view page + , viewModal model + ] + ] } diff --git a/src/UnisonShare/Route.elm b/src/UnisonShare/Route.elm index e6a9e30..80435d2 100644 --- a/src/UnisonShare/Route.elm +++ b/src/UnisonShare/Route.elm @@ -38,11 +38,9 @@ import Url.Builder exposing (relative) {- - Routing - ======= + URL Scheme for Projects + ======================= - URL Scheme - ---------- Directly on the codebase /[latest|:codebase-hash]/[namespaces|types|terms]/[:namespace-name|:definition-name|:definition-hash] @@ -51,11 +49,14 @@ import Url.Builder exposing (relative) Within a namespace /[latest|:codebase-hash]/[namespaces]/[:namespace-name]/-/[types|terms]/[:definition-name|:definition-hash] + TODO: + /projects/[:project-name]/-/[namespaces|types|terms]/[:definition-name|:definition-hash] + /projects/[:project-name]/-/[namespaces]/[:namespace-name]/-/[types|terms]/[:definition-name|:definition-hash] Relative examples ----------------- - Top level of a Codebase: / + Top level of a Codebase: / Top level of a Codebase: /latest With namespace context: /latest/namespaces/base/List Definitions: /latest/[types|terms]/base/List/map @@ -185,7 +186,7 @@ fromUrl basePath url = "/" ++ path parse url_ = - Result.withDefault (Project (ByCodebase Relative) ProjectRoot) (Parser.run toRoute url_) + Result.withDefault Catalog (Parser.run toRoute url_) in url |> .path diff --git a/tests/UnisonShare/RouteTests.elm b/tests/UnisonShare/RouteTests.elm index 138c580..4e64664 100644 --- a/tests/UnisonShare/RouteTests.elm +++ b/tests/UnisonShare/RouteTests.elm @@ -21,16 +21,13 @@ catalogRoute = mkUrl "/catalog" in Expect.equal Catalog (Route.fromUrl "" url) - - {- TODO enable when cutting over to catalog - , test "Matches root to Catalog" <| - \_ -> - let - url = - mkUrl "/" - in - Expect.equal Catalog (Route.fromUrl "" url) - -} + , test "Matches root to Catalog" <| + \_ -> + let + url = + mkUrl "/" + in + Expect.equal Catalog (Route.fromUrl "" url) ] From 03cb98f16be1befa198d32d219699657f0204793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Tue, 1 Feb 2022 08:47:04 -0500 Subject: [PATCH 51/54] Only show mobile menu toggle on project pages --- src/Definition/Doc.elm | 2 +- src/UnisonShare/App.elm | 24 +++++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Definition/Doc.elm b/src/Definition/Doc.elm index 012ec8a..3f683d6 100644 --- a/src/Definition/Doc.elm +++ b/src/Definition/Doc.elm @@ -400,7 +400,7 @@ view refToMsg toggleFoldMsg docFoldToggles document = span [ class "strikethrough" ] [ viewAtCurrentSectionLevel d ] Style cssClass d -> - span [ cssClass |> stringToClass |> class ] [ viewAtCurrentSectionLevel d ] + div [ cssClass |> stringToClass |> class ] [ viewAtCurrentSectionLevel d ] Anchor id_ d -> a [ id id_, target id_ ] [ viewAtCurrentSectionLevel d ] diff --git a/src/UnisonShare/App.elm b/src/UnisonShare/App.elm index a4cfeab..f7eaa45 100644 --- a/src/UnisonShare/App.elm +++ b/src/UnisonShare/App.elm @@ -504,8 +504,8 @@ appTitle click = ) -appHeader : AppHeader.AppHeader Msg -appHeader = +appHeader : Maybe Msg -> AppHeader.AppHeader Msg +appHeader menuToggle = let banner = Just @@ -515,7 +515,7 @@ appHeader = "Check it out!" ) in - { menuToggle = Just ToggleSidebar + { menuToggle = menuToggle , appTitle = appTitle (Click.Href "/") , banner = banner , rightButton = Just (Button.button (ShowModal AppModal.PublishModal) "Publish on Unison Share" |> Button.share) @@ -697,13 +697,13 @@ view model = , content = PageLayout.PageContent [ pageContent ] } - ( pageId, page ) = + ( pageId, page, menuToggle ) = case model.route of Route.Catalog -> - ( "catalog-page", Html.map CatalogMsg (Catalog.view model.catalog) ) + ( "catalog-page", Html.map CatalogMsg (Catalog.view model.catalog), Nothing ) Route.User _ -> - ( "user-page", Html.map UserPageMsg (UserPage.view model.userPage) ) + ( "user-page", Html.map UserPageMsg (UserPage.view model.userPage), Nothing ) Route.Project _ Route.ProjectRoot -> let @@ -716,7 +716,10 @@ view model = |> withSidebar |> PageLayout.view in - ( "project-page", page_ ) + ( "project-page" + , page_ + , Just ToggleSidebar + ) Route.Project _ (Route.ProjectDefinition _) -> let @@ -725,12 +728,15 @@ view model = |> withSidebar |> PageLayout.view in - ( "project-page", page_ ) + ( "project-page" + , page_ + , Just ToggleSidebar + ) in { title = "Unison Share" , body = [ div [ id "app", class pageId ] - [ AppHeader.view appHeader + [ AppHeader.view (appHeader menuToggle) , page , Html.map AppModalMsg (AppModal.view model.env model.appModal) ] From 9cd12a693c3f43baa995cb7bc4fd7c3a31d0bff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 7 Feb 2022 09:42:17 -0500 Subject: [PATCH 52/54] div tooltip trigger --- src/UI/Tooltip.elm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UI/Tooltip.elm b/src/UI/Tooltip.elm index e7bdfeb..3177fb8 100644 --- a/src/UI/Tooltip.elm +++ b/src/UI/Tooltip.elm @@ -128,7 +128,7 @@ view { arrow, content, trigger, position } = [ content_ ] ] in - span [ class "tooltip-trigger" ] [ tooltip_, trigger ] + div [ class "tooltip-trigger" ] [ tooltip_, trigger ] From 2cddd4b1d715194fbaa2821c0af721185c37e06f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Mon, 7 Feb 2022 09:44:10 -0500 Subject: [PATCH 53/54] remove unused import --- src/UI/Tooltip.elm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UI/Tooltip.elm b/src/UI/Tooltip.elm index 3177fb8..0b0bd55 100644 --- a/src/UI/Tooltip.elm +++ b/src/UI/Tooltip.elm @@ -12,7 +12,7 @@ module UI.Tooltip exposing , withPosition ) -import Html exposing (Html, div, span, text) +import Html exposing (Html, div, text) import Html.Attributes exposing (class) import UI import UI.Click as Click exposing (Click) From 3f0171de70264eb88ba0099caad31579933b35de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8jberg?= Date: Tue, 8 Feb 2022 14:40:02 -0500 Subject: [PATCH 54/54] Perspective Landing: always show "Find Definition" Fix a bug where on the perspective landing page, when the namespace had a readme, there would be no button to open the finder. --- src/PerspectiveLanding.elm | 36 ++++++++++++++++++++++----------- src/UI/Toolbar.elm | 19 +++++++++++++++++ src/Workspace.elm | 12 ++++++----- src/css/elements.css | 1 + src/css/elements/toolbar.css | 15 ++++++++++++++ src/css/main.css | 1 + src/css/perspective-landing.css | 3 ++- src/css/workspace.css | 21 +------------------ 8 files changed, 70 insertions(+), 38 deletions(-) create mode 100644 src/UI/Toolbar.elm create mode 100644 src/css/elements/toolbar.css diff --git a/src/PerspectiveLanding.elm b/src/PerspectiveLanding.elm index 90fb66b..27d2710 100644 --- a/src/PerspectiveLanding.elm +++ b/src/PerspectiveLanding.elm @@ -15,6 +15,7 @@ import RemoteData exposing (RemoteData(..)) import UI import UI.Button as Button exposing (Button) import UI.Icon as Icon +import UI.Toolbar as Toolbar type alias Model = @@ -114,7 +115,7 @@ viewEmptyStateCodebase : AppContext -> Html Msg viewEmptyStateCodebase appContext = let button = - Button.iconThenLabel Find Icon.search "Find Definitions" + Button.iconThenLabel Find Icon.search "Find Definition" |> Button.primaryMono |> Button.medium in @@ -168,17 +169,28 @@ view env model = viewLoading Success (Namespace _ _ { readme }) -> - case readme of - Just r -> - container - [ div [ class "perspective-landing-readme" ] - [ header [] [ Icon.view Icon.doc, text "README" ] - , Readme.view OpenReference ToggleDocFold model r - ] - ] - - Nothing -> - viewEmptyStateNamespace fqn + let + content = + case readme of + Just r -> + container + [ div [ class "perspective-landing-readme" ] + [ header [ class "title" ] [ Icon.view Icon.doc, text "README" ] + , Readme.view OpenReference ToggleDocFold model r + ] + ] + + Nothing -> + viewEmptyStateNamespace fqn + in + div [] + [ Button.iconThenLabel Find Icon.search "Find Definition" + |> Button.small + |> Button.view + |> Toolbar.toolbar + |> Toolbar.view + , content + ] Failure error -> viewError fqn (Api.errorToString error) diff --git a/src/UI/Toolbar.elm b/src/UI/Toolbar.elm new file mode 100644 index 0000000..3d1b24d --- /dev/null +++ b/src/UI/Toolbar.elm @@ -0,0 +1,19 @@ +module UI.Toolbar exposing (..) + +import Html exposing (Html, header) +import Html.Attributes exposing (class) + + +type alias Toolbar msg = + { content : Html msg + } + + +toolbar : Html msg -> Toolbar msg +toolbar = + Toolbar + + +view : Toolbar msg -> Html msg +view { content } = + header [ class "toolbar" ] [ content ] diff --git a/src/Workspace.elm b/src/Workspace.elm index 9b59e66..7c481f4 100644 --- a/src/Workspace.elm +++ b/src/Workspace.elm @@ -18,7 +18,7 @@ import Env exposing (Env) import FullyQualifiedName exposing (FQN) import Hash import HashQualified as HQ -import Html exposing (Html, article, div, header, section) +import Html exposing (Html, article, div, section) import Html.Attributes exposing (class, id) import Http import KeyboardShortcut exposing (KeyboardShortcut(..)) @@ -28,6 +28,7 @@ import Perspective exposing (Perspective) import Task import UI.Button as Button import UI.Icon as Icon +import UI.Toolbar as Toolbar import Workspace.WorkspaceItem as WorkspaceItem exposing (Item, WorkspaceItem) import Workspace.WorkspaceItems as WorkspaceItems exposing (WorkspaceItems) @@ -499,10 +500,11 @@ view model = WorkspaceItems.WorkspaceItems _ -> article [ id "workspace" ] - [ header - [ id "workspace-toolbar" ] - [ Button.iconThenLabel Find Icon.search "Find Definition" |> Button.small |> Button.view - ] + [ Button.iconThenLabel Find Icon.search "Find Definition" + |> Button.small + |> Button.view + |> Toolbar.toolbar + |> Toolbar.view , section [ id "workspace-content" ] [ section [ class "definitions-pane" ] (viewWorkspaceItems model.workspaceItems) ] diff --git a/src/css/elements.css b/src/css/elements.css index 4e396a2..c250d70 100644 --- a/src/css/elements.css +++ b/src/css/elements.css @@ -179,3 +179,4 @@ p { @import "./elements/fully-qualified-name.css"; @import "./elements/card.css"; @import "./elements/hashvatar.css"; +@import "./elements/toolbar.css"; diff --git a/src/css/elements/toolbar.css b/src/css/elements/toolbar.css new file mode 100644 index 0000000..8163863 --- /dev/null +++ b/src/css/elements/toolbar.css @@ -0,0 +1,15 @@ +.toolbar { + height: var(--toolbar-height); + padding-left: 2.625rem; + padding-right: 1rem; + font-size: var(--font-size-medium); + background: var(--color-main-bg); + border-bottom: 1px solid var(--color-main-border); + display: flex; + flex-direction: row; + align-items: center; +} + +.toolbar .right { + margin-left: auto; +} diff --git a/src/css/main.css b/src/css/main.css index bc59123..87728f4 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -6,6 +6,7 @@ --main-sidebar-collapsed-width: 4.5rem; --main-content-width: 45.5rem; --border-radius-base: 0.25rem; + --toolbar-height: 3.5rem; /* -- Layers ------------------------------------------------------------- */ --layer-beneath: 0; diff --git a/src/css/perspective-landing.css b/src/css/perspective-landing.css index 4662883..158adc8 100644 --- a/src/css/perspective-landing.css +++ b/src/css/perspective-landing.css @@ -146,13 +146,14 @@ justify-content: flex-end; } -.perspective-landing-readme header { +.perspective-landing-readme header.title { font-size: var(--font-size-medium); font-weight: bold; height: 1.5rem; line-height: 1; display: flex; flex-direction: row; + align-items: center; } .perspective-landing-readme header .icon { diff --git a/src/css/workspace.css b/src/css/workspace.css index aaf576a..dfdb22d 100644 --- a/src/css/workspace.css +++ b/src/css/workspace.css @@ -6,31 +6,12 @@ background: var(--color-workspace-bg); color: var(--color-workspace-fg); - --workspace-toolbar-height: 3.5rem; --workspace-content-width: var(--main-content-width); } -#workspace-toolbar { - height: var(--workspace-toolbar-height); - padding-left: 2.625rem; - padding-right: 1rem; - font-size: var(--font-size-medium); - background: var(--color-workspace-item-bg); - border-bottom: 1px solid var(--color-workspace-border); - display: flex; - flex-direction: row; - align-items: center; -} - -#workspace-toolbar .right { - margin-left: auto; -} - #workspace-content { overflow: auto; - height: calc( - calc(100vh - var(--workspace-toolbar-height)) - var(--app-header-height) - ); + height: calc(calc(100vh - var(--toolbar-height)) - var(--app-header-height)); padding-top: 2rem; scroll-behavior: smooth; scrollbar-width: auto;