Giter Site home page Giter Site logo

gluon-framework / gluon Goto Github PK

View Code? Open in Web Editor NEW
3.1K 3.1K 75.0 1.06 MB

A new framework for creating desktop apps from websites, using system installed browsers and NodeJS

Home Page: https://gluonjs.org

License: MIT License

JavaScript 100.00%
framework javascript nodejs web

gluon's People

Contributors

artamonovtech avatar canadahonk avatar cijiugechu avatar jaymakes11 avatar kyza avatar maisymoe avatar mantreshkhurana avatar popaprozac avatar raisinten avatar smartfrigde 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  avatar  avatar  avatar  avatar  avatar  avatar

gluon's Issues

Change behavior of deno branch to create 'gluon_data/chrome' in current directory

When running the compiled exe of the deno branch, it creates 'gluon_data/chrome' in the physical path of the developer's project. This behavior is inconvenient when distributing the exe to third parties.

After reviewing the implementation in 'gluon/src/index.js', it was found that the browser's temporary directory is created using fileURLToPath(import.meta.url) and files are placed there. import.meta.url contains the physical path of the project at compile time in the compiled exe. Therefore, the behavior is determined by where the developer clones the gluon project.

I would like to request that 'gluon_data/chrome' be created in the current directory instead.

Here are relevant lines from 'gluon/src/index.js' :

(Line 17)
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

(Line 147)
const ranJsDir = !Deno.args[0] ? __dirname : (Deno.args[0].endsWith('.js') ? dirname(Deno.args[0]) : Deno.args[0]);
const getDataPath = browser => join(ranJsDir, 'gluon_data', browser);

Thank you for your attention to this issue.

CDP reply error.

I'm getting this error message:

[Gluon] warn: CDP reply error. method: Page.stopLoading error: {
message: 'Page.stopLoading',
data: 'RemoteAgentError@chrome://remote/content/cdp/Error.sys.mjs:20:5\n' +
'UnknownMethodError@chrome://remote/content/cdp/Error.sys.mjs:103:7\n' +
'execute@chrome://remote/content/cdp/domains/DomainCache.sys.mjs:92:13\n' +
'receiveMessage@chrome://remote/content/cdp/sessions/ContentProcessSession.sys.mjs:79:45\n'
}

My code is:

import * as Gluon from '@gluon-framework/gluon';

const Window = await Gluon.open('https://www.fimfiction.net', {});

Is this a problem with the site or something I'm doing wrong?

Custom titlebar support

Chrome's window controls overlay defines 4 CSS env variables which are insanely useful for detecting the titlebar size, those are:

  left: env(titlebar-area-x, 0);
  /* Use the environment variable for the top anchoring with a fallback. */
  top: env(titlebar-area-y, 0);
  /* Use the environment variable for setting the width with a fallback. */
  width: env(titlebar-area-width, 100%);
  /* Use the environment variable for setting the height with a fallback. */
  height: env(titlebar-area-height, 33px);

currently gluton doesn't define these, would be cool if it did

Inherit browser flags

Currently gluton doesn't inherit the source browsers flags, this is a major issue for browsers like brave, which inherently don't support some API's like FileSystemAccess or LocalFontAccess, which need to be enabled via flags, gluton should either inherit those flags, or allow flags to be enabled via launch options

Custom useragent

Add a way to override the useragent
For example:

import * as Gluon from '@gluon-framework/gluon';

const Window = await Gluon.open('https://gluonjs.org', {
  userAgent: 'Mozilla/5.0 (Android 13; Mobile; LG-M255; rv:109.0) Gecko/109.0 Firefox/109.0'
});

Print API

Is there any way to print silently bypassing the OS prompt? Electron and NWJS have it. It would be a great addition and surpass Tauri and Neutralinojs.
:-)
Looks great BTW.

Using gluon on existing Node.js app

Hi, I was trying to use Gluon on my existing node.js cli application I tried to run gluon like this but when I try that. I get this error. What should I do if I don't wanna gluon as started point of node.js.

https://app.gluon/
HTTP ERROR 404

index.js

import { gluon } from "./glu.js";
const gluon1 = gluon();

glu.js

import * as Gluon from "@gluon-framework/gluon";

export const gluon = async () => {
  const browsers = process.argv.slice(2).filter((x) => !x.startsWith("-"));

  if (browsers.length > 0) {
    // use argv as browsers to use
    for (const forceBrowser of browsers) {
      await Gluon.open("index.html", {
        windowSize: [800, 500],
        forceBrowser,
      });
    }

    return;
  }

  await Gluon.open("index.html", {
    windowSize: [800, 500],
  });
};

