Giter Site home page Giter Site logo

typecellos / blocknote Goto Github PK

View Code? Open in Web Editor NEW
5.2K 32.0 312.0 41.72 MB

A React Rich Text Editor that's block-based (Notion style) and extensible. Built on top of Prosemirror and Tiptap.

Home Page: https://www.blocknotejs.org/

License: Mozilla Public License 2.0

TypeScript 89.61% CSS 4.57% JavaScript 0.21% HTML 5.61%
block-based editor javascript notion prosemirror react rich-text-editor tiptap typescript wysiwyg

blocknote's Introduction

TypeCell

Welcome to BlockNote! The open source Block-Based React rich text editor. Easily add a modern text editing experience to your app.

Discord

Homepage - Documentation - Quickstart - Examples

Live demo

See our homepage @ https://www.blocknotejs.org or browse the examples.

Example code (React)

npm version

import { BlockNoteView, useCreateBlockNote } from "@blocknote/react";
import "@blocknote/core/fonts/inter.css";
import "@blocknote/react/style.css";

function App() {
  const editor = useCreateBlockNote();

  return <BlockNoteView editor={editor} />;
}

@blocknote/react comes with a fully styled UI that makes it an instant, polished editor ready to use in your app.

If you prefer to create your own UI components (menus), or don't want to use React, you can use @blocknote/core (advanced, see docs).

Features

BlockNote comes with a number of features and components to make it easy to embed a high-quality block-based editor in your app:

Animations:

Helpful placeholders:

Drag and drop blocks:

Nesting / indentation with tab and shift+tab:

Slash (/) menu:

Format menu:

Real-time collaboration:

Feedback ๐Ÿ™‹โ€โ™‚๏ธ๐Ÿ™‹โ€โ™€๏ธ

We'd love to hear your thoughts and see your experiments, so come and say hi on Discord or Matrix.

Contributing ๐Ÿ™Œ

See CONTRIBUTING.md for more info and guidance on how to run the project (TLDR: just use npm start).

Directory structure:

