Giter Site home page Giter Site logo

re-frame-10x's Introduction

Clojars Project GitHub issues GitHub

re-frame-10x

re-frame-10x lets you instrument, and then inspect, the inner workings of a running re-frame application. It presents as a programmer's dashboard, delivering curated insight and illumination.

It helps you to find your false assumptions faster.

Show Me

Describe It To Me

It Is Epoch Oriented

re-frame applications are computationally regular. First an event happens, and then boom, boom, boom go a series of known computational steps (aka dominoes), in a known order. When this chain reaction completes, a re-frame app enters a quiescent state waiting for another event to kick off the next iteration of the same process.

Each re-frame event and its consequent computation forms a bounded "epoch" which can be inspected, analysed and understood independently of other epochs. This tool is epoch-oriented - it shows you one at a time.

And, yes, it has "time travel debugger" capabilities - you can go backwards and forwards through epochs - but that's really not the most interesting or powerful aspect of what re-frame-10x delivers.

It Is About Trace Data

As it runs, re-frame logs "trace" as data, and this provides an x-ray (MRI?) of your app's inner functions. At its most basic level, re-frame-10x is a consumer, processor and presenter of trace data.

It Is About The Data Flow

re-frame is a functional framework but it's design is "data oriented". It "flows" data, in a loop, through the functions you provide. To understand what is happening in your re-frame app, you must understand what data is "happening".

It Is Always About The Data

So, that's two good re-frame-specific reasons why data is at the core of re-frame-10x, but actually, the importance of data is even more fundamental that that.

Each time you put a println into your program, you are printing out what? And why? Invariably, it is data which fuels your debugging investigation, confirming your current hypothesis, or not.

And when you write your unit tests, you represent your expections as what? Code is proved correct by the data it produces.

So, for debugging and understanding activities, "more data, more easily" is winning. If re-frame-10x does its job, it shouldn't be necessary for you to add printlns. The data you need should be captured and presented, and, if further experimentation is required, it should be available in your REPL too.

Data Brings Code To Life

Perhaps you have seen LightTable in action?

In the small, it is a delightfully productive debugging environment because it co-renders code with the data generated by running the code. The data provides a "paper trail" which brings the code to life, revealing its dynamics and enriching a programmer's understanding.

re-frame-10x has a similar goal, although the method is different.

It Is A Data Dashboard

Observing raw data trace is both interesting and valuable, but it isn't enough. First, we want to leverage this data for insights. And, second, there's often too much data - you can drown in the detail.

So, re-frame-10x tries to be a "dashboard" which curates this "raw data" into "information" through various kinds of analysis and "roll ups". It should deliver insight "at a glance", while still allowing you to drill down into the detail.

Helps Me How?

Four ways:

  1. It helps you to learn re-frame. Simply looking at the "raw traces" provides insight into how it operates. Even experienced re-framians, er, like me, have learned a lot.

  2. It helps you to explore and learn an unfamiliar re-frame codebase. When I click, over here, on this "X" button, it shows me what event is dispatch-ed and in which namespace the associated event handler is registered. And, "oh look, that's interesting - four subscriptions recalculated". Etc.

  3. It helps you with debugging. You see an x-ray of your app's functioning. In particular, it will assist you to write and debug event handlers, which is useful because they hold most of the logic in your re-frame apps.

  4. It helps you to find performance problems and/or detect where there is unnecessary computation occurring.

Point 3, is primary, of course. But Point 2 is almost as important because we all spend a lot of our time groking unfamiliar codebases. Being able to observe the inner workings of a running app is a great way to bring code to life, reveal key features, and build a cognitive map of how the code is structured.

Temporary Warning

Some of the claims above are aspirational. re-frame-10x remains a WIP.

Of Sausage And Sizzle

Internal discussion about a name meandered for a while. Initially, it was re-frame-trace, which is accurate, sure, but it is also 100% sausage because it talks about low level function, and not higher level benefit (sizzle, sizzle). Side stepping the issue, I wanted to call it vox-datum (voice of the data) but that was cruelly rejected, for reasons I don't care to remember. The pain. I mean, who the hell doesn't like a Latin name?? Philistines.

