This PR adds the ability to control `<head>` tags using a new `HTMLTitle` view and `HTMLMeta` view. Taking inspiration from Next.js `<NextHead>`, you can use these views anywhere in your view hierarchy and they will be hoisted to the top `<head>` section of the html.
Use as a view:
```swift
var body: some View {
VStack {
...
HTMLTitle("Hello, Tokamak")
HTMLMeta(charset: "utf-8")
...
}
}
```
Use as a view modifier:
```swift
var body: some View {
VStack {
...
}
.htmlTitle("Hello, Tokamak")
.htmlMeta(charset: "utf-8")
}
```
And the resulting html (no matter where these are used in your view hierarchy):
```html
<html>
<head>
<title>Hello, Tokamak</title>
<meta charset="utf-8">
</head>
<body>
...
</body>
</html>
```
* Make actual use of `rootEnvironment` passed into functions, falling back to `.defaultEnvironment`
* Add `.merge(_:)`/`.merging(_:)` to `EnvironmentValues`
* Merge `.defaultEnvironment` with `rootEnvironment`
* Add `@_spi(TokamakCore)` protection for `EnvironmentValues.merge(_:)`/`.merging(_:)`
* Add initial implementations of Canvas and TimelineView
* Add CanvasDemo
* Add the demo to the native project
* Use Xcode 13.0 for macOS builds
* Disable macOS builds until Monterey is available
* Mark CanvasDemo as iOS 15/macOS 12 only, fix LinkButtonStyle reference on iOS
* Add _VariadicView and symbol rendering
* Fix linter warnings
* Add image support
* Revise AnimationTimelineSchedule and requestAnimationFrame cancellation
* Fix pausing of animated TimelineView in TokamakDOM
Co-authored-by: Max Desiatov <max@desiatov.com>
This allows writing tests for `TokamakStaticHTML`, `TokamakDOM`, and `TokamakGTK` targets.
The issue was caused by conflicting `ViewDeferredToRenderer` conformances declared in different modules, including the `TokamakTestRenderer` module.
This works around a general limitation in Swift, which was [discussed at length on Swift Forums previously](https://forums.swift.org/t/an-implementation-model-for-rational-protocol-conformance-behavior/37171). When multiple conflicting conformances to the same protocol (`ViewDeferredToRenderer` in our case) exist in different modules, only one of them is available in a given binary (even a test binary). Also, only of them can be loaded and used. Which one exactly is loaded can't be known at compile-time, which is hard to debug and leads to breaking tests that cover code in different renderers. We had to disable `TokamakStaticHTMLTests` for this reason.
The workaround is to declare two new functions in the `Renderer` protocol:
```swift
public protocol Renderer: AnyObject {
// ...
// Functions unrelated to the issue at hand skipped for brevity.
/** Returns a body of a given pritimive view, or `nil` if `view` is not a primitive view for
this renderer.
*/
func body(for view: Any) -> AnyView?
/** Returns `true` if a given view type is a primitive view that should be deferred to this
renderer.
*/
func isPrimitiveView(_ type: Any.Type) -> Bool
}
```
Now each renderer can declare their own protocols for their primitive views, i.e. `HTMLPrimitive`, `DOMPrimitive`, `GTKPrimitive` etc, delegating to them from the implementations of `body(for view:)` and `isPrimitiveView(_:)`. Conformances to these protocols can't conflict across different modules. Also, these protocols can have `internal` visibility, as opposed to `ViewDeferredToRenderer`, which had to be declared as `public` in `TokamakCore` to be visible in renderer modules.