Git Product home page Git Product logo

tokamak's Introduction

Tokamak logo

SwiftUI-compatible framework for building browser apps with WebAssembly

CI status Discord

At the moment Tokamak implements a very basic subset of SwiftUI. Its DOM renderer supports a few view types and modifiers (you can check the current list in the progress document), and a new HTML view for constructing arbitrary HTML. The long-term goal of Tokamak is to implement as much of SwiftUI API as possible and to provide a few more helpful additions that simplify HTML and CSS interactions.

If there's some SwiftUI API that's missing but you'd like to use it, please review the existing issues and PRs to get more details about the current status, or create a new issue to let us prioritize the development based on the demand. We also try to make the development of views and modifiers easier (with the help from the HTML view, see the example below), so pull requests are very welcome! Don't forget to check the "Contributing" section first.

If you'd like to participate in the growing SwiftWasm community, you're also very welcome to join our Discord server, or the #webassembly channel in the SwiftPM Slack.

Example code

Tokamak API attempts to resemble SwiftUI API as much as possible. The main difference is that you use import TokamakShim instead of import SwiftUI in your files. The former makes your views compatible with Apple platforms, as well as platforms supported by Tokamak (currently only WebAssembly/WASI with more coming in the future):

import TokamakShim

struct Counter: View {
  @State var count: Int
  let limit: Int

  var body: some View {
    if count < limit {
      VStack {
        Button("Increment") { count += 1 }
        Text("\(count)")
      }
      .onAppear { print("Counter.VStack onAppear") }
      .onDisappear { print("Counter.VStack onDisappear") }
    } else {
      VStack { Text("Limit exceeded") }
    }
  }
}

@main
struct CounterApp: App {
  var body: some Scene {
    WindowGroup("Counter Demo") {
      Counter(count: 5, limit: 15)
    }
  }
}

Arbitrary HTML

With the HTML view you can also render any HTML you want, including inline SVG:

struct SVGCircle: View {
  var body: some View {
    HTML("svg", ["width": "100", "height": "100"]) {
      HTML("circle", [
        "cx": "50", "cy": "50", "r": "40",
        "stroke": "green", "stroke-width": "4", "fill": "yellow",
      ])
    }
  }
}

HTML doesn't support event listeners, and is declared in the TokamakStaticHTML module, which TokamakDOM re-exports. The benefit of HTML is that you can use it for static rendering in libraries like TokamakVapor and TokamakPublish.

Another option is the DynamicHTML view provided by the TokamakDOM module, which has a listeners property with a corresponding initializer parameter. You can pass closures that can handle onclick, onmouseover and other DOM events for you in the listeners dictionary. Check out MDN docs for the full list.

An example of mouse events handling with DynamicHTML would look like this:

struct MouseEventsView: View {
  @State var position: CGPoint = .zero
  @State var isMouseButtonDown: Bool = false

  var body: some View {
    DynamicHTML(
      "div",
      ["style": "width: 200px; height: 200px; background-color: red;"],
      listeners: [
        "mousemove": { event in
          guard
            let x = event.offsetX.jsValue.number,
            let y = event.offsetY.jsValue.number
          else { return }

          position = CGPoint(x: x, y: y)
        },
        "mousedown": { _ in isMouseButtonDown = true },
        "mouseup": { _ in isMouseButtonDown = false },
      ]
    ) {
      Text("position is \(position), is mouse button down? \(isMouseButtonDown)")
    }
  }
}

Arbitrary styles and scripts

While JavaScriptKit is a great option for occasional interactions with JavaScript, sometimes you need to inject arbitrary scripts or styles, which can be done through direct DOM access:

import JavaScriptKit

let document = JSObject.global.document
let script = document.createElement("script")
script.setAttribute("src", "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js")
document.head.appendChild(script)