blocknote
โ”œโ”€โ”€ packages/core       - The core of the editor
โ”œโ”€โ”€ packages/react      - The main library for use in React apps
โ”œโ”€โ”€ examples            - Example apps
โ”œโ”€โ”€ playground          - App to browse the example apps (https://playground.blocknotejs.org)
โ””โ”€โ”€ tests               - Playwright end to end tests

The codebase is automatically tested using Vitest and Playwright.

License ๐Ÿ“ƒ

BlockNote is licensed under the MPL 2.0 license, which allows you to use BlockNote in commercial (and closed-source) applications. If you make changes to the BlockNote source files, you're expected to publish these changes so the wider community can benefit as well.

Credits โค๏ธ

BlockNote builds directly on two awesome projects; Prosemirror by Marijn Haverbeke and Tiptap. Consider sponsoring those libraries when using BlockNote: Prosemirror, Tiptap.

BlockNote is built as part of TypeCell. TypeCell is proudly sponsored by the renowned NLNet foundation who are on a mission to support an open internet, and protect the privacy and security of internet users. Check them out!

NLNet

Hosting and deployments powered by Vercel:

NLNet

blocknote's People

Contributors

17amir17 avatar charlesfrisbee avatar ctnicholas avatar cuire avatar dalperin avatar danlgz avatar fogle avatar github-actions[bot] avatar guyserfaty avatar horacioh avatar i-am-chitti avatar kashifwahaj avatar lonerifle avatar matthewlipski avatar naotocoding avatar niclas-j avatar philipwillms avatar richmengsix avatar sudarshanshenoy avatar tcper avatar tensor-tian avatar tomeryp avatar yousefed 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  avatar  avatar  avatar  avatar  avatar  avatar

blocknote's Issues

Docs: Discord link is broken

Hey. I really like this project and I want to see how I can participate in its development. But now the link to discord is text channel link, not an invite link.

Documentation Request: can BlockNote use TipTap extensions (e.g. for Tables)

BlockNote looks absolutely amazing, but my requirement (unfortunately) includes a need to support user-editable tables. Can BlockNote use TipTap extensions? Will they automatically appear in the slash menu? I would love some documentation on using TipTap extensions in BlockNote, if it is possible.

Thanks!

Migrate to React automatic JSX runtime

Didn't get this to work yet (some dependencies failed for this). Investigate further.

Steps required:

  • vite.config.ts files, remove jsxRuntime: "classic"
  • tsconfig files, set jsx to react-jsx

When we get this working, remove import React from "react" statements as those are not necessary anymore

Make it easy to add slash menu items

Right now slash menu items are hardcoded, it would be really cool for it to be configurable.

I believe it's already doable if the SlashMenuExtension is exported from @blocknote/core.

and just add it to the extensions like this:

extensions: [
  SlashMenuExtension.configure({
    slashMenuFactory: ReactSlashMenuFactory,
  }),
],

But again you would have more control over the keyboard shortcut and icons, etc..

UI issue in bullets

In MacOS Safari when you select a bullet list the type cursor appears before the bullet (as per this img)

This is an image

TailwindCSS and React18 Support

Hey folks awesome project.

Really, liked the notion style UI and the way it's all coming together.

Is there a way to make this work on React 18? Right now it's erroring a bit since it depends on ReactDom.render();

Also TailwindCSS support would be very much appreciated. Though, do love mantine.

fix Playwright tests on CI

The Playwright tests on CI currently fail, because there are no linux snapshots in the repo. What's the best way to fix this?

Other recommendations to improve the playwright setup?

safari: fix caret position

image

I think this might be possible to fix adding "float:left" to the caret, but haven't tested extensively yet

Bubble menu block type dropdown

Add a dropdown to change the blocktype:

image

In BlockNote, currently blocks can be both Heading and List at the same time. This means we have two options:

A. Block type dropdown only contains Text / Heading options

  • Make the block type dropdown only have options for Text / Heading (1/2/3)
  • Add separate menu buttons for the "list type":

image

Pros:

  • user can create all possible block type combinations from the menu

Cons:

  • more menu items = more clutter

B. Block type dropdown only contains Text / Heading options

  • The dropdown should have options for: Text, Heading 1 / 2 / 3, Bulleted list, Numbered list, To-do list.
  • A heading that's also a list, should show as Heading (i.e.: Heading takes precedence)
  • When changing block types, drop the other attributes (i.e.: changing a bullet-heading to text, removes both the bullet and the heading attributes, changing a list item to a heading removes the list attribute)

Pros:

  • Less menu items = cleaner

Cons:

  • Some block types won't be possible to create via the menu, but will be via keyboard (e.g.: when you type "-" in front of a heading

I think option B would have my preference atm, what do you think?

Other notes:

Image Support

Does BlockNote have img capabilities yet? If not, does it have plugin/custom block development capabilities that would allow for adding img capabilities without forking?

PS: Incredible work on the project! I would love to contribute for adding image support, custom block support, or otherwise. Would greatly appreciate it if you could let me know where can I get started.

Block node attributes are applied to both inner and outer block HTML elements

The way that TipTap Block nodes are rendered to HTML in the browser is through a block element which is wrapped in an block-outer element. Node attributes are added to both, which shouldn't be necessary.

Basically, this node:

{
  "type": "block",
  "attrs": {
    "headingType": "1"
  },
  "content": [
    {
      "type": "content",
      "attrs": {},
      "content": [
        {
          "type": "text",
          "text": "Heading"
        }
      ]
    }
  ]
}

Gets rendered as this HTML:

<div data-heading-type="1" class="..." data-node-type="block-outer">
  <div data-heading-type="1" class="..." data-node-type="block">
    <div class="..." data-node-type="block-content">
      <div>
        This is a heading
      </div>
    </div>
  </div>
</div>

Notice that the heading type attribute is present in both the block and outer-block elements. Currently, removing attributes from either causes issues with things like placeholders and styling, but it should be possible to only add attributes to one of the two.

Drag multiple elements at once

It would be great to drag more than 1 block at a time. If you multi-select right now and drag, it only drags the top element.

Add a + button next to DragHandle

Similar to Notion and other block-based editors, we should show a + menu next to the draghandle. UX should be similar to this:

image

When clicked, it can open a similar menu as the slash-menu.

This menu will be used to create new blocks, and later on (when we integrate live coding), probably also to insert community blocks / code snippets, such as charts / maps / etc.).