Dropped a demo code to try proof of problem. Little edited version of gluworld.

RFC: IPC Store

Proposal for adding a "Store" to IPC, accessible to both Node and Web, for easily sharing common data between them. Not persistent. Example usage:

Proposal 1 - Async getter/getter

Web would fetch config key from IPC when gotten, hence being async.

// Node backend
Window.ipc.store.config = {
  env: 'prod'
};
// Web frontend
const runningEnv = (await Gluon.ipc.store.config).env;

Proposal 2 - Sync getter/getter

Web would be sent updated Store key and values whenever they are set, hence being sync.

// Node backend
Window.ipc.store.config = {
  env: 'prod'
};
// Web frontend
const runningEnv = Gluon.ipc.store.config.env;

Proposal 3 - Async Functions

// Node backend
Window.ipc.store.set('config', {
  env: 'prod'
});
// Web frontend
const runningEnv = (await Gluon.ipc.store.get('config')).env;

Proposal 4 - Sync Functions

// Node backend
Window.ipc.store.set('config', {
  env: 'prod'
});
// Web frontend
const runningEnv = Gluon.ipc.store.get('config').env;

Please comment with which proposal(s), you prefer! Open to implementing 1/2 and 3/4.

RFC: Local CSP

Apps using Local (giving a path to Gluon.open()) should have a way of having CSP easily by default, without being annoying or intrusive for development.

My current thoughts is to have a csp option to Gluon.open() for Local, with options like:

  • none: No (Gluon set) CSP
  • local: Gluon Local-only CSP
  • <custom>: Sets CSP header to other custom value given

Browser unsupported for nextcloud server site

I have a personal cloud server, so I'm looking for an web app maker like gluon.
I have switched the gludoom site in index.js to my own and this is the result.

image

Also nextcloud talk does not seem to work.

image

Also, how do I get rid of the default sites bar?

[Meta] Security

Meta tracking issue for security in general. Whilst Gluon is already probably one of the most secure frameworks of it's kind, we should still be constantly focused on improving it as best as possible by default.

Todo

  • Block HTTP, by default (done in v0.13)
  • #45: Implement strict CSP for Local, by default (planned for v0.13)
  • #51

Potential

  • When exposing functions, check name for possible generic function and warn?

Affiliation with the Gluon App?

Hey there,

Just wondering if Gluon has any affiliation with Gluon for Micro.blog? It's been around for some time.

If not, perhaps you can make it clear it's not affiliated? Was a little confused at first.

Open to suggestions.

– Vincent

Sample app crashing

Sample app works great on my desktop computer but crashes instantly on my laptop. Same version of Nodejs (16) and both Windows 11 machines. Any ideas would be much appreciated!

[Gluon] starting browser...
[Gluon] found browser chrome (chromium based) at path: C:\Program Files\Google\Chrome\Application\chrome.exe
[Gluon] data path: C:\Users\zanec\OneDrive\Documents\GitHub\gluworld\gluon_data\chrome
[Gluon] connecting to CDP over stdio pipe...
[Gluon] browser: Chrome/110.0.5481.178
[Gluon] acquiring target...
.[Gluon] pipe read closed
file:///C:/Users/zanec/OneDrive/Documents/GitHub/gluworld/node_modules/@gluon-framework/gluon/src/launcher/inject.js:77
    Window.close();
    ^

ReferenceError: Cannot access 'Window' before initialization

Instructions from README don't work on my mac m1 with chrome installed

Full output:

borkdude@m1 /tmp $ git clone https://github.com/gluon-framework/examples.git
Cloning into 'examples'...
remote: Enumerating objects: 48, done.
remote: Counting objects: 100% (48/48), done.
remote: Compressing objects: 100% (34/34), done.
remote: Total 48 (delta 12), reused 44 (delta 11), pack-reused 0
Receiving objects: 100% (48/48), 260.47 KiB | 1.65 MiB/s, done.
Resolving deltas: 100% (12/12), done.
borkdude@m1 /tmp $ cd examples
borkdude@m1 /tmp/examples (main) $ cd gluworld
borkdude@m1 /tmp/examples/gluworld (main) $ npm install

added 2 packages, and audited 3 packages in 4s

found 0 vulnerabilities
borkdude@m1 /tmp/examples/gluworld (main) $ cat package.json
{"type":"module","dependencies":{"@gluon-framework/gluon":"^0.9.0"}}%
borkdude@m1 /tmp/examples/gluworld (main) $ node .
[Gluon] starting browser...
[Gluon] checking if chrome exists: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome true
[Gluon] browser path: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[Gluon] data path: /private/tmp/examples/gluworld/node_modules/@gluon-framework/gluon/chrome_data
[Gluon] connecting to CDP over stdio pipe...
[Gluon] browser: Chrome/108.0.5359.124
file:///private/tmp/examples/gluworld/node_modules/@gluon-framework/gluon/src/launcher/inject.js:22
targetId: target.targetId,
^