_ = document.head.insertAdjacentHTML("beforeend", #"""
<link
  rel="stylesheet"
  href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css">
"""#)

This way both Semantic UI styles and moment.js localized date formatting (or any arbitrary style/script/font added that way) are available in your app.

Fiber renderers

A new reconciler modeled after React's Fiber reconciler is optionally available. It can provide faster updates and allow for larger View hierarchies. It also includes layout steps that can match SwiftUI layouts closer than CSS approximations.

You can specify which reconciler to use in your App's configuration:

struct CounterApp: App {
  static let _configuration: _AppConfiguration = .init(
    // Specify `useDynamicLayout` to enable the layout steps in place of CSS approximations.
    reconciler: .fiber(useDynamicLayout: true)
  )

  var body: some Scene {
    WindowGroup("Counter Demo") {
      Counter(count: 5, limit: 15)
    }
  }
}

Note: Not all Views and ViewModifiers are supported by Fiber renderers yet.

Requirements

For app developers

  • macOS 11 and Xcode 13.2 or later when using VS Code. macOS 12 and Xcode 13.3 or later are recommended if you'd like to use Xcode for auto-completion, or when developing multi-platform apps that target WebAssembly and macOS at the same time.
  • Swift 5.6 or later and Ubuntu 18.04/20.04 if you'd like to use Linux. Other Linux distributions are currently not supported.
  • carton 0.15.x (carton is our build tool, see the "Getting started" section for installation steps)

For users of apps depending on Tokamak

Any recent browser that supports WebAssembly and required JavaScript features should work, which currently includes:

  • Edge 84+
  • Firefox 79+
  • Chrome 84+
  • Desktop Safari 14.1+
  • Mobile Safari 14.8+

If you need to support older browser versions, you'll have to build with JAVASCRIPTKIT_WITHOUT_WEAKREFS flag, passing -Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS flags when compiling. This should lower browser requirements to these versions:

  • Edge 16+
  • Firefox 61+
  • Chrome 66+
  • (Mobile) Safari 12+

Not all of these versions are tested on regular basis though, compatibility reports are very welcome!

Getting started

Tokamak relies on carton as a primary build tool. As a part of these steps you'll install carton via Homebrew on macOS (unfortunately you'll have to build it manually on Linux). Assuming you already have Homebrew installed, you can create a new Tokamak app by following these steps:

  1. Install carton:
brew install swiftwasm/tap/carton

If you had carton installed before this, make sure you have version 0.15.0 or greater:

carton --version
  1. Create a directory for your project and make it current:
mkdir TokamakApp && cd TokamakApp
  1. Initialize the project from a template with carton:
carton init --template tokamak
  1. Build the project and start the development server, carton dev can be kept running during development:
carton dev
  1. Open http://127.0.0.1:8080/ in your browser to see the app running. You can edit the app source code in your favorite editor and save it, carton will immediately rebuild the app and reload all browser tabs that have the app open.

You can also clone this repository and run carton dev --product TokamakDemo in its root directory. This will build the demo app that shows almost all of the currently implemented APIs.

If you have any questions, pleaes check out the FAQ document, and/or join the #tokamak channel on the SwiftWasm Discord server.

Security

By default, the DOM renderer will escape HTML control characters in Text views. If you wish to override this functionality, you can use the _domTextSanitizer modifier:

Text("<font color='red'>Unsanitized Text</font>")
  ._domTextSanitizer(Sanitizers.HTML.insecure)

You can also use custom sanitizers; the argument to _domTextSanitizer is simply a String -> String closure. If _domTextSanitizer is applied to a non-Text view, it will apply to all Text in subviews, unless overridden.

If you use user-generated or otherwise unsafe strings elsewhere, make sure to properly sanitize them yourself.

Troubleshooting

unable to find utility "xctest" error when building

This error can only happen on macOS, so make sure you have Xcode installed as listed in the requirements. If you do have Xcode installed but still get the error, please refer to this StackOverflow answer.

Syntax highlighting and autocomplete don't work in Xcode

Open Package.swift of your project that depends on Tokamak with Xcode and build it for macOS. As Xcode currently doesn't support cross-compilation for non-Apple platforms, your project can't be indexed if it doesn't build for macOS, even if it isn't fully function on macOS when running. If you need to exclude some WebAssembly-specific code in your own app that doesn't compile on macOS, you can rely on #if os(WASI) compiler directives.

All relevant modules of Tokamak (including TokamakDOM) should compile on macOS. You may see issues with TokamakShim on macOS Catalina, where relevant SwiftUI APIs aren't supported, but replacing import TokamakShim with import TokamakDOM should resolve the issue until you're able to update to macOS Big Sur.

If you stumble upon code in Tokamak that doesn't build on macOS and prevents syntax highlighting or autocomplete from working in Xcode, please report it as a bug.

Syntax highlighting and autocomplete don't work in VSCode

Make sure you have the SourceKit LSP extension installed. If you don't trust this unofficial release, please follow the manual building and installation guide. Apple currently doesn't provide an official build of the extension on the VSCode Marketplace unfortunately.

Contributing

All contributions, no matter how small, are very welcome. You don't have to be a web developer or a SwiftUI expert to meaningfully contribute. In fact, by checking out how some of the simplest views are implemented in Tokamak you may learn more how SwiftUI may work under the hood.

Updating our documentation and taking on the starter bugs is also appreciated. Don't forget to join our Discord server to get in touch with the maintainers and other users. See CONTRIBUTING.md for more details.

Code of Conduct

This project adheres to the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to [email protected].

Sponsorship

If this library saved you any amount of time or money, please consider sponsoring the work of its maintainers on their sponsorship pages: @carson-katri, @kateinoigakukun, and @MaxDesiatov. While some of the sponsorship tiers give you priority support or even consulting time, any amount is appreciated and helps in maintaining the project.

Maintainers

In alphabetical order: Carson Katri, Ezra Berch, Jed Fox, Morten Bek Ditlevsen, Yuta Saito.

Acknowledgments

  • Thanks to the Swift community for building one of the best programming languages available!
  • Thanks to everyone who developed React with its reconciler/renderer architecture that inspired Tokamak in the first place.
  • Thanks to the designers of the SwiftUI API who showed us how to write UI apps in Swift declaratively (arguably even in a better way than React did).
  • Thanks to SwiftWebUI for reverse-engineering some of the bits of SwiftUI and kickstarting the front-end Swift ecosystem for the web.
  • Thanks to Render, ReSwift, Katana UI and Komponents for inspiration!

SwiftUI is a trademark owned by Apple Inc. Software maintained as a part of the Tokamak project is not affiliated with Apple Inc.

License

Tokamak is available under the Apache 2.0 license. 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 file for more info.

tokamak's People

Contributors

maxdesiatov avatar carson-katri avatar hodovani avatar j-f1 avatar ezraberch avatar mortenbekditlevsen avatar kateinoigakukun avatar foscomputerservices avatar yonihemi avatar filip-sakel avatar regexident avatar ahti avatar andrewbarba avatar agg23 avatar twof avatar benedictst avatar literalpie avatar mbrandonw avatar snowy1803 avatar gregcotten avatar joannis avatar mattpolzin avatar outcue avatar

Stargazers

Keitaro Kawahara avatar Oleksandr Kostenko avatar Sean Armstrong  avatar Honza Dvorsky avatar  avatar Tezy Apps avatar Jiaming Su avatar Jonas Templestein avatar Anis Lazaar avatar Bug Williams avatar Rebecca Slatkin avatar David Lajos avatar Tatsunori Yoshioka avatar Adalberto Pompolo avatar Gamono avatar Marko OpačiΔ‡ avatar Felipe Ruz avatar Jakob Kongsrud avatar  avatar Timo Bechtel avatar kotomi1227 avatar Aqil Contractor avatar ShikiSuen avatar Ali Al-aish avatar Ata Etgi avatar Alec Wilson avatar Blazej SLEBODA avatar Fumiya Tanaka avatar Marcello Gonzatto Birkan avatar Mustafa Gezen avatar Ennio Italiano avatar tsudzuki avatar Libranner avatar lucas bigeardel avatar Sam Roman avatar Elias Jackson avatar Nyx avatar  avatar Γ©. urcades avatar Sunghyun Kim avatar Yuki Takahashi avatar Neil Faulkner avatar Pavel Ivanov avatar iStuces avatar  avatar Zsolt KovΓ‘cs avatar Park Seongheon avatar Edditoria avatar Vincent avatar Daniel Tarazona avatar Vladimir Gusev avatar  avatar Daven Quinn avatar Mohamed Akram avatar  avatar Sergio Rubio avatar Alexander Solovyov avatar Burak Sezer avatar Prashanth Pai avatar Paul Gessinger avatar  avatar khimaros avatar Alexander Pushkov avatar Dylan Frankland avatar Michael T Tang avatar Sasha Chedygov avatar  avatar SungJun Hong avatar Brian Ward avatar Myroslav Kiurchev avatar Brian Anglin avatar Mirko Viviani avatar HellPie avatar  avatar Chris Zelazo avatar θ’‹θ‰Ί avatar Hyeseong Kim avatar SoHyeon Kim avatar Jason Morganson avatar Haeseok Lee avatar Harry Rabin avatar Julio Tello avatar  avatar Chawye Hsu avatar YYudai avatar Brett Henderson avatar Dan Murfin avatar Vladimir avatar  avatar Rychillie avatar Yasin Aktimur avatar Bumseok Lee avatar Nielle avatar Tomasz WyrowiΕ„ski avatar Thiago Brezinski avatar Luiz Guerra avatar Daniel Cardona Rojas avatar DajosPatryk avatar mrfakename avatar Davit Balagyozyan avatar

Watchers

Kevin M. Ryan avatar Jacob Relkin avatar Brad Jones avatar Rick Hohler avatar  avatar Eric Freitas avatar James Cloos avatar MohsinAli avatar Timothy Davison avatar  avatar Jimmy Hough Jr. avatar edgency avatar Carabineiro avatar Marcus avatar XiaoDong avatar  avatar Ahn Jung Min avatar Libranner avatar Mindaugas avatar Denis avatar win avatar  avatar  avatar liuweiguang avatar Linz avatar oldbird avatar  avatar Lex avatar Roman Kerimov avatar Mawesome4ever avatar  avatar Sevan avatar Matheus Silveira Venturini avatar Joshua Kaunert avatar  avatar Arthur Olevskiy avatar Jeff L. avatar Simon Leeb avatar  avatar czihao avatar Jihoon Ahn avatar  avatar Iason Orlandos avatar Lyam Zambaz avatar

tokamak's Issues

Production readiness?

Hello,
I would like to ask you about confidence of using Tokamak in production applications for e-commerce.

Image host component

Most of Image.Props should mirror properties of UIImage, but the actual target would be UIImageView. Properties that are read-only on UIImage would need to create a new instance when changed, one prominent example being renderingMode.

I imagine we could start with Image.Props looking like this:

public struct Props {
  public enum RenderingMode {
    // ...
  }
  public enum Source {
    case name(String)
    case data(Data)
  }
  // when changed initializes new image with `UIImage(named:)` or `UIImage(data:)`
  public let source: Source
  // when changed creates new image with `withRenderingMode`
  public let renderingMode: RenderingMode 
  // when changed initializes new image with given scale
  public let scale: Double 
  // mirrors `flipsForRightToLeftLayoutDirection`
  public let flipsForRTL: Bool
}

Expose priorities on auto layout constraints

Currently there's no way to customise priority of a constraint. This probably should be exposed as an optional argument to equal function, but different approaches could be explored.

Important caveat:

Priorities may not change from nonrequired to required, or from required to nonrequired. An exception will be thrown if a priority of required in macOS or UILayoutPriorityRequired in iOS is changed to a lower priority, or if a lower priority is changed to a required priority after the constraints is added to a view. Changing from one optional priority to another optional priority is allowed even after the constraint is installed on a view.

Custom CSS styles

I've been thinking about CSS-specific styles that one might want to add to further customize generated styles, somewhat similar in principle to the HTML view that allows injecting arbitrary HTML. I'm still not sure if there should be a modifier that allows adding arbitrary HTML attributes to a rendered HTML node, or specialized modifiers like cssStyle() for specifying styles and cssClassName() for specifying a class name.

I also had in mind something like protocol CustomCSSAttributes which would allow any user to conform their views (or existing views provided by Tokamak) to this protocol, providing their custom global attributes. The use case for this are popular styling libraries (and obviously proprietary design systems that people may use) that have existing CSS code. For example, Semantic UI requires buttons to have ui button classes on them:

<button class="ui button">
  Button
</button>

Tokamak could allow achieving that for all buttons globally like this:

extension Button: CustomCSSAttributes {
  var customAttributes: [String: String] { ["class": "ui button"] }
}

The protocol approach and the modifiers approach would not be mutually exclusive, the reasoning is that modifiers would apply tweaks only locally, while protocol conformance would be visible globally. There's some bikeshedding needed here, but overall what are your thoughts?

Don't know if creating a separate issue for this proposal would be more suitable.

Rename `EventHandlerProps` to `ControlProps`

Looks like there is an important property available on subclasses of UIControl that we need to make available on related control-like host components, namely isEnabled. It would make sense to add it on EventHandlerProps since that one is available for props of all control-like components. But then it would also make sense to rename it to ControlProps as the addition makes its purpose more general.

TextField host component

In TokamakUIKit this would be rendered to UITextField. This is a subclass of UIControl, so for the first version TextField should conform to the UIControlComponent protocol. Essential props that would be great to have in the initial version are:

  • textColor (that uses Tokamak type Color)
  • textAlignment (there's probably already an existing enum TextAlignment from Label that can be reused here?)
  • placeholder
  • clearsOnBeginEditing
  • clearsOnInsertion
  • borderStyle
  • clearButtonMode
  • autocapitalizationType
  • autocorrectionType
  • spellCheckingType
  • keyboardType
  • keyboardAppearance
  • returnKeyType
  • secureTextEntry

all adding new enums to the Tokamak module where required.

Add scroll properties to `ListView`/`CollectionView` props

As both of these components are rendered to UIScrollView and NSScrollView subclasses on iOS and macOS respectively, it would make sense to create a structure for props of all of these components to be shared and handled uniformly in the renderers code.

ZStack?

What about ZStack? Have you plans to implements it?

Implement a test renderer

This renderer should use a new simple TestView class as a target, which stores references to subviews, also directly stores given props and is able to trigger events with a function like TestView.trigger(event: Event).

It would make sense to keep this renderer and TestView class in a separate module/subspec/target. This would make the core module smaller and allow importing the test renderer only in test targets. I imagine GluonTest is an ok name for the module.

Implement UserInterfaceSizeClass with media queries support

Shame that there are only two size classes in SwiftUI: compact and regular. Also not sure what the default breakpoint for these should be. Even if we decide on one, I think that users should be able redefine it, or maybe even add their own size classes with new breakpoints.

Add `hooks.context`

This would allow Tokamak to create contexts for passing values conforming to Equatable through deep component trees without needlessly updating all levels of the tree

Add `hooks.reducer`

This would allow Gluon to manage complex state without requiring additional libraries like Redux

0.2.0 release

Just kickstarting a possible discussion here of what you'd like to see in the first release with the SwiftUI API subset supporting WebAssembly. I am personally a fan of "release early and often" idea and wouldn't mind tagging a release with whatever API coverage we already have. It's more about letting people know that this thing exists and it can do basic stuff.

These seem to be release blockers, let me know if you think anything's missing:

  • Basic documentation
    The existing progress.md should be enough for this first release, I guess? We can integrate TokamakDocs somehow in later versions, unless @carson-katri has any objections.
  • Cleaning up the demo example
    I think even without NavigationView and friends, I'll just add some padding and headers to describe what's what
  • Updated CHANGELOG.md
  • Tagging and sharing on Twitter

What are your thoughts @carson-katri @j-f1?

Add Tokamak project linter

Create command-line util to lint projects that use Tokamak.

Todo:

  • Add Linter.md for linter documentation
  • Verify that SwiftSyntax 0.5 is able to parse the Example project
  • Add lint error Props is not Equatable
  • Add unit-tests to lint functions
  • Add lintFile -> [LintError]
  • Make lintFolder use lintFile
  • Make error output more user readable
  • Fix TokamakLint types access scope: remove public from TokenVisitor, Rule, Reporter, PropsIsEquatableRule
  • Fix TokenVisitor handling of Range, start and end are always equal to each other:
    node.range.startRow = row
    node.range.startColumn = column
    node.range.endRow = row
    node.range.endColumn = column
  • Fix processToken, kind variable is unused:
  private func processToken(_ token: TokenSyntax) {
    var kind = "\(token.tokenKind)"
    if let index = kind.firstIndex(of: "(") {
      kind = String(kind.prefix(upTo: index))
    }
    if kind.hasSuffix("Keyword") {
      kind = "keyword"
    }

    column += token.text.count
  }
  • Fix walkAndGrab, make it private, remove if node.children.count > 0 condition
  • Investigate if SwiftCLI package can be used in TokamakCLI module
  • Rename TokamakCLI to tokamak in Package.swift for Swift 5.0 and 4.2
.executable(name: "tokamak", targets: ["TokamakCLI"])
  • Lint "Only one render in component"
  • One render function rule should check all components, not only hooked components.
  • Lint "Rules of Hooks"
  • Lint "Components can only be declared as structs"
  • Integrate swift-log package
  • Remove direct usage of print, implement configurable output for stdout or files with a custom LogHandler implementation for swift-log

Long-term:

  • Lint "Arguments order"
  • Lint "Unused argument: hooks, children"
  • Lint "Declare non-static properties and functions in component"
  • Lint Rules of Hooks within extensions for all Hooks, not just state

Add ListView host component

I find it confusing that UITableView is named that way in UIKit, while it has no support for columns whatsoever, compare it to NSTableView in AppKit. I think it would be more fitting to name the corresponding host component in Gluon as ListView.

UICollectionView's component could keep that name, it's doing fine πŸ˜„

ScrollView host component

Implementation of this component in TokamakUIKit would need special care to make it work with auto layout. The approach I would try is to copy width constraint of a scroll view and its parent to the root subview. This would automatically do the same what people need to do manually when working with programmatic UIScrollView and auto layout. We should also provide a flag in props to turn off these additional helper constraints if a user would like to avoid that. We also need to support both vertical and horizontal scrolling and inference of those additional constraints for both cases.

Add DatePicker component

This component should be rendered to UIDatePicker with UIKitRenderer. This is a subclass of UIControl, so it could reuse a lot of infrastructure from UIControlComponent protocol in GluonUIKit module.

Implement all main layout constraints

Currently the only available auto layout constraint available is Edges.equal, we need to make all basic auto layout constraints available, working and tested with this new DSL, e.g. Width.equal, Height.equal, Leading.equal etc

Set target refs in UIKitRenderer

Currently refs passed to nodes won't be initialized during rendering with UIKitRenderer. It's important to get targets stored in refs to enable usage of those targets in effects.

Stepper missing props

Properties that should be added to Stepper.Props and be rendered in the UIKit renderer: autorepeat, wraps, minimumValue, maximumValue, stepValue, all are documented in UIStepper docs.

View has - by default - a Body of type Never.

This introduces bugs and almost defeats the purpose of compile-time errors. To prevent the program compiling successfully without producing an error for a type like Foo:

struct Foo: View {
    
}

There are two options - that I can think of:

  1. Add another _Never conditional conformances - to a dummy type like _Never that will require disambiguating when omitting the body:

    enum _Never: View {
        var body: Never { fatalError() }
    }
    
    extension View where Body == _Never {
        var body: _Never {
            fatalError("Can't access body of Never View.")
        }
    }

    That is a hack though and the error ("Type 'H' does not conform to protocol 'Block'") doesn't provide a fix possibly leaving a novice developer confused.

  2. Remove the extension that provides an automatic conformance when Body is of type Never and move that behaviour to the ViewDeferredToRenderer protocol - or to an underlying view protocol such as _StuctureView, _View, _FatalView:

    protocol _StuctureView: View where Body == Never {}
    
    extension _StuctureView {
         var body: Never {
            fatalError("Can't access body of Structure View.")
        }
    }

    That IMO is the best option, as it avoids automatic type-inference altogether and provides a useful error message - that offers a fix.

Implement SubscriptionView compatible with OpenCombine

This is required for view subscriptions such as onReceive:

extension View {
  public func onReceive<P>(
    _ publisher: P, 
    perform action: @escaping (P.Output) -> ()
  ) -> some View where P : OpenCombine.Publisher, P.Failure == Never {
    SubscriptionView(content: self, publisher: publisher, action: action)
  }
}

Verify `.next` target for all layout constraints

Not sure if next sibling is correctly passed from the renderer/reconciler to auto layout extensions in TokamakUIKit, this needs to be verified and fixed if broken. Good to have some demo screens in the Example project for verification.

Add style support

Many views support some kind of .fooStyle() method that takes one of a variety of XFooStyle() objects. I took a quick look and it seems like this kind of thing has not been implemented for any of the views. Ideally having one implementation of this would provide a base from which to implement similar methods for other views.

Some primitive Views cannot access @Environment

For instance, the Text element inside of EnvironmentDemo will never get any Environment injected. You can see this by putting the following print after // Inject @Environment values in StackReconciler.swift:

print(compositeView.view.type)

The Text element is never logged.

I'm not sure how to fix this. Do you have any ideas @MaxDesiatov ?

Implement basic AppKit renderer

AppKit renderer would be a great proof of concept for cross-platform Tokamak and also would make reconciler testing much easier without requiring iOS simulator overhead.

Server-side rendering and static websites support

It would be great if Tokamak supported rendering to a static HTML string without requiring DOM access, similar to what React already can do (and numerous other JS libraries probably too). There are many use cases for this, the most beneficial would be to pre-render your Tokamak app into a static HTML, serve that by default, and then hydrate that after the WebAssembly binary is loaded. This is beneficial for performance and usability as a user wouldn't need to wait for the WebAssembly module to be loaded and compiled before they can see anything useful on a web page.

Additionally, many static websites (such as blogs) wouldn't need components that utilize @State, those wouldn't need a WebAssembly binary at all, pre-rendered HTML would be enough. Of course, you could write your HTML by hand, but the abundance of React-based static websites generators (such as Gatsby) proves that it is nice to be able to reuse your existing code. Additionally, views described with Tokamak (even without any @State and other stateful property wrappers) clearly compose better than plain HTML.

I think this could look like a separate TokamakHTML module that doesn't depend on JavaScriptKit and could be compiled for any platform, not just WebAssembly. Then you'd supply any view to this HTML renderer and get an HTML string as an output. I hope that TokamakHTML could then be reused in the TokamakDOM module.

Implement `hooks.effect`

The only standard hook currently available is state, which isn't enough to schedule and finalise side effects from components. Gluon should support hooks.effect corresponding to useEffect in React.

Getting value of type 'String' has no member 'components'

Try to build with Swiftwasm in wasm-DEVELOPMENT-SNAPSHOT-2020-04-07-a.
Getting following error

.build/checkouts/Runtime/Sources/Runtime/Metadata/TupleMetadata.swift:33:68: error: value of type 'String' has no member 'components' var labels = String(cString: pointer.pointee.labelsString).components(separatedBy: " ")

NavigationView/NavigationLink

Having this view ready would clean up the demo page and make it easier to add or fix just one view. It’s probably good to start with the 2-column iPad style and maybe expand later on to an iPhone style on narrower viewports.

Logo for the project

If you know of a suitable logo, or know anyone who could produce one, or can propose one yourself, please do. I've used stock images for the previous release with the React API, you can see them on the Twitter profile. If you're not familar with tokamaks, please use the Wikipedia page for inspirations. The whole idea was (and still is) to make a pun on React's atomic logo πŸ˜„

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.