The way it's implemented in Notion is that it actually adds an empty block with the "slash menu" triggered and "Type to filter" as placeholder. This is pretty smart as it makes it possible to filter the menu:

image

If this is doable I think it's a smart approach, but let's also try to keep it somewhat simple (i.e.: don't spend days trying to figure this out)

Bug: TipTap's `liftListItem` causing error in block key handlers

For this particular nesting pattern:

block1
|  block2
|  |  block3
|  |  |  block4
|  block5

With the cursor at the start of block2, attempting to un-indent the block using Backspace or Shift+Tab will result in the following error: Uncaught TransformError: Invalid content for node block. This seems to be an issue with TipTap's liftListItem function, which is used in both key handlers. There are definitely other situations where this happens, the example above is just one case.

It's probably worth mentioning this issue was present before PR #64, so it's almost certainly something going wrong within liftListItem.

Bug: Bubble menu placement not updating correctly

When setting the block type using the bubble menu, e.g. from a paragraph to a heading, the new bubble menu position is not updated correctly due to the animation. This is because the bounding box for the editor's selection is computed when the user clicks the paragraph/heading/list item button, meaning it's computed at the start of the animation. As the block is visually still of the old type at this point, the bounding box doesn't change. Only after the user reselects the text does the bounding box get updated to the correct values.

Example:

User selects Heading 1:
image
Bubble menu position doesn't change:
image
Bubble menu position updates correctly only after user reselects the text:
image

This was previously somewhat fixed by delaying updating the bubble menu until the animation completes, but this was not a robust solution as it caused unnecessary delays when setting font styles and sometimes caused the menu to attempt to update when it was hidden.

DragHandle positioning

We need to clean up the position of the draghandle:

  • Fix vertical position: the draghandle is now too far to the top. Check for both headings and regular elements
  • Fix horizontal position (see below)

Horizontal position
The draghandle now displays right before elements, even when they are indented. This is ugly as it overlaps with the vertical line ("the indentation guide").

Design-wise; I don't think there's an easy option where we can have both indentation guides and a draghandle right next to the block.

So there are two options here:

a) remove indentation guides (notion also doesn't have this, it's more inspired by Roam) and display draghandles right next to the element similar to Notion
b) always show the draghandles on the left side of the document (so at the same horizontal position as root-level elements)

image

Preferably, indentation lines can be configured by the consumer library, and if enabled we show Draghandles at position B, otherwise at position A

Numbering of nested ordered lists is inherited from parent list's numbering

When nesting an ordered list inside another one, the first item in the nested list should not have the same numbering as if it were at the level of its parent.