If benefit was a must, then there was -insight and -illumination, but adding either made the name waaaay too long. Naming things - it really is a nightmare!

Finally, -10x cheekily materialised, delivering 100 decibels of audacious sizzle, and consequently a challenge for us to live up to. A 10x programmer starts by having 10x more knowledge and insight - so go make that tool, smarty pants.

Installation

re-frame-10x configuration is per-project, only one person in your team needs to configure a project to use it, and then everyone else can benefit.

If you are using leiningen, modify project.clj in the following ways. When puzzling over the various possible leiningen configurations, it's often helpful to look at a sample project.clj.

IMPORTANT PREREQUISITES

  • You must have a :main specified in your :compiler config or an entry in :modules (shadow-cljs) for the :preloads and :closure-defines to take effect
  • You must be running with the Closure define goog.DEBUG as true. This is the default under :optimizations :none.
  • You must be using :optimizations :none.
  • You should be using re-frame 1.2.0 or later. Earlier versions may work, but are not supported.

If you don't meet those pre-requisites, see the docs on advanced setups for other ways to install re-frame-10x.

Clojars Project

Easy setup

  • Update your re-frame dependency to at least 1.2.0 - [re-frame "1.2.0"].

  • Add re-frame-10x as a dev dependency by placing [day8.re-frame/re-frame-10x "1.6.0"] within :dependencies

    • For shadow-cljs, within dependencies. For example:
    :dependencies
    [[day8.re-frame/tracing      "0.6.2"]
     [day8.re-frame/re-frame-10x "1.6.0"]]
    • For Leiningen, within :profiles :dev :dependencies. For example:
    :profiles
       {:dev
          {:dependencies [[day8.re-frame/tracing      "0.6.2"] 
                          [day8.re-frame/re-frame-10x "1.6.0"]] }}
    • For deps.edn, create a new aliass, or add the dependencies in your dev alias:
    {:aliases 
      {:dev {:extra-deps
             {day8.re-frame/tracing      {:mvn/version "0.6.2"}
              day8.re-frame/re-frame-10x {:mvn/version "1.6.0"}}}}}
  • Locate your compiler config for your development build and add :closure-defines and :preloads to enable re-frame-10x.

    For example using shadow-cljs update your shadow-cljs.edn:

    :builds
    {:client
     {:target     :browser
      :output-dir "resources/public/js"
      :modules
      {:client
       {:init-fn todomvc.core/main}}
      :devtools
      {:preloads [day8.re-frame-10x.preload]}
      :dev
      {:compiler-options
       {:closure-defines
        {re-frame.trace.trace-enabled?        true
         day8.re-frame.tracing.trace-enabled? true}}}
      :release
      {:build-options
       {:ns-aliases
        {day8.re-frame.tracing day8.re-frame.tracing-stubs}}}}}}

    For example using cljsbuild and Leiningen update your project.clj:

    :profiles 
    {:dev  
      {:cljsbuild
        {:builds
          [{:id           "dev"
            :source-paths ["src" "dev"]
            :compiler     {...
                           :closure-defines      {"re_frame.trace.trace_enabled_QMARK_" true}
                           :preloads             [day8.re-frame-10x.preload]
                           :main                 "myapp.core" ;; You must specify a :main or follow the advanced setup ^^^
                           }}]}}}

    If you're using figwheel-main, update your dev.cljs.edn file:

    ^{:watch-dirs   ["src/main/cljs"]
      :ring-handler "...}
    {:main            "myapp.core" ;; You must specify a :main or follow the advanced setup ^^^
     :closure-defines {"re_frame.trace.trace_enabled_QMARK_" true}
     :preloads        [day8.re-frame-10x.preload]}

    The key things you need to add are:

    • :closure-defines {"re_frame.trace.trace_enabled_QMARK_" true}
    • :preloads [day8.re-frame-10x.preload]

cljs-devtools is not required to use re-frame-10x, but it is highly recommended.

Compatibility Matrix

Reagent Versions React Versions re-frame-10x Artifact Status
1.2.x 17.x - 18.x clojars ci
1.0.x 17.x [day8.re-frame/re-frame-10x "1.8.1"] Frozen
0.10.x 16.13.x [day8.re-frame/re-frame-10x "0.7.0"] Frozen
0.9.x 16.9.x [day8.re-frame/re-frame-10x "0.5.2"] Frozen
0.8.x 16.x.x - 16.8.6 [day8.re-frame/re-frame-10x "0.4.3"] Frozen
0.6.x - 0.7.x 15.x [day8.re-frame/re-frame-10x "0.3.7"] Frozen

For React 18: Consider using {:preloads [day8.re-frame-10x.preload.react-18]}. This will use React's new render API. Otherwise, expect deprecation warnings from React.

For versions < 0.4.0: If your project uses React 16 and Reagent 0.8.0-alpha2 (or higher) then you will need to add the qualifier -react16 to the version, e.g. [day8.re-frame/re-frame-10x "VERSION-react16"].

Note: If also using re-com then on upgrading reagent you may also need to upgrade re-com.

Code Tracing

re-frame-10x includes an experimental code tracing feature for tracing the code in your event handlers. See day8/re-frame-debux for instructions on how to set it up.

Project configuration

For some settings, re-frame-10x supports project-level configuration via additional closure-defines. This way, some default behavior of 10x can be version-controlled alongside your project. Example:

{:closure-defines
      {re-frame.trace.trace-enabled?        true
       day8.re-frame.tracing.trace-enabled? true
       day8.re-frame-10x.hidden-namespaces "[com.me.uninteresting re-com.box]"}}

Supported keys:

name description type example
...history-size how many epochs to retain integer 25
...ignored-events ignore events of this type seq of keywords "[:init :event-B]"
...hidden-namespaces hide trace from these namespaces seq of symbols "[re-com.box re-com.input-text]"
...time-travel? selecting an event reverts your app-db boolean true
...ignored-libs ignore low-level trace seq of keywords "[:reagent :re-frame]"
...ns-aliases display aliased keywords in data inspectors map of symbol->symbol "{long-namespace ln}"
...trace-when trace your app's events always, never, or only when the panel is open (default) :panel, :always or :never :always

Usage

  • Make sure you have followed all of the installation instructions above.

  • Start up your application.

  • Once it is loaded, focus the document window and press ctrl-shift-x to slide open the trace panel and enable tracing.

  • When the panel is closed, tracing is disabled.

Controlling the Trace Panel

By default, press ctrl-shift-x to open or close the trace panel. You can re-bind or disable this keystroke in the settings menu.

You can also control the panel programmatically:

  • At the REPL: (day8.re-frame-10x/show-panel! true)
  • At the browser console: day8.re_frame_10x.show_panel_BANG_(true)
  • With a re-frame event: (re-frame.core/dispatch [:day8.re-frame-10x.panels.settings.events/show-panel? true])

Use Cases

app-db

app-db-panel

  • Inspect a portion of app-db's state with the path inspector, allowing you to focus on just the parts you care about.
  • Reset app-db to before an event was run to run it again, instead of resetting the whole application
  • Toggle app-db before and after states for running an event, to inspect UI changes.

Subs

subs-panel

  • See the output and diff of a subscription running
  • Spot layer 2 subscriptions that should really be layer 3's.
  • Spot subscriptions that are running when they shouldn't
  • Spot subscriptions that are being destroyed and recreated unnecessarily

Timing

timing-panel

  • Answer the question "Why is my app slow when it runs this event?"
  • See whether time is spent in processing an event, running the subscriptions, or rendering the changes

Event

event-panel

  • See the coeffects given to an event handler
  • See the effects produced by an event handler
  • See the interceptors involved in handling an event.

Trace

trace-panel

  • Dig into the low level execution details of an epoch. We've tried to surface the useful information in the other panels, so if you're constantly referring to this panel, open an issue with your use case.

Troubleshooting

  • Try a lein clean
  • Make sure you have followed all the installation steps.
  • Make sure you have checked the prerequisites

Factory reset

re-frame-10x persists its state in local storage. If you experience a bug, this mechanism may be reproducing your bug, even after the root cause is fixed. To fully refresh the state:

  • Click "Factory Reset" at the bottom of the settings panel.
  • If you can't load the settings panel, run day8.re_frame_10x.factory_reset_BANG_() in the JavaScript console.
  • If neither of those work, remove all of the keys with the prefix day8.re-frame.trace.

If the re-frame-10x window won't show up when pressing ctrl-shift-x

  • Make sure that your browser window doesn't have focus in a text-box or something else that is intercepting keyboard events.
  • Make sure that the re-frame-10x div has been created. Run document.querySelector('#--re-frame-10x--') in your browser's console to check if an element is returned. If this returns null then something else isn't working correctly.
  • Try a factory reset. This should restore the default key bindings.
  • You can also open it programmatically.

If re-frame-10x throws an exception on startup

Some parts of re-frame-10x seem to work but others don't

  • Make sure you are using the minimum recommended re-frame version in the prerequisistes. Some parts of re-frame-10x rely on updated versions of re-frame to capture the information it needs.

The pop-out doesn't respond to input

  • Make sure your original browser window is open to the tab where your app is running. If you need other tabs, consider visiting them in a different window.
  • In other words, keeping your app in a single browser window with no other tabs should always work.
  • This is due to a limitation built into modern browsers. We discuss it in depth at #209.
  • In rare cases, a factory reset may help.

How does it work?

re-frame is instrumented - all important activity generates trace data.

re-frame-10x consumes this trace data and renders useful visualisations of the re-frame process. Currently, re-frame's tracing capabilities are in alpha and are subject to change at any time. We're testing the utility of the the trace by building an app on top.

By default, re-frame tracing is "compiled out", so it won't impose a performance cost in production. The trade-off here is that you need to explicitly enable it in development.

The preloads option (:preloads [day8.re-frame-10x.preload]) has to be set in order to automatically monkeypatch Reagent to add appropriate lifecycle hooks. Yes this is gross, and yes we will make a PR to reagent to add proper hooks, once we know exactly what we need. The preload namespace also injects a div containing the devtools panel into the DOM.

Developing/Contributing

If you want to work on re-frame-10x, see DEVELOPERS.md.

Citations

  • open by Bluetip Design from the Noun Project
  • reload by Adnen Kadri from the Noun Project
  • Camera by Christian Shannon from the Noun Project
  • Delete by logan from the Noun Project
  • Settings by arjuazka from the Noun Project
  • Wrench by Aleksandr Vector from the Noun Project
  • pause by Bhuvan from the Noun Project
  • play by Bhuvan from the Noun Project
  • Log Out by Arthur Shlain from the Noun Project
  • Arrow Up by Alice Design from the Noun Project
  • Arrow Down by Alice Design from the Noun Project
  • Arrow Left by Alice Design from the Noun Project
  • Arrow Right by Alice Design from the Noun Project
  • Collapse right by By useiconic.com, from the Noun Project

re-frame-10x's People

Contributors

daiyi avatar danielcompton avatar darwin avatar deraen avatar dexterminator avatar dijonkitchen avatar dpsutton avatar gadfly361 avatar gnl avatar gregg8 avatar heralden avatar hipitihop avatar hugoleodev avatar jacekschae avatar kimo-k avatar lwhorton avatar mainej avatar marianafantini avatar mawiraike avatar mhuebert avatar mike-thompson-day8 avatar mynomoto avatar palfrey avatar quezion avatar rlovtangen avatar sardtok avatar shen-tian avatar smee avatar solatis avatar superstructor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

re-frame-10x's Issues

filtering polish

some things that need to be implemented to polish the filter functionality:

for traces filtered by milliseconds:

  • at the moment, it is possible to enter several slower-than filters
    • either disable adding a new slower-than filter if another one is already in the list
    • or apply only filter that has been entered last

for traces filtered by words:

  • prevent adding duplicate search queries

Add information about source-paths to the readme

We ran into a couple interesting questions over the last weeks where the answer was "source-paths":

  1. How does re-frame-trace build?? I don't see anything in the project.clj that looks like it will build.

    Answer: when you add a path to the :source-paths of the test project used to develop the trace panel against, figwheel of the test project will know to watch and build it too! So cool!

  2. Why are changes in my local re-frame repo reflected in the example project? Shouldn't it be pulling from clojars?

    Answer: It's because if you are using the example project that lives in the re-frame repo, it has the local re-frame repo in the :source-paths! (as "../../src").

This might be helpful in the readme! I'm sure this is something many clojure developers are familiar with, but I myself learned it during the course of this project, so others might benefit too.

Save filter sets

Allow users to save filter sets, and choose filter defaults for when the panel first opens.

We have no idea what this might look like UI-wise, so this is a super nice-to-have-in-the-future type of task.

React Native support?

Based on my reading of the source, this seems to be web-focused. The tracing aspect of it, and monkey patching of Reagent, should be portable though, I think. Is there any plan to support React Native, or make this modular so it can be reused by someone looking to build a similar UI for React Native?

I see that re-frame.trace exists, and is used here, but the debugging documentation seems dated and doesn't mention it. I could really use this detailed tracing and would appreciate any suggestions.

Filter by trace types

As part of the filtering options, present a list of trace categories to be shown /hidden.

The list could be a checklist, or maybe some pills to be toggled.

Categories:

  • re-frame internal
  • reagent rendering
  • application events
  • subscriptions
  • ???

Tree view for traces

web-screenshot

Be able to help users answer the question "Why did that sub fire?". We have some of the information from the hierarchy by chained subs (although not all data dependencies go through chained subs).

Improve performance of string handling

The string.split and string/includes? calls get very expensive when there are a large number of them. We should look into a cheaper way of doing this, perhaps by creating another Reagent function which returns a vector of the component hierarchy, rather than a string.

Fix hiccup rendering of operation names

  • re-frame operation names in the form of [:idle :add-event] are incorrectly being interpreted as hiccup
  • change to extract operation name only
  • possible form for operation names:
    • ReagentInput
    • vector of keywords: [:idle :add-event]
    • keyword: :pieces

Fix logging data structures to console

  • steps to reproduce: click on data structure to print to console
  • following error occurs for all operation type details except for event details:
    CLJS DevTools internal error: TypeError: Cannot use 'in' operator to search for 'constructor' in rx10

Data browser

One of the challenges with this tool is effectively viewing the data. Oftentimes dispatched events are very large, and it is not efficient or useful to pretty print it all to a string at once. Having a live data structure browser that could be used to progressively unpack event tags, and to view app-db would be a really really great feature.

Dirac has a really great implementation of this, and re-frisk also has a data browser (but it's not quite as fully featured as Dirac's).

https://docs.google.com/document/d/1FTascZXT9cxfetuPRT2eXPQKXui4nWFivUnS_335T3U/preview

http://www.mattzeunert.com/2016/02/19/custom-chrome-devtools-object-formatters.html
https://gist.github.com/mattzeunert/a9a5aebc736f9f30e53f

Bounded, more efficient pprint function

When printing the tags for a trace, some of them may get very large. As a quick and dirty fix for #52 before that is completed, a bounded pprint function could be created. This would be the same as pprint, but when printing each top-level keys value, it would only print the first (say) 200 characters, and if the limit was hit, add a trailing elipsis.

This would be more efficient than our current implementation which pprints the entire datastructure, then trims the string. It would also be more useful as it would show at least part of each key, rather than one big value dominating the rest of the string.

Another option to do perhaps in conjunction with the idea above is to use fipp as it is much faster. We still want to not print the whole string though, as the string might get way too big.

Development: where do CSS files go?

Originally, the LESS and CSS files were in src/day8/re_frame/trace/css and src/day8/re_frame/trace/less.

Then, they were both moved to resources/day8/re_frame/trace because it's more proper naming convention to places styles into the resources folder.

However, now the host project can't find the CSS, because we gave instructions to change the source-paths to look like this:

:source-paths ["src" "../../src" "checkouts/re-frame-trace/src"]

and resources sits outside of this.

Which is a preferable fix?

  1. adding "checkouts/re-frame-trace/resources" to :source-paths
  2. moving the LESS and CSS files back into src/day8/re_frame/trace

Actionable item: update the README and/or file locations to reflect the decision made here!

Better filtering

Filtering is quite crude at the moment, it would be good to have better mechanisms for filtering.

Add an id for the re-frame-trace div

This will allow you to grab it later on when re-rendering it if you're developing re-frame-trace. The id should be sufficiently unique to be unlikely to collide with anything else.

Use CSS preprocessing

Isolating trace panel and host project styles is pretty gnarly at this juncture. We have two problems:

  1. prevent trace panel styles from interfering with the host project
  2. prevent host project styles from interfering with the trace panel

Previously, we were addressing 1) in a painful way, which is manually writing out the name-spacing CSS class every single time we define a new style. Preprocessing makes this much much nicer.

