packages feed

miso 1.9.0.0 → 1.10.0.0

raw patch · 18 files changed

+1127/−233 lines, 18 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Miso.JSON: instance ((TypeError ...), Miso.JSON.GFromJSON a, Miso.JSON.GFromJSON b) => Miso.JSON.GFromJSON (a GHC.Generics.:+: b)
- Miso.JSON: instance ((TypeError ...), Miso.JSON.GToJSON a, Miso.JSON.GToJSON b) => Miso.JSON.GToJSON (a GHC.Generics.:+: b)
- Miso.JSON: instance (GHC.Generics.Selector s, Miso.JSON.FromJSON a) => Miso.JSON.GFromJSON (GHC.Generics.S1 s (GHC.Generics.K1 i (GHC.Maybe.Maybe a)))
- Miso.JSON: instance (GHC.Generics.Selector s, Miso.JSON.FromJSON a) => Miso.JSON.GFromJSON (GHC.Generics.S1 s (GHC.Generics.K1 i a))
- Miso.JSON: instance (GHC.Generics.Selector s, Miso.JSON.ToJSON a) => Miso.JSON.GToJSON (GHC.Generics.S1 s (GHC.Generics.K1 i a))
- Miso.JSON: instance (Miso.JSON.GFromJSON a, Miso.JSON.GFromJSON b) => Miso.JSON.GFromJSON (a GHC.Generics.:*: b)
- Miso.JSON: instance (Miso.JSON.GToJSON a, Miso.JSON.GToJSON b) => Miso.JSON.GToJSON (a GHC.Generics.:*: b)
- Miso.JSON: instance Miso.JSON.GFromJSON GHC.Generics.U1
- Miso.JSON: instance Miso.JSON.GFromJSON a => Miso.JSON.GFromJSON (GHC.Generics.C1 i a)
- Miso.JSON: instance Miso.JSON.GFromJSON a => Miso.JSON.GFromJSON (GHC.Generics.D1 i a)
- Miso.JSON: instance Miso.JSON.GToJSON GHC.Generics.U1
- Miso.JSON: instance Miso.JSON.GToJSON GHC.Generics.V1
- Miso.JSON: instance Miso.JSON.GToJSON a => Miso.JSON.GToJSON (GHC.Generics.C1 i a)
- Miso.JSON: instance Miso.JSON.GToJSON a => Miso.JSON.GToJSON (GHC.Generics.D1 i a)
+ Miso: mailChildren :: ToJSON message => message -> Effect parent model action
+ Miso.Binding: (<--->>) :: Lens' parent field -> Lens' child field -> Binding parent child
+ Miso.Binding: (<-->>) :: Lens parent field -> Lens child field -> Binding parent child
+ Miso.Binding: (<<--->) :: Lens' parent field -> Lens' child field -> Binding parent child
+ Miso.Binding: (<<-->) :: Lens parent field -> Lens child field -> Binding parent child
+ Miso.Binding: Child :: Precedence
+ Miso.Binding: Parent :: Precedence
+ Miso.Binding: data Precedence
+ Miso.Binding: instance GHC.Classes.Eq Miso.Binding.Precedence
+ Miso.Binding: instance GHC.Show.Show Miso.Binding.Precedence
+ Miso.Html.Event: onInputWith :: (MisoString -> DOMRef -> action) -> Attribute action
+ Miso.JSON: PositionalFields :: [Value] -> Fields
+ Miso.JSON: RecordFields :: [(MisoString, Value)] -> Fields
+ Miso.JSON: [allNullaryToStringTag] :: Options -> Bool
+ Miso.JSON: [constructorTagModifier] :: Options -> String -> String
+ Miso.JSON: [omitNothingFields] :: Options -> Bool
+ Miso.JSON: class GAllNullary (f :: Type -> Type)
+ Miso.JSON: class GFromFields (f :: Type -> Type)
+ Miso.JSON: class GFromJSONSum (f :: Type -> Type)
+ Miso.JSON: class GToFields (f :: Type -> Type)
+ Miso.JSON: class GToJSONSum (f :: Type -> Type)
+ Miso.JSON: data Fields
+ Miso.JSON: gAllNullary :: GAllNullary f => Bool
+ Miso.JSON: gFieldCount :: GFromFields f => Int
+ Miso.JSON: gFromJSONSum :: GFromJSONSum f => Options -> Value -> Parser (f a)
+ Miso.JSON: gFromPositional :: GFromFields f => Options -> [Value] -> Parser (f a)
+ Miso.JSON: gFromRecord :: GFromFields f => Options -> Object -> Parser (f a)
+ Miso.JSON: gIsRecord :: GFromFields f => Bool
+ Miso.JSON: gToFields :: GToFields f => Options -> f a -> Fields
+ Miso.JSON: gToJSONSum :: GToJSONSum f => Options -> f a -> Value
+ Miso.JSON: instance (GHC.Generics.Constructor m, Miso.JSON.GFromFields f) => Miso.JSON.GFromJSONSum (GHC.Generics.C1 m f)
+ Miso.JSON: instance (GHC.Generics.Constructor m, Miso.JSON.GToFields f) => Miso.JSON.GToJSONSum (GHC.Generics.C1 m f)
+ Miso.JSON: instance (GHC.Generics.Selector m, Miso.JSON.FromJSON a) => Miso.JSON.GFromFields (GHC.Generics.S1 m (GHC.Generics.K1 r (GHC.Maybe.Maybe a)))
+ Miso.JSON: instance (GHC.Generics.Selector m, Miso.JSON.FromJSON a) => Miso.JSON.GFromFields (GHC.Generics.S1 m (GHC.Generics.K1 r a))
+ Miso.JSON: instance (GHC.Generics.Selector m, Miso.JSON.GToFields f) => Miso.JSON.GToFields (GHC.Generics.S1 m f)
+ Miso.JSON: instance (GHC.Generics.Selector m, Miso.JSON.ToJSON a) => Miso.JSON.GToFields (GHC.Generics.S1 m (GHC.Generics.K1 r (GHC.Maybe.Maybe a)))
+ Miso.JSON: instance (Miso.JSON.GAllNullary f, Miso.JSON.GAllNullary g) => Miso.JSON.GAllNullary (f GHC.Generics.:*: g)
+ Miso.JSON: instance (Miso.JSON.GAllNullary f, Miso.JSON.GAllNullary g) => Miso.JSON.GAllNullary (f GHC.Generics.:+: g)
+ Miso.JSON: instance (Miso.JSON.GFromFields f, Miso.JSON.GFromFields g) => Miso.JSON.GFromFields (f GHC.Generics.:*: g)
+ Miso.JSON: instance (Miso.JSON.GFromJSONSum f, Miso.JSON.GFromJSONSum g) => Miso.JSON.GFromJSONSum (f GHC.Generics.:+: g)
+ Miso.JSON: instance (Miso.JSON.GFromJSONSum f, Miso.JSON.GFromJSONSum g, Miso.JSON.GFromJSONSumNullary f, Miso.JSON.GFromJSONSumNullary g, Miso.JSON.GAllNullary f, Miso.JSON.GAllNullary g) => Miso.JSON.GFromJSONRep (f GHC.Generics.:+: g)
+ Miso.JSON: instance (Miso.JSON.GFromJSONSumNullary f, Miso.JSON.GFromJSONSumNullary g) => Miso.JSON.GFromJSONSumNullary (f GHC.Generics.:+: g)
+ Miso.JSON: instance (Miso.JSON.GToFields f, Miso.JSON.GToFields g) => Miso.JSON.GToFields (f GHC.Generics.:*: g)
+ Miso.JSON: instance (Miso.JSON.GToJSONSum f, Miso.JSON.GToJSONSum g) => Miso.JSON.GToJSONSum (f GHC.Generics.:+: g)
+ Miso.JSON: instance (Miso.JSON.GToJSONSum f, Miso.JSON.GToJSONSum g, Miso.JSON.GToJSONSumNullary f, Miso.JSON.GToJSONSumNullary g, Miso.JSON.GAllNullary f, Miso.JSON.GAllNullary g) => Miso.JSON.GToJSONRep (f GHC.Generics.:+: g)
+ Miso.JSON: instance (Miso.JSON.GToJSONSumNullary f, Miso.JSON.GToJSONSumNullary g) => Miso.JSON.GToJSONSumNullary (f GHC.Generics.:+: g)
+ Miso.JSON: instance GHC.Generics.Constructor m => Miso.JSON.GFromJSONSumNullary (GHC.Generics.C1 m GHC.Generics.U1)
+ Miso.JSON: instance GHC.Generics.Constructor m => Miso.JSON.GFromJSONSumNullary (GHC.Generics.C1 m f)
+ Miso.JSON: instance GHC.Generics.Constructor m => Miso.JSON.GToJSONSumNullary (GHC.Generics.C1 m GHC.Generics.U1)
+ Miso.JSON: instance GHC.Generics.Constructor m => Miso.JSON.GToJSONSumNullary (GHC.Generics.C1 m f)
+ Miso.JSON: instance Miso.JSON.GAllNullary (GHC.Generics.K1 r a)
+ Miso.JSON: instance Miso.JSON.GAllNullary GHC.Generics.U1
+ Miso.JSON: instance Miso.JSON.GAllNullary f => Miso.JSON.GAllNullary (GHC.Generics.C1 m f)
+ Miso.JSON: instance Miso.JSON.GAllNullary f => Miso.JSON.GAllNullary (GHC.Generics.S1 m f)
+ Miso.JSON: instance Miso.JSON.GFromFields GHC.Generics.U1
+ Miso.JSON: instance Miso.JSON.GFromFields GHC.Generics.V1
+ Miso.JSON: instance Miso.JSON.GFromFields f => Miso.JSON.GFromJSONRep (GHC.Generics.C1 m f)
+ Miso.JSON: instance Miso.JSON.GFromJSONRep f => Miso.JSON.GFromJSON (GHC.Generics.D1 m f)
+ Miso.JSON: instance Miso.JSON.GToFields GHC.Generics.U1
+ Miso.JSON: instance Miso.JSON.GToFields GHC.Generics.V1
+ Miso.JSON: instance Miso.JSON.GToFields f => Miso.JSON.GToJSONRep (GHC.Generics.C1 m f)
+ Miso.JSON: instance Miso.JSON.GToJSONRep f => Miso.JSON.GToJSON (GHC.Generics.D1 m f)
+ Miso.JSON: instance Miso.JSON.ToJSON a => Miso.JSON.GToFields (GHC.Generics.K1 r a)
+ Miso.Lens.Generic: class Generic s => HasLens (name :: Symbol) s a | name s -> a
+ Miso.Lens.Generic: field :: forall (name :: Symbol) s a. HasLens name s a => Lens s a
+ Miso.Lens.Generic: getLens :: HasLens name s a => Lens s a
+ Miso.Lens.Generic: instance (GHC.Records.HasField name s a, Miso.Lens.Generic.TotalityCheck name s a (Miso.Lens.Generic.GetFieldType name (GHC.Generics.Rep s)), Miso.Lens.Generic.GSet name a (GHC.Generics.Rep s), GHC.Generics.Generic s) => Miso.Lens.Generic.HasLens name s a
+ Miso.Lens.Generic: instance (Miso.Lens.Generic.GSet name typ a, Miso.Lens.Generic.GSet name typ b) => Miso.Lens.Generic.GSet name typ (a GHC.Generics.:*: b)
+ Miso.Lens.Generic: instance (Miso.Lens.Generic.GSet name typ a, Miso.Lens.Generic.GSet name typ b) => Miso.Lens.Generic.GSet name typ (a GHC.Generics.:+: b)
+ Miso.Lens.Generic: instance Miso.Lens.Generic.GSet name typ (GHC.Generics.S1 ('GHC.Generics.MetaSel ('GHC.Maybe.Just anotherName) b c d) x)
+ Miso.Lens.Generic: instance Miso.Lens.Generic.GSet name typ (GHC.Generics.S1 ('GHC.Generics.MetaSel ('GHC.Maybe.Just name) b c d) (GHC.Generics.Rec0 typ))
+ Miso.Lens.Generic: instance Miso.Lens.Generic.GSet name typ f => Miso.Lens.Generic.GSet name typ (GHC.Generics.C1 x f)
+ Miso.Lens.Generic: instance Miso.Lens.Generic.GSet name typ f => Miso.Lens.Generic.GSet name typ (GHC.Generics.D1 x f)
+ Miso.Lens.Generic: instance Miso.Lens.Generic.HasLens name s a => GHC.OverloadedLabels.IsLabel name (Miso.Lens.Lens s a)
+ Miso.Router: Fragment :: Fragment (path :: Symbol)
+ Miso.Router: data Fragment (path :: Symbol)
+ Miso.Router: fragment :: MisoString -> RouteParser MisoString
+ Miso.Router: instance GHC.Classes.Eq (Miso.Router.Fragment path)
+ Miso.Router: instance GHC.Show.Show (Miso.Router.Fragment path)
+ Miso.Router: instance GHC.TypeLits.KnownSymbol frag => Miso.Router.GRouter (GHC.Generics.K1 m (Miso.Router.Fragment frag))
+ Miso.Router: instance GHC.TypeLits.KnownSymbol frag => Miso.String.ToMisoString (Miso.Router.Fragment frag)
+ Miso.Types: VFrag :: Maybe Key -> [View model action] -> View model action
+ Miso.Types: VFragType :: VTreeType
+ Miso.Types: fragment :: [View model action] -> View model action
+ Miso.Types: fragment_ :: MisoString -> [View model action] -> View model action
+ Miso.Types: keyed :: MisoString -> View model action -> View model action
+ Miso.Types: optionalVoidAttrs :: ([Attribute action] -> View model action) -> [Attribute action] -> Bool -> [Attribute action] -> View model action
+ Miso.Types: vfrag :: [View model action] -> View model action
+ Miso.Types: vfrag_ :: MisoString -> [View model action] -> View model action
+ Miso.Util.Lexer: instance Control.Monad.Fail.MonadFail Miso.Util.Lexer.Lexer
- Miso.Binding: Bidirectional :: (parent -> field) -> (field -> parent -> parent) -> (child -> field) -> (field -> child -> child) -> Binding parent child
+ Miso.Binding: Bidirectional :: Precedence -> (parent -> field) -> (field -> parent -> parent) -> (child -> field) -> (field -> child -> child) -> Binding parent child
- Miso.Html.Element: area_ :: [Attribute action] -> [View model action] -> View model action
+ Miso.Html.Element: area_ :: [Attribute action] -> View model action
- Miso.Html.Element: base_ :: [Attribute action] -> [View model action] -> View model action
+ Miso.Html.Element: base_ :: [Attribute action] -> View model action
- Miso.Html.Element: textarea_ :: [Attribute action] -> [View model action] -> View model action
+ Miso.Html.Element: textarea_ :: [Attribute action] -> View model action
- Miso.JSON: Options :: (String -> String) -> Options
+ Miso.JSON: Options :: (String -> String) -> (String -> String) -> Bool -> Bool -> Options
- Miso.JSON: gToJSON :: GToJSON f => Options -> [Pair] -> f a -> [Pair]
+ Miso.JSON: gToJSON :: GToJSON f => Options -> f a -> Value
- Miso.Types: Bidirectional :: (parent -> field) -> (field -> parent -> parent) -> (child -> field) -> (field -> child -> child) -> Binding parent child
+ Miso.Types: Bidirectional :: Precedence -> (parent -> field) -> (field -> parent -> parent) -> (child -> field) -> (field -> child -> child) -> Binding parent child

Files