For example, this is correct (not because GitHub's markdown editor correctly number it, but because the number is editable and I forced it to be correct):

  1. this should be "1"
  2. this should be "2"
    a. this should NOT be "3"

In the case of BlockNote, it is neither automatically numbered correctly nor is it possible for the user to force it to be correct.
BlockNote

UI migration to Mantine

Currently, BlockNote uses AtlasKit to style most of its UI components. However, AtlasKit is meant more for internal use by Atlassian, and it's less suited for use in BlockNote. Therefore, the UI should be migrated to Mantine.

TODO items

Add support for TODO items:

  • TODO items should show a checkbox on the side
  • The checked state should be stored in the prosemirror state
  • If possible, content of checked todo items should appear strikethrough (I think we can do this directly from CSS)
  • show "To-do" as placeholder

We currently store a listType attribute on Blocks. On first thought, we could:
a. use listType="todo-checked" and listType="todo-unchecked"
b. use listType="todo" and todo-checked="true" as attributes

Option B seems cleaner, but option A has other benefits (we don't need to clean for "invalid" Prosemirror states, for example, what does listType="li" and todo-checked mean? In that case todo-checked should be removed. What do you think is the best architecture for this?

Bubble menu button hints not disappearing after pressing arrow key

The button hints that show what bubble menu buttons do currently do not disappear properly after pressing an arrow key. If the bubble menu is open, navigating with the arrow keys closes the bubble menu as intended (since the selection becomes empty), but any hints that were open at the time don't disappear, and instead move to the top-left of the viewport.
ezgif-3-2ef98cf42d

(NextJS on M1 OSX) Reference error: Navigator not defined

Firstly love the implementation and would love to contribute to make it better.

I am using nextJS on macOS with the following configuration file.

  "name": "token-dashboard",
  "version": "3.8.0",
  "scripts": {
    "dev": "next dev -p 3040",
    "build": "next build",
    "start": "next start -p 3040",
    "build-stats": "cross-env ANALYZE=true pnpm run build",
    "export": "next export",
    "build-prod": "run-s clean build export",
    "clean": "rimraf .next out",
    "lint": "next lint",
    "format": "next lint --fix && prettier '**/*.{json,yaml}' --write --ignore-path .gitignore",
    "check-types": "tsc --noEmit --pretty",
    "test": "jest",
    "postbuild": "next-sitemap"
  },
  "dependencies": {
    "@blocknote/react": "0.4.6-alpha.3",
    "@emotion/react": "^11.10.6",
    "@emotion/server": "^11.10.0",
    "@mantine/carousel": "^6.0.1",
    "@mantine/core": "^6.0.1",
    "@mantine/dates": "^6.0.1",
    "@mantine/dropzone": "^6.0.1",
    "@mantine/ds": "^6.0.1",
    "@mantine/form": "^6.0.1",
    "@mantine/hooks": "^6.0.1",
    "@mantine/modals": "^6.0.1",
    "@mantine/next": "^6.0.1",
    "@mantine/notifications": "^6.0.1",
    "@mantine/nprogress": "^6.0.1",
    "@mantine/prism": "^6.0.1",
    "@mantine/spotlight": "^6.0.1",
    "@next/env": "*",
    "@tabler/icons-react": "^2.10.0",
    "@tanstack/react-query": "^4.26.1",
    "cookies-next": "^2.1.1",
    "dayjs": "^1.11.7",
    "embla-carousel-react": "^7.1.0",
    "next": "^13.2.4",
    "next-seo": "^5.15.0",
    "next-sitemap": "^4.0.5",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-query": "^3.39.3",
    "remeda": "^1.9.0"
  },
  "devDependencies": {
    "@next/bundle-analyzer": "^13.2.4",
    "@percy/cli": "^1.20.3",
    "@semantic-release/changelog": "^6.0.2",
    "@semantic-release/git": "^10.0.1",
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^14.0.0",
    "@types/jest": "^29.4.0",
    "@types/node": "^18.15.0",
    "@types/react": "^18.0.28",
    "@typescript-eslint/eslint-plugin": "^5.54.1",
    "@typescript-eslint/parser": "^5.54.1",
    "autoprefixer": "^10.4.14",
    "commitizen": "^4.3.0",
    "cross-env": "^7.0.3",
    "cssnano": "^5.1.15",
    "eslint": "^8.35.0",
    "eslint-config-airbnb-base": "^15.0.0",
    "eslint-config-airbnb-typescript": "^17.0.0",
    "eslint-config-next": "^13.2.4",
    "eslint-config-prettier": "^8.7.0",
    "eslint-plugin-import": "^2.27.5",
    "eslint-plugin-jest": "^27.2.1",
    "eslint-plugin-jest-dom": "^4.0.3",
    "eslint-plugin-jest-formatting": "^3.1.0",
    "eslint-plugin-jsx-a11y": "^6.7.1",
    "eslint-plugin-prettier": "^4.2.1",
    "eslint-plugin-react": "^7.32.2",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-simple-import-sort": "^10.0.0",
    "eslint-plugin-tailwindcss": "^3.10.1",
    "eslint-plugin-testing-library": "^5.10.2",
    "eslint-plugin-unused-imports": "^2.0.0",
    "inquirer": "^8.0.0",
    "jest": "^29.5.0",
    "jest-environment-jsdom": "^29.5.0",
    "npm-run-all": "^4.1.5",
    "postcss": "^8.4.21",
    "prettier": "^2.8.4",
    "rimraf": "^4.4.0",
    "semantic-release": "^19.0.5",
    "start-server-and-test": "^2.0.0",
    "tailwindcss": "^3.2.7",
    "typescript": "^4.9.5"
  },
  "release": {
    "branches": [
      "main"
    ],
    "plugins": [
      "@semantic-release/commit-analyzer",
      "@semantic-release/release-notes-generator",
      "@semantic-release/changelog",
      [
        "@semantic-release/npm",
        {
          "npmPublish": false
        }
      ],
      "@semantic-release/git",
      "@semantic-release/github"
    ]
  },
  "author": "Ishan Sharma"
}

If I try to refresh the page from the browser, I am getting the following error.
Screenshot 2023-03-15 at 1 00 56 PM

DragHandle menu

Add a menu when you click the DragHandle. Initially it only needs a "Delete" option.

Start of this is in #21

Npm package

Hi there, are there any published packages like this one in npm?

Bug: Drag handle at incorrect position after moving headings

When moving blocks with different font sizes (e.g. headings with different levels), just after dragging and dropping an element, the drag handle sometimes remains in the dragged block's previous position. It does, however, get updated to the correct position on a mousemove event.

Why Block?

Hello! I am very interested in editors and Blocknote, and I would love to learn more about the design philosophy behind Blocknote. I am particularly curious about the importance of the "block" concept and its indispensable role.

As an example, let's say I want to add color to some content. Without the "block" concept, I could potentially set the color directly on the parent element and achieve the same effect. However, I am interested in learning why the "block" concept is considered important and what benefits it provides in terms of design and functionality.

Bug: Drag handle test snapshot is incorrect for Firefox

One of the tests for the drag handle involves creating a heading using the slash menu, then adding another through the drag handle:

test("Clicking add button should create new block", async () => {
  await executeSlashCommand(page, "h1");
  await page.keyboard.type("Hover over this text");
  await hoverAndAddBlockFromDragHandle(page, H_ONE_BLOCK_SELECTOR, "h2");
  await page.keyboard.type("This is an h2");
  await page.waitForSelector(H_TWO_BLOCK_SELECTOR);

  await page.waitForTimeout(1000);
  await compareDocToSnapshot(page, "draghandleadd");
});

However, running the test on Firefox adds an empty nested paragraph to the second heading, as can be seen when comparing the Firefox, Chromium, and Webkit snapshots. Since the reference snapshot includes this bug, the test still passes, though this should be changed such that Firefox behaviour is the same as Chromium and Webkit,

src directory is included in npm package

Hi!
Thanks for your great work, I do like what you made ๐Ÿ˜„

I was giving a try for it, and found that the whole source code was still there inside my node_moduels where I installed @blocknote/core.

image

Maybe you should consider omitting them like using .npmignore.

Have a good day!

Bug: Multiple block dragging doesn't always work

In specific cases, dragging and dropping multiple blocks doesn't work for unknown reasons. Two known cases where this occurs include:

  1. When two or more selected blocks are the same (same type and content):
    Screen Recording 2023-01-11 at 18 56 36
    Dragging one of the selected blocks will only drag that block, not all of them.
  2. When dragging and dropping multiple blocks out of the editor:
    Screen Recording 2023-01-11 at 18 53 06
    Attempting to drag one of same blocks again will only drag that block, not all of them.

Possible to customize the placeholder text?

Incredible work on BlockNote, Yousef!

For Platforms Starter Kit 2.0, I'm planning to integrate OpenAI text completions to the text editor.

Therefore, I was wondering if there's any way we can customize the placeholder text in BlockNote? My idea is to have a debounced callback to OpenAI's ChatGPT API to do text completions in the style of a placeholder text ๐Ÿ˜„

LMK what you think! I'm happy to contribute to the source code as well if this feature is not available yet!

Bug: Content copied from outside the editor sometimes isn't parsed correctly

While copying & pasting in BlockNote works for the majority of content, there are 2 cases in which it breaks:

  1. When pasting two elements, e.g.:
<p>Foo</p>
<p>Bar<p>

Here, both elements are parsed as blockContent` and the editor doesn't know if they should be interpreted as two separate blocks or a single, nested, block. It always chooses the latter, meaning that the example is parsed as:

{
  block: {
    textContent: "Foo",
    blockGroup: {
      block: {
        textContent: "Bar"
      } 
    } 
  }
}

In this scenario, the type of the first element is also lost, so it's always interpreted as textContent.

  1. When pasting nested elements, e.g.:
<ul>
  <li>Foo
    <ul>
      <li>Bar</li>
    </ul>
  </li>
</ul>

Here, the nesting information is lost. This is mainly applicable to list items, since paragraph and heading elements can't be nested as per the HTML spec. When pasting list items into BlockNote, they too are parsed as blockContent, which can't have nested items. Therefore, the text of each nested item is concatenated to the end of the text of the parent item and the example gets parsed as:

{
  block: {
    listItemContent: "Foo Bar",
  }
}

I've already attempted to fix these issues on the copy-paste-new-architecture branch if it's of any help, though I haven't had much success.

Once this is working we'll also need additional tests for it.

Customizing BlockNote Editor Styles

I've been trying to customize the editor's fonts using _tiptapOptions but it doesn't seem to work:

const editor: BlockNoteEditor | null = useBlockNote({
    onEditorContentChange: debouncedChanges,
    _tiptapOptions: {
      editorProps: {
        attributes: {
          class: "prose font-vercel text-red-600",
        },
      },
    },
  });

The goal is to handle all the styling via @tailwind/typography.

Add drag handle tests

Currently, tests for the drag handle do not cover checking drag & drop functionality. Tests for checking if the drag handle is rendered and working properly for multiple nested & unnested blocks in a single document are also needed.

Bug: List items are converted to paragraphs when copying BlockNote content to other editors

The way BlockNote represents lists in HTML is by setting the data-list-type attribute to each list item. Therefore, list items don't need a parent list the way they normally do in HTML, which uses ol/ul tags. This non-standard way of representing list items means they are interpreted as paragraphs when copied to other editors, which only recognize that they are div elements.

Bug: Ordered list item animations not always working correctly.

For ordered list items, the animations that are supposed to play when nesting/un-nesting blocks aren't working. The implementation of ordered list animations is a lot more complicated than for unordered lists as the list item indices sometimes need to be updated. These updates are done by the OrderedListItemIndexPlugin, which applies new transactions to the document that the PreviousBlockTypePlugin then has to deal with.

When changing the nesting level of a block, the PreviousBlockTypePlugin normally applies a decoration which allows us to get the block's previous depth (prev-depth attribute), so we can set the start & end styles of the animation. However, changing the nesting level of an ordered list item will always require an update to its index, which happens immediately after. This seems to cause the decoration which sets prev-depth to not be applied, despite the PreviousBlockTypePlugin filtering these transactions correctly, resulting in the animation not working.

Additionally, the animation for ordered list items plays when they are pasted into the editor, which is not correct.

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.