Preprocessing doesn't address 2), but it would certainly make it easier to do a more complete job of making sure we properly overwrite all host panel CSS.

LESS or SASS?

I was looking for the best way to use SASS in clojure projects, and it seems most of them require you to have Ruby installed, or at least their readmes claim as much. I didn't want to deal with that kind of overhead.

LESS did not require the developer to install Ruby, so I am going with LESS.

I also looked at re-frame-template for inspiration and noticed SASS option was removed a couple weeks ago due to compatibility issues: day8/re-frame-template@5ee0540

That aside, LESS is less draconian and so it's more friendly to develop with for people who have just a bit of experience with CSS, so figured it wasn't that much of a loss.

Improve the node viewer

At the moment the node viewer is a bit unpredictable when things change. It needs someone to really get in to D3 and the Reagent lifecycle to understand what's going on.

More intuitive time filtering

Ideas

  • have the time filter be a slider rather than a textbox, so you can see immediate feedback of how many events are passing the threshold you're adjusting.
  • sort by time by clicking the header name

Make CSS not inline

Possible solutions:

This is a prerequisite for #17, because the panel is currently picking up styles from its parent window, so we need to add a CSS-reset, which would be very unwieldy inline (though I suppose it's totally possible).

Edit: We will use clojure macro to read a CSS file (improving the current way, in which we are defining CSS in a string within cljs), and a separate issue is made to introduce CSS preprocessing!