README.md view
@@ -29,7 +29,7 @@  ## -**Miso** is a small, production-ready, component-oriented, [reactive](https://github.com/haskell-miso/miso-reactive), [isomorphic](http://nerds.airbnb.com/isomorphic-javascript-future-web-apps/) [Haskell](https://www.haskell.org/) front-end framework for quickly building highly interactive single-page web and [mobile](https://github.com/dmjio/miso-native) applications. It features a virtual-dom, recursive diffing / patching algorithm, attribute and property normalization, event delegation, event batching, SVG, 2D/3D Canvas (WebGL), Server-sent events ([SSE](https://github.com/haskell-miso/miso-sse)), [Websockets](https://github.com/haskell-miso/miso-websocket), type-safe [servant](https://haskell-servant.github.io/)-style routing and an extensible Subscription-based subsystem. Inspired by [Elm](http://elm-lang.org/) and [React](http://react.dev/). **Miso** is pure by default, but side effects can be introduced into the system via the `Effect` data type.+**Miso** is a small, production-ready, component-oriented, [reactive](https://github.com/haskell-miso/miso-reactive), [isomorphic](http://nerds.airbnb.com/isomorphic-javascript-future-web-apps/) [Haskell](https://www.haskell.org/) front-end framework for quickly building highly interactive single-page web and [mobile](https://github.com/dmjio/miso-native) applications. It features a virtual-dom, recursive diffing / patching algorithm, attribute and property normalization, event delegation, event batching, SVG, 2D/3D Canvas (WebGL), [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), Server-sent events ([SSE](https://github.com/haskell-miso/miso-sse)), [Websockets](https://github.com/haskell-miso/miso-websocket), type-safe [servant](https://haskell-servant.github.io/)-style routing and an extensible Subscription-based subsystem. Inspired by [Elm](http://elm-lang.org/) and [React](http://react.dev/). **Miso** is pure by default, but side effects can be introduced into the system via the `Effect` data type.  **Miso** makes heavy use of the [GHC Javascript FFI](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/wasm.html#javascript-ffi-in-the-wasm-backend) and therefore has minimal dependencies. **Miso** can be considered a shallow [embedded domain-specific language](https://wiki.haskell.org/Embedded_domain_specific_language) for modern web programming. @@ -77,9 +77,6 @@  Miso aims to [bridge the gap](https://wiki.haskell.org/The_JavaScript_Problem) between modern JavaScript frameworks (such as [React](https://reactjs.org), [Vue.js](https://vuejs.org), etc.) and functional programming in [Haskell](https://haskell.org). It has since grown to encompass more features from the JavaScript community like [Components](https://react.dev/learn/your-first-component) and [Renderers](https://github.com/chentsulin/awesome-react-renderer). Miso also now supports [native development](https://github.com/haskell-miso/miso-lynx) for [iOS](https://www.apple.com/ios/), [Android](https://www.android.com/) and [HarmonyOS](https://device.harmonyos.com/en/) devices via [LynxJS](https://lynxjs.org) and targets additional backends like [Web Assembly](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/wasm.html). -> [!Note]-> [React-style Components](https://github.com/dmjio/miso/pull/766) are now added to `miso` as of version `1.9`. This has not yet been released, we recommend developing against `master` if you'd like to use latest features.- ## Docs 📚   See the [Miso](https://haddocks.haskell-miso.org/miso/Miso.html) module.@@ -198,9 +195,9 @@ foreign export javascript "hs_start" main :: IO () #endif ------------------------------------------------------------------------------- | `component` takes as arguments the initial model, update function, view function+-- | `vcomp` takes as arguments the initial model, update function, view function app :: App Int Action-app = component 0 updateModel viewModel+app = vcomp 0 updateModel viewModel ---------------------------------------------------------------------------- -- | Updates model, optionally introduces side effects updateModel :: Action -> Effect parent Int Action@@ -213,10 +210,7 @@ ---------------------------------------------------------------------------- -- | Constructs a virtual DOM from a model viewModel :: Int -> View Int Action-viewModel x =-  H.div_-    [ P.className "counter"-    ]+viewModel x = vfrag     [ H.button_ [ H.onClick AddOne ] [ text "+" ]     , text (ms x)     , H.button_ [ H.onClick SubtractOne ] [ text "-" ]@@ -447,12 +441,6 @@ ## Backers  Become a [financial contributor](https://opencollective.com/miso/contribute) and help us sustain our project and community. We are very grateful and thankful for our individual sponsors.--  - Moses Tschanz-  - [@MaxGabriel](https://github.com/MaxGabriel)-  - [@maybetonyfu](https://github.com/maybetonyfu)-  - [@jhrcek](https://github.com/jhrcek)-  - etc.  <a href="https://opencollective.com/miso"><img src="https://opencollective.com/miso/individuals.svg?width=890"></a> 
cbits/foreign.c view
@@ -7,21 +7,21 @@ static void *stored_value = NULL;  // Store a pointer-void x_store(void *ptr) {+void miso_x_store(void *ptr) {     stored_value = ptr; }  // Get the stored pointer-void *x_get(void) {+void *miso_x_get(void) {     return stored_value; }  // Check if a pointer is stored (non-NULL)-int x_exists(void) {+int miso_x_exists(void) {     return stored_value != NULL; }  // Clear the stored pointer-void x_clear(void) {+void miso_x_clear(void) {     stored_value = NULL; }
js/miso.js view
@@ -179,7 +179,7 @@   if (!current.parent) {     return;   }-  latest.nextSibling = current.nextSibling ? null : current.nextSibling;+  latest.nextSibling = current.nextSibling;   latest.parent = current.parent;   current.parent.child = latest; }@@ -220,22 +220,49 @@ function mathRandom() {   return Math.random(); }-function getDOMRef(tree) {+function forEachDOMRef(tree, cb) {   switch (tree.type) {+    case 3 /* VFrag */:+      for (const child of tree.children)+        forEachDOMRef(child, cb);+      break;     case 0 /* VComp */:-      return drill(tree);+      if (tree.child)+        forEachDOMRef(tree.child, cb);+      break;     default:+      cb(tree.domRef);+      break;+  }+}+function getFirstDOMRef(tree) {+  switch (tree.type) {+    case 3 /* VFrag */: {+      if (!tree.children || tree.children.length === 0)+        return null;+      return getFirstDOMRef(tree.children[0]);+    }+    case 0 /* VComp */:+      if (!tree.child)+        return null;+      return getFirstDOMRef(tree.child);+    default:       return tree.domRef;   } }-function drill(c) {-  if (!c.child)-    throw new Error("'drill' called on an unmounted Component. This should never happen, please make an issue.");-  switch (c.child.type) {+function getLastDOMRef(tree) {+  switch (tree.type) {+    case 3 /* VFrag */: {+      if (!tree.children || tree.children.length === 0)+        return null;+      return getLastDOMRef(tree.children[tree.children.length - 1]);+    }     case 0 /* VComp */:-      return drill(c.child);+      if (!tree.child)+        return null;+      return getLastDOMRef(tree.child);     default:-      return c.child.domRef;+      return tree.domRef;   } } @@ -258,6 +285,14 @@       return;     }     replace(c, n, parent, context);+  } else if (c.type === 3 /* VFrag */ && n.type === 3 /* VFrag */) {+    if (n.key === c.key) {+      const lastRef = getLastDOMRef(c);+      const endAnchor = lastRef ? lastRef.nextSibling : context.nextSibling(c);+      diffChildren(c.children, n.children, parent, context, endAnchor);+    } else {+      replace(c, n, parent, context);+    }   } else if (c.type === 1 /* VNode */ && n.type === 1 /* VNode */) {     if (n.tag === c.tag && n.key === c.key) {       n.domRef = c.domRef;@@ -275,6 +310,17 @@   return; } function replace(c, n, parent, context) {+  if (c.type === 3 /* VFrag */) {+    const lastRef2 = getLastDOMRef(c);+    const anchor = lastRef2 ? lastRef2.nextSibling : context.nextSibling(c);+    destroy(c, parent, context);+    if (anchor) {+      createElement(parent, 2 /* INSERT_BEFORE */, anchor, n, context);+    } else {+      create(n, parent, context);+    }+    return;+  }   switch (c.type) {     case 2 /* VText */:       break;@@ -282,7 +328,26 @@       callBeforeDestroyedRecursive(c);       break;   }-  createElement(parent, 1 /* REPLACE */, getDOMRef(c), n, context);+  const firstRef = getFirstDOMRef(c);+  const lastRef = getLastDOMRef(c);+  if (!firstRef || !lastRef) {+    const anchor = context.nextSibling(c);+    if (anchor) {+      createElement(parent, 2 /* INSERT_BEFORE */, anchor, n, context);+    } else {+      create(n, parent, context);+    }+  } else if (firstRef !== lastRef) {+    const anchor = lastRef.nextSibling;+    forEachDOMRef(c, (ref) => context.removeChild(parent, ref));+    if (anchor) {+      createElement(parent, 2 /* INSERT_BEFORE */, anchor, n, context);+    } else {+      create(n, parent, context);+    }+  } else {+    createElement(parent, 1 /* REPLACE */, firstRef, n, context);+  }   switch (c.type) {     case 2 /* VText */:       break;@@ -295,11 +360,15 @@   switch (c.type) {     case 2 /* VText */:       break;+    case 3 /* VFrag */:+      for (const child of c.children)+        destroy(child, parent, context);+      return;     default:       callBeforeDestroyedRecursive(c);       break;   }-  context.removeChild(parent, getDOMRef(c));+  forEachDOMRef(c, (ref) => context.removeChild(parent, ref));   switch (c.type) {     case 2 /* VText */:       break;@@ -309,20 +378,22 @@   } } function callDestroyedRecursive(c) {+  if (c.type === 3 /* VFrag */) {+    for (const child of c.children)+      if (child.type !== 2 /* VText */)+        callDestroyedRecursive(child);+    return;+  }   callDestroyed(c);   switch (c.type) {     case 1 /* VNode */:-      for (const child of c.children) {-        if (child.type === 1 /* VNode */ || child.type === 0 /* VComp */) {+      for (const child of c.children)+        if (child.type !== 2 /* VText */)           callDestroyedRecursive(child);-        }-      }       break;     case 0 /* VComp */:-      if (c.child) {-        if (c.child.type === 1 /* VNode */ || c.child.type === 0 /* VComp */)-          callDestroyedRecursive(c.child);-      }+      if (c.child && c.child.type !== 2 /* VText */)+        callDestroyedRecursive(c.child);       break;   } }@@ -345,6 +416,12 @@   } } function callBeforeDestroyedRecursive(c) {+  if (c.type === 3 /* VFrag */) {+    for (const child of c.children)+      if (child.type !== 2 /* VText */)+        callBeforeDestroyedRecursive(child);+    return;+  }   callBeforeDestroyed(c);   switch (c.type) {     case 1 /* VNode */:@@ -355,10 +432,8 @@       }       break;     case 0 /* VComp */:-      if (c.child) {-        if (c.child.type === 1 /* VNode */ || c.child.type === 0 /* VComp */)-          callBeforeDestroyedRecursive(c.child);-      }+      if (c.child && c.child.type !== 2 /* VText */)+        callBeforeDestroyedRecursive(c.child);       break;   } }@@ -424,7 +499,7 @@     }   }   for (const n in nProps) {-    if (cProps && cProps[n])+    if (cProps && n in cProps)       continue;     newProp = nProps[n];     if (isSvg) {@@ -458,12 +533,22 @@   }   return true; }-function diffChildren(cs, ns, parent, context) {+function diffChildren(cs, ns, parent, context, endAnchor = null) {   if (shouldSync(cs, ns)) {-    syncChildren(cs, ns, parent, context);+    syncChildren(cs, ns, parent, context, endAnchor);   } else {-    for (let i = 0;i < Math.max(ns.length, cs.length); i++)-      diff(cs[i], ns[i], parent, context);+    for (let i = 0;i < Math.max(ns.length, cs.length); i++) {+      const c = cs[i], n = ns[i];+      if (!c && n) {+        if (endAnchor) {+          createElement(parent, 2 /* INSERT_BEFORE */, endAnchor, n, context);+        } else {+          create(n, parent, context);+        }+      } else {+        diff(c, n, parent, context);+      }+    }   } } function populateDomRef(c, context) {@@ -495,6 +580,14 @@           break;       }       break;+    case 3 /* VFrag */:+      for (const child of n.children) {+        createElement(parent, 2 /* INSERT_BEFORE */, replacing, child, context);+      }+      if (op === 1 /* REPLACE */ && replacing) {+        context.removeChild(parent, replacing);+      }+      break;     case 0 /* VComp */:       mountComponent(parent, op, replacing, n, context);       break;@@ -531,12 +624,23 @@   n.componentId = mounted.componentId;   n.child = mounted.componentTree;   mounted.componentTree.parent = n;+  const componentDOMRef = getFirstDOMRef(mounted.componentTree);   if (mounted.componentTree.type !== 0 /* VComp */) {-    const childDomRef = getDOMRef(mounted.componentTree);     if (op === 1 /* REPLACE */ && replacing) {-      context.replaceChild(parent, childDomRef, replacing);+      if (!componentDOMRef) {+        context.removeChild(parent, replacing);+      } else if (mounted.componentTree.type === 3 /* VFrag */) {+        forEachDOMRef(mounted.componentTree, (ref) => context.insertBefore(parent, ref, replacing));+        context.removeChild(parent, replacing);+      } else {+        context.replaceChild(parent, componentDOMRef, replacing);+      }     } else if (op === 2 /* INSERT_BEFORE */) {-      context.insertBefore(parent, childDomRef, replacing);+      if (replacing) {+        forEachDOMRef(mounted.componentTree, (ref) => context.insertBefore(parent, ref, replacing));+      } else {+        forEachDOMRef(mounted.componentTree, (ref) => context.appendChild(parent, ref));+      }     }   } }@@ -544,12 +648,35 @@   createElement(parent, 0 /* APPEND */, null, n, context); } function insertBefore(parent, n, o, context) {-  context.insertBefore(parent, getDOMRef(n), o ? getDOMRef(o) : null);+  const anchor = o ? getFirstDOMRef(o) ?? context.nextSibling(o) : null;+  if (anchor) {+    forEachDOMRef(n, (ref) => context.insertBefore(parent, ref, anchor));+  } else {+    forEachDOMRef(n, (ref) => context.appendChild(parent, ref));+  } } function swapDOMRef(oLast, oFirst, parent, context) {-  context.swapDOMRefs(getDOMRef(oLast), getDOMRef(oFirst), parent);+  const oLastRef = getFirstDOMRef(oLast);+  const oFirstRef = getFirstDOMRef(oFirst);+  if (oLastRef && oFirstRef && (oLast.type === 1 /* VNode */ || oLast.type === 2 /* VText */) && (oFirst.type === 1 /* VNode */ || oFirst.type === 2 /* VText */)) {+    context.swapDOMRefs(oLastRef, oFirstRef, parent);+    return;+  }+  const lastRef = getLastDOMRef(oLast);+  const tmp = lastRef ? lastRef.nextSibling : context.nextSibling(oLast);+  const anchor = getFirstDOMRef(oFirst) ?? context.nextSibling(oFirst);+  if (anchor) {+    forEachDOMRef(oLast, (ref) => context.insertBefore(parent, ref, anchor));+  } else {+    forEachDOMRef(oLast, (ref) => context.appendChild(parent, ref));+  }+  if (tmp) {+    forEachDOMRef(oFirst, (ref) => context.insertBefore(parent, ref, tmp));+  } else {+    forEachDOMRef(oFirst, (ref) => context.appendChild(parent, ref));+  } }-function syncChildren(os, ns, parent, context) {+function syncChildren(os, ns, parent, context, endAnchor = null) {   var oldFirstIndex = 0, newFirstIndex = 0, oldLastIndex = os.length - 1, newLastIndex = ns.length - 1, tmp, nFirst, nLast, oLast, oFirst, found, node;   for (;; ) {     if (newFirstIndex > newLastIndex && oldFirstIndex > oldLastIndex) {@@ -560,8 +687,13 @@     oFirst = os[oldFirstIndex];     oLast = os[oldLastIndex];     if (oldFirstIndex > oldLastIndex) {-      diff(null, nFirst, parent, context);-      insertBefore(parent, nFirst, oFirst, context);+      const oFirstRef = oFirst ? getFirstDOMRef(oFirst) ?? context.nextSibling(oFirst) : null;+      const anchor = oFirstRef ?? endAnchor;+      if (anchor) {+        createElement(parent, 2 /* INSERT_BEFORE */, anchor, nFirst, context);+      } else {+        create(nFirst, parent, context);+      }       os.splice(newFirstIndex, 0, nFirst);       newFirstIndex++;     } else if (newFirstIndex > newLastIndex) {@@ -581,7 +713,13 @@       diff(os[oldFirstIndex++], ns[newFirstIndex++], parent, context);       diff(os[oldLastIndex--], ns[newLastIndex--], parent, context);     } else if (oFirst.key === nLast.key) {-      insertBefore(parent, oFirst, oLast.nextSibling, context);+      const lastRef = getLastDOMRef(oLast);+      const afterOLast = lastRef ? lastRef.nextSibling : context.nextSibling(oLast);+      if (afterOLast) {+        forEachDOMRef(oFirst, (ref) => context.insertBefore(parent, ref, afterOLast));+      } else {+        forEachDOMRef(oFirst, (ref) => context.appendChild(parent, ref));+      }       os.splice(oldLastIndex, 0, os.splice(oldFirstIndex, 1)[0]);       diff(os[oldLastIndex--], ns[newLastIndex--], parent, context);     } else if (oLast.key === nFirst.key) {@@ -606,7 +744,12 @@         insertBefore(parent, node, os[oldFirstIndex], context);         newFirstIndex++;       } else {-        createElement(parent, 2 /* INSERT_BEFORE */, getDOMRef(oFirst), nFirst, context);+        const anchor = getFirstDOMRef(oFirst) ?? context.nextSibling(oFirst);+        if (anchor) {+          createElement(parent, 2 /* INSERT_BEFORE */, anchor, nFirst, context);+        } else {+          create(nFirst, parent, context);+        }         os.splice(oldFirstIndex++, 0, nFirst);         newFirstIndex++;         oldLastIndex++;@@ -667,6 +810,14 @@   } else if (stack.length > 1) {     if (obj.type === 2 /* VText */) {       return;+    } else if (obj.type === 3 /* VFrag */) {+      for (const child of obj.children) {+        if (containsDOMRef(child, stack[0], context)) {+          delegateEvent(event, child, stack, debug, context);+          return;+        }+      }+      return;     } else if (obj.type === 0 /* VComp */) {       if (!obj.child) {         if (debug) {@@ -693,8 +844,9 @@         }         stack.splice(0, 1);         for (const child of obj.children) {-          if (context.isEqual(getDOMRef(child), stack[0])) {+          if (containsDOMRef(child, stack[0], context)) {             delegateEvent(event, child, stack, debug, context);+            return;           }         }       }@@ -705,6 +857,13 @@       if (obj.child) {         delegateEvent(event, obj.child, stack, debug, context);       }+    } else if (obj.type === 3 /* VFrag */) {+      for (const child of obj.children) {+        if (containsDOMRef(child, stack[0], context)) {+          delegateEvent(event, child, stack, debug, context);+          return;+        }+      }     } else if (obj.type === 1 /* VNode */) {       const eventCaptureObj = obj.events.captures[event.type];       if (eventCaptureObj && !event["captureStopped"]) {@@ -741,6 +900,9 @@     switch (vtree.type) {       case 2 /* VText */:         break;+      case 3 /* VFrag */:+        vtree = vtree.parent;+        break;       case 1 /* VNode */:         const eventObj = vtree.events.bubbles[event.type];         if (eventObj) {@@ -791,6 +953,19 @@   }   return newObj; }+function containsDOMRef(vtree, target, context) {+  switch (vtree.type) {+    case 3 /* VFrag */:+      for (const child of vtree.children)+        if (containsDOMRef(child, target, context))+          return true;+      return false;+    case 0 /* VComp */:+      return vtree.child ? containsDOMRef(vtree.child, target, context) : false;+    default:+      return context.isEqual(vtree.domRef, target);+  }+} function getAllPropertyNames(obj) {   var props = {}, i = 0;   do {@@ -860,12 +1035,19 @@ }; var drawingContext = {   nextSibling: (node) => {-    if (node.nextSibling) {-      switch (node.nextSibling.type) {+    let sibling = node.nextSibling;+    while (sibling) {+      switch (sibling.type) {         case 0 /* VComp */:-          return drill(node.nextSibling);+        case 3 /* VFrag */: {+          const ref = getFirstDOMRef(sibling);+          if (ref)+            return ref;+          sibling = sibling.nextSibling;+          break;+        }         default:-          return node.nextSibling.domRef;+          return sibling.domRef;       }     }     return null;@@ -968,6 +1150,11 @@     }     adjusted[++ax] = vs[ix];   }+  for (const v of adjusted) {+    if (v.type === 3 /* VFrag */) {+      v.children = collapseSiblingTextNodes(v.children);+    }+  }   return adjusted; } function hydrate(logLevel, mountPoint, vtree, context, drawingContext2) {@@ -993,6 +1180,10 @@   if (logLevel)     console.warn("[DEBUG_HYDRATE] VTree differed from node", vtree, node); }+function nextAfter(tree, current) {+  const lastRef = getLastDOMRef(tree);+  return lastRef ? lastRef.nextSibling : current;+} function walk(logLevel, vtree, node, context, drawingContext2) {   switch (vtree.type) {     case 0 /* VComp */:@@ -1004,6 +1195,18 @@         return false;       }       break;+    case 3 /* VFrag */:+      vtree.children = collapseSiblingTextNodes(vtree.children);+      for (const child of vtree.children) {+        if (!node) {+          diagnoseError(logLevel, child, null);+          return false;+        }+        if (!walk(logLevel, child, node, context, drawingContext2))+          return false;+        node = nextAfter(child, node);+      }+      break;     case 2 /* VText */:       if (node.nodeType !== 3 || vtree.text.trim() !== node.textContent.trim()) {         diagnoseError(logLevel, vtree, node);@@ -1019,16 +1222,17 @@       vtree.domRef = node;       vtree.children = collapseSiblingTextNodes(vtree.children);       callCreated(node, vtree, drawingContext2);+      let domCursor = node.firstChild;       for (var i = 0;i < vtree.children.length; i++) {         const vdomChild = vtree.children[i];-        const domChild = node.childNodes[i];-        if (!domChild) {-          diagnoseError(logLevel, vdomChild, domChild);+        if (!domCursor) {+          diagnoseError(logLevel, vdomChild, null);           return false;         }-        if (!walk(logLevel, vdomChild, domChild, context, drawingContext2)) {+        if (!walk(logLevel, vdomChild, domCursor, context, drawingContext2)) {           return false;         }+        domCursor = nextAfter(vdomChild, domCursor);       }       break;   }
js/miso.prod.js view
@@ -1,1 +1,1 @@-var L="1.9.0.0";function w(U,z){var G=function(){var H=document.getElementById(U);if(H&&H.focus)H.focus()};z>0?setTimeout(G,z):G()}function I(U,z){var G=function(){var H=document.getElementById(U);if(H&&H.blur)H.blur()};z>0?setTimeout(G,z):G()}function V(U,z){var G=function(){var H=document.getElementById(U);if(H&&typeof H.select==="function")H.select()};z>0?setTimeout(G,z):G()}function O(U,z,G,H){var K=function(){var X=document.getElementById(U);if(X&&typeof X.setSelectionRange==="function")X.setSelectionRange(z,G,"none")};H>0?setTimeout(K,H):K()}function P(U,z,G,H,K,X,Q){var Z={method:z,headers:H};if(G)Z.body=G;let _={},$=null;try{fetch(U,Z).then((Y)=>{$=Y.status;for(let[W,J]of Y.headers)_[W]=J;if(!Y.ok)throw new Error(Y.statusText);if(Q=="json")return Y.json();else if(Q=="text")return Y.text();else if(Q==="arrayBuffer")return Y.arrayBuffer();else if(Q==="blob")return Y.blob();else if(Q==="bytes")return Y.bytes();else if(Q==="formData")return Y.formData();else if(Q==="none")return K({error:null,body:null,headers:_,status:$})}).then((Y)=>K({error:null,body:Y,headers:_,status:$})).catch((Y)=>X({error:null,body:Y,headers:_,status:$}))}catch(Y){X({body:null,error:Y.message,headers:_,status:$})}}function f(U,z,G,H,K,X,Q,Z,_){try{let $=new WebSocket(U);return $.onopen=function(){z()},$.onclose=function(Y){G(Y)},$.onerror=function(Y){console.error(Y),Z("WebSocket error received")},$.onmessage=function(Y){if(typeof Y.data==="string")try{if(_){if(H)H(Y.data);return}let W=JSON.parse(Y.data);if(K)K(W)}catch(W){if(_&&H)H(Y.data);else Z(W.message)}else if(Y.data instanceof Blob){if(X)X(Y.data)}else if(Y.data instanceof ArrayBuffer){if(Q)Q(Y.data)}else console.error("Received unknown message type from WebSocket",Y),Z("Unknown message received from WebSocket")},$}catch($){Z($.message)}}function j(U){if(U)U.close(),U=null}function k(U,z){if(z&&U&&U.readyState===WebSocket.OPEN)U.send(z)}function b(U,z,G,H,K,X){try{let Q=new EventSource(U);return Q.onopen=function(){z()},Q.onerror=function(){K("EventSource error received")},Q.onmessage=function(Z){try{if(X){if(G)G(Z.data);return}let _=JSON.parse(Z.data);if(H)H(_)}catch(_){if(X&&G)G(Z.data);else K(_.message)}},Q}catch(Q){K(Q.message)}}function T(U){if(U)U.close(),U=null}function g(U,z){if(!U.classList)U.classList=new Set;for(let G of z)for(let H of G.trim().split(" "))if(H)U.classList.add(H)}function h(U,z){if(!U.parent)return;z.nextSibling=U.nextSibling?null:U.nextSibling,z.parent=U.parent,U.parent.child=z}function y(U,z={}){let G=Object.keys(z),H=Object.values(z);return new Function(...G,U)(...H)}function m(U){if(U===null||U===void 0)return 0;if(typeof U==="number")return 1;if(typeof U==="string")return 2;if(typeof U==="boolean")return 3;if(Array.isArray(U))return 4;return 5}function v(U){return function(){U|=0,U=U+2654435769|0;var z=U^U>>>15;return z=Math.imul(z,2246822507),z=z^z>>>13,z=Math.imul(z,3266489909),((z^z>>>16)>>>0)/4294967296}}function u(){let U=new Uint32Array(1);return crypto.getRandomValues(U)[0]}function l(){return Math.random()}function q(U){switch(U.type){case 0:return S(U);default:return U.domRef}}function S(U){if(!U.child)throw new Error("'drill' called on an unmounted Component. This should never happen, please make an issue.");switch(U.child.type){case 0:return S(U.child);default:return U.child.domRef}}function A(U,z,G,H){if(!U&&!z)return;else if(!U)EU(z,G,H);else if(!z)o(U,G,H);else if(U.type===2&&z.type===2)HU(U,z,H);else if(U.type===0&&z.type===0){if(z.key===U.key){if(z.child=U.child,z.componentId=U.componentId,U.child)U.child.parent=z;return}x(U,z,G,H)}else if(U.type===1&&z.type===1)if(z.tag===U.tag&&z.key===U.key)z.domRef=U.domRef,n(U,z,H);else x(U,z,G,H);else x(U,z,G,H)}function HU(U,z,G){if(U.text!==z.text)G.setTextContent(U.domRef,z.text);z.domRef=U.domRef;return}function x(U,z,G,H){switch(U.type){case 2:break;default:C(U);break}switch(i(G,1,q(U),z,H),U.type){case 2:break;default:R(U);break}}function o(U,z,G){switch(U.type){case 2:break;default:C(U);break}switch(G.removeChild(z,q(U)),U.type){case 2:break;default:R(U);break}}function R(U){switch(KU(U),U.type){case 1:for(let z of U.children)if(z.type===1||z.type===0)R(z);break;case 0:if(U.child){if(U.child.type===1||U.child.type===0)R(U.child)}break}}function KU(U){if(U.type===1&&U.onDestroyed)U.onDestroyed();if(U.type===0)AU(U)}function QU(U){switch(U.type){case 0:break;case 1:if(U.onBeforeDestroyed)U.onBeforeDestroyed();break;default:break}}function C(U){switch(QU(U),U.type){case 1:for(let z of U.children){if(z.type===2)continue;C(z)}break;case 0:if(U.child){if(U.child.type===1||U.child.type===0)C(U.child)}break}}function n(U,z,G){YU(U?U.props:{},z.props,z.domRef,z.ns==="svg",G),XU(U?U.classList:null,z.classList,z.domRef,G),ZU(U?U.css:{},z.css,z.domRef,G),_U(U?U.children:[],z.children,z.domRef,G),qU(z)}function XU(U,z,G,H){if(!U&&!z)return;if(!U){for(let K of z)H.addClass(K,G);return}if(!z){for(let K of U)H.removeClass(K,G);return}for(let K of U)if(!z.has(K))H.removeClass(K,G);for(let K of z)if(!U.has(K))H.addClass(K,G);return}function YU(U,z,G,H,K){var X;for(let Q in U)if(X=z[Q],X===void 0)if(H||!(Q in G)||Q==="disabled")K.removeAttribute(G,Q);else K.setAttribute(G,Q,"");else{if(X===U[Q]&&Q!=="checked"&&Q!=="value")continue;if(H)if(Q==="href")K.setAttributeNS(G,"http://www.w3.org/1999/xlink","href",X);else K.setAttribute(G,Q,X);else if(Q in G&&!(Q==="list"||Q==="form"))G[Q]=X;else K.setAttribute(G,Q,X)}for(let Q in z){if(U&&U[Q])continue;if(X=z[Q],H)if(Q==="href")K.setAttributeNS(G,"http://www.w3.org/1999/xlink","href",X);else K.setAttribute(G,Q,X);else if(Q in G&&!(Q==="list"||Q==="form"))G[Q]=z[Q];else K.setAttribute(G,Q,X)}}function ZU(U,z,G,H){H.setInlineStyle(U,z,G)}function $U(U,z){if(U.length===0||z.length===0)return!1;for(var G=0;G<U.length;G++)if(U[G].key===null||U[G].key===void 0)return!1;for(var G=0;G<z.length;G++)if(z[G].key===null||z[G].key===void 0)return!1;return!0}function _U(U,z,G,H){if($U(U,z))SU(U,z,G,H);else for(let K=0;K<Math.max(z.length,U.length);K++)A(U[K],z[K],G,H)}function WU(U,z){if(U.ns==="svg")U.domRef=z.createElementNS("http://www.w3.org/2000/svg",U.tag);else if(U.ns==="mathml")U.domRef=z.createElementNS("http://www.w3.org/1998/Math/MathML",U.tag);else U.domRef=z.createElement(U.tag)}function t(U,z,G){if(z.onCreated)z.onCreated(z.domRef)}function i(U,z,G,H,K){switch(H.type){case 2:switch(H.domRef=K.createTextNode(H.text),z){case 2:K.insertBefore(U,H.domRef,G);break;case 0:K.appendChild(U,H.domRef);break;case 1:K.replaceChild(U,H.domRef,G);break}break;case 0:JU(U,z,G,H,K);break;case 1:if(H.onBeforeCreated)H.onBeforeCreated();if(WU(H,K),H.onCreated)H.onCreated(H.domRef);switch(n(null,H,K),z){case 2:K.insertBefore(U,H.domRef,G);break;case 0:K.appendChild(U,H.domRef);break;case 1:K.replaceChild(U,H.domRef,G);break}break}}function qU(U){if(U.tag==="canvas"&&U.draw)U.draw(U.domRef)}function AU(U){U.unmount(U.componentId)}function JU(U,z,G,H,K){let X=H.mount(U);if(H.componentId=X.componentId,H.child=X.componentTree,X.componentTree.parent=H,X.componentTree.type!==0){let Q=q(X.componentTree);if(z===1&&G)K.replaceChild(U,Q,G);else if(z===2)K.insertBefore(U,Q,G)}}function EU(U,z,G){i(z,0,null,U,G)}function N(U,z,G,H){H.insertBefore(U,q(z),G?q(G):null)}function DU(U,z,G,H){H.swapDOMRefs(q(U),q(z),G)}function SU(U,z,G,H){var K=0,X=0,Q=U.length-1,Z=z.length-1,_,$,Y,W,J,F,c;for(;;){if(X>Z&&K>Q)break;if($=z[X],Y=z[Z],J=U[K],W=U[Q],K>Q)A(null,$,G,H),N(G,$,J,H),U.splice(X,0,$),X++;else if(X>Z){_=Q;while(Q>=K)o(U[Q--],G,H);U.splice(K,_-K+1);break}else if(J.key===$.key)A(U[K++],z[X++],G,H);else if(W.key===Y.key)A(U[Q--],z[Z--],G,H);else if(J.key===Y.key&&$.key===W.key)DU(W,J,G,H),BU(U,K,Q),A(U[K++],z[X++],G,H),A(U[Q--],z[Z--],G,H);else if(J.key===Y.key)N(G,J,W.nextSibling,H),U.splice(Q,0,U.splice(K,1)[0]),A(U[Q--],z[Z--],G,H);else if(W.key===$.key)N(G,W,J,H),U.splice(K,0,U.splice(Q,1)[0]),A(U[K++],$,G,H),X++;else{F=!1,_=K;while(_<=Q){if(U[_].key===$.key){F=!0,c=U[_];break}_++}if(F)U.splice(K,0,U.splice(_,1)[0]),A(U[K++],$,G,H),N(G,c,U[K],H),X++;else i(G,2,q(J),$,H),U.splice(K++,0,$),X++,Q++}}}function BU(U,z,G){let H=U[z];U[z]=U[G],U[G]=H}function p(U,z,G,H,K){for(let X of z)K.addEventListener(U,X.name,function(Q){NU(Q,U,G,H,K)},X.capture)}function NU(U,z,G,H,K){G(function(X){if(Array.isArray(U))for(let Q of U)e(Q,X,z,H,K);else e(U,X,z,H,K)})}function e(U,z,G,H,K){var X=K.getTarget(U);if(X){let Q=RU(G,X,K);D(U,z,Q,H,K)}}function RU(U,z,G){var H=[];while(!G.isEqual(U,z))if(H.unshift(z),z&&G.parentNode(z))z=G.parentNode(z);else return H;return H}function D(U,z,G,H,K){if(!G.length){if(H)console.warn('Event "'+U.type+'" did not find an event handler to dispatch on',z,U);return}else if(G.length>1){if(z.type===2)return;else if(z.type===0){if(!z.child){if(H)throw console.error("VComp has no child property set during event delegation",z),console.error("This means the Component has not been fully mounted, this should never happen"),new Error("VComp has no .child property set during event delegation");return}return D(U,z.child,G,H,K)}else if(z.type===1){if(K.isEqual(z.domRef,G[0])){let X=z.events.captures[U.type];if(X){let Q=X.options;if(Q.preventDefault)U.preventDefault();if(!U.captureStopped)X.runEvent(U,z.domRef);if(Q.stopPropagation)U.captureStopped=!0}G.splice(0,1);for(let Q of z.children)if(K.isEqual(q(Q),G[0]))D(U,Q,G,H,K)}return}}else if(z.type===0){if(z.child)D(U,z.child,G,H,K)}else if(z.type===1){let X=z.events.captures[U.type];if(X&&!U.captureStopped){let Z=X.options;if(K.isEqual(G[0],z.domRef)){if(Z.preventDefault)U.preventDefault();if(X.runEvent(U,G[0]),Z.stopPropagation)U.captureStopped=!0}}let Q=z.events.bubbles[U.type];if(Q&&!U.captureStopped){let Z=Q.options;if(K.isEqual(G[0],z.domRef)){if(Z.preventDefault)U.preventDefault();if(Q.runEvent(U,G[0]),!Z.stopPropagation)UU(z.parent,U)}}else if(!U.captureStopped)UU(z.parent,U)}}function UU(U,z){while(U)switch(U.type){case 2:break;case 1:let G=U.events.bubbles[z.type];if(G){let H=G.options;if(H.preventDefault)z.preventDefault();if(G.runEvent(z,U.domRef),H.stopPropagation)return}U=U.parent;break;case 0:if(!U.eventPropagation)return;U=U.parent;break}}function B(U,z){if(typeof U[0]==="object"){var G=[];for(var H=0;H<U.length;H++)G.push(B(U[H],z));return G}for(let Z of U)z=z[Z];var K;if(z instanceof Array||"length"in z&&z.localName!=="select"){K=[];for(var X=0;X<z.length;X++)K.push(B([],z[X]));return K}K={};for(var Q in CU(z)){if(z.localName==="input"&&(Q==="selectionDirection"||Q==="selectionStart"||Q==="selectionEnd"))continue;if(typeof z[Q]=="string"||typeof z[Q]=="number"||typeof z[Q]=="boolean")K[Q]=z[Q]}return K}function CU(U){var z={},G=0;do{var H=Object.getOwnPropertyNames(U);for(G=0;G<H.length;G++)z[H[G]]=null}while(U=Object.getPrototypeOf(U));return z}var M={addEventListener:(U,z,G,H)=>{U.addEventListener(z,G,H)},delegator:(U,z,G,H,K)=>{p(U,z,G,H,K)},isEqual:(U,z)=>{return U===z},getTarget:(U)=>{return U.target},parentNode:(U)=>{return U.parentNode}},zU={getInlineStyle:(U,z)=>{return U.style[z]},firstChild:(U)=>{return U.firstChild},lastChild:(U)=>{return U.lastChild},getAttribute:(U,z)=>{if(z==="class")return U.className;if(z in U)return U[z];return U.getAttribute(z)},getTag:(U)=>{return U.nodeName},getTextContent:(U)=>{return U.textContent},children:(U)=>{return U.childNodes}},GU={mountComponent:function(U,z){return},unmountComponent:function(U){return},modelHydration:function(U,z){return}},d={nextSibling:(U)=>{if(U.nextSibling)switch(U.nextSibling.type){case 0:return S(U.nextSibling);default:return U.nextSibling.domRef}return null},createTextNode:(U)=>{return document.createTextNode(U)},createElementNS:(U,z)=>{return document.createElementNS(U,z)},appendChild:(U,z)=>{return U.appendChild(z)},replaceChild:(U,z,G)=>{return U.replaceChild(z,G)},removeChild:(U,z)=>{return U.removeChild(z)},createElement:(U)=>{return document.createElement(U)},addClass:(U,z)=>{if(U)z.classList.add(U)},removeClass:(U,z)=>{if(U)z.classList.remove(U)},insertBefore:(U,z,G)=>{return U.insertBefore(z,G)},swapDOMRefs:(U,z,G)=>{let H=U.nextSibling;G.insertBefore(U,z),G.insertBefore(z,H);return},setInlineStyle:(U,z,G)=>{var H;for(let K in U)if(H=z[K],!H)if(K in G.style)G.style[K]="";else G.style.setProperty(K,"");else if(H!==U[K])if(K in G.style)G.style[K]=H;else G.style.setProperty(K,H);for(let K in z){if(U&&U[K])continue;if(K in G.style)G.style[K]=z[K];else G.style.setProperty(K,z[K])}return},setAttribute:(U,z,G)=>{return U.setAttribute(z,G)},setAttributeNS:(U,z,G,H)=>{return U.setAttributeNS(z,G,H)},removeAttribute:(U,z)=>{return U.removeAttribute(z)},setTextContent:(U,z)=>{U.textContent=z;return},flush:()=>{return},getHead:function(){return document.head},getRoot:function(){return document.body}};function MU(U){var z=0,G=U.length>0?[U[0]]:[];for(var H=1;H<U.length;H++){if(G[z].type===2&&U[H].type===2){G[z].text+=U[H].text;continue}G[++z]=U[H]}return G}function s(U,z,G,H,K){if(!G||!z)return!1;if(z.nodeType===3)return!1;if(!r(U,G,H.firstChild(z),H,K)){if(U)console.warn("[DEBUG_HYDRATE] Could not copy DOM into virtual DOM, falling back to diff");while(H.firstChild(z))K.removeChild(z,H.lastChild(z));return!1}else if(U)console.info("[DEBUG_HYDRATE] Successfully prerendered page");return!0}function a(U,z,G){if(U)console.warn("[DEBUG_HYDRATE] VTree differed from node",z,G)}function r(U,z,G,H,K){switch(z.type){case 0:let Q=z.mount(G.parentNode);if(z.componentId=Q.componentId,z.child=Q.componentTree,Q.componentTree.parent=z,!r(U,z.child,G,H,K))return!1;break;case 2:if(G.nodeType!==3||z.text.trim()!==G.textContent.trim())return a(U,z,G),!1;z.domRef=G;break;case 1:if(G.nodeType!==1)return a(U,z,G),!1;z.domRef=G,z.children=MU(z.children),t(G,z,K);for(var X=0;X<z.children.length;X++){let Z=z.children[X],_=G.childNodes[X];if(!_)return a(U,Z,_),!1;if(!r(U,Z,_,H,K))return!1}break}return!0}globalThis.miso={hydrationContext:zU,eventContext:M,drawingContext:d,componentContext:GU,diff:A,hydrate:s,version:L,callBlur:I,callFocus:w,callSelect:V,callSetSelectionRange:O,eventJSON:B,fetchCore:P,eventSourceConnect:b,eventSourceClose:T,websocketConnect:f,websocketClose:j,websocketSend:k,updateRef:h,inline:y,typeOf:m,mathRandom:l,getRandomValues:u,splitmix32:v,populateClass:g,delegateEvent:D,delegator:M.delegator,setDrawingContext:function(U){let z=globalThis[U].drawingContext,G=globalThis[U].eventContext,H=globalThis[U].componentContext;if(!z)console.error('Custom rendering engine ("drawingContext") is not defined at globalThis[name].drawingContext',U);if(!G)console.error('Custom event delegation ("eventContext") is not defined at globalThis[name].eventContext',U);if(!H)console.error('Custom component context ("componentContext") is not defined at globalThis[name].componentContext',U);globalThis.miso.drawingContext=z,globalThis.miso.eventContext=G,globalThis.miso.componentContext=H}};+var m="1.9.0.0";function y(U,z){var G=function(){var H=document.getElementById(U);if(H&&H.focus)H.focus()};z>0?setTimeout(G,z):G()}function h(U,z){var G=function(){var H=document.getElementById(U);if(H&&H.blur)H.blur()};z>0?setTimeout(G,z):G()}function u(U,z){var G=function(){var H=document.getElementById(U);if(H&&typeof H.select==="function")H.select()};z>0?setTimeout(G,z):G()}function g(U,z,G,H){var K=function(){var Q=document.getElementById(U);if(Q&&typeof Q.setSelectionRange==="function")Q.setSelectionRange(z,G,"none")};H>0?setTimeout(K,H):K()}function v(U,z,G,H,K,Q,X){var Y={method:z,headers:H};if(G)Y.body=G;let $={},_=null;try{fetch(U,Y).then((Z)=>{_=Z.status;for(let[B,S]of Z.headers)$[B]=S;if(!Z.ok)throw Error(Z.statusText);if(X=="json")return Z.json();else if(X=="text")return Z.text();else if(X==="arrayBuffer")return Z.arrayBuffer();else if(X==="blob")return Z.blob();else if(X==="bytes")return Z.bytes();else if(X==="formData")return Z.formData();else if(X==="none")return K({error:null,body:null,headers:$,status:_})}).then((Z)=>K({error:null,body:Z,headers:$,status:_})).catch((Z)=>Q({error:null,body:Z,headers:$,status:_}))}catch(Z){Q({body:null,error:Z.message,headers:$,status:_})}}function l(U,z,G,H,K,Q,X,Y,$){try{let _=new WebSocket(U);return _.onopen=function(){z()},_.onclose=function(Z){G(Z)},_.onerror=function(Z){console.error(Z),Y("WebSocket error received")},_.onmessage=function(Z){if(typeof Z.data==="string")try{if($){if(H)H(Z.data);return}let B=JSON.parse(Z.data);if(K)K(B)}catch(B){if($&&H)H(Z.data);else Y(B.message)}else if(Z.data instanceof Blob){if(Q)Q(Z.data)}else if(Z.data instanceof ArrayBuffer){if(X)X(Z.data)}else console.error("Received unknown message type from WebSocket",Z),Y("Unknown message received from WebSocket")},_}catch(_){Y(_.message)}}function x(U){if(U)U.close(),U=null}function i(U,z){if(z&&U&&U.readyState===WebSocket.OPEN)U.send(z)}function d(U,z,G,H,K,Q){try{let X=new EventSource(U);return X.onopen=function(){z()},X.onerror=function(){K("EventSource error received")},X.onmessage=function(Y){try{if(Q){if(G)G(Y.data);return}let $=JSON.parse(Y.data);if(H)H($)}catch($){if(Q&&G)G(Y.data);else K($.message)}},X}catch(X){K(X.message)}}function p(U){if(U)U.close(),U=null}function a(U,z){if(!U.classList)U.classList=new Set;for(let G of z)for(let H of G.trim().split(" "))if(H)U.classList.add(H)}function s(U,z){if(!U.parent)return;z.nextSibling=U.nextSibling,z.parent=U.parent,U.parent.child=z}function r(U,z={}){let G=Object.keys(z),H=Object.values(z);return Function(...G,U)(...H)}function c(U){if(U===null||U===void 0)return 0;if(typeof U==="number")return 1;if(typeof U==="string")return 2;if(typeof U==="boolean")return 3;if(Array.isArray(U))return 4;return 5}function o(U){return function(){U|=0,U=U+2654435769|0;var z=U^U>>>15;return z=Math.imul(z,2246822507),z=z^z>>>13,z=Math.imul(z,3266489909),((z^z>>>16)>>>0)/4294967296}}function n(){let U=new Uint32Array(1);return crypto.getRandomValues(U)[0]}function t(){return Math.random()}function W(U,z){switch(U.type){case 3:for(let G of U.children)W(G,z);break;case 0:if(U.child)W(U.child,z);break;default:z(U.domRef);break}}function q(U){switch(U.type){case 3:{if(!U.children||U.children.length===0)return null;return q(U.children[0])}case 0:if(!U.child)return null;return q(U.child);default:return U.domRef}}function N(U){switch(U.type){case 3:{if(!U.children||U.children.length===0)return null;return N(U.children[U.children.length-1])}case 0:if(!U.child)return null;return N(U.child);default:return U.domRef}}function A(U,z,G,H){if(!U&&!z)return;else if(!U)D(z,G,H);else if(!z)O(U,G,H);else if(U.type===2&&z.type===2)JU(U,z,H);else if(U.type===0&&z.type===0){if(z.key===U.key){if(z.child=U.child,z.componentId=U.componentId,U.child)U.child.parent=z;return}V(U,z,G,H)}else if(U.type===3&&z.type===3)if(z.key===U.key){let K=N(U),Q=K?K.nextSibling:H.nextSibling(U);XU(U.children,z.children,G,H,Q)}else V(U,z,G,H);else if(U.type===1&&z.type===1)if(z.tag===U.tag&&z.key===U.key)z.domRef=U.domRef,QU(U,z,H);else V(U,z,G,H);else V(U,z,G,H)}function JU(U,z,G){if(U.text!==z.text)G.setTextContent(U.domRef,z.text);z.domRef=U.domRef;return}function V(U,z,G,H){if(U.type===3){let X=N(U),Y=X?X.nextSibling:H.nextSibling(U);if(O(U,G,H),Y)C(G,2,Y,z,H);else D(z,G,H);return}switch(U.type){case 2:break;default:P(U);break}let K=q(U),Q=N(U);if(!K||!Q){let X=H.nextSibling(U);if(X)C(G,2,X,z,H);else D(z,G,H)}else if(K!==Q){let X=Q.nextSibling;if(W(U,(Y)=>H.removeChild(G,Y)),X)C(G,2,X,z,H);else D(z,G,H)}else C(G,1,K,z,H);switch(U.type){case 2:break;default:R(U);break}}function O(U,z,G){switch(U.type){case 2:break;case 3:for(let H of U.children)O(H,z,G);return;default:P(U);break}switch(W(U,(H)=>G.removeChild(z,H)),U.type){case 2:break;default:R(U);break}}function R(U){if(U.type===3){for(let z of U.children)if(z.type!==2)R(z);return}switch(AU(U),U.type){case 1:for(let z of U.children)if(z.type!==2)R(z);break;case 0:if(U.child&&U.child.type!==2)R(U.child);break}}function AU(U){if(U.type===1&&U.onDestroyed)U.onDestroyed();if(U.type===0)wU(U)}function BU(U){switch(U.type){case 0:break;case 1:if(U.onBeforeDestroyed)U.onBeforeDestroyed();break;default:break}}function P(U){if(U.type===3){for(let z of U.children)if(z.type!==2)P(z);return}switch(BU(U),U.type){case 1:for(let z of U.children){if(z.type===2)continue;P(z)}break;case 0:if(U.child&&U.child.type!==2)P(U.child);break}}function QU(U,z,G){NU(U?U.props:{},z.props,z.domRef,z.ns==="svg",G),SU(U?U.classList:null,z.classList,z.domRef,G),CU(U?U.css:{},z.css,z.domRef,G),XU(U?U.children:[],z.children,z.domRef,G),IU(z)}function SU(U,z,G,H){if(!U&&!z)return;if(!U){for(let K of z)H.addClass(K,G);return}if(!z){for(let K of U)H.removeClass(K,G);return}for(let K of U)if(!z.has(K))H.removeClass(K,G);for(let K of z)if(!U.has(K))H.addClass(K,G);return}function NU(U,z,G,H,K){var Q;for(let X in U)if(Q=z[X],Q===void 0)if(H||!(X in G)||X==="disabled")K.removeAttribute(G,X);else K.setAttribute(G,X,"");else{if(Q===U[X]&&X!=="checked"&&X!=="value")continue;if(H)if(X==="href")K.setAttributeNS(G,"http://www.w3.org/1999/xlink","href",Q);else K.setAttribute(G,X,Q);else if(X in G&&!(X==="list"||X==="form"))G[X]=Q;else K.setAttribute(G,X,Q)}for(let X in z){if(U&&X in U)continue;if(Q=z[X],H)if(X==="href")K.setAttributeNS(G,"http://www.w3.org/1999/xlink","href",Q);else K.setAttribute(G,X,Q);else if(X in G&&!(X==="list"||X==="form"))G[X]=z[X];else K.setAttribute(G,X,Q)}}function CU(U,z,G,H){H.setInlineStyle(U,z,G)}function EU(U,z){if(U.length===0||z.length===0)return!1;for(var G=0;G<U.length;G++)if(U[G].key===null||U[G].key===void 0)return!1;for(var G=0;G<z.length;G++)if(z[G].key===null||z[G].key===void 0)return!1;return!0}function XU(U,z,G,H,K=null){if(EU(U,z))PU(U,z,G,H,K);else for(let Q=0;Q<Math.max(z.length,U.length);Q++){let X=U[Q],Y=z[Q];if(!X&&Y)if(K)C(G,2,K,Y,H);else D(Y,G,H);else A(X,Y,G,H)}}function DU(U,z){if(U.ns==="svg")U.domRef=z.createElementNS("http://www.w3.org/2000/svg",U.tag);else if(U.ns==="mathml")U.domRef=z.createElementNS("http://www.w3.org/1998/Math/MathML",U.tag);else U.domRef=z.createElement(U.tag)}function YU(U,z,G){if(z.onCreated)z.onCreated(z.domRef)}function C(U,z,G,H,K){switch(H.type){case 2:switch(H.domRef=K.createTextNode(H.text),z){case 2:K.insertBefore(U,H.domRef,G);break;case 0:K.appendChild(U,H.domRef);break;case 1:K.replaceChild(U,H.domRef,G);break}break;case 3:for(let Q of H.children)C(U,2,G,Q,K);if(z===1&&G)K.removeChild(U,G);break;case 0:MU(U,z,G,H,K);break;case 1:if(H.onBeforeCreated)H.onBeforeCreated();if(DU(H,K),H.onCreated)H.onCreated(H.domRef);switch(QU(null,H,K),z){case 2:K.insertBefore(U,H.domRef,G);break;case 0:K.appendChild(U,H.domRef);break;case 1:K.replaceChild(U,H.domRef,G);break}break}}function IU(U){if(U.tag==="canvas"&&U.draw)U.draw(U.domRef)}function wU(U){U.unmount(U.componentId)}function MU(U,z,G,H,K){let Q=H.mount(U);H.componentId=Q.componentId,H.child=Q.componentTree,Q.componentTree.parent=H;let X=q(Q.componentTree);if(Q.componentTree.type!==0){if(z===1&&G)if(!X)K.removeChild(U,G);else if(Q.componentTree.type===3)W(Q.componentTree,(Y)=>K.insertBefore(U,Y,G)),K.removeChild(U,G);else K.replaceChild(U,X,G);else if(z===2)if(G)W(Q.componentTree,(Y)=>K.insertBefore(U,Y,G));else W(Q.componentTree,(Y)=>K.appendChild(U,Y))}}function D(U,z,G){C(z,0,null,U,G)}function KU(U,z,G,H){let K=G?q(G)??H.nextSibling(G):null;if(K)W(z,(Q)=>H.insertBefore(U,Q,K));else W(z,(Q)=>H.appendChild(U,Q))}function RU(U,z,G,H){let K=q(U),Q=q(z);if(K&&Q&&(U.type===1||U.type===2)&&(z.type===1||z.type===2)){H.swapDOMRefs(K,Q,G);return}let X=N(U),Y=X?X.nextSibling:H.nextSibling(U),$=q(z)??H.nextSibling(z);if($)W(U,(_)=>H.insertBefore(G,_,$));else W(U,(_)=>H.appendChild(G,_));if(Y)W(z,(_)=>H.insertBefore(G,_,Y));else W(z,(_)=>H.appendChild(G,_))}function PU(U,z,G,H,K=null){var Q=0,X=0,Y=U.length-1,$=z.length-1,_,Z,B,S,J,f,HU;for(;;){if(X>$&&Q>Y)break;if(Z=z[X],B=z[$],J=U[Q],S=U[Y],Q>Y){let M=(J?q(J)??H.nextSibling(J):null)??K;if(M)C(G,2,M,Z,H);else D(Z,G,H);U.splice(X,0,Z),X++}else if(X>$){_=Y;while(Y>=Q)O(U[Y--],G,H);U.splice(Q,_-Q+1);break}else if(J.key===Z.key)A(U[Q++],z[X++],G,H);else if(S.key===B.key)A(U[Y--],z[$--],G,H);else if(J.key===B.key&&Z.key===S.key)RU(S,J,G,H),FU(U,Q,Y),A(U[Q++],z[X++],G,H),A(U[Y--],z[$--],G,H);else if(J.key===B.key){let I=N(S),M=I?I.nextSibling:H.nextSibling(S);if(M)W(J,(T)=>H.insertBefore(G,T,M));else W(J,(T)=>H.appendChild(G,T));U.splice(Y,0,U.splice(Q,1)[0]),A(U[Y--],z[$--],G,H)}else if(S.key===Z.key)KU(G,S,J,H),U.splice(Q,0,U.splice(Y,1)[0]),A(U[Q++],Z,G,H),X++;else{f=!1,_=Q;while(_<=Y){if(U[_].key===Z.key){f=!0,HU=U[_];break}_++}if(f)U.splice(Q,0,U.splice(_,1)[0]),A(U[Q++],Z,G,H),KU(G,HU,U[Q],H),X++;else{let I=q(J)??H.nextSibling(J);if(I)C(G,2,I,Z,H);else D(Z,G,H);U.splice(Q++,0,Z),X++,Y++}}}}function FU(U,z,G){let H=U[z];U[z]=U[G],U[G]=H}function e(U,z,G,H,K){for(let Q of z)K.addEventListener(U,Q.name,function(X){LU(X,U,G,H,K)},Q.capture)}function LU(U,z,G,H,K){G(function(Q){if(Array.isArray(U))for(let X of U)ZU(X,Q,z,H,K);else ZU(U,Q,z,H,K)})}function ZU(U,z,G,H,K){var Q=K.getTarget(U);if(Q){let X=VU(G,Q,K);E(U,z,X,H,K)}}function VU(U,z,G){var H=[];while(!G.isEqual(U,z))if(H.unshift(z),z&&G.parentNode(z))z=G.parentNode(z);else return H;return H}function E(U,z,G,H,K){if(!G.length){if(H)console.warn('Event "'+U.type+'" did not find an event handler to dispatch on',z,U);return}else if(G.length>1){if(z.type===2)return;else if(z.type===3){for(let Q of z.children)if(F(Q,G[0],K)){E(U,Q,G,H,K);return}return}else if(z.type===0){if(!z.child){if(H)throw console.error("VComp has no child property set during event delegation",z),console.error("This means the Component has not been fully mounted, this should never happen"),Error("VComp has no .child property set during event delegation");return}return E(U,z.child,G,H,K)}else if(z.type===1){if(K.isEqual(z.domRef,G[0])){let Q=z.events.captures[U.type];if(Q){let X=Q.options;if(X.preventDefault)U.preventDefault();if(!U.captureStopped)Q.runEvent(U,z.domRef);if(X.stopPropagation)U.captureStopped=!0}G.splice(0,1);for(let X of z.children)if(F(X,G[0],K)){E(U,X,G,H,K);return}}return}}else if(z.type===0){if(z.child)E(U,z.child,G,H,K)}else if(z.type===3){for(let Q of z.children)if(F(Q,G[0],K)){E(U,Q,G,H,K);return}}else if(z.type===1){let Q=z.events.captures[U.type];if(Q&&!U.captureStopped){let Y=Q.options;if(K.isEqual(G[0],z.domRef)){if(Y.preventDefault)U.preventDefault();if(Q.runEvent(U,G[0]),Y.stopPropagation)U.captureStopped=!0}}let X=z.events.bubbles[U.type];if(X&&!U.captureStopped){let Y=X.options;if(K.isEqual(G[0],z.domRef)){if(Y.preventDefault)U.preventDefault();if(X.runEvent(U,G[0]),!Y.stopPropagation)$U(z.parent,U)}}else if(!U.captureStopped)$U(z.parent,U)}}function $U(U,z){while(U)switch(U.type){case 2:break;case 3:U=U.parent;break;case 1:let G=U.events.bubbles[z.type];if(G){let H=G.options;if(H.preventDefault)z.preventDefault();if(G.runEvent(z,U.domRef),H.stopPropagation)return}U=U.parent;break;case 0:if(!U.eventPropagation)return;U=U.parent;break}}function L(U,z){if(typeof U[0]==="object"){var G=[];for(var H=0;H<U.length;H++)G.push(L(U[H],z));return G}for(let Y of U)z=z[Y];var K;if(z instanceof Array||"length"in z&&z.localName!=="select"){K=[];for(var Q=0;Q<z.length;Q++)K.push(L([],z[Q]));return K}K={};for(var X in OU(z)){if(z.localName==="input"&&(X==="selectionDirection"||X==="selectionStart"||X==="selectionEnd"))continue;if(typeof z[X]=="string"||typeof z[X]=="number"||typeof z[X]=="boolean")K[X]=z[X]}return K}function F(U,z,G){switch(U.type){case 3:for(let H of U.children)if(F(H,z,G))return!0;return!1;case 0:return U.child?F(U.child,z,G):!1;default:return G.isEqual(U.domRef,z)}}function OU(U){var z={},G=0;do{var H=Object.getOwnPropertyNames(U);for(G=0;G<H.length;G++)z[H[G]]=null}while(U=Object.getPrototypeOf(U));return z}var j={addEventListener:(U,z,G,H)=>{U.addEventListener(z,G,H)},delegator:(U,z,G,H,K)=>{e(U,z,G,H,K)},isEqual:(U,z)=>{return U===z},getTarget:(U)=>{return U.target},parentNode:(U)=>{return U.parentNode}},_U={getInlineStyle:(U,z)=>{return U.style[z]},firstChild:(U)=>{return U.firstChild},lastChild:(U)=>{return U.lastChild},getAttribute:(U,z)=>{if(z==="class")return U.className;if(z in U)return U[z];return U.getAttribute(z)},getTag:(U)=>{return U.nodeName},getTextContent:(U)=>{return U.textContent},children:(U)=>{return U.childNodes}},WU={mountComponent:function(U,z){return},unmountComponent:function(U){return},modelHydration:function(U,z){return}},UU={nextSibling:(U)=>{let z=U.nextSibling;while(z)switch(z.type){case 0:case 3:{let G=q(z);if(G)return G;z=z.nextSibling;break}default:return z.domRef}return null},createTextNode:(U)=>{return document.createTextNode(U)},createElementNS:(U,z)=>{return document.createElementNS(U,z)},appendChild:(U,z)=>{return U.appendChild(z)},replaceChild:(U,z,G)=>{return U.replaceChild(z,G)},removeChild:(U,z)=>{return U.removeChild(z)},createElement:(U)=>{return document.createElement(U)},addClass:(U,z)=>{if(U)z.classList.add(U)},removeClass:(U,z)=>{if(U)z.classList.remove(U)},insertBefore:(U,z,G)=>{return U.insertBefore(z,G)},swapDOMRefs:(U,z,G)=>{let H=U.nextSibling;G.insertBefore(U,z),G.insertBefore(z,H);return},setInlineStyle:(U,z,G)=>{var H;for(let K in U)if(H=z[K],!H)if(K in G.style)G.style[K]="";else G.style.setProperty(K,"");else if(H!==U[K])if(K in G.style)G.style[K]=H;else G.style.setProperty(K,H);for(let K in z){if(U&&U[K])continue;if(K in G.style)G.style[K]=z[K];else G.style.setProperty(K,z[K])}return},setAttribute:(U,z,G)=>{return U.setAttribute(z,G)},setAttributeNS:(U,z,G,H)=>{return U.setAttributeNS(z,G,H)},removeAttribute:(U,z)=>{return U.removeAttribute(z)},setTextContent:(U,z)=>{U.textContent=z;return},flush:()=>{return},getHead:function(){return document.head},getRoot:function(){return document.body}};function zU(U){var z=0,G=U.length>0?[U[0]]:[];for(var H=1;H<U.length;H++){if(G[z].type===2&&U[H].type===2){G[z].text+=U[H].text;continue}G[++z]=U[H]}for(let K of G)if(K.type===3)K.children=zU(K.children);return G}function GU(U,z,G,H,K){if(!G||!z)return!1;if(z.nodeType===3)return!1;if(!b(U,G,H.firstChild(z),H,K)){if(U)console.warn("[DEBUG_HYDRATE] Could not copy DOM into virtual DOM, falling back to diff");while(H.firstChild(z))K.removeChild(z,H.lastChild(z));return!1}else if(U)console.info("[DEBUG_HYDRATE] Successfully prerendered page");return!0}function k(U,z,G){if(U)console.warn("[DEBUG_HYDRATE] VTree differed from node",z,G)}function qU(U,z){let G=N(U);return G?G.nextSibling:z}function b(U,z,G,H,K){switch(z.type){case 0:let X=z.mount(G.parentNode);if(z.componentId=X.componentId,z.child=X.componentTree,X.componentTree.parent=z,!b(U,z.child,G,H,K))return!1;break;case 3:z.children=zU(z.children);for(let $ of z.children){if(!G)return k(U,$,null),!1;if(!b(U,$,G,H,K))return!1;G=qU($,G)}break;case 2:if(G.nodeType!==3||z.text.trim()!==G.textContent.trim())return k(U,z,G),!1;z.domRef=G;break;case 1:if(G.nodeType!==1)return k(U,z,G),!1;z.domRef=G,z.children=zU(z.children),YU(G,z,K);let Y=G.firstChild;for(var Q=0;Q<z.children.length;Q++){let $=z.children[Q];if(!Y)return k(U,$,null),!1;if(!b(U,$,Y,H,K))return!1;Y=qU($,Y)}break}return!0}globalThis.miso={hydrationContext:_U,eventContext:j,drawingContext:UU,componentContext:WU,diff:A,hydrate:GU,version:m,callBlur:h,callFocus:y,callSelect:u,callSetSelectionRange:g,eventJSON:L,fetchCore:v,eventSourceConnect:d,eventSourceClose:p,websocketConnect:l,websocketClose:x,websocketSend:i,updateRef:s,inline:r,typeOf:c,mathRandom:t,getRandomValues:n,splitmix32:o,populateClass:a,delegateEvent:E,delegator:j.delegator,setDrawingContext:function(U){let z=globalThis[U].drawingContext,G=globalThis[U].eventContext,H=globalThis[U].componentContext;if(!z)console.error('Custom rendering engine ("drawingContext") is not defined at globalThis[name].drawingContext',U);if(!G)console.error('Custom event delegation ("eventContext") is not defined at globalThis[name].eventContext',U);if(!H)console.error('Custom component context ("componentContext") is not defined at globalThis[name].componentContext',U);globalThis.miso.drawingContext=z,globalThis.miso.eventContext=G,globalThis.miso.componentContext=H}};
miso.cabal view
@@ -1,6 +1,6 @@ cabal-version:       2.2 name:                miso-version:             1.9.0.0+version:             1.10.0.0 category:            Web, Miso, Data Structures license:             BSD-3-Clause license-file:        LICENSE@@ -135,6 +135,7 @@     Miso.JSON.Parser     Miso.JSON.Types     Miso.Lens+    Miso.Lens.Generic     Miso.Mathml     Miso.Mathml.Element     Miso.Mathml.Property@@ -216,6 +217,6 @@     base          < 5,     bytestring    < 0.13,     containers    < 0.9,-    transformers  < 0.6.2.0,+    transformers  < 0.7,     mtl           < 2.4,     text          < 2.2
src/Miso.hs view
@@ -76,8 +76,41 @@ -- * __update__: @'update' :: action -> 'Effect' parent model action@ --   The 'update' function handles how the 'model' evolves over time in response --   to events that are raised by the application. This function takes any @action@,---   updating the @model@ and optionally introducing 'IO' into the system.+--   updating the @model@ and optionally introduces 'IO' into the system. --+-- = t'View' DSL+--+-- The 'View' type is the core type for both templating and adding interactivity to a web page. It is similar to+-- a [Rose tree](https://en.wikipedia.org/wiki/Rose_tree) data structure. This is how the Virtual DOM is constructed. It is+-- mutually recursive with the 'Component' type (via the 'view' function), which allows us to embed+-- 'Component' inside other 'Component'.+--+-- @+-- data 'View' model action+--   = 'VNode' 'Namespace' 'Tag' ['Attribute' action] ['View' model action]+--   | 'VText' (Maybe 'Key') 'MisoString'+--   | 'VComp' (Maybe 'Key') ('SomeComponent' model)+--   | 'VFrag' (Maybe 'Key') ['View' model action]+-- @+--+-- 'VNode' and 'VText' have a one-to-one mapping from the virtual DOM to the physical DOM. The 'VComp' and 'VFrag' constructors are abstract (live only on the virtual DOM) and do not contain a reference to the physical DOM. The existential type of 'SomeComponent' is defined recursively in terms of 'View' and is what allows us to embed other polymorphic 'Component'.+--+-- @+-- data 'SomeComponent' parent+--   = forall model action . Eq model+--   => 'SomeComponent' ('Component' parent model action)+-- @+--+-- The smart constructors:+--+-- * 'node', 'vnode'+-- * 'text', 'vtext'+-- * 'component', 'vcomp'+-- * 'fragment_', 'fragment', 'vfrag', 'vfrag_'+-- * ('+>')+--+-- are used to build 'VNode', 'VText', 'VFrag' and 'VComp' respectively. A list of all the smart constructors defined in terms of 'node' (e.g. 'Miso.Html.Element.div_') can be found in "Miso.Html.Element".+-- -- = Your first t'Component' -- -- To define a 'Component' the 'component' smart constructor can be used.@@ -109,7 +142,7 @@ --       Subtract -> 'this' -= 1 -- --     v :: Int -> 'View' Int Action---     v x = H.div_+--     v x = 'vfrag' --       [ H.button_ [ HE.onClick Add, HP.id_ "add" ] [ "+" ] --       , text (ms x) --       , H.button_ [ HE.onClick Subtract, HP.id_ "subtract" ] [ "-" ]@@ -256,6 +289,38 @@ -- -- As a convention, the @*with@ variant of 'VNode' lifecycle hooks (e.g. 'Miso.Event.onCreatedWith') provide the target 'DOMRef' in the callback function (shown above). --+-- = 'VFrag' (Fragment nodes)+--+-- Similar to the [React Fragment](https://react.dev/reference/react/Fragment) API (@\<Fragment\>@ or @\<\>\<\/\>@ syntax), and to @DocumentFragment@ in the browser DOM API. @miso@ provides a 'VFrag' constructor for grouping together sibling nodes without introducing an extra wrapper element in the DOM.+--+-- @+-- -- Renders two \<li\> elements as direct siblings, no enclosing element+-- 'fragment' [ 'li_' [] [ 'text' "Item A" ], 'li_' [] [ 'text' "Item B" ] ]+-- @+--+-- A 'VFrag' may optionally carry a 'Key'. Keyed fragments participate in the same+-- reconciliation algorithm as keyed 'VNode' and 'VText' nodes, allowing the virtual+-- DOM differ to identify, reorder, and reuse groups of siblings efficiently.+--+-- @+-- -- Keyed fragment — survives reordering without full teardown\/remount+-- 'vfrag_' "my-key" [ 'li_' [] [ 'text' "A" ], 'li_' [] [ 'text' "B" ] ]+-- @+--+-- Fragments may be nested — a 'VFrag' child may itself be a 'VFrag'. The diff function+-- recurses into nested fragments and processes all fragments as if they were+-- flat sequence of sibling DOM nodes, so nesting carries no runtime cost beyond the extra 'VFrag' constructor allocation.+--+-- Empty fragments (@'fragment' []@) in child nodes are erased from the virtual DOM tree in the+-- Haskell layer before they reach diffing in JavaScript and are therefore a no-op.+--+-- The smart constructors for 'VFrag' are:+--+-- * 'fragment'   — unkeyed fragment+-- * 'vfrag'      — unkeyed fragment (alias)+-- * 'fragment_'  — keyed fragment+-- * 'vfrag_'     — keyed fragment (alias, infix-friendly: @\"key\" \`vfrag_\` [...]@)+-- -- = 'Key' -- -- A 'Key' is a unique identifier used to optimize diffing.@@ -279,40 +344,10 @@ --   , 'li_' [ 'key_' "key-2" ] [ "b" ] --   , "key-3" '+>' counter --   , 'textKey' "key-4" "text here"+--   , vfrag_ "key-5" [ "foo", "bar" ] --   ] -- @ ----- = t'View' DSL------ The 'View' type is the core type for templating a web page. It is similar to--- a [Rose tree](https://en.wikipedia.org/wiki/Rose_tree) data structure. This is how the Virtual DOM is constructed. It is--- mutually recursive with the 'Component' type (via the 'view' function), which allows us to embed--- 'Component' inside other 'Component'.------ @--- data 'View' model action---   = 'VNode' 'Namespace' 'Tag' ['Attribute' action] ['View' model action]---   | 'VText' (Maybe 'Key') 'MisoString'---   | 'VComp' (Maybe 'Key') ('SomeComponent' model)--- @------ 'VNode' and 'VText' have a one-to-one mapping from the virtual DOM to the physical DOM. The 'VComp' constructor is abstract and does not contain a reference to the physical DOM. The existential type of 'SomeComponent' is defined recursively in terms of 'View' and is what allows us to embed other polymorphic 'Component'.------ @--- data 'SomeComponent' parent---   = forall model action . Eq model---   => 'SomeComponent' ('Component' parent model action)--- @------ The smart constructors:------ * 'node', 'vnode'--- * 'text', 'vtext'--- * 'component', 'vcomp'--- * ('+>')------ are used to build 'VNode', 'VText' and 'VComp' respectively. A list of all the smart constructors defined in terms of 'node' (e.g. 'Miso.Html.Element.div_') can be found in "Miso.Html.Element".--- -- = 'Events' -- -- * Event Delegation@@ -416,7 +451,7 @@ -- type 'Sink' a => a -> 'IO' () -- @ ----- The 'Sink' function allows one to write any @action@ to the global event queue.+-- The 'Sink' function allows one to write any @action@ to the global event queue. See 'withSink' for more information. -- -- == Managing 'model' state. --@@ -512,7 +547,7 @@ -- 'Miso.Subscription.Util.createSub' is a helper function for creating a 'Sub' using the 'Control.Exception.bracket' pattern. -- This ensures that event listeners can be unregistered when a 'Component' unmounts. For example usage -- please see the "Miso.Subscription" sub modules. 'createSub' is only meant to be used in scenarios where--- custom event listeners are required (shown below).+-- custom event listeners are required. -- -- = (2D/3D) Canvas support --@@ -632,14 +667,14 @@ -- A simple example of static prerendering would be an @index.html@ page with some HTML -- -- @--- echo "<html><head><//head><body>hello world<//body>" > index.html+-- echo "\<html\>\<head\>\<\/head\>\<body\>hello world\<\/body\>" > index.html -- @ -- -- And a miso application that looks like: -- -- @ -- main :: IO ()--- main = prerender defaultEvents $ (vcomp () noop $ \() -> "hello world") { logLevel = DebugPrerender }+-- main = 'prerender' 'defaultEvents' $ ('vcomp' () 'noop' $ \\() -> "hello world") { 'logLevel' = 'DebugPrerender' } -- @ -- -- Assuming the JS / WASM payload and @index.html@ are delivered together from the web server, the console should output below@@ -684,6 +719,7 @@   , checkMail   , parent   , mailParent+  , mailChildren   , broadcast     -- ** Subscriptions   , startSub
src/Miso/Binding.hs view
@@ -32,11 +32,16 @@ module Miso.Binding   ( -- ** Types     Binding (..)+  , Precedence (..)     -- ** Combinators   , (<-->)+  , (<<-->)+  , (<-->>)   , (<--)   , (-->)   , (<--->)+  , (<<--->)+  , (<--->>)   , (<---)   , (--->)   ) where@@ -62,8 +67,13 @@ data Binding parent child   = forall field . ParentToChild (parent -> field) (field -> child -> child)   | forall field . ChildToParent (field -> parent -> parent) (child -> field)-  | forall field . Bidirectional (parent -> field) (field -> parent -> parent) (child -> field) (field -> child -> child)+  | forall field . Bidirectional Precedence (parent -> field) (field -> parent -> parent) (child -> field) (field -> child -> child) -----------------------------------------------------------------------------+-- | Data type used to express if the Child state should take precendence+-- over the parent state during 'Component' mount.+data Precedence = Child | Parent+  deriving (Eq, Show)+----------------------------------------------------------------------------- -- | Unidirectionally binds a parent field to a child field -- -- @since 1.9.0.0@@ -85,7 +95,7 @@ -- @since 1.9.0.0 infix 0 <--> (<-->) :: Lens parent field -> Lens child field -> Binding parent child-p <--> c = Bidirectional (_get p) (_set p) (_get c) (_set c)+p <--> c = Bidirectional Parent (_get p) (_set p) (_get c) (_set c) ----------------------------------------------------------------------------- -- | Bidirectionally binds a child field to a parent field, using @Lens'@ --@@ -94,11 +104,35 @@ -- @since 1.9.0.0 infix 0 <---> (<--->) :: Lens' parent field -> Lens' child field -> Binding parent child-p <---> c = Bidirectional (get_ p) (set_ p) (get_ c) (set_ c)+p <---> c = Bidirectional Parent (get_ p) (set_ p) (get_ c) (set_ c)   where     get_ lens_ record = getConst (lens_ Const record)     set_ lens_ field = runIdentity . lens_ (\_ -> Identity field) -----------------------------------------------------------------------------+-- | Like '<--->' but biases to inherit 'Parent' state on 'Component' 'mount'.+--+-- @since 1.10.0.0+(<--->>)+  :: Lens' parent field+  -> Lens' child field+  -> Binding parent child+l <--->> r =+  case l <---> r of+    Bidirectional _ w x y z -> Bidirectional Parent w x y z+    _ -> error "impossible"+-----------------------------------------------------------------------------+-- | Like '<--->' but biases to inherit 'Child' state on 'Component' 'mount'.+--+-- @since 1.10.0.0+(<<--->)+  :: Lens' parent field+  -> Lens' child field+  -> Binding parent child+l <<---> r =+  case l <---> r of+    Bidirectional _ w x y z -> Bidirectional Child w x y z+    _ -> error "impossible"+----------------------------------------------------------------------------- -- | Unidirectionally binds a parent field to a child field, for van Laarhoven -- style @Lens'@ --@@ -109,6 +143,30 @@   where     get_ lens_ record = getConst (lens_ Const record)     set_ lens_ field = runIdentity . lens_ (\_ -> Identity field)+-----------------------------------------------------------------------------+-- | Like '<-->' but biases to inherit 'Parent' state on 'Component' 'mount'.+--+-- @since 1.10.0.0+(<-->>)+  :: Lens parent field+  -> Lens child field+  -> Binding parent child+l <-->> r =+  case l <--> r of+    Bidirectional _ w x y z -> Bidirectional Parent w x y z+    _ -> error "impossible"+-----------------------------------------------------------------------------+-- | Like '<-->' but biases to inherit 'Child' state on 'Component' 'mount'.+--+-- @since 1.10.0.0+(<<-->)+  :: Lens parent field+  -> Lens child field+  -> Binding parent child+l <<--> r =+  case l <--> r of+    Bidirectional _ w x y z -> Bidirectional Child w x y z+    _ -> error "impossible" ----------------------------------------------------------------------------- -- | Unidirectionally binds a child field to a parent field, for van Laarhoven -- style @Lens'@
src/Miso/Html/Element.hs view
@@ -1,4 +1,6 @@ -----------------------------------------------------------------------------+{-# LANGUAGE CPP               #-}+{-# LANGUAGE LambdaCase        #-} {-# LANGUAGE OverloadedStrings #-} ----------------------------------------------------------------------------- -- |@@ -146,6 +148,9 @@     , svg_     ) where -----------------------------------------------------------------------------+#ifdef VANILLA+import           Miso.JSON (Value(String))+#endif import           Miso.Types ----------------------------------------------------------------------------- import           Miso.Svg.Element (svg_)@@ -496,8 +501,26 @@ option_ = nodeHtml "option" ----------------------------------------------------------------------------- -- | [\<textarea\>](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/textarea)-textarea_ :: [Attribute action] -> [View model action] -> View model action-textarea_ = nodeHtml "textarea"+--+-- @+-- textarea_ [ id_ "txt", P.value_ (model ^. txt) ]+-- @+--+-- When compiling on the server, this combinator will render HTML as \<textarea\>text\<\/textarea\>.+--+-- @since 1.9.0.0+textarea_ :: [Attribute action] -> View model action+#ifdef VANILLA+textarea_ attrs = nodeHtml "textarea" newAttrs+  [ text x+  | Property "value" (String x) <- attrs+  ] where+      newAttrs = flip filter attrs $ \case+        Property "value" _ -> False+        _ -> True+#else+textarea_ = flip (nodeHtml "textarea") []+#endif ----------------------------------------------------------------------------- -- | [\<sub\>](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/sub) sub_ :: [Attribute action] -> [View model action] -> View model action@@ -597,14 +620,14 @@ -- | [\<area\>](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/area) -- -- @since 1.9.0.0-area_ :: [Attribute action] -> [View model action] -> View model action-area_ = nodeHtml "area"+area_ :: [Attribute action] -> View model action+area_ = flip (nodeHtml "area") [] ----------------------------------------------------------------------------- -- | [\<base\>](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/base) -- -- @since 1.9.0.0-base_ :: [Attribute action] -> [View model action] -> View model action-base_ = nodeHtml "base"+base_ :: [Attribute action] -> View model action+base_ = flip (nodeHtml "base") [] ----------------------------------------------------------------------------- -- | [\<data\>](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/data) --
src/Miso/Html/Event.hs view
@@ -35,6 +35,7 @@   , onEnter   -- *** Form   , onInput+  , onInputWith   , onChange   , onChangeWith   , onChecked@@ -199,6 +200,10 @@ -- | https://developer.mozilla.org/en-US/docs/Web/Events/input onInput :: (MisoString -> action) -> Attribute action onInput f = on "input" valueDecoder (\action _ -> f action)+-----------------------------------------------------------------------------+-- | https://developer.mozilla.org/en-US/docs/Web/Events/input+onInputWith :: (MisoString -> DOMRef -> action) -> Attribute action+onInputWith = on "input" valueDecoder ----------------------------------------------------------------------------- -- | https://developer.mozilla.org/en-US/docs/Web/Events/change onChange :: (MisoString -> action) -> Attribute action
src/Miso/Html/Render.hs view
@@ -139,6 +139,7 @@ #else       vkids = [ unsafeCoerce $ (view vcomp_) (model vcomp_) ] #endif+renderBuilder (VFrag _ kids) = foldMap renderBuilder kids ---------------------------------------------------------------------------- renderAttrs :: Attribute action -> Builder renderAttrs (ClassList classes) =
src/Miso/JSON.hs view
@@ -7,11 +7,11 @@ {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE DefaultSignatures          #-} {-# LANGUAGE OverloadedStrings          #-}+{-# LANGUAGE TypeApplications           #-}+{-# LANGUAGE AllowAmbiguousTypes        #-} {-# LANGUAGE FlexibleInstances          #-} {-# LANGUAGE ScopedTypeVariables        #-}-#if __GLASGOW_HASKELL__ <= 865 {-# LANGUAGE UndecidableInstances       #-}-#endif {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# OPTIONS_GHC -Wno-orphans #-} -----------------------------------------------------------------------------@@ -82,8 +82,14 @@   , defaultOptions   -- * Generics   , GToJSON (..)-  , genericToJSON+  , GToFields (..)+  , GToJSONSum (..)+  , GAllNullary (..)+  , Fields (..)   , GFromJSON (..)+  , GFromFields (..)+  , GFromJSONSum (..)+  , genericToJSON   , genericParseJSON   -- * Modifiers   , camelTo2@@ -119,11 +125,10 @@ import           Miso.JSON.Types import qualified Miso.JSON.Parser as Parser ----------------------------------------------------------------------------+import           Numeric (showHex) #ifndef VANILLA import           Control.Monad.Trans.Maybe import           System.IO.Unsafe (unsafePerformIO)-#else-import           Numeric (showHex) #endif  ----------------------------------------------------------------------------@@ -161,15 +166,32 @@   toJSON = genericToJSON defaultOptions ---------------------------------------------------------------------------- genericToJSON :: (Generic a, GToJSON (Rep a)) => Options -> a -> Value-genericToJSON opts = object . gToJSON opts [] . from+genericToJSON opts = gToJSON opts . from ---------------------------------------------------------------------------- data Options   = Options-  { fieldLabelModifier :: String -> String+  { fieldLabelModifier    :: String -> String+  -- ^ Modify record field names before encoding (default: identity).+  , constructorTagModifier :: String -> String+  -- ^ Modify constructor names used as tags before encoding (default: identity).+  , allNullaryToStringTag :: Bool+  -- ^ When 'True' (the default, matching aeson) and every constructor of a+  -- sum type is nullary, encode/decode each constructor as a bare JSON+  -- 'String' (e.g. @\"Red\"@) rather than a tagged object+  -- (e.g. @{\"tag\":\"Red\"}@).+  , omitNothingFields :: Bool+  -- ^ When 'True', record fields whose value is 'Nothing' are omitted from+  -- the encoded object entirely. When 'False' (the default, matching aeson)+  -- they are encoded as @null@.   } ---------------------------------------------------------------------------- defaultOptions :: Options-defaultOptions = Options { fieldLabelModifier = \x -> x }+defaultOptions = Options+  { fieldLabelModifier     = \x -> x+  , constructorTagModifier = \x -> x+  , allNullaryToStringTag  = True+  , omitNothingFields      = False+  } ---------------------------------------------------------------------------- camelTo2 :: Char -> String -> String camelTo2 c = Prelude.map toLower . go2 . go1@@ -180,35 +202,171 @@           go2 (l:u:xs) | isLower l && isUpper u = l : c : u : go2 xs           go2 (x:xs) = x : go2 xs ----------------------------------------------------------------------------+-- | Intermediate representation of a constructor's fields after encoding.+--+-- 'RecordFields' is produced when every selector has a name (record syntax);+-- 'PositionalFields' is produced for all other constructors.+data Fields+  = RecordFields   [(MisoString, Value)]+  -- ^ Named fields (record constructor)+  | PositionalFields [Value]+  -- ^ Positional fields (non-record constructor)+----------------------------------------------------------------------------+combineFields :: Fields -> Fields -> Fields+combineFields (RecordFields   xs) (RecordFields   ys) = RecordFields   (xs <> ys)+combineFields (PositionalFields xs) (PositionalFields ys) = PositionalFields (xs <> ys)+combineFields _ _ = PositionalFields []  -- mixed; shouldn't occur in valid GHC Generics+----------------------------------------------------------------------------+-- | Collect a constructor's fields into 'Fields'.+class GToFields (f :: Type -> Type) where+  gToFields :: Options -> f a -> Fields+----------------------------------------------------------------------------+instance GToFields U1 where+  gToFields _ _ = PositionalFields []+----------------------------------------------------------------------------+instance GToFields V1 where+  gToFields _ v = v `seq` PositionalFields []+----------------------------------------------------------------------------+instance (GToFields f, GToFields g) => GToFields (f :*: g) where+  gToFields opts (x :*: y) = combineFields (gToFields opts x) (gToFields opts y)+----------------------------------------------------------------------------+instance (Selector m, GToFields f) => GToFields (S1 m f) where+  gToFields opts (M1 x) =+    let n = selName (M1 undefined :: S1 m f ())+    in if null n+       then gToFields opts x+       else case gToFields opts x of+              PositionalFields [v] -> RecordFields [(ms (fieldLabelModifier opts n), v)]+              fs                   -> fs  -- shouldn't happen+----------------------------------------------------------------------------+instance ToJSON a => GToFields (K1 r a) where+  gToFields _ (K1 x) = PositionalFields [toJSON x]+----------------------------------------------------------------------------+-- | Special 'GToFields' instance for @'Maybe' a@ fields that honours+-- 'omitNothingFields': when the option is 'True' and the value is+-- 'Nothing', the field is omitted from the encoded object entirely.+instance {-# OVERLAPPING #-} (Selector m, ToJSON a)+    => GToFields (S1 m (K1 r (Maybe a))) where+  gToFields opts (M1 (K1 mx)) =+    let n   = selName (M1 undefined :: S1 m (K1 r (Maybe a)) ())+        key = ms (fieldLabelModifier opts n)+    in if null n+       then PositionalFields [toJSON mx]+       else case mx of+              Nothing | omitNothingFields opts -> RecordFields []+              _                                -> RecordFields [(key, toJSON mx)]+----------------------------------------------------------------------------+-- | Determine at the type level whether every constructor of a sum type+-- is nullary (has no fields). Used to implement 'allNullaryToStringTag'.+class GAllNullary (f :: Type -> Type) where+  gAllNullary :: Bool+----------------------------------------------------------------------------+instance GAllNullary U1 where+  gAllNullary = True+----------------------------------------------------------------------------+instance GAllNullary (K1 r a) where+  gAllNullary = False+----------------------------------------------------------------------------+instance (GAllNullary f, GAllNullary g) => GAllNullary (f :*: g) where+  gAllNullary = False  -- has multiple fields, definitely not nullary+----------------------------------------------------------------------------+instance GAllNullary f => GAllNullary (S1 m f) where+  gAllNullary = gAllNullary @f+----------------------------------------------------------------------------+instance GAllNullary f => GAllNullary (C1 m f) where+  gAllNullary = gAllNullary @f+----------------------------------------------------------------------------+instance (GAllNullary f, GAllNullary g) => GAllNullary (f :+: g) where+  gAllNullary = gAllNullary @f && gAllNullary @g+----------------------------------------------------------------------------+-- | Encode a single-constructor (product) type. No tag is added.+--+-- * Record:       @{"field1": v, ...}@+-- * 0 fields:     @[]@+-- * 1 field:      the value itself (unwrapped, like a newtype)+-- * 2+ fields:    @[v1, v2, ...]@+encodeProduct :: Fields -> Value+encodeProduct = \case+  RecordFields   kvs  -> Object (M.fromList kvs)+  PositionalFields []  -> Array []+  PositionalFields [v] -> v+  PositionalFields vs  -> Array vs+----------------------------------------------------------------------------+-- | Encode a sum constructor. Adds a @\"tag\"@ key.+--+-- * Record:       @{\"tag\": \"C\", \"field1\": v, ...}@+-- * 0 fields:     @{\"tag\": \"C\"}@+-- * 1 field:      @{\"tag\": \"C\", \"contents\": v}@+-- * 2+ fields:    @{\"tag\": \"C\", \"contents\": [v1, v2, ...]}@+encodeTaggedCon :: MisoString -> Fields -> Value+encodeTaggedCon tag = \case+  RecordFields   kvs  -> Object (M.fromList (("tag", String tag) : kvs))+  PositionalFields []  -> Object (M.singleton "tag" (String tag))+  PositionalFields [v] -> object [("tag", String tag), ("contents", v)]+  PositionalFields vs  -> object [("tag", String tag), ("contents", Array vs)]+----------------------------------------------------------------------------+-- | Top-level generic encoding class.+--+-- Encoding rules match aeson's defaults:+--+-- * All-nullary sum + 'allNullaryToStringTag':  @\"C\"@+-- * Single-constructor record:                  @{\"field1\": v1, ...}@+-- * Single-constructor positional:              @v@ (1 field), @[v1,v2,...]@ (n>1), @[]@ (0)+-- * Sum record constructor:                     @{\"tag\": \"C\", \"field1\": v1, ...}@+-- * Sum nullary constructor:                    @{\"tag\": \"C\"}@+-- * Sum positional constructor:                 @{\"tag\": \"C\", \"contents\": v}@ or @[...]@ class GToJSON (f :: Type -> Type) where-  gToJSON :: Options -> [Pair] -> f a -> [Pair]+  gToJSON :: Options -> f a -> Value -----------------------------------------------------------------------------instance GToJSON a => GToJSON (D1 i a) where-  gToJSON opts acc (M1 x) = gToJSON opts acc x+instance GToJSONRep f => GToJSON (D1 m f) where+  gToJSON opts (M1 x) = gToJSONRep opts x -----------------------------------------------------------------------------instance GToJSON a => GToJSON (C1 i a) where-  gToJSON opts acc (M1 x) = gToJSON opts acc x+-- Internal: dispatches single-constructor vs sum at the child of D1.+class GToJSONRep (f :: Type -> Type) where+  gToJSONRep :: Options -> f a -> Value+-- Single constructor: no tag+instance GToFields f => GToJSONRep (C1 m f) where+  gToJSONRep opts (M1 x) = encodeProduct (gToFields opts x)+-- Sum: branch on allNullaryToStringTag+instance (GToJSONSum f, GToJSONSum g, GToJSONSumNullary f, GToJSONSumNullary g, GAllNullary f, GAllNullary g)+    => GToJSONRep (f :+: g) where+  gToJSONRep opts x+    | allNullaryToStringTag opts && gAllNullary @f && gAllNullary @g+    = gToJSONSumNullary opts x+    | otherwise+    = gToJSONSum opts x -----------------------------------------------------------------------------instance (GToJSON a, GToJSON b) => GToJSON (a :*: b) where-  gToJSON opts acc (x :*: y) = gToJSON opts acc x <> gToJSON opts acc y+-- | Encode all-nullary sum constructors as bare 'String' values.+class GToJSONSumNullary (f :: Type -> Type) where+  gToJSONSumNullary :: Options -> f a -> Value -----------------------------------------------------------------------------instance (TypeError ('Text "Sum types unsupported"), GToJSON a, GToJSON b) => GToJSON (a :+: b) where-  gToJSON opts acc = \case-    L1 x -> gToJSON opts acc x-    R1 x -> gToJSON opts acc x+instance (GToJSONSumNullary f, GToJSONSumNullary g) => GToJSONSumNullary (f :+: g) where+  gToJSONSumNullary opts (L1 x) = gToJSONSumNullary opts x+  gToJSONSumNullary opts (R1 x) = gToJSONSumNullary opts x -----------------------------------------------------------------------------instance GToJSON U1 where-  gToJSON _ acc U1 = acc+instance Constructor m => GToJSONSumNullary (C1 m U1) where+  gToJSONSumNullary opts _ =+    String (ms (constructorTagModifier opts (conName (undefined :: C1 m U1 ())))) -----------------------------------------------------------------------------instance GToJSON V1 where-  gToJSON _ acc _ = acc+-- | Catch-all for non-nullary constructors — unreachable when 'gAllNullary'+-- guards are in place, but required for instance resolution.+instance {-# OVERLAPPABLE #-} Constructor m => GToJSONSumNullary (C1 m f) where+  gToJSONSumNullary _ _ = error "GToJSONSumNullary: non-nullary constructor (impossible)" -----------------------------------------------------------------------------instance (Selector s, ToJSON a) => GToJSON (S1 s (K1 i a)) where-  gToJSON opts acc (M1 (K1 x)) = ms field .= toJSON x : acc-    where-      field :: String-      field = fieldLabelModifier opts $ selName (undefined :: S1 s (K1 i a) ())+-- | Encode sum constructors with a @\"tag\"@ key.+class GToJSONSum (f :: Type -> Type) where+  gToJSONSum :: Options -> f a -> Value ----------------------------------------------------------------------------+instance (GToJSONSum f, GToJSONSum g) => GToJSONSum (f :+: g) where+  gToJSONSum opts (L1 x) = gToJSONSum opts x+  gToJSONSum opts (R1 x) = gToJSONSum opts x+----------------------------------------------------------------------------+instance (Constructor m, GToFields f) => GToJSONSum (C1 m f) where+  gToJSONSum opts (M1 x) =+    encodeTaggedCon+      (ms (constructorTagModifier opts (conName (undefined :: C1 m f ()))))+      (gToFields opts x)+---------------------------------------------------------------------------- instance ToJSON () where   toJSON () = Array [] ----------------------------------------------------------------------------@@ -313,45 +471,160 @@   default parseJSON :: (Generic a, GFromJSON (Rep a)) => Value -> Parser a   parseJSON = genericParseJSON defaultOptions ----------------------------------------------------------------------------+-- | Top-level generic decoding class. Symmetric with 'GToJSON'.+--+-- Decoding rules match aeson's defaults (see 'Options' and 'defaultOptions'). class GFromJSON (f :: Type -> Type) where   gParseJSON :: Options -> Value -> Parser (f a) ---------------------------------------------------------------------------- genericParseJSON :: (Generic a, GFromJSON (Rep a)) => Options -> Value -> Parser a genericParseJSON opts value = to <$> gParseJSON opts value -----------------------------------------------------------------------------instance GFromJSON a => GFromJSON (D1 i a) where-  gParseJSON opts x = M1 <$> gParseJSON opts x+instance GFromJSONRep f => GFromJSON (D1 m f) where+  gParseJSON opts v = M1 <$> gFromJSONRep opts v -----------------------------------------------------------------------------instance GFromJSON a => GFromJSON (C1 i a) where-  gParseJSON opts x = M1 <$> gParseJSON opts x+-- Internal: dispatches single-constructor vs sum at the child of D1.+class GFromJSONRep (f :: Type -> Type) where+  gFromJSONRep :: Options -> Value -> Parser (f a)+-- Single constructor+instance GFromFields f => GFromJSONRep (C1 m f) where+  gFromJSONRep opts v = M1 <$> parseProd opts v+-- Sum type: branch on allNullaryToStringTag+instance (GFromJSONSum f, GFromJSONSum g, GFromJSONSumNullary f, GFromJSONSumNullary g, GAllNullary f, GAllNullary g)+    => GFromJSONRep (f :+: g) where+  gFromJSONRep opts v+    | allNullaryToStringTag opts && gAllNullary @f && gAllNullary @g+    = gFromJSONSumNullary opts v+    | otherwise+    = gFromJSONSum opts v -----------------------------------------------------------------------------instance (GFromJSON a, GFromJSON b) => GFromJSON (a :*: b) where-  gParseJSON opts x = (:*:) <$> gParseJSON opts x <*> gParseJSON opts x+-- | Parse all-nullary sum constructors from bare 'String' values.+class GFromJSONSumNullary (f :: Type -> Type) where+  gFromJSONSumNullary :: Options -> Value -> Parser (f a) -----------------------------------------------------------------------------instance (TypeError ('Text "Sum types unsupported"), GFromJSON a, GFromJSON b) => GFromJSON (a :+: b) where-  gParseJSON opts x = (L1 <$> gParseJSON opts x) <|> (R1 <$> gParseJSON opts x)+instance (GFromJSONSumNullary f, GFromJSONSumNullary g) => GFromJSONSumNullary (f :+: g) where+  gFromJSONSumNullary opts v =+    (L1 <$> gFromJSONSumNullary opts v) <|> (R1 <$> gFromJSONSumNullary opts v) -----------------------------------------------------------------------------instance GFromJSON U1 where-  gParseJSON _ _ = pure U1+instance Constructor m => GFromJSONSumNullary (C1 m U1) where+  gFromJSONSumNullary opts v =+    let tag = ms (constructorTagModifier opts (conName (undefined :: C1 m U1 ())))+    in case v of+         String t | t == tag  -> pure (M1 U1)+                  | otherwise -> pfail ("expected \"" <> tag <> "\" got \"" <> t <> "\"")+         _        -> pfail ("expected String for nullary constructor " <> tag) -----------------------------------------------------------------------------instance {-# OVERLAPPABLE #-} (Selector s, FromJSON a) => GFromJSON (S1 s (K1 i a)) where-  gParseJSON opts = \case-    Object o ->-      M1 . K1 <$> o .: ms field-    v ->-      M1 . K1 <$> parseJSON v-    where-      field = fieldLabelModifier opts $ selName (undefined :: S1 s (K1 i a) ())+-- | Catch-all for non-nullary constructors — unreachable when 'gAllNullary'+-- guards are in place, but required for instance resolution.+instance {-# OVERLAPPABLE #-} Constructor m => GFromJSONSumNullary (C1 m f) where+  gFromJSONSumNullary _ _ = pfail "GFromJSONSumNullary: non-nullary constructor (impossible)" -----------------------------------------------------------------------------instance {-# OVERLAPPING #-} (Selector s, FromJSON a) => GFromJSON (S1 s (K1 i (Maybe a))) where-  gParseJSON opts = \case-    Object o ->-      M1 . K1 <$> o .:? ms field-    v ->-      M1 . K1 <$> parseJSON v-    where-      field = fieldLabelModifier opts $ selName (undefined :: S1 s (K1 i (Maybe a)) ())+-- | Parse sum constructors, trying each branch left-to-right.+class GFromJSONSum (f :: Type -> Type) where+  gFromJSONSum :: Options -> Value -> Parser (f a) ----------------------------------------------------------------------------+instance (GFromJSONSum f, GFromJSONSum g) => GFromJSONSum (f :+: g) where+  gFromJSONSum opts v = (L1 <$> gFromJSONSum opts v) <|> (R1 <$> gFromJSONSum opts v)+----------------------------------------------------------------------------+instance (Constructor m, GFromFields f) => GFromJSONSum (C1 m f) where+  gFromJSONSum opts v = M1 <$> parseTaggedCon tag opts v+    where tag = ms (constructorTagModifier opts (conName (undefined :: C1 m f ())))+----------------------------------------------------------------------------+-- | Parse a single-constructor (product) type from a 'Value'.+parseProd :: forall f a. GFromFields f => Options -> Value -> Parser (f a)+parseProd opts v+  | gIsRecord @f = withObject "generic record" (gFromRecord opts) v+  | otherwise    = case v of+      Array vs+        | gFieldCount @f == 1 -> gFromPositional opts [Array vs]+        | otherwise           -> gFromPositional opts vs+      _+        | gFieldCount @f == 0 -> pfail "expected Array [] for 0-field constructor"+        | gFieldCount @f == 1 -> gFromPositional opts [v]  -- single-field shorthand+        | otherwise           -> pfail "expected JSON Array for multi-field constructor"+----------------------------------------------------------------------------+-- | Parse a tagged sum constructor from an Object envelope.+parseTaggedCon :: forall f a. GFromFields f => MisoString -> Options -> Value -> Parser (f a)+parseTaggedCon tag opts = \case+  Object o -> do+    t <- case M.lookup "tag" o of+           Just (String t) -> pure t+           Just _          -> pfail "\"tag\" field is not a string"+           Nothing         -> pfail "missing \"tag\" field"+    if t /= tag+      then pfail ("expected tag " <> ms (show tag) <> ", got " <> ms (show t))+      else if gIsRecord @f+           then gFromRecord opts o+           else case M.lookup "contents" o of+                  Just (Array vs)+                    | gFieldCount @f == 1 -> gFromPositional opts [Array vs]+                    | otherwise           -> gFromPositional opts vs+                  Just single     -> gFromPositional opts [single]+                  Nothing         -> gFromPositional opts []+  _ -> pfail ("expected JSON object for constructor " <> ms (show tag))+----------------------------------------------------------------------------+-- | Field-level decoder. Knows whether the constructor is a record and+-- how many fields it has; can decode from a JSON 'Object' (record mode)+-- or a positional '[Value]' list.+class GFromFields (f :: Type -> Type) where+  -- | Is this a record constructor (all selectors have names)?+  gIsRecord      :: Bool+  -- | Number of fields.+  gFieldCount    :: Int+  -- | Decode from a JSON 'Object' (record mode: look up by field name).+  gFromRecord    :: Options -> Object -> Parser (f a)+  -- | Decode from a positional list of 'Value'.+  gFromPositional :: Options -> [Value] -> Parser (f a)+----------------------------------------------------------------------------+instance GFromFields U1 where+  gIsRecord       = False+  gFieldCount     = 0+  gFromRecord   _ _ = pure U1+  gFromPositional _ _ = pure U1+----------------------------------------------------------------------------+instance GFromFields V1 where+  gIsRecord       = False+  gFieldCount     = 0+  gFromRecord   _ _ = pfail "V1"+  gFromPositional _ _ = pfail "V1"+----------------------------------------------------------------------------+instance (GFromFields f, GFromFields g) => GFromFields (f :*: g) where+  gIsRecord       = gIsRecord @f+  gFieldCount     = gFieldCount @f + gFieldCount @g+  gFromRecord opts o =+    (:*:) <$> gFromRecord opts o+          <*> gFromRecord opts o+  gFromPositional opts vs =+    let n = gFieldCount @f+    in (:*:) <$> gFromPositional opts (take n vs)+             <*> gFromPositional opts (drop n vs)+----------------------------------------------------------------------------+-- | Selector with a 'Maybe' field: uses '.:?' so missing keys decode as Nothing.+instance {-# OVERLAPPING #-} (Selector m, FromJSON a)+    => GFromFields (S1 m (K1 r (Maybe a))) where+  gIsRecord       = not (null name)+    where name = selName (M1 undefined :: S1 m (K1 r (Maybe a)) ())+  gFieldCount     = 1+  gFromRecord opts o =+    M1 . K1 <$> o .:? ms (fieldLabelModifier opts+                    (selName (M1 undefined :: S1 m (K1 r (Maybe a)) ())))+  gFromPositional _ vs = case vs of+    (v:_) -> M1 . K1 <$> parseJSON v+    []    -> pure (M1 (K1 Nothing))+----------------------------------------------------------------------------+-- | General selector.+instance {-# OVERLAPPABLE #-} (Selector m, FromJSON a)+    => GFromFields (S1 m (K1 r a)) where+  gIsRecord       = not (null name)+    where name = selName (M1 undefined :: S1 m (K1 r a) ())+  gFieldCount     = 1+  gFromRecord opts o =+    M1 . K1 <$> o .: ms (fieldLabelModifier opts+                    (selName (M1 undefined :: S1 m (K1 r a) ())))+  gFromPositional _ vs = case vs of+    (v:_) -> M1 . K1 <$> parseJSON v+    []    -> pfail "gFromPositional: unexpected end of fields"+---------------------------------------------------------------------------- instance FromJSON Value where   parseJSON = pure ----------------------------------------------------------------------------@@ -528,7 +801,6 @@ instance FromMisoString Value where   fromMisoStringEither = Parser.decodePure -----------------------------------------------------------------------------#ifdef VANILLA -- | Escape special characters in a string for JSON serialization -- Handles: \, ", and all JSON control characters per RFC 8259 escapeJSONString :: MisoString -> MisoString@@ -550,17 +822,12 @@     padHex n = MS.pack $ replicate (4 - length h) '0' ++ h       where h = showHex n "" -----------------------------------------------------------------------------#endif instance ToMisoString Value where   toMisoString = \case-    String s ->-#ifdef VANILLA-      "\"" <> escapeJSONString s <> "\""-#else-      "\"" <> s <> "\""-#endif-    Number n ->-      ms n+    String s -> "\"" <> escapeJSONString s <> "\""+    Number n+      | (i, 0.0) <- properFraction n -> ms @Int i+      | otherwise -> ms n     Null ->       "null"     Array xs ->@@ -571,18 +838,14 @@       "false"     Object o ->       "{" <>-#ifdef VANILLA         MS.intercalate "," [ "\"" <> escapeJSONString k <> "\"" <> ":" <> ms v | (k,v) <- M.toList o ]-#else-        MS.intercalate "," [ "\"" <> k <> "\"" <> ":" <> ms v | (k,v) <- M.toList o ]-#endif       <> "}" ---------------------------------------------------------------------------- #ifdef VANILLA decode :: FromJSON a => MisoString -> Maybe a decode s   | Right x <- Parser.decodePure s-  , Success v <- fromJSON x = v+  , Success v <- fromJSON x = Just v   | otherwise = Nothing #else decode :: FromJSON a => MisoString -> Maybe a
src/Miso/JSON/Lexer.hs view
@@ -17,12 +17,13 @@ ---------------------------------------------------------------------------- module Miso.JSON.Lexer (Token (..), tokens) where -----------------------------------------------------------------------------import           Control.Applicative (optional, Alternative (some, many))+import           Control.Applicative (Alternative (some, many), optional)+import           Control.Monad (replicateM) import           Data.Char (isHexDigit, chr, isSpace) import           Data.Foldable (Foldable (fold)) import           Data.Functor (void) import           Data.Ix (Ix (inRange))-import           Data.Maybe (catMaybes, listToMaybe)+import           Data.Maybe (catMaybes) import           Numeric (readHex) import           Prelude hiding (null) ----------------------------------------------------------------------------@@ -66,7 +67,8 @@       , escapedCharacter       ]     hexDigit = satisfy isHexDigit-    escapedCharacter = char '\\' *> oneOf+    escaped = (char '\\' *>)+    escapedCharacter = escaped $ oneOf       [ char '"'       , char '\\'       , char '/'@@ -75,16 +77,26 @@       , '\n' <$ char 'n'       , '\r' <$ char 'r'       , '\t' <$ char 't'-      , do-          a <- hexDigit-          b <- hexDigit-          c <- hexDigit-          d <- hexDigit-          maybe oops (pure . chr . fst)-              . listToMaybe-              . readHex-              $ [a, b, c, d]+      , unicodeHexQuad >>= \high -> do+          if inRange highSurrogateRange high+            then do+              low <- escaped unicodeHexQuad+              if inRange lowSurrogateRange low+                then+                  pure . chr . sum $+                    [ (high - fst highSurrogateRange) * 0x400+                    , low - fst lowSurrogateRange+                    , 0x10000+                    ]+                else oops+            else+              pure $ chr high       ]+    highSurrogateRange = (0xD800, 0xDBFF)+    lowSurrogateRange = (0xDC00, 0xDFFF)+    unicodeHexQuad = char 'u' *> do+        [(num, "")] <- readHex <$> replicateM 4 hexDigit+        pure num ---------------------------------------------------------------------------- null :: Lexer () null = void (string "null")
+ src/Miso/Lens/Generic.hs view
@@ -0,0 +1,103 @@+-----------------------------------------------------------------------------+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -Wno-orphans #-}+{-# LANGUAGE UndecidableSuperClasses #-}+{-# LANGUAGE FunctionalDependencies #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Miso.Lens.Generic+-- Copyright   :  (C) 2016-2026 David M. Johnson+-- License     :  BSD3-style (see the file LICENSE)+-- Maintainer  :  David M. Johnson <code@dmj.io>+-- Stability   :  experimental+-- Portability :  non-portable+-----------------------------------------------------------------------------+module Miso.Lens.Generic (HasLens(..), field) where++-----------------------------------------------------------------------------+import Data.Kind (Constraint, Type)+import GHC.Generics (C1, D1, Generic (..), K1 (..), M1 (..), Meta (..), Rec0, S1, (:*:) (..), (:+:) (..))+import GHC.OverloadedLabels (IsLabel (..))+import GHC.Records (HasField (..))+import GHC.TypeLits (ErrorMessage (..), Symbol, TypeError)+-----------------------------------------------------------------------------+import Miso.Lens (Lens, lens)+-----------------------------------------------------------------------------++class Generic s => HasLens (name :: Symbol) s a | name s -> a where +  getLens :: Lens s a++instance +  (HasField name s a, TotalityCheck name s a (GetFieldType name (Rep s)), GSet name a (Rep s), Generic s) => +  HasLens name s a where+  getLens = lens (getField @name) (\s v -> to . gSet @name v . from $ s)+  {-# INLINE getLens #-}++instance HasLens name s a => IsLabel name (Lens s a)+  where fromLabel = getLens @name++{-# INLINE field #-}+field :: forall name s a. HasLens name s a => Lens s a +field = fromLabel @name++class GSet (name :: Symbol) typ f where+  gSet :: typ -> f x -> f x++instance (GSet name typ a, GSet name typ b) => GSet name typ (a :*: b) where+  gSet v (l :*: r) = gSet @name v l :*: gSet @name v r+  {-# INLINE gSet #-}++instance (GSet name typ a, GSet name typ b) => GSet name typ (a :+: b) where+  gSet v (L1 l) = L1 $ gSet @name v l+  gSet v (R1 r) = R1 $ gSet @name v r+  {-# INLINE gSet #-}++instance (GSet name typ f) => GSet name typ (C1 x f) where+  gSet v (M1 f) = M1 $ gSet @name v f+  {-# INLINE gSet #-}++instance (GSet name typ f) => GSet name typ (D1 x f) where+  gSet v (M1 f) = M1 $ gSet @name v f+  {-# INLINE gSet #-}++instance {-# OVERLAPPING #-} GSet name typ (S1 ('MetaSel ('Just name) b c d) (Rec0 typ)) where+  gSet v (M1 (K1 _)) = M1 (K1 v)++instance {-# OVERLAPPABLE #-} GSet name typ (S1 ('MetaSel ('Just anotherName) b c d) x) where+  gSet _ f = f+  {-# INLINE gSet #-}++type family TotalityCheck (name :: Symbol) r a (res :: Maybe Type) :: Constraint where+  TotalityCheck _ _ _ ('Just _) = ()+  TotalityCheck name r a 'Nothing =+    TypeError+      ( 'ShowType r+          ':<>: 'Text ": "+          ':<>: 'Text name+          ':<>: 'Text " field missing or not in all constructors"+      )++type family GetFieldType (field :: Symbol) f :: Maybe Type where+  GetFieldType field (S1 ('MetaSel ('Just field) _ _ _) (Rec0 t)) ='Just t+  GetFieldType field (l :*: r) = Or (GetFieldType field l) (GetFieldType field r)+  GetFieldType field (l :+: r) = And (GetFieldType field l) (GetFieldType field r)+  GetFieldType field (C1 _ f) = GetFieldType field f+  GetFieldType field (D1 _ f) = GetFieldType field f+  GetFieldType field x = 'Nothing++type family And (l :: Maybe Type) (r :: Maybe Type) :: Maybe Type where+  And ('Just a) ('Just a) = 'Just a+  And l r = 'Nothing++type family Or (l :: Maybe Type) (r :: Maybe Type) :: Maybe Type where+  Or ('Just l) _ = 'Just l+  Or _ r = r
src/Miso/Reload.hs view
@@ -71,16 +71,16 @@ import           Foreign hiding (void) import           Foreign.C.Types ------------------------------------------------------------------------------foreign import ccall unsafe "x_store"+foreign import ccall unsafe "miso_x_store"   x_store :: StablePtr a -> IO () ------------------------------------------------------------------------------foreign import ccall unsafe "x_get"+foreign import ccall unsafe "miso_x_get"   x_get :: IO (StablePtr a) ------------------------------------------------------------------------------foreign import ccall unsafe "x_exists"+foreign import ccall unsafe "miso_x_exists"   x_exists :: IO CInt ------------------------------------------------------------------------------foreign import ccall unsafe "x_clear"+foreign import ccall unsafe "miso_x_clear"   x_clear :: IO () ----------------------------------------------------------------------------- #define MISO_JS_PATH "js/miso.js"
src/Miso/Router.hs view
@@ -122,6 +122,7 @@   , Path (..)   , QueryParam (..)   , QueryFlag (..)+  , Fragment (..)   , Token (..)   , URI (..)     -- ** Errors@@ -143,6 +144,7 @@   , queryParam   , capture   , path+  , fragment     -- ** Lexing   , lexTokens   , tokensToURI@@ -159,7 +161,7 @@ import           GHC.Generics import           GHC.TypeLits ------------------------------------------------------------------------------import           Miso.Types hiding (model)+import           Miso.Types hiding (model, fragment, fragment_) import           Miso.Util import qualified Miso.Html.Property as P import           Miso.Util.Parser hiding (NoParses)@@ -186,6 +188,13 @@ newtype QueryParam (path :: Symbol) a = QueryParam (Maybe a)   deriving stock (Eq, Show) -----------------------------------------------------------------------------+-- | Type used for representing fragments+data Fragment (path :: Symbol) = Fragment+  deriving stock (Eq, Show)+-----------------------------------------------------------------------------+instance (KnownSymbol frag) => ToMisoString (Fragment frag) where+  toMisoString Fragment = "#" <> ms (symbolVal (Proxy @frag))+----------------------------------------------------------------------------- instance (ToMisoString a, KnownSymbol path) => ToMisoString (QueryParam path a) where   toMisoString (QueryParam maybeVal) =     maybe mempty (\param -> "?" <> param <> "=" <> val) (ms <$> maybeVal)@@ -313,6 +322,12 @@   when (specified /= "index") (fail "index")   pure "/" -----------------------------------------------------------------------------+fragment :: MisoString -> RouteParser MisoString+fragment specified = do+  FragmentToken frag <- indexToken+  when (specified /= frag) (fail "fragment")+  pure frag+----------------------------------------------------------------------------- -- | URI parsing parseURI :: MisoString -> Either MisoString URI parseURI txt =@@ -420,6 +435,12 @@   gFromRoute (K1 x) = pure $ CaptureOrPathToken (ms x)   gRouteParser = K1 <$> capture -----------------------------------------------------------------------------+instance {-# OVERLAPS #-} KnownSymbol frag => GRouter (K1 m (Fragment frag)) where+  gFromRoute (K1 x) = pure $ FragmentToken (ms x)+  gRouteParser = K1 Fragment <$ fragment frag+    where+      frag = ms (symbolVal (Proxy :: Proxy frag))+----------------------------------------------------------------------------- instance {-# OVERLAPS #-} forall param m a . (ToMisoString a, FromMisoString a, KnownSymbol param) =>   GRouter (K1 m (QueryParam param a)) where     gFromRoute (K1 (QueryParam maybeParam)) = do@@ -517,7 +538,7 @@               CaptureOrPathToken <$> chars             fragmentLexer = do               void (L.char '#')-              FragmentToken <$> fragment+              FragmentToken <$> query             queryParamLexer = QueryParamTokens <$> do               void (L.char '?')               sepBy (L.char '&') $ do@@ -533,9 +554,6 @@ ----------------------------------------------------------------------------- pchar :: Lexer MisoString pchar = unreserved <|> pctEncoded <|> subDelims <|> L.string ":" <|> L.string "@"-------------------------------------------------------------------------------fragment :: Lexer MisoString-fragment = query ----------------------------------------------------------------------------- query :: Lexer MisoString query = foldr (<|>) empty
src/Miso/Runtime.hs view
@@ -50,6 +50,7 @@   , broadcast   , parent   , mailParent+  , mailChildren   -- ** WebSocket   , websocketConnect   , websocketConnectJSON@@ -125,6 +126,7 @@ #endif import           Unsafe.Coerce (unsafeCoerce) -----------------------------------------------------------------------------+import           Miso.Binding (Precedence(..)) import           Miso.Concurrent (Waiter(..), waiter) import           Miso.CSS (renderStyleSheet) import           Miso.Delegate (delegator)@@ -183,7 +185,6 @@   _componentSubThreads <- liftIO (newIORef M.empty)    frame <- newEmptyMVar :: IO (MVar Double)-  _componentModel <- liftIO (pure initializedModel)   _componentMailbox <- pure S.empty    rAFCallback <-@@ -221,17 +222,62 @@         , _componentTopics = mempty         , _componentModelDirty = modelCheck         , _componentChildren = mempty+        , _componentModel = initializedModel         , ..         }    when isRoot (delegator _componentDOMRef _componentVTree events (logLevel `elem` [DebugEvents, DebugAll]))   registerComponent vcomponent++  -- Inherit bindings state (if applicable)+  _componentModel <- inheritParentBindings _componentParentId initializedModel bindings+  modifyComponent _componentId (componentModel .= _componentModel)+   initSubs subs _componentSubThreads _componentSink-  initialDraw initializedModel events hydrate isRoot comp vcomponent+  initialDraw _componentModel events hydrate isRoot comp vcomponent   forM_ mount _componentSink   FFI.mountComponent _componentId =<< toObject jsNull   pure vcomponent -----------------------------------------------------------------------------+inheritParentBindings+  :: ComponentId+  -- ^ ParentId+  -> child+  -- ^ Child model+  -> [ Binding parent child ]+  -> IO child+inheritParentBindings compParentId childModel bindings = do+  inheritChildBindings compParentId childModel bindings+  foldM (\m -> \case+            ParentToChild getParentField setChildField -> do+              ComponentState {..} <- (IM.! compParentId) <$> readIORef components+              pure (setChildField (getParentField _componentModel) m)+            Bidirectional Parent getParentField _ _ setChildField -> do+              ComponentState {..} <- (IM.! compParentId) <$> readIORef components+              pure (setChildField (getParentField _componentModel) m)+            _ -> pure m+        ) childModel bindings+-----------------------------------------------------------------------------+inheritChildBindings+  :: ComponentId+  -- ^ ParentId+  -> child+  -- ^ Child component+  -> [ Binding parent child ]+  -> IO ()+inheritChildBindings compParentId childState bindings = do+  forM_ bindings $ \case+     ChildToParent setParentField getChildField -> do+       modifyComponent compParentId $ do+         componentModel %= setParentField (getChildField childState)+         isDirty .= True+     Bidirectional Child _ setParentField getChildField _ -> do+       modifyComponent compParentId $ do+         componentModel %= setParentField (getChildField childState)+         isDirty .= True+     _ -> do+       pure ()+----------------------------------------------------------------------------- initSubs :: [Sub action] -> IORef (Map MisoString ThreadId) -> Sink action -> IO () initSubs subs_ _componentSubThreads _componentSink = do   forM_ subs_ $ \sub_ -> do@@ -292,21 +338,21 @@           pure dirtySet         else           pure mempty-    ------------------------------------------------------------------------------    -- | Perform a top-down rendering of the 'Component' tree.-    ---    -- We lookup the components each time to account for unmounting.-    -- Reset the dirty bit if a render occurs-    ---    renderComponents :: ComponentIds -> IO ()-    renderComponents dirtySet = do-      forM_ (IS.toAscList dirtySet) $ \vcompId ->-        IM.lookup vcompId <$> liftIO (readIORef components) >>= mapM \ComponentState {..} -> do-          when _componentIsDirty $ do-            _componentDraw _componentModel-            FFI.modelHydration _componentId =<< toObject jsNull-          modifyComponent _componentId (isDirty .= False) -----------------------------------------------------------------------------+-- | Perform a top-down rendering of the 'Component' tree.+--+-- We lookup the components each time to account for unmounting.+-- Reset the dirty bit if a render occurs+--+renderComponents :: ComponentIds -> IO ()+renderComponents dirtySet = do+  forM_ (IS.toAscList dirtySet) $ \vcompId ->+    IM.lookup vcompId <$> liftIO (readIORef components) >>= mapM \ComponentState {..} -> do+      when _componentIsDirty $ do+        _componentDraw _componentModel+        FFI.modelHydration _componentId =<< toObject jsNull+      modifyComponent _componentId (isDirty .= False)+----------------------------------------------------------------------------- -- | Modify a single t'Component p m a' at a t'ComponentId'. -- -- Auxiliary function@@ -397,7 +443,7 @@               currentFieldValue = getCurrentField (currentState ^. componentModel)               updatedChildModel = setChildField currentFieldValue currentChildModel           pure (childState & componentModel .~ updatedChildModel)-        Bidirectional getCurrentField _ _ setChildField -> do+        Bidirectional _ getCurrentField _ _ setChildField -> do           let currentChildModel = _componentModel childState               currentFieldValue = getCurrentField (currentState ^. componentModel)               updatedChildModel = setChildField currentFieldValue currentChildModel@@ -433,7 +479,7 @@             currentFieldValue = getCurrentField (currentState ^. componentModel)             updatedParentModel = setParentField currentFieldValue currentParentModel         pure (parentState & componentModel .~ updatedParentModel)-      Bidirectional _ setParentField getCurrentField _ -> do+      Bidirectional _ _ setParentField getCurrentField _ -> do         let currentParentModel = parentState ^. componentModel             currentFieldValue = getCurrentField (currentState ^. componentModel)             updatedParentModel = setParentField currentFieldValue currentParentModel@@ -449,7 +495,7 @@ ----------------------------------------------------------------------------- pop :: Synch p m a (Maybe (ComponentState p m a)) pop = use stack >>= \case-  [] -> +  [] ->     pure Nothing   x : xs -> do     stack .= xs@@ -1026,16 +1072,19 @@     pure (VTree vnode_)       where         procreate parentVTree = do-          kidsViews <- forM kids $ \kid -> do-            VTree child <- buildVTree events_ parentId_ vcompId hydrate snk logLevel_ kid-            FFI.set "parent" parentVTree child-            pure child-          setNextSibling kidsViews-          pure kidsViews+          kidsViews <- foldM (buildKid parentVTree) [] kids+          let ordered = reverse kidsViews+          setNextSibling ordered+          pure ordered             where               setNextSibling xs =                 zipWithM_ (flip setField "nextSibling")                   xs (drop 1 xs)+              buildKid _ acc (VFrag _ []) = pure acc+              buildKid p acc kid = do+                VTree child <- buildVTree events_ parentId_ vcompId hydrate snk logLevel_ kid+                FFI.set "parent" p child+                pure (child : acc)   VText key t -> do     vtree <- create     flip (FFI.set "type") vtree =<< toJSVal VTextType@@ -1043,6 +1092,25 @@     FFI.set "ns" ("text" :: MisoString) vtree     FFI.set "text" t vtree     pure (VTree vtree)+  VFrag maybeKey kids -> do+    frag <- create+    FFI.set "type" VFragType frag+    forM_ maybeKey $ \(Key k) -> FFI.set "key" k frag+    vchildren <- toJSVal =<< procreateFragChildren frag+    FFI.set "children" vchildren frag+    pure (VTree frag)+      where+        procreateFragChildren parentVTree = do+          kidsViews <- foldM buildKid [] kids+          let ordered = reverse kidsViews+          zipWithM_ (flip setField "nextSibling") ordered (drop 1 ordered)+          pure ordered+            where+              buildKid acc (VFrag _ []) = pure acc+              buildKid acc kid = do+                VTree child <- buildVTree events_ parentId_ vcompId hydrate snk logLevel_ kid+                FFI.set "parent" parentVTree child+                pure (child : acc) ----------------------------------------------------------------------------- -- | @createNode@ -- A helper function for constructing a vtree (used for @vcomp@ and @vnode@)@@ -1244,6 +1312,24 @@ mailParent msg = do   ComponentInfo {..} <- ask   io_ (mail _componentInfoParentId msg)+-----------------------------------------------------------------------------+-- | Send any @ToJSON message => message@ to the children's t'Miso.Types.Component' mailbox+--+-- @+-- mailChildren ("test message" :: MisoString) :: Effect parent model action+-- @+--+-- @since 1.9.0.0+mailChildren+  :: ToJSON message+  => message+  -- ^ Message to send+  -> Effect parent model action+mailChildren msg = do+  ComponentInfo {..} <- ask+  io_ $ do+    ComponentState {..} <- (IM.! _componentInfoId) <$> readIORef components+    forM_ (IS.toList _componentChildren) (flip mail msg) ---------------------------------------------------------------------------- -- | Helper function for processing @Mail@ from 'mail'. --@@ -1508,7 +1594,7 @@ websocketClose socketId = do   ComponentInfo {..} <- ask   io_ $ do-    result <- +    result <-       atomicModifyIORef' websocketConnections $ \imap ->         dropWebSocket _componentInfoId socketId imap =:           getWebSocket _componentInfoId socketId imap
src/Miso/Types.hs view
@@ -66,9 +66,17 @@   -- ** Component mounting   , (+>)   , mount_+  -- ** Key combinators+  , keyed+  -- ** Fragment combinators+  , fragment+  , fragment_+  , vfrag+  , vfrag_   -- ** Utils   , getMountPoint   , optionalAttrs+  , optionalVoidAttrs   , optionalChildren   , prettyURI   , prettyQueryString@@ -296,6 +304,7 @@   = VNode Namespace Tag [Attribute action] [View model action]   | VText (Maybe Key) MisoString   | VComp (Maybe Key) (SomeComponent model)+  | VFrag (Maybe Key) [View model action]   deriving Functor ----------------------------------------------------------------------------- -- | Existential wrapper allowing nesting of t'Miso.Types.Component' in t'Miso.Types.Component'@@ -303,6 +312,63 @@    = forall model action . Eq model   => SomeComponent (Component parent model action) -----------------------------------------------------------------------------+-- | Like '+>' but operates on any 'View', not just 'Component'.+--+-- This appends a 'Key' to any 'View'.+--+-- @+-- keyed "key" ("some text" :: View model action)+-- keyed "key" $ div_ [ id_ "container" ] [ "content" ]+-- keyed "key" (mount_ calendarComponent)+-- @+--+-- @since 1.10.0.0+keyed+  :: MisoString+  -> View model action+  -> View model action+keyed key = \case+    VText _ txt ->+      VText (Just (Key key)) txt+    VComp _ comp ->+      VComp (Just (Key key)) comp+    VFrag _ kids ->+      VFrag (Just (Key key)) kids+    VNode ns tag attrs kids ->+      VNode ns tag (Property "key" (toJSON key) : attrs) kids+-----------------------------------------------------------------------------+-- | Create a fragment (keyless).+--+-- A fragment groups multiple sibling 'View' nodes without introducing+-- an extra DOM element.+--+-- Synonym for `fragment'+--+-- @since 1.10.0.0+vfrag :: [View model action] -> View model action+vfrag = fragment+-----------------------------------------------------------------------------+-- | Create a fragment (keyless).+--+-- A fragment groups multiple sibling 'View' nodes without introducing+-- an extra DOM element.+--+-- @since 1.10.0.0+fragment :: [View model action] -> View model action+fragment = VFrag Nothing+-----------------------------------------------------------------------------+-- | Like 'fragment', but keyed for efficient diffing.+--+-- @since 1.10.0.0+vfrag_ :: MisoString -> [View model action] -> View model action+vfrag_ key = VFrag (Just (Key key))+-----------------------------------------------------------------------------+-- | Like 'fragment', but keyed for efficient diffing.+--+-- @since 1.10.0.0+fragment_ :: MisoString -> [View model action] -> View model action+fragment_ key = VFrag (Just (Key key))+----------------------------------------------------------------------------- -- | t'Miso.Types.Component' mounting combinator -- -- Used in the @view@ function to mount a t'Miso.Types.Component' on any 'VNode'.@@ -556,7 +622,7 @@ -- -- @ -- view :: Bool -> View model action--- view danger = optionalAttrs textarea_ [ id_ "txt" ] danger [ class_ "danger" ] ["child"]+-- view danger = optionalAttrs div_ [ id_ "some-div" ] danger [ class_ "danger" ] ["child"] -- @ -- -- @since 1.9.0.0@@ -573,6 +639,27 @@       let newAttrs = concat [ opts | condition ] ++ attrs       VNode ns name newAttrs kids     x -> x+-----------------------------------------------------------------------------+-- | Utility function to make it easy to specify conditional attributes for void elements.+--+-- @+-- view :: Bool -> View model action+-- view shouldClear = optionalVoidAttrs textarea_ [ value_ "" ] shouldClear [ id_ "text-area-id" ]+-- @+--+-- @since 1.9.0.0+optionalVoidAttrs+  :: ([Attribute action] -> View model action)+  -> [Attribute action] -- ^ Attributes to be added unconditionally+  -> Bool -- ^ A condition+  -> [Attribute action] -- ^ Additional attributes to add if the condition is True+  -> View model action+optionalVoidAttrs element attrs condition opts =+  case element attrs of+    VNode ns name _ kids -> do+      let newAttrs = concat [ opts | condition ] ++ attrs+      VNode ns name newAttrs kids+    x -> x ---------------------------------------------------------------------------- -- | Conditionally adds children. --@@ -639,6 +726,7 @@   = VCompType   | VNodeType   | VTextType+  | VFragType   deriving (Show, Eq) ----------------------------------------------------------------------------- instance ToJSVal VTreeType where@@ -646,4 +734,5 @@     VCompType -> toJSVal (0 :: Int)     VNodeType -> toJSVal (1 :: Int)     VTextType -> toJSVal (2 :: Int)+    VFragType -> toJSVal (3 :: Int) -----------------------------------------------------------------------------
src/Miso/Util/Lexer.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module      :  Miso.Util.Lexer@@ -35,6 +36,9 @@   ) where ---------------------------------------------------------------------------- import           Control.Monad+#if __GLASGOW_HASKELL__ <= 865+import           Control.Monad.Fail+#endif import           Control.Applicative ---------------------------------------------------------------------------- import           Miso.String (MisoString, ToMisoString)@@ -129,6 +133,9 @@   m >>= f = Lexer $ \input -> do     (x, s) <- runLexer m input     runLexer (f x) s+----------------------------------------------------------------------------+instance MonadFail Lexer where+  fail _ = oops ---------------------------------------------------------------------------- instance Alternative Lexer where   empty = Lexer $ \(Stream s l)  -> Left (unexpected s l)