TypeError: Cannot read properties of undefined (reading 'targetId')
at default (file:///private/tmp/examples/gluworld/node_modules/@gluon-framework/gluon/src/launcher/inject.js:22:24)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async default (file:///private/tmp/examples/gluworld/node_modules/@gluon-framework/gluon/src/launcher/start.js:36:10)
at async default (file:///private/tmp/examples/gluworld/node_modules/@gluon-framework/gluon/src/browser/chromium.js:11:10)
at async startBrowser (file:///private/tmp/examples/gluworld/node_modules/@gluon-framework/gluon/src/index.js:113:18)
at async Module.open (file:///private/tmp/examples/gluworld/node_modules/@gluon-framework/gluon/src/index.js:131:19)
at async file:///private/tmp/examples/gluworld/index.js:38:19

Node.js v17.8.0

HTTPS required for gluon apps?

Hi,

I have a web app that accesses a JSON API over http. It work fine in Chrome. When I try it with gluon I get this error

image

The app serving the JSON API doesn't use HTTPS and I have no control over that. Is this a gluon restriction? Can it be by by-passed?

Thanks,
Bob

Window position management

Electron allows developers to spawn the window at specific X/Y on the screen, an api to gather current position of the window would be also useful. An implementation respecting multi-monitor setups should be considered.

Example:

const Window = await Gluon.open('https://gluonjs.org', {
  x: 360,
  y: 200
});
Window.ipc.getWindowX = () => {
  return Window.Page.x;
};
Window.ipc.getWindowY = () => {
  return Window.Page.y;
};

Can't find chrome on Arch

Gluon checks for chrome binaries called chrome and chrome-canary.
If google chrome bin packages have been installed from the AUR, these are called google-chrome-stable and google-chrome-unstable.

System installer

  • gluon-framework/glustrap#1
  • Installation API
    • Manage desktop shortcut
    • Manage shell (eg. start menu) shortcut
    • Installation directory
    • Manage System registration (eg. appears in Control Panel)
    • gluon-framework/gluon_native#1
    • Uninstall (automated removal of above items, and if there is no other gluon app installation, cleanup of shared framework files)

Thorium as a recommended gluon brower-base

Some of us gluon enjoyers want less RAM but we can't live in a perfect tauri world, gluon is the least worst alternative from avoiding JavaScript completely.

Some of us want performance, so why not recommend using Thorium since it's a fast browser (not as secure as hardened firefox but, it's not like you're gonna browse the web with a gluon app, are you 🤣?)

Mac support for browser installed via homebrew

I installed my browser (firefox and chrome) via homebrew, when i run the gluworld example. i got error:

image

❯ node .
[Gluon] starting browser...
file:///Users/d/Data/labs/glutonis/node_modules/@gluon-framework/gluon/src/index.js:86
  const [ browserPath, browserName ] = await findBrowserPath(forceBrowser);
                                       ^

TypeError: (intermediate value) is not iterable
    at startBrowser (file:///Users/d/Data/labs/glutonis/node_modules/@gluon-framework/gluon/src/index.js:86:40)
    at async Module.open (file:///Users/d/Data/labs/glutonis/node_modules/@gluon-framework/gluon/src/index.js:114:19)
    at async file:///Users/d/Data/labs/glutonis/index.js:38:19

RFC: IPC API v2

Second (major) iteration of IPC API. I dislike having to use an event-based system, I think there should be a wrapper for most developers to use which allows seemingly exposing functions into the web context, either via a function or using a setter in the IPC API object.

Expose

Function

Implementation A

Window.ipc.expose('key', (arg1, arg2) => {});

Implementation B

Window.ipc.expose({
  key: (arg1, arg2) => {}
});

Setter

Window.ipc.key = (arg1, arg2) => {};

Example usage

// node
Window.ipc.myFunction = () => {}; // or

Window.ipc.expose('myFunction', () => {}); // or

Window.ipc.expose({
  myFunction: () => {}
});

// web
Gluon.ipc.myFunction();

Unexpose

Function

Window.ipc.unexpose('key');

Setter

delete.Window.ipc.key:

Example usage

delete Window.ipc.key; // or

Window.ipc.unexpose('key');

I'm leaning towards using a setter, but it might seem strange/wrong to some people (for the first time). Please comment with which you prefer and/or opinions on all!

webmanifest support

Supporting webmanifest could be cool, this would allow for stuff like custom titlebar colors, icons, shortcuts etc, I can see 2 ways of implementing this:

  1. suffer and implement each feature by hand
  2. register the site as an app: going to the options burger, more tools, create shortcut and setting it as a window creates the site as a ""standalone app"" which registers an app ID, then the browser simply launches it with --app-id ex: chrome_proxy.exe --profile-directory=Default --app-id=cinhimbnkkaeohfgghhklpknlkffjgod, this would automatically support all webmanifest features, even ones enabled experimentally with flags and fix stuff like #12

Rewrite data path generation

Use require.main.filename as basis, with a central gluon_data (?) directory with subdirs for Chromium/Firefox (not per browser, just per type?),

RFC: Restrict Window navigation

Window/top-level navigations should be restricted by default. This will be handled by the allowRedirects Window option, with the values:

  • false: No redirects are allowed.
  • same-origin: Redirects are allowed if the redirect URL is the same origin (as the URL given to open()).
  • true: All redirects are allowed. Not recommended.

Switch CDP -> WebDriver BiDi

Very long term thing. Move from CDP to WebDriver BiDi for browser control/communication.

Depends on many various (niche) upstream BiDi features for a full switch for most compat/wants, in order of importance:

Wishlist (probably never going to be added to BiDi):

  • Caching API (JS / see V8Cache)
  • Cancel/filter navigations

Use user-preferred browser by default

Currently gluton hardcodes a few browsers, however is someone is using a different browser like for example a custom chromium build or browsers like opera or brave this fails.

A decent solution would be:

  • use the system preferred browser
    • windows:
      • HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\Shell\Associations\URLAssociations\https\UserChoice
      • ProgID - REG_SZ
      • HKEY_LOCAL_MACHINE\SOFTWARE\Classes\${ProgID}\shell\open\command
      • (Default) - REG_SZ
    • linux: ???
    • macOS: ???
  • get all available browsers for the OS
    • ??

for windows the best module for navigating regedit seems to be winreg, not sure how to do this on linux/macos

RFC: Next-gen IPC

Edit: This got long, sorry for the novel!

Happy to break up this issue and close it in favor of multiple issues if any of the ideas seem valuable, or move it to Discord for a more live discussion, but it seemed helpful to present as a single concept first!

We take a very similar approach to Gluon's IPC methods / stores in our Electron app with a homebrew state management library, and all of the concepts below relate to the challenges we ran into as our code base began to scale and design decisions we made to make IPC methods / stores work better together in large codebases. I thought some of the ideas could be helpful as the Gluon API continues to evolve!


First: I love the direction of this new framework – thank you so much for creating it! Very excited to watch (and hopefully help with?) how it develops.

We build a fairly complex Electron app, and I'd love to evaluate migrating to Gluon in the near future as a lighter weight option once it hits 1.0.0.

Specifically, your built-in exposed IPC methods and state objects (forgive me, I'm going to use "state" and "store" interchangeably here), resonate perfectly with how we've ended up architecting data flow in our app. The ergonomics of IPC methods / state over Electron's more 'events only' model is something we've focused closely on too.

We actually went as far as to develop a framework that jams something similar on top of Electron's IPC events API: https://github.com/universe/electron-state

I wanted to drop a flew thoughts here and see if some of the use cases (and growing pains) we encountered using IPC methods / state at-scale might be helpful with API design.

The three biggest problems we ran into when building out our own methods / state model as our app scaled up included:

  • Bloated API footprints as we added more methods and state shared between the main and renderer process
  • Too much boilerplate to wire frontend frameworks up to changes in state
  • Namespace pollution on shared objects between the main and renderer processes

If we adopted Gluon right now, I would see us running into similar issues since all shared state and methods exist on the singe IPC object!

We built around these issues in electron-state by leaning on class syntax to couple and namespace relevant methods and state, in a way that automatically emits events from the class singleton whenever data changes so we can easily wire up any frontend framework to respond to state updates.

There are three independent ideas from our stab at IPC methods/state that may be interesting to take into account for Gluon, and conveniently can all be considered independently!

  1. Avoid API Namespace Pollution: Separate out user-defined methods from the other IPC helper methods, like you do with shared state using ipc.store. Perhaps only allow writing user-defined methods to something like ipc.methods (to mirror the store API) instead. If you encourage users to write directly to the main IPC object, once you hit 1.0.0 you can not add any more framework methods without bumping a major version: you might accidentally be overwriting a user created function! This will give Gluon more API flexibility in the long run, and allow new IPC helper methods on minor version bumps instead of major version bumps. This will also make the inevitable Typescript types for the library (written by you, or a 3rd party) much more managable.
  2. Reduce Responsive Frontend Boilerplate: Trigger user facing events when ipc.store is modified! I see you trigger an un-document 'web store change' IPC event to write data back to the main process from the renderer, but more granular event triggers would allow front-end frameworks to create hooks into state changes so the UI can be more easily reflected. It may be helpful to create a separate event channel than the current user-created event Gluon.ipc.on API for these automated events, in service of isolating the event namespaces as well. (Humble recommendation for all internal data sync events too – currently, if I for some reason wanted to create both a myFun and exposed myFunc function, I think things would not run as expected!)
  3. Allow User Provided Namespaces: Currently, all user provided methods and state appear to be written to a single, global namespace. This won't scale well as apps become more and more complex! Yes, users can cleverly nest their store objects, but then both you and the user loose change detection fidelity since the state store uses a proxy under the hood. Other than function name prefixes, there appears to be no nice way to organize the IPC methods. Our electron-state module does this by co-opting JS class syntax for for very dev-friendly namespaces, and the base class that these IPC state objects extend from is responsible for wiring up all the IPC sync event channels, ensuring it remains a singleton, etc. Notably though, this couples the concept of state and methods. I think this is a feature (it mirrors the "data down actions up" principle we find in frontend component design!) but a lower level, less opinionated, version of this for Gluon may simply allow for named IPC method collections or state objects that users can create and retrieve through the IPC API. E.g. Gluon.ipc.getState('my-state-object-name') or Gluon.ipc.getMethods('my-functionality-collection-name')

Let me know if any of these are worth exploring – I'm always happy to chat about them more if they align with how you imagine Gluon evolving!

ReCaptcha v2 doesnt work

image
<-- Here at bottom should be a recaptcha v2, but instead it shows on console: Uncaught (in promise) ReCaptcha error: Failed to load script

And i litteraly just got this problem with gluon. What should i do then?

Window events

Gluon should provide window events that will trigger when window is resized/maximized/closed etc.
For example here's a list of all available BrowserWindow events in Electron. This could help changing the default behaviour for window management as seen in #33

Automatic browser failure fallback

When following the browser priority order and a browser fails in some way (eg. Thorium on Windows), automatically fallback to any other browser(s) present on the system.

Minimize on close

Is this possible to support the minimize on close functionality? Not necessarily moving to the menu bar, but at least minimizing the dock when clicking the close button? I miss this functionality, because I want the app to display content immediately when activated, and I don't wait for the page to load. I am looking for something similar to this functionality in Electron


mainWindow.on('close', function (event) {
    if(!application.isQuiting){
        event.preventDefault();
        mainWindow.hide();
    }

    return false;
});

IPC will not be available after page refresh

use code like

// backend
const win = await Gluon.open('index.html', {
    windowSize: [800, 500]
  });
win.ipc.test= async (data) => {
    //....
})
// frontend
await Gluon.ipc.test('Message!');

invoke success on first run but after refresh page invoke will got error Uncaught (in promise) TypeError: Gluon.ipc.test is not a function

Single instance lock API

Gluon doesn't provide a way to implement single instance lock in apps that use it. Consequently, clicking on the app shortcut starts another instance of the app. This could be unwanted behavior by many developers

Could use similar approach as Electron

Error on startup of first example

510 git clone https://github.com/gluon-framework/examples.git
511 ll
512 cd examples
513 ll
514 cd gluworld
515 npm install
516 node .
517 h
dean@pop-os ~/.../examples/gluworld (main) $ node .
[Gluon] starting browser...
file:///home/dean/projects/gatsby/LIBRARY/gluon/examples/gluworld/node_modules/@gluon-framework/gluon/src/index.js:198
const [ browserPath, browserName ] = await findBrowserPath(forceBrowser, forceEngine);
^

TypeError: (intermediate value) is not iterable
at startBrowser (file:///home/dean/projects/gatsby/LIBRARY/gluon/examples/gluworld/node_modules/@gluon-framework/gluon/src/index.js:198:40)
at async Module.open (file:///home/dean/projects/gatsby/LIBRARY/gluon/examples/gluworld/node_modules/@gluon-framework/gluon/src/index.js:255:19)
at async file:///home/dean/projects/gatsby/LIBRARY/gluon/examples/gluworld/index.js:17:3

Node.js v18.12.1
dean@pop-os ~/.../examples/gluworld (main) $

Happy to help any way I can.

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.