Epochs

Epochs are defined as being the bounds between an event being fired, and all subsequent events, db updates, subscriptions, and renders being run. This is inherently a little bit of a fuzzy concept, as there may be events added to the re-frame event queue by async network activity which becomes part of the epoch, even though it is logically separate. For developing and debugging purposes to show the separation of events this is good enough.

How do we know when we are in a new epoch? How do we know when it is finished? Just based on events? Should there be something fired at the end of the render?

We also want to show in the trace view the bounds of each epoch.

Handle changing window viewport size when trace window is hidden

When re-frame-trace is hidden, it is just moved to the right hand edge of the screen, so it can smoothly animate on when it is unhidden. However if you change the size of the window (say by making it wider, or removing Chrome Devtools, the tracing window stays in it's fixed position. It would be good for it to reset it's position to the right hand side of the window whenever the viewport size changes.

Edit app-state

Text, integers, and booleans should be editable in the app-state browser tab!

Dock component

  • Automatically scroll to bottom when new info gets added
  • Rotate around different screen edges
  • Handle resizing better (using Facebook's Dom Mouse Move lib?)

Display fully qualified namespace for events in traces

  • for bigger applications, it is common to have similar event names across multiple namespaces
  • that makes it harder to understand where event is being dispatched
  • suggestion: display fully qualified namespace for events

Print dispatched params for event traces

Currently, the extra info under each trace is the metadata.

screenshot from 2017-08-16 10-55-40

See above: the metadata for the :event trace is empty because the event doesn't have metadata!

The :event traces extra info should instead show the dispatched event params, because this is something that people often println to check.

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.