Support meta tags in StaticHTMLRenderer (#483)

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>
```
This commit is contained in:
Andrew Barba 2022-05-23 16:03:28 -04:00 committed by GitHub
parent 8177fc8cae
commit 2ba548810c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 2425 additions and 28 deletions

View File

@ -78,9 +78,6 @@ final class MountedCompositeView<R: Renderer>: MountedCompositeElement<R> {
if let preferenceModifier = self.view.view as? _PreferenceWritingViewProtocol {
self.view = preferenceModifier.modifyPreferenceStore(&self.preferenceStore)
if let parent = parent {
parent.preferenceStore.merge(with: self.preferenceStore)
}
}
if let preferenceReader = self.view.view as? _PreferenceReadingViewProtocol {

View File

@ -94,13 +94,9 @@ public class MountedElement<R: Renderer> {
public internal(set) var environmentValues: EnvironmentValues
weak var parent: MountedElement<R>?
/// `didSet` on this field propagates the preference changes up the view tree.
var preferenceStore: _PreferenceStore = .init() {
didSet {
parent?.preferenceStore.merge(with: preferenceStore)
}
}
private(set) weak var parent: MountedElement<R>?
var preferenceStore: _PreferenceStore = .init()
public internal(set) var viewTraits: _ViewTraitStore
@ -110,6 +106,7 @@ public class MountedElement<R: Renderer> {
self.environmentValues = environmentValues
viewTraits = .init()
updateEnvironment()
connectParentPreferenceStore()
}
init(_ scene: _AnyScene, _ environmentValues: EnvironmentValues, _ parent: MountedElement<R>?) {
@ -118,6 +115,7 @@ public class MountedElement<R: Renderer> {
self.environmentValues = environmentValues
viewTraits = .init()
updateEnvironment()
connectParentPreferenceStore()
}
init(
@ -131,6 +129,7 @@ public class MountedElement<R: Renderer> {
self.environmentValues = environmentValues
self.viewTraits = viewTraits
updateEnvironment()
connectParentPreferenceStore()
}
func updateEnvironment() {
@ -145,6 +144,10 @@ public class MountedElement<R: Renderer> {
}
}
func connectParentPreferenceStore() {
preferenceStore.parent = parent?.preferenceStore
}
/// You must call `super.prepareForMount` before all other mounting work.
func prepareForMount(with transaction: Transaction) {
// `GroupView`'s don't really mount, so let their children transition if the group can.

View File

@ -48,10 +48,12 @@ public extension _PreferenceValue {
}
}
public struct _PreferenceStore {
public final class _PreferenceStore {
/// The backing values of the `_PreferenceStore`.
private var values: [String: Any]
weak var parent: _PreferenceStore?
public init(values: [String: Any] = [:]) {
self.values = values
}
@ -63,23 +65,12 @@ public struct _PreferenceStore {
?? _PreferenceValue(valueList: [Key.defaultValue])
}
public mutating func insert<Key>(_ value: Key.Value, forKey key: Key.Type = Key.self)
public func insert<Key>(_ value: Key.Value, forKey key: Key.Type = Key.self)
where Key: PreferenceKey
{
let previousValues = self.value(forKey: key).valueList
values[String(reflecting: key)] = _PreferenceValue<Key>(valueList: previousValues + [value])
}
public mutating func merge(with other: Self) {
self = merging(with: other)
}
public func merging(with other: Self) -> Self {
var result = values
for (key, value) in other.values {
result[key] = value
}
return .init(values: result)
parent?.insert(value, forKey: key)
}
}

View File

@ -58,6 +58,12 @@ public final class StackReconciler<R: Renderer> {
*/
public let rootTarget: R.TargetType
/** A root renderer's main preference store.
*/
public var preferenceStore: _PreferenceStore {
rootElement.preferenceStore
}
/** A root of the mounted elements tree to which all other mounted elements are attached to.
*/
private let rootElement: MountedElement<R>

View File

@ -19,7 +19,7 @@ public struct Group<Content> {
}
}
extension Group: _PrimitiveView & View where Content: View {}
extension Group: _PrimitiveView, View where Content: View {}
extension Group: ParentView where Content: View {
@_spi(TokamakCore)

View File

@ -24,7 +24,7 @@ public extension App {
}
static func _setTitle(_ title: String) {
StaticHTMLRenderer.title = title
// no-op: use Title view
}
var _phasePublisher: AnyPublisher<ScenePhase, Never> {

View File

@ -0,0 +1,37 @@
// Copyright 2022 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Created by Andrew Barba on 5/20/22.
//
import TokamakCore
public struct HTMLTitlePreferenceKey: PreferenceKey {
public static var defaultValue: String = ""
public static func reduce(value: inout String, nextValue: () -> String) {
value = nextValue()
}
}
public struct HTMLMetaPreferenceKey: PreferenceKey {
public static var defaultValue: [HTMLMeta.MetaTag] = []
public static func reduce(
value: inout [HTMLMeta.MetaTag],
nextValue: () -> [HTMLMeta.MetaTag]
) {
value += nextValue()
}
}

View File

@ -58,18 +58,40 @@ struct HTMLBody: AnyHTML {
]
}
extension HTMLMeta.MetaTag {
func outerHTML() -> String {
switch self {
case let .charset(charset):
return #"<meta charset="\#(charset)">"#
case let .name(name, content):
return #"<meta name="\#(name)" content="\#(content)">"#
case let .property(property, content):
return #"<meta property="\#(property)" content="\#(content)">"#
case let .httpEquiv(httpEquiv, content):
return #"<meta http-equiv="\#(httpEquiv)" content="\#(content)">"#
}
}
}
public final class StaticHTMLRenderer: Renderer {
private var reconciler: StackReconciler<StaticHTMLRenderer>?
var rootTarget: HTMLTarget
static var title: String = ""
var title: String {
reconciler?.preferenceStore.value(forKey: HTMLTitlePreferenceKey.self).value ?? ""
}
var meta: [HTMLMeta.MetaTag] {
reconciler?.preferenceStore.value(forKey: HTMLMetaPreferenceKey.self).value ?? []
}
public func render(shouldSortAttributes: Bool = false) -> String {
"""
<html>
<head>
<title>\(Self.title)</title>
<title>\(title)</title>
\(meta.map { $0.outerHTML() }.joined(separator: "\n "))
<style>
\(tokamakStyles)
</style>

View File

@ -0,0 +1,83 @@
// Copyright 2022 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Created by Andrew Barba on 5/20/22.
//
import TokamakCore
public struct HTMLMeta: View {
public enum MetaTag: Equatable, Hashable {
case charset(_ charset: String)
case name(_ name: String, content: String)
case property(_ property: String, content: String)
case httpEquiv(_ httpEquiv: String, content: String)
}
var meta: MetaTag
public init(_ value: MetaTag) {
meta = value
}
public init(charset: String) {
meta = .charset(charset)
}
public init(name: String, content: String) {
meta = .name(name, content: content)
}
public init(property: String, content: String) {
meta = .property(property, content: content)
}
public init(httpEquiv: String, content: String) {
meta = .httpEquiv(httpEquiv, content: content)
}
public var body: some View {
EmptyView()
.preference(key: HTMLMetaPreferenceKey.self, value: [meta])
}
}
public extension View {
func htmlMeta(_ value: HTMLMeta.MetaTag) -> some View {
htmlMeta(.init(value))
}
func htmlMeta(charset: String) -> some View {
htmlMeta(.init(charset: charset))
}
func htmlMeta(name: String, content: String) -> some View {
htmlMeta(.init(name: name, content: content))
}
func htmlMeta(property: String, content: String) -> some View {
htmlMeta(.init(property: property, content: content))
}
func htmlMeta(httpEquiv: String, content: String) -> some View {
htmlMeta(.init(httpEquiv: httpEquiv, content: content))
}
func htmlMeta(_ meta: HTMLMeta) -> some View {
Group {
self
meta
}
}
}

View File

@ -0,0 +1,44 @@
// Copyright 2022 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Created by Andrew Barba on 5/20/22.
//
import TokamakCore
public struct HTMLTitle: View {
var title: String
public init(_ title: String) {
self.title = title
}
public var body: some View {
EmptyView()
.preference(key: HTMLTitlePreferenceKey.self, value: title)
}
}
public extension View {
func htmlTitle(_ title: String) -> some View {
htmlTitle(.init(title))
}
func htmlTitle(_ title: HTMLTitle) -> some View {
Group {
self
title
}
}
}

View File

@ -92,6 +92,120 @@ final class HTMLTests: XCTestCase {
.render(shouldSortAttributes: true)
assertSnapshot(matching: insecureHTML, as: .html)
}
func testTitle() {
let resultingHTML = StaticHTMLRenderer(
VStack {
HTMLTitle("Tokamak")
Text("Hello, world!")
}
).render(shouldSortAttributes: true)
assert(resultingHTML.contains("<title>Tokamak</title>"))
assertSnapshot(matching: resultingHTML, as: .html)
}
func testDoubleTitle() {
let resultingHTML = StaticHTMLRenderer(
VStack {
HTMLTitle("Tokamak 1")
Text("Hello, world!")
VStack {
HTMLTitle("Tokamak 2")
}
}
).render(shouldSortAttributes: true)
assert(resultingHTML.contains("<title>Tokamak 2</title>") == true)
assert(resultingHTML.contains("<title>Tokamak 1</title>") == false)
assertSnapshot(matching: resultingHTML, as: .html)
}
func testTitleModifier() {
let resultingHTML = StaticHTMLRenderer(
Text("Hello, world!")
.htmlTitle("Tokamak")
).render(shouldSortAttributes: true)
assert(resultingHTML.contains("<title>Tokamak</title>"))
assertSnapshot(matching: resultingHTML, as: .html)
}
func testDoubleTitleModifier() {
let resultingHTML = StaticHTMLRenderer(
Text("Hello, world!")
.htmlTitle("Tokamak 1")
.htmlTitle("Tokamak 2")
).render(shouldSortAttributes: true)
assert(resultingHTML.contains("<title>Tokamak 2</title>") == true)
assert(resultingHTML.contains("<title>Tokamak 1</title>") == false)
assertSnapshot(matching: resultingHTML, as: .html)
}
func testMetaCharset() {
let resultingHTML = StaticHTMLRenderer(
VStack {
HTMLMeta(charset: "utf-8")
Text("Hello, world!")
}
).render(shouldSortAttributes: true)
assertSnapshot(matching: resultingHTML, as: .html)
}
func testMetaCharsetModifier() {
let resultingHTML = StaticHTMLRenderer(
Text("Hello, world!")
.htmlMeta(charset: "utf-8")
).render(shouldSortAttributes: true)
assertSnapshot(matching: resultingHTML, as: .html)
}
func testMetaAll() {
let resultingHTML = StaticHTMLRenderer(
VStack {
HTMLMeta(charset: "utf-8")
HTMLMeta(name: "description", content: "SwiftUI on the web")
HTMLMeta(property: "og:image", content: "https://image.png")
HTMLMeta(httpEquiv: "refresh", content: "60")
Text("Hello, world!")
}
).render(shouldSortAttributes: true)
assert(resultingHTML.components(separatedBy: "<meta ").count == 5)
assertSnapshot(matching: resultingHTML, as: .html)
}
func testPreferencePropagation() {
var title0 = ""
var title1 = ""
var title2 = ""
var title3 = ""
let resultingHTML = StaticHTMLRenderer(
VStack {
HTMLTitle("Tokamak 1")
.onPreferenceChange(HTMLTitlePreferenceKey.self) { title1 = $0 }
VStack {
HTMLTitle("Tokamak 2")
}
.onPreferenceChange(HTMLTitlePreferenceKey.self) { title2 = $0 }
VStack {
HTMLTitle("Tokamak 3")
}
.onPreferenceChange(HTMLTitlePreferenceKey.self) { title3 = $0 }
}
.onPreferenceChange(HTMLTitlePreferenceKey.self) { title0 = $0 }
).render(shouldSortAttributes: true)
assert(title0 == "Tokamak 3")
assert(title1 == "Tokamak 1")
assert(title2 == "Tokamak 2")
assert(title3 == "Tokamak 3")
assertSnapshot(matching: resultingHTML, as: .html)
}
}
#endif

View File

@ -0,0 +1,267 @@
<html>
<head>
<title>Tokamak 2</title>
<style>
._tokamak-stack {
display: grid;
}
._tokamak-hstack {
grid-auto-flow: column;
column-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-vstack {
grid-auto-flow: row;
row-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-scrollview-hideindicators {
scrollbar-color: transparent;
scrollbar-width: 0;
}
._tokamak-scrollview-hideindicators::-webkit-scrollbar {
width: 0;
height: 0;
}
._tokamak-list {
list-style: none;
overflow-y: auto;
width: 100%;
height: 100%;
padding: 0;
}
._tokamak-disclosuregroup-label {
cursor: pointer;
}
._tokamak-disclosuregroup-chevron-container {
width: .25em;
height: .25em;
padding: 10px;
display: inline-block;
}
._tokamak-disclosuregroup-chevron {
width: 100%;
height: 100%;
transform: rotate(45deg);
border-right: solid 2px rgba(0, 0, 0, 0.25);
border-top: solid 2px rgba(0, 0, 0, 0.25);
}
._tokamak-disclosuregroup-content {
display: flex;
flex-direction: column;
margin-left: 1em;
}
._tokamak-buttonstyle-reset {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: transparent;
border: none;
margin: 0;
padding: 0;
font-size: inherit;
}
@supports (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
-webkit-appearance: default-button;
}
}
@supports not (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
background-color: rgb(55, 120, 246);
border: 1px solid rgb(88, 156, 248);
border-radius: 4px;
}
._tokamak-button-prominence-increased:active {
background-color: rgb(38, 99, 226);
}
@media (prefers-color-scheme:dark) {
._tokamak-button-prominence-increased {
background-color: rgb(56, 116, 225);
}
._tokamak-button-prominence-increased:active {
background-color: rgb(56, 134, 247);
}
}
}
._tokamak-text-redacted {
position: relative;
}
._tokamak-text-redacted::after {
content: " ";
background-color: rgb(200, 200, 200);
position: absolute;
left: 0;
width: calc(100% + .1em);
height: 1.2em;
border-radius: .1em;
}
._tokamak-geometryreader {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
._tokamak-navigationview {
display: flex;
flex-direction: row;
align-items: stretch;
width: 100%;
height: 100%;
}
._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
padding-top: 50px;
}
._tokamak-navigationview-destination {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
flex-grow: 1;
height: 100%;
}
._tokamak-toolbar {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 50px;
display: flex;
align-items: center;
overflow: hidden;
background: rgba(200, 200, 200, 0.2);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
._tokamak-toolbar-content {
flex: 1 1 auto;
display: flex;
height: 100%;
}
._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
margin-right: 8px;
}
._tokamak-toolbar-trailing > * {
margin-left: 8px;
}
._tokamak-toolbar-leading {
margin-right: auto;
align-items: center;
justify-content: flex-start;
padding-left: 16px;
}
._tokamak-toolbar-center {
align-items: center;
justify-content: center;
}
._tokamak-toolbar-trailing {
margin-left: auto;
align-items: center;
justify-content: flex-end;
padding-right: 16px;
}
._tokamak-toolbar-button {
padding: 2px 4px;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.05);
height: 25px;
padding: 0 8px;
display: flex;
align-items: center;
}
._tokamak-toolbar-button:hover {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.05);
}
._tokamak-toolbar-button:active {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.1);
}
._tokamak-toolbar-textfield input {
padding: 4px 4px 4px 8px;
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.05);
width: 100%;
height: 100%;
}
._tokamak-formcontrol, ._tokamak-formcontrol-reset {
color-scheme: light dark;
}
._tokamak-formcontrol-reset {
background: none;
border: none;
}
._tokamak-link {
text-decoration: none;
}
._tokamak-texteditor {
width: 100%;
height: 100%;
}
._tokamak-aspect-ratio-fill > img {
object-fit: fill;
}
._tokamak-aspect-ratio-fit > img {
object-fit: contain;
}
@media (prefers-color-scheme:dark) {
._tokamak-text-redacted::after {
background-color: rgb(100, 100, 100);
}
._tokamak-disclosuregroup-chevron {
border-right-color: rgba(255, 255, 255, 0.25);
border-top-color: rgba(255, 255, 255, 0.25);
}
._tokamak-toolbar {
background: rgba(100, 100, 100, 0.2);
}
._tokamak-toolbar-button {
border: 1px solid rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-button:hover {
background-color: rgba(255, 255, 255, 0.05);
}
._tokamak-toolbar-button:active {
background-color: rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-textfield input {
background-color: rgba(255, 255, 255, 0.05);
color: #FFFFFF;
}
}
</style>
</head>
<body style="margin: 0;display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
overflow: hidden;"><div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
"><span style="font-family: system, -apple-system, '.SFNSText-Regular', 'San Francisco', 'Roboto', 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif; font-size: 17.0; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;
color: rgba(0.0, 0.0, 0.0, 1.0);
font-style: normal;
font-weight: 400;
letter-spacing: normal;
vertical-align: baseline;
text-decoration: none;
text-decoration-color: inherit;
text-align: left;">Hello, world!</span>
<div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
"></div></div></body>
</html>

View File

@ -0,0 +1,260 @@
<html>
<head>
<title>Tokamak 2</title>
<style>
._tokamak-stack {
display: grid;
}
._tokamak-hstack {
grid-auto-flow: column;
column-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-vstack {
grid-auto-flow: row;
row-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-scrollview-hideindicators {
scrollbar-color: transparent;
scrollbar-width: 0;
}
._tokamak-scrollview-hideindicators::-webkit-scrollbar {
width: 0;
height: 0;
}
._tokamak-list {
list-style: none;
overflow-y: auto;
width: 100%;
height: 100%;
padding: 0;
}
._tokamak-disclosuregroup-label {
cursor: pointer;
}
._tokamak-disclosuregroup-chevron-container {
width: .25em;
height: .25em;
padding: 10px;
display: inline-block;
}
._tokamak-disclosuregroup-chevron {
width: 100%;
height: 100%;
transform: rotate(45deg);
border-right: solid 2px rgba(0, 0, 0, 0.25);
border-top: solid 2px rgba(0, 0, 0, 0.25);
}
._tokamak-disclosuregroup-content {
display: flex;
flex-direction: column;
margin-left: 1em;
}
._tokamak-buttonstyle-reset {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: transparent;
border: none;
margin: 0;
padding: 0;
font-size: inherit;
}
@supports (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
-webkit-appearance: default-button;
}
}
@supports not (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
background-color: rgb(55, 120, 246);
border: 1px solid rgb(88, 156, 248);
border-radius: 4px;
}
._tokamak-button-prominence-increased:active {
background-color: rgb(38, 99, 226);
}
@media (prefers-color-scheme:dark) {
._tokamak-button-prominence-increased {
background-color: rgb(56, 116, 225);
}
._tokamak-button-prominence-increased:active {
background-color: rgb(56, 134, 247);
}
}
}
._tokamak-text-redacted {
position: relative;
}
._tokamak-text-redacted::after {
content: " ";
background-color: rgb(200, 200, 200);
position: absolute;
left: 0;
width: calc(100% + .1em);
height: 1.2em;
border-radius: .1em;
}
._tokamak-geometryreader {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
._tokamak-navigationview {
display: flex;
flex-direction: row;
align-items: stretch;
width: 100%;
height: 100%;
}
._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
padding-top: 50px;
}
._tokamak-navigationview-destination {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
flex-grow: 1;
height: 100%;
}
._tokamak-toolbar {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 50px;
display: flex;
align-items: center;
overflow: hidden;
background: rgba(200, 200, 200, 0.2);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
._tokamak-toolbar-content {
flex: 1 1 auto;
display: flex;
height: 100%;
}
._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
margin-right: 8px;
}
._tokamak-toolbar-trailing > * {
margin-left: 8px;
}
._tokamak-toolbar-leading {
margin-right: auto;
align-items: center;
justify-content: flex-start;
padding-left: 16px;
}
._tokamak-toolbar-center {
align-items: center;
justify-content: center;
}
._tokamak-toolbar-trailing {
margin-left: auto;
align-items: center;
justify-content: flex-end;
padding-right: 16px;
}
._tokamak-toolbar-button {
padding: 2px 4px;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.05);
height: 25px;
padding: 0 8px;
display: flex;
align-items: center;
}
._tokamak-toolbar-button:hover {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.05);
}
._tokamak-toolbar-button:active {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.1);
}
._tokamak-toolbar-textfield input {
padding: 4px 4px 4px 8px;
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.05);
width: 100%;
height: 100%;
}
._tokamak-formcontrol, ._tokamak-formcontrol-reset {
color-scheme: light dark;
}
._tokamak-formcontrol-reset {
background: none;
border: none;
}
._tokamak-link {
text-decoration: none;
}
._tokamak-texteditor {
width: 100%;
height: 100%;
}
._tokamak-aspect-ratio-fill > img {
object-fit: fill;
}
._tokamak-aspect-ratio-fit > img {
object-fit: contain;
}
@media (prefers-color-scheme:dark) {
._tokamak-text-redacted::after {
background-color: rgb(100, 100, 100);
}
._tokamak-disclosuregroup-chevron {
border-right-color: rgba(255, 255, 255, 0.25);
border-top-color: rgba(255, 255, 255, 0.25);
}
._tokamak-toolbar {
background: rgba(100, 100, 100, 0.2);
}
._tokamak-toolbar-button {
border: 1px solid rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-button:hover {
background-color: rgba(255, 255, 255, 0.05);
}
._tokamak-toolbar-button:active {
background-color: rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-textfield input {
background-color: rgba(255, 255, 255, 0.05);
color: #FFFFFF;
}
}
</style>
</head>
<body style="margin: 0;display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
overflow: hidden;"><span style="font-family: system, -apple-system, '.SFNSText-Regular', 'San Francisco', 'Roboto', 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif; font-size: 17.0; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;
color: rgba(0.0, 0.0, 0.0, 1.0);
font-style: normal;
font-weight: 400;
letter-spacing: normal;
vertical-align: baseline;
text-decoration: none;
text-decoration-color: inherit;
text-align: left;">Hello, world!</span></body>
</html>

View File

@ -0,0 +1,266 @@
<html>
<head>
<title></title>
<meta charset="utf-8">
<meta name="description" content="SwiftUI on the web">
<meta property="og:image" content="https://image.png">
<meta http-equiv="refresh" content="60">
<style>
._tokamak-stack {
display: grid;
}
._tokamak-hstack {
grid-auto-flow: column;
column-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-vstack {
grid-auto-flow: row;
row-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-scrollview-hideindicators {
scrollbar-color: transparent;
scrollbar-width: 0;
}
._tokamak-scrollview-hideindicators::-webkit-scrollbar {
width: 0;
height: 0;
}
._tokamak-list {
list-style: none;
overflow-y: auto;
width: 100%;
height: 100%;
padding: 0;
}
._tokamak-disclosuregroup-label {
cursor: pointer;
}
._tokamak-disclosuregroup-chevron-container {
width: .25em;
height: .25em;
padding: 10px;
display: inline-block;
}
._tokamak-disclosuregroup-chevron {
width: 100%;
height: 100%;
transform: rotate(45deg);
border-right: solid 2px rgba(0, 0, 0, 0.25);
border-top: solid 2px rgba(0, 0, 0, 0.25);
}
._tokamak-disclosuregroup-content {
display: flex;
flex-direction: column;
margin-left: 1em;
}
._tokamak-buttonstyle-reset {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: transparent;
border: none;
margin: 0;
padding: 0;
font-size: inherit;
}
@supports (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
-webkit-appearance: default-button;
}
}
@supports not (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
background-color: rgb(55, 120, 246);
border: 1px solid rgb(88, 156, 248);
border-radius: 4px;
}
._tokamak-button-prominence-increased:active {
background-color: rgb(38, 99, 226);
}
@media (prefers-color-scheme:dark) {
._tokamak-button-prominence-increased {
background-color: rgb(56, 116, 225);
}
._tokamak-button-prominence-increased:active {
background-color: rgb(56, 134, 247);
}
}
}
._tokamak-text-redacted {
position: relative;
}
._tokamak-text-redacted::after {
content: " ";
background-color: rgb(200, 200, 200);
position: absolute;
left: 0;
width: calc(100% + .1em);
height: 1.2em;
border-radius: .1em;
}
._tokamak-geometryreader {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
._tokamak-navigationview {
display: flex;
flex-direction: row;
align-items: stretch;
width: 100%;
height: 100%;
}
._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
padding-top: 50px;
}
._tokamak-navigationview-destination {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
flex-grow: 1;
height: 100%;
}
._tokamak-toolbar {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 50px;
display: flex;
align-items: center;
overflow: hidden;
background: rgba(200, 200, 200, 0.2);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
._tokamak-toolbar-content {
flex: 1 1 auto;
display: flex;
height: 100%;
}
._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
margin-right: 8px;
}
._tokamak-toolbar-trailing > * {
margin-left: 8px;
}
._tokamak-toolbar-leading {
margin-right: auto;
align-items: center;
justify-content: flex-start;
padding-left: 16px;
}
._tokamak-toolbar-center {
align-items: center;
justify-content: center;
}
._tokamak-toolbar-trailing {
margin-left: auto;
align-items: center;
justify-content: flex-end;
padding-right: 16px;
}
._tokamak-toolbar-button {
padding: 2px 4px;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.05);
height: 25px;
padding: 0 8px;
display: flex;
align-items: center;
}
._tokamak-toolbar-button:hover {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.05);
}
._tokamak-toolbar-button:active {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.1);
}
._tokamak-toolbar-textfield input {
padding: 4px 4px 4px 8px;
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.05);
width: 100%;
height: 100%;
}
._tokamak-formcontrol, ._tokamak-formcontrol-reset {
color-scheme: light dark;
}
._tokamak-formcontrol-reset {
background: none;
border: none;
}
._tokamak-link {
text-decoration: none;
}
._tokamak-texteditor {
width: 100%;
height: 100%;
}
._tokamak-aspect-ratio-fill > img {
object-fit: fill;
}
._tokamak-aspect-ratio-fit > img {
object-fit: contain;
}
@media (prefers-color-scheme:dark) {
._tokamak-text-redacted::after {
background-color: rgb(100, 100, 100);
}
._tokamak-disclosuregroup-chevron {
border-right-color: rgba(255, 255, 255, 0.25);
border-top-color: rgba(255, 255, 255, 0.25);
}
._tokamak-toolbar {
background: rgba(100, 100, 100, 0.2);
}
._tokamak-toolbar-button {
border: 1px solid rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-button:hover {
background-color: rgba(255, 255, 255, 0.05);
}
._tokamak-toolbar-button:active {
background-color: rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-textfield input {
background-color: rgba(255, 255, 255, 0.05);
color: #FFFFFF;
}
}
</style>
</head>
<body style="margin: 0;display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
overflow: hidden;"><div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
"><span style="font-family: system, -apple-system, '.SFNSText-Regular', 'San Francisco', 'Roboto', 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif; font-size: 17.0; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;
color: rgba(0.0, 0.0, 0.0, 1.0);
font-style: normal;
font-weight: 400;
letter-spacing: normal;
vertical-align: baseline;
text-decoration: none;
text-decoration-color: inherit;
text-align: left;">Hello, world!</span></div></body>
</html>

View File

@ -0,0 +1,263 @@
<html>
<head>
<title></title>
<meta charset="utf-8">
<style>
._tokamak-stack {
display: grid;
}
._tokamak-hstack {
grid-auto-flow: column;
column-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-vstack {
grid-auto-flow: row;
row-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-scrollview-hideindicators {
scrollbar-color: transparent;
scrollbar-width: 0;
}
._tokamak-scrollview-hideindicators::-webkit-scrollbar {
width: 0;
height: 0;
}
._tokamak-list {
list-style: none;
overflow-y: auto;
width: 100%;
height: 100%;
padding: 0;
}
._tokamak-disclosuregroup-label {
cursor: pointer;
}
._tokamak-disclosuregroup-chevron-container {
width: .25em;
height: .25em;
padding: 10px;
display: inline-block;
}
._tokamak-disclosuregroup-chevron {
width: 100%;
height: 100%;
transform: rotate(45deg);
border-right: solid 2px rgba(0, 0, 0, 0.25);
border-top: solid 2px rgba(0, 0, 0, 0.25);
}
._tokamak-disclosuregroup-content {
display: flex;
flex-direction: column;
margin-left: 1em;
}
._tokamak-buttonstyle-reset {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: transparent;
border: none;
margin: 0;
padding: 0;
font-size: inherit;
}
@supports (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
-webkit-appearance: default-button;
}
}
@supports not (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
background-color: rgb(55, 120, 246);
border: 1px solid rgb(88, 156, 248);
border-radius: 4px;
}
._tokamak-button-prominence-increased:active {
background-color: rgb(38, 99, 226);
}
@media (prefers-color-scheme:dark) {
._tokamak-button-prominence-increased {
background-color: rgb(56, 116, 225);
}
._tokamak-button-prominence-increased:active {
background-color: rgb(56, 134, 247);
}
}
}
._tokamak-text-redacted {
position: relative;
}
._tokamak-text-redacted::after {
content: " ";
background-color: rgb(200, 200, 200);
position: absolute;
left: 0;
width: calc(100% + .1em);
height: 1.2em;
border-radius: .1em;
}
._tokamak-geometryreader {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
._tokamak-navigationview {
display: flex;
flex-direction: row;
align-items: stretch;
width: 100%;
height: 100%;
}
._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
padding-top: 50px;
}
._tokamak-navigationview-destination {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
flex-grow: 1;
height: 100%;
}
._tokamak-toolbar {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 50px;
display: flex;
align-items: center;
overflow: hidden;
background: rgba(200, 200, 200, 0.2);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
._tokamak-toolbar-content {
flex: 1 1 auto;
display: flex;
height: 100%;
}
._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
margin-right: 8px;
}
._tokamak-toolbar-trailing > * {
margin-left: 8px;
}
._tokamak-toolbar-leading {
margin-right: auto;
align-items: center;
justify-content: flex-start;
padding-left: 16px;
}
._tokamak-toolbar-center {
align-items: center;
justify-content: center;
}
._tokamak-toolbar-trailing {
margin-left: auto;
align-items: center;
justify-content: flex-end;
padding-right: 16px;
}
._tokamak-toolbar-button {
padding: 2px 4px;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.05);
height: 25px;
padding: 0 8px;
display: flex;
align-items: center;
}
._tokamak-toolbar-button:hover {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.05);
}
._tokamak-toolbar-button:active {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.1);
}
._tokamak-toolbar-textfield input {
padding: 4px 4px 4px 8px;
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.05);
width: 100%;
height: 100%;
}
._tokamak-formcontrol, ._tokamak-formcontrol-reset {
color-scheme: light dark;
}
._tokamak-formcontrol-reset {
background: none;
border: none;
}
._tokamak-link {
text-decoration: none;
}
._tokamak-texteditor {
width: 100%;
height: 100%;
}
._tokamak-aspect-ratio-fill > img {
object-fit: fill;
}
._tokamak-aspect-ratio-fit > img {
object-fit: contain;
}
@media (prefers-color-scheme:dark) {
._tokamak-text-redacted::after {
background-color: rgb(100, 100, 100);
}
._tokamak-disclosuregroup-chevron {
border-right-color: rgba(255, 255, 255, 0.25);
border-top-color: rgba(255, 255, 255, 0.25);
}
._tokamak-toolbar {
background: rgba(100, 100, 100, 0.2);
}
._tokamak-toolbar-button {
border: 1px solid rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-button:hover {
background-color: rgba(255, 255, 255, 0.05);
}
._tokamak-toolbar-button:active {
background-color: rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-textfield input {
background-color: rgba(255, 255, 255, 0.05);
color: #FFFFFF;
}
}
</style>
</head>
<body style="margin: 0;display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
overflow: hidden;"><div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
"><span style="font-family: system, -apple-system, '.SFNSText-Regular', 'San Francisco', 'Roboto', 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif; font-size: 17.0; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;
color: rgba(0.0, 0.0, 0.0, 1.0);
font-style: normal;
font-weight: 400;
letter-spacing: normal;
vertical-align: baseline;
text-decoration: none;
text-decoration-color: inherit;
text-align: left;">Hello, world!</span></div></body>
</html>

View File

@ -0,0 +1,260 @@
<html>
<head>
<title></title>
<meta charset="utf-8">
<style>
._tokamak-stack {
display: grid;
}
._tokamak-hstack {
grid-auto-flow: column;
column-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-vstack {
grid-auto-flow: row;
row-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-scrollview-hideindicators {
scrollbar-color: transparent;
scrollbar-width: 0;
}
._tokamak-scrollview-hideindicators::-webkit-scrollbar {
width: 0;
height: 0;
}
._tokamak-list {
list-style: none;
overflow-y: auto;
width: 100%;
height: 100%;
padding: 0;
}
._tokamak-disclosuregroup-label {
cursor: pointer;
}
._tokamak-disclosuregroup-chevron-container {
width: .25em;
height: .25em;
padding: 10px;
display: inline-block;
}
._tokamak-disclosuregroup-chevron {
width: 100%;
height: 100%;
transform: rotate(45deg);
border-right: solid 2px rgba(0, 0, 0, 0.25);
border-top: solid 2px rgba(0, 0, 0, 0.25);
}
._tokamak-disclosuregroup-content {
display: flex;
flex-direction: column;
margin-left: 1em;
}
._tokamak-buttonstyle-reset {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: transparent;
border: none;
margin: 0;
padding: 0;
font-size: inherit;
}
@supports (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
-webkit-appearance: default-button;
}
}
@supports not (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
background-color: rgb(55, 120, 246);
border: 1px solid rgb(88, 156, 248);
border-radius: 4px;
}
._tokamak-button-prominence-increased:active {
background-color: rgb(38, 99, 226);
}
@media (prefers-color-scheme:dark) {
._tokamak-button-prominence-increased {
background-color: rgb(56, 116, 225);
}
._tokamak-button-prominence-increased:active {
background-color: rgb(56, 134, 247);
}
}
}
._tokamak-text-redacted {
position: relative;
}
._tokamak-text-redacted::after {
content: " ";
background-color: rgb(200, 200, 200);
position: absolute;
left: 0;
width: calc(100% + .1em);
height: 1.2em;
border-radius: .1em;
}
._tokamak-geometryreader {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
._tokamak-navigationview {
display: flex;
flex-direction: row;
align-items: stretch;
width: 100%;
height: 100%;
}
._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
padding-top: 50px;
}
._tokamak-navigationview-destination {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
flex-grow: 1;
height: 100%;
}
._tokamak-toolbar {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 50px;
display: flex;
align-items: center;
overflow: hidden;
background: rgba(200, 200, 200, 0.2);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
._tokamak-toolbar-content {
flex: 1 1 auto;
display: flex;
height: 100%;
}
._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
margin-right: 8px;
}
._tokamak-toolbar-trailing > * {
margin-left: 8px;
}
._tokamak-toolbar-leading {
margin-right: auto;
align-items: center;
justify-content: flex-start;
padding-left: 16px;
}
._tokamak-toolbar-center {
align-items: center;
justify-content: center;
}
._tokamak-toolbar-trailing {
margin-left: auto;
align-items: center;
justify-content: flex-end;
padding-right: 16px;
}
._tokamak-toolbar-button {
padding: 2px 4px;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.05);
height: 25px;
padding: 0 8px;
display: flex;
align-items: center;
}
._tokamak-toolbar-button:hover {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.05);
}
._tokamak-toolbar-button:active {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.1);
}
._tokamak-toolbar-textfield input {
padding: 4px 4px 4px 8px;
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.05);
width: 100%;
height: 100%;
}
._tokamak-formcontrol, ._tokamak-formcontrol-reset {
color-scheme: light dark;
}
._tokamak-formcontrol-reset {
background: none;
border: none;
}
._tokamak-link {
text-decoration: none;
}
._tokamak-texteditor {
width: 100%;
height: 100%;
}
._tokamak-aspect-ratio-fill > img {
object-fit: fill;
}
._tokamak-aspect-ratio-fit > img {
object-fit: contain;
}
@media (prefers-color-scheme:dark) {
._tokamak-text-redacted::after {
background-color: rgb(100, 100, 100);
}
._tokamak-disclosuregroup-chevron {
border-right-color: rgba(255, 255, 255, 0.25);
border-top-color: rgba(255, 255, 255, 0.25);
}
._tokamak-toolbar {
background: rgba(100, 100, 100, 0.2);
}
._tokamak-toolbar-button {
border: 1px solid rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-button:hover {
background-color: rgba(255, 255, 255, 0.05);
}
._tokamak-toolbar-button:active {
background-color: rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-textfield input {
background-color: rgba(255, 255, 255, 0.05);
color: #FFFFFF;
}
}
</style>
</head>
<body style="margin: 0;display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
overflow: hidden;"><span style="font-family: system, -apple-system, '.SFNSText-Regular', 'San Francisco', 'Roboto', 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif; font-size: 17.0; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;
color: rgba(0.0, 0.0, 0.0, 1.0);
font-style: normal;
font-weight: 400;
letter-spacing: normal;
vertical-align: baseline;
text-decoration: none;
text-decoration-color: inherit;
text-align: left;">Hello, world!</span></body>
</html>

View File

@ -0,0 +1,261 @@
<html>
<head>
<title>Tokamak 3</title>
<style>
._tokamak-stack {
display: grid;
}
._tokamak-hstack {
grid-auto-flow: column;
column-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-vstack {
grid-auto-flow: row;
row-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-scrollview-hideindicators {
scrollbar-color: transparent;
scrollbar-width: 0;
}
._tokamak-scrollview-hideindicators::-webkit-scrollbar {
width: 0;
height: 0;
}
._tokamak-list {
list-style: none;
overflow-y: auto;
width: 100%;
height: 100%;
padding: 0;
}
._tokamak-disclosuregroup-label {
cursor: pointer;
}
._tokamak-disclosuregroup-chevron-container {
width: .25em;
height: .25em;
padding: 10px;
display: inline-block;
}
._tokamak-disclosuregroup-chevron {
width: 100%;
height: 100%;
transform: rotate(45deg);
border-right: solid 2px rgba(0, 0, 0, 0.25);
border-top: solid 2px rgba(0, 0, 0, 0.25);
}
._tokamak-disclosuregroup-content {
display: flex;
flex-direction: column;
margin-left: 1em;
}
._tokamak-buttonstyle-reset {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: transparent;
border: none;
margin: 0;
padding: 0;
font-size: inherit;
}
@supports (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
-webkit-appearance: default-button;
}
}
@supports not (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
background-color: rgb(55, 120, 246);
border: 1px solid rgb(88, 156, 248);
border-radius: 4px;
}
._tokamak-button-prominence-increased:active {
background-color: rgb(38, 99, 226);
}
@media (prefers-color-scheme:dark) {
._tokamak-button-prominence-increased {
background-color: rgb(56, 116, 225);
}
._tokamak-button-prominence-increased:active {
background-color: rgb(56, 134, 247);
}
}
}
._tokamak-text-redacted {
position: relative;
}
._tokamak-text-redacted::after {
content: " ";
background-color: rgb(200, 200, 200);
position: absolute;
left: 0;
width: calc(100% + .1em);
height: 1.2em;
border-radius: .1em;
}
._tokamak-geometryreader {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
._tokamak-navigationview {
display: flex;
flex-direction: row;
align-items: stretch;
width: 100%;
height: 100%;
}
._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
padding-top: 50px;
}
._tokamak-navigationview-destination {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
flex-grow: 1;
height: 100%;
}
._tokamak-toolbar {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 50px;
display: flex;
align-items: center;
overflow: hidden;
background: rgba(200, 200, 200, 0.2);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
._tokamak-toolbar-content {
flex: 1 1 auto;
display: flex;
height: 100%;
}
._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
margin-right: 8px;
}
._tokamak-toolbar-trailing > * {
margin-left: 8px;
}
._tokamak-toolbar-leading {
margin-right: auto;
align-items: center;
justify-content: flex-start;
padding-left: 16px;
}
._tokamak-toolbar-center {
align-items: center;
justify-content: center;
}
._tokamak-toolbar-trailing {
margin-left: auto;
align-items: center;
justify-content: flex-end;
padding-right: 16px;
}
._tokamak-toolbar-button {
padding: 2px 4px;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.05);
height: 25px;
padding: 0 8px;
display: flex;
align-items: center;
}
._tokamak-toolbar-button:hover {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.05);
}
._tokamak-toolbar-button:active {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.1);
}
._tokamak-toolbar-textfield input {
padding: 4px 4px 4px 8px;
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.05);
width: 100%;
height: 100%;
}
._tokamak-formcontrol, ._tokamak-formcontrol-reset {
color-scheme: light dark;
}
._tokamak-formcontrol-reset {
background: none;
border: none;
}
._tokamak-link {
text-decoration: none;
}
._tokamak-texteditor {
width: 100%;
height: 100%;
}
._tokamak-aspect-ratio-fill > img {
object-fit: fill;
}
._tokamak-aspect-ratio-fit > img {
object-fit: contain;
}
@media (prefers-color-scheme:dark) {
._tokamak-text-redacted::after {
background-color: rgb(100, 100, 100);
}
._tokamak-disclosuregroup-chevron {
border-right-color: rgba(255, 255, 255, 0.25);
border-top-color: rgba(255, 255, 255, 0.25);
}
._tokamak-toolbar {
background: rgba(100, 100, 100, 0.2);
}
._tokamak-toolbar-button {
border: 1px solid rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-button:hover {
background-color: rgba(255, 255, 255, 0.05);
}
._tokamak-toolbar-button:active {
background-color: rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-textfield input {
background-color: rgba(255, 255, 255, 0.05);
color: #FFFFFF;
}
}
</style>
</head>
<body style="margin: 0;display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
overflow: hidden;"><div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
"><div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
"></div>
<div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
"></div></div></body>
</html>

View File

@ -0,0 +1,263 @@
<html>
<head>
<title>Tokamak</title>
<style>
._tokamak-stack {
display: grid;
}
._tokamak-hstack {
grid-auto-flow: column;
column-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-vstack {
grid-auto-flow: row;
row-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-scrollview-hideindicators {
scrollbar-color: transparent;
scrollbar-width: 0;
}
._tokamak-scrollview-hideindicators::-webkit-scrollbar {
width: 0;
height: 0;
}
._tokamak-list {
list-style: none;
overflow-y: auto;
width: 100%;
height: 100%;
padding: 0;
}
._tokamak-disclosuregroup-label {
cursor: pointer;
}
._tokamak-disclosuregroup-chevron-container {
width: .25em;
height: .25em;
padding: 10px;
display: inline-block;
}
._tokamak-disclosuregroup-chevron {
width: 100%;
height: 100%;
transform: rotate(45deg);
border-right: solid 2px rgba(0, 0, 0, 0.25);
border-top: solid 2px rgba(0, 0, 0, 0.25);
}
._tokamak-disclosuregroup-content {
display: flex;
flex-direction: column;
margin-left: 1em;
}
._tokamak-buttonstyle-reset {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: transparent;
border: none;
margin: 0;
padding: 0;
font-size: inherit;
}
@supports (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
-webkit-appearance: default-button;
}
}
@supports not (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
background-color: rgb(55, 120, 246);
border: 1px solid rgb(88, 156, 248);
border-radius: 4px;
}
._tokamak-button-prominence-increased:active {
background-color: rgb(38, 99, 226);
}
@media (prefers-color-scheme:dark) {
._tokamak-button-prominence-increased {
background-color: rgb(56, 116, 225);
}
._tokamak-button-prominence-increased:active {
background-color: rgb(56, 134, 247);
}
}
}
._tokamak-text-redacted {
position: relative;
}
._tokamak-text-redacted::after {
content: " ";
background-color: rgb(200, 200, 200);
position: absolute;
left: 0;
width: calc(100% + .1em);
height: 1.2em;
border-radius: .1em;
}
._tokamak-geometryreader {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
._tokamak-navigationview {
display: flex;
flex-direction: row;
align-items: stretch;
width: 100%;
height: 100%;
}
._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
padding-top: 50px;
}
._tokamak-navigationview-destination {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
flex-grow: 1;
height: 100%;
}
._tokamak-toolbar {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 50px;
display: flex;
align-items: center;
overflow: hidden;
background: rgba(200, 200, 200, 0.2);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
._tokamak-toolbar-content {
flex: 1 1 auto;
display: flex;
height: 100%;
}
._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
margin-right: 8px;
}
._tokamak-toolbar-trailing > * {
margin-left: 8px;
}
._tokamak-toolbar-leading {
margin-right: auto;
align-items: center;
justify-content: flex-start;
padding-left: 16px;
}
._tokamak-toolbar-center {
align-items: center;
justify-content: center;
}
._tokamak-toolbar-trailing {
margin-left: auto;
align-items: center;
justify-content: flex-end;
padding-right: 16px;
}
._tokamak-toolbar-button {
padding: 2px 4px;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.05);
height: 25px;
padding: 0 8px;
display: flex;
align-items: center;
}
._tokamak-toolbar-button:hover {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.05);
}
._tokamak-toolbar-button:active {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.1);
}
._tokamak-toolbar-textfield input {
padding: 4px 4px 4px 8px;
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.05);
width: 100%;
height: 100%;
}
._tokamak-formcontrol, ._tokamak-formcontrol-reset {
color-scheme: light dark;
}
._tokamak-formcontrol-reset {
background: none;
border: none;
}
._tokamak-link {
text-decoration: none;
}
._tokamak-texteditor {
width: 100%;
height: 100%;
}
._tokamak-aspect-ratio-fill > img {
object-fit: fill;
}
._tokamak-aspect-ratio-fit > img {
object-fit: contain;
}
@media (prefers-color-scheme:dark) {
._tokamak-text-redacted::after {
background-color: rgb(100, 100, 100);
}
._tokamak-disclosuregroup-chevron {
border-right-color: rgba(255, 255, 255, 0.25);
border-top-color: rgba(255, 255, 255, 0.25);
}
._tokamak-toolbar {
background: rgba(100, 100, 100, 0.2);
}
._tokamak-toolbar-button {
border: 1px solid rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-button:hover {
background-color: rgba(255, 255, 255, 0.05);
}
._tokamak-toolbar-button:active {
background-color: rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-textfield input {
background-color: rgba(255, 255, 255, 0.05);
color: #FFFFFF;
}
}
</style>
</head>
<body style="margin: 0;display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
overflow: hidden;"><div class="_tokamak-stack _tokamak-vstack" style="justify-items: center;
"><span style="font-family: system, -apple-system, '.SFNSText-Regular', 'San Francisco', 'Roboto', 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif; font-size: 17.0; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;
color: rgba(0.0, 0.0, 0.0, 1.0);
font-style: normal;
font-weight: 400;
letter-spacing: normal;
vertical-align: baseline;
text-decoration: none;
text-decoration-color: inherit;
text-align: left;">Hello, world!</span></div></body>
</html>

View File

@ -0,0 +1,260 @@
<html>
<head>
<title>Tokamak</title>
<style>
._tokamak-stack {
display: grid;
}
._tokamak-hstack {
grid-auto-flow: column;
column-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-vstack {
grid-auto-flow: row;
row-gap: var(--tokamak-stack-gap, 8px);
}
._tokamak-scrollview-hideindicators {
scrollbar-color: transparent;
scrollbar-width: 0;
}
._tokamak-scrollview-hideindicators::-webkit-scrollbar {
width: 0;
height: 0;
}
._tokamak-list {
list-style: none;
overflow-y: auto;
width: 100%;
height: 100%;
padding: 0;
}
._tokamak-disclosuregroup-label {
cursor: pointer;
}
._tokamak-disclosuregroup-chevron-container {
width: .25em;
height: .25em;
padding: 10px;
display: inline-block;
}
._tokamak-disclosuregroup-chevron {
width: 100%;
height: 100%;
transform: rotate(45deg);
border-right: solid 2px rgba(0, 0, 0, 0.25);
border-top: solid 2px rgba(0, 0, 0, 0.25);
}
._tokamak-disclosuregroup-content {
display: flex;
flex-direction: column;
margin-left: 1em;
}
._tokamak-buttonstyle-reset {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: transparent;
border: none;
margin: 0;
padding: 0;
font-size: inherit;
}
@supports (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
-webkit-appearance: default-button;
}
}
@supports not (-webkit-appearance: default-button) {
._tokamak-button-prominence-increased {
background-color: rgb(55, 120, 246);
border: 1px solid rgb(88, 156, 248);
border-radius: 4px;
}
._tokamak-button-prominence-increased:active {
background-color: rgb(38, 99, 226);
}
@media (prefers-color-scheme:dark) {
._tokamak-button-prominence-increased {
background-color: rgb(56, 116, 225);
}
._tokamak-button-prominence-increased:active {
background-color: rgb(56, 134, 247);
}
}
}
._tokamak-text-redacted {
position: relative;
}
._tokamak-text-redacted::after {
content: " ";
background-color: rgb(200, 200, 200);
position: absolute;
left: 0;
width: calc(100% + .1em);
height: 1.2em;
border-radius: .1em;
}
._tokamak-geometryreader {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
._tokamak-navigationview {
display: flex;
flex-direction: row;
align-items: stretch;
width: 100%;
height: 100%;
}
._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
padding-top: 50px;
}
._tokamak-navigationview-destination {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
flex-grow: 1;
height: 100%;
}
._tokamak-toolbar {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 50px;
display: flex;
align-items: center;
overflow: hidden;
background: rgba(200, 200, 200, 0.2);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
._tokamak-toolbar-content {
flex: 1 1 auto;
display: flex;
height: 100%;
}
._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
margin-right: 8px;
}
._tokamak-toolbar-trailing > * {
margin-left: 8px;
}
._tokamak-toolbar-leading {
margin-right: auto;
align-items: center;
justify-content: flex-start;
padding-left: 16px;
}
._tokamak-toolbar-center {
align-items: center;
justify-content: center;
}
._tokamak-toolbar-trailing {
margin-left: auto;
align-items: center;
justify-content: flex-end;
padding-right: 16px;
}
._tokamak-toolbar-button {
padding: 2px 4px;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.05);
height: 25px;
padding: 0 8px;
display: flex;
align-items: center;
}
._tokamak-toolbar-button:hover {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.05);
}
._tokamak-toolbar-button:active {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.1);
}
._tokamak-toolbar-textfield input {
padding: 4px 4px 4px 8px;
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.05);
width: 100%;
height: 100%;
}
._tokamak-formcontrol, ._tokamak-formcontrol-reset {
color-scheme: light dark;
}
._tokamak-formcontrol-reset {
background: none;
border: none;
}
._tokamak-link {
text-decoration: none;
}
._tokamak-texteditor {
width: 100%;
height: 100%;
}
._tokamak-aspect-ratio-fill > img {
object-fit: fill;
}
._tokamak-aspect-ratio-fit > img {
object-fit: contain;
}
@media (prefers-color-scheme:dark) {
._tokamak-text-redacted::after {
background-color: rgb(100, 100, 100);
}
._tokamak-disclosuregroup-chevron {
border-right-color: rgba(255, 255, 255, 0.25);
border-top-color: rgba(255, 255, 255, 0.25);
}
._tokamak-toolbar {
background: rgba(100, 100, 100, 0.2);
}
._tokamak-toolbar-button {
border: 1px solid rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-button:hover {
background-color: rgba(255, 255, 255, 0.05);
}
._tokamak-toolbar-button:active {
background-color: rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-textfield input {
background-color: rgba(255, 255, 255, 0.05);
color: #FFFFFF;
}
}
</style>
</head>
<body style="margin: 0;display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
overflow: hidden;"><span style="font-family: system, -apple-system, '.SFNSText-Regular', 'San Francisco', 'Roboto', 'Segoe UI', 'Helvetica Neue', 'Lucida Grande', sans-serif; font-size: 17.0; font-style: normal; font-variant: normal; font-weight: 400; line-height: normal;
color: rgba(0.0, 0.0, 0.0, 1.0);
font-style: normal;
font-weight: 400;
letter-spacing: normal;
vertical-align: baseline;
text-decoration: none;
text-decoration-color: inherit;
text-align: left;">Hello, world!</span></body>
</html>