├── public
├── favicon.ico
├── octicons.eot
├── octicons.ttf
├── octicons.woff
├── octicons.woff2
├── application.css
├── octicons.min.css
└── octicons.svg
├── .babelrc
├── .gitignore
├── prettier.config.js
├── webpack.config.js
├── .eslintrc.json
├── src
├── Layout.js
├── index.js
├── template.html
├── createRelayEnvironment.js
├── RepositoryIcon.js
├── App.js
├── RepositoryListItem.js
├── RepositoryStar.js
└── Dashboard.js
├── .graphqlconfig
├── package.json
├── README.md
└── LICENSE
/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react"],
3 | "plugins": ["relay"]
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __generated__/
2 | dist/
3 | node_modules/
4 | schema.graphql
5 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = require('eslint-plugin-github/prettier.config')
2 |
--------------------------------------------------------------------------------
/public/octicons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/github/github-graphql-relay-example/HEAD/public/octicons.eot
--------------------------------------------------------------------------------
/public/octicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/github/github-graphql-relay-example/HEAD/public/octicons.ttf
--------------------------------------------------------------------------------
/public/octicons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/github/github-graphql-relay-example/HEAD/public/octicons.woff
--------------------------------------------------------------------------------
/public/octicons.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/github/github-graphql-relay-example/HEAD/public/octicons.woff2
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const config = require('webpack-config-github')
2 |
3 | module.exports = env => config(env, {template: 'src/template.html'})
4 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true
4 | },
5 | "extends": ["plugin:github/es6", "plugin:github/browser", "plugin:github/react"]
6 | }
7 |
--------------------------------------------------------------------------------
/src/Layout.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default function Layout({children}) {
4 | return
{children}
5 | }
6 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './App'
4 |
5 | ReactDOM.render(, document.getElementById('root'))
6 |
--------------------------------------------------------------------------------
/.graphqlconfig:
--------------------------------------------------------------------------------
1 | {
2 | "schemaPath": "schema.graphql",
3 | "extensions": {
4 | "endpoints": {
5 | "production": {
6 | "url": "https://api.github.com/graphql",
7 | "headers": {
8 | "Authorization": "Bearer ${env:API_TOKEN}"
9 | }
10 | }
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/createRelayEnvironment.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable github/no-then */
2 |
3 | import {Environment, Network, RecordSource, Store} from 'relay-runtime'
4 |
5 | function fetchQuery(operation, variables) {
6 | return fetch('/graphql', {
7 | method: 'POST',
8 | headers: {
9 | Accept: 'application/json',
10 | 'Content-Type': 'application/json'
11 | },
12 | body: JSON.stringify({query: operation.text, variables})
13 | }).then(response => {
14 | return response.json()
15 | })
16 | }
17 |
18 | const network = Network.create(fetchQuery)
19 | const source = new RecordSource()
20 | const store = new Store(source)
21 |
22 | export default new Environment({network, store})
23 |
--------------------------------------------------------------------------------
/src/RepositoryIcon.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {createFragmentContainer, graphql} from 'react-relay'
3 |
4 | export default createFragmentContainer(
5 | RepositoryIcon,
6 | graphql`
7 | fragment RepositoryIcon_repository on Repository {
8 | isFork
9 | isMirror
10 | isPrivate
11 | }
12 | `
13 | )
14 |
15 | function RepositoryIcon({repository}) {
16 | if (repository.isFork) {
17 | return
18 | } else if (repository.isPrivate) {
19 | return
20 | } else if (repository.isMirror) {
21 | return
22 | } else {
23 | return
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import {QueryRenderer, graphql} from 'react-relay'
4 |
5 | import environment from './createRelayEnvironment'
6 |
7 | import Layout from './Layout'
8 | import Dashboard from './Dashboard'
9 |
10 | function App() {
11 | const query = graphql`
12 | query AppQuery($count: Int!, $cursor: String) {
13 | ...Dashboard
14 | }
15 | `
16 |
17 | const variables = {
18 | count: 10
19 | }
20 |
21 | return
22 | }
23 |
24 | function RenderApp({error, props}) {
25 | if (error) {
26 | return {error.message}
27 | } else if (props) {
28 | return (
29 |
30 |
31 |
32 | )
33 | } else {
34 | return Loading
35 | }
36 | }
37 |
38 | export default App
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "github-graphql-relay-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "babel-preset-react": "^6.24.1",
7 | "react": "^16.1.1",
8 | "react-dom": "^16.1.1",
9 | "react-relay": "^1.4.1"
10 | },
11 | "devDependencies": {
12 | "babel-core": "^6.26.0",
13 | "babel-loader": "^7.1.2",
14 | "babel-plugin-relay": "^1.4.1",
15 | "babel-runtime": "^6.26.0",
16 | "eslint": "^4.11.0",
17 | "eslint-plugin-github": "^0.19.1",
18 | "graphql": "^0.11.7",
19 | "graphql-cli": "^1.1.2",
20 | "node-fetch": "^1.7.3",
21 | "relay-compiler": "^1.4.1",
22 | "relay-runtime": "^1.4.1",
23 | "webpack": "^3.8.1",
24 | "webpack-config-github": "^0.2.2",
25 | "webpack-dev-server": "^2.9.4"
26 | },
27 | "scripts": {
28 | "start": "yarn install && graphql get-schema && webpack-dev-server"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/RepositoryListItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {createFragmentContainer, graphql} from 'react-relay'
3 |
4 | import RepositoryIcon from './RepositoryIcon'
5 | import RepositoryStar from './RepositoryStar'
6 |
7 | export default createFragmentContainer(
8 | RepositoryListItem,
9 | graphql`
10 | fragment RepositoryListItem_repository on Repository {
11 | name
12 | owner {
13 | login
14 | }
15 | url
16 | ...RepositoryIcon_repository
17 | ...RepositoryStar_repository
18 | }
19 | `
20 | )
21 |
22 | function RepositoryListItem({repository}) {
23 | return (
24 |
25 |
26 |
27 |
28 |
29 | {repository.owner.login}/{repository.name}
30 |
31 |
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GitHub GraphQL Relay example application
2 |
3 | Demonstrates how to use [Relay Modern](https://facebook.github.io/relay/docs/relay-modern.html) to build a simple
4 | repository listing web view against the GitHub GraphQL API.
5 |
6 |
7 |
8 | ### Running locally
9 |
10 | First, you'll need a GitHub API access token to make GraphQL API requests. You can get that
11 | [here](https://github.com/settings/tokens/new).
12 |
13 | ```
14 | $ git clone https://github.com/github/github-graphql-relay-example
15 | $ cd github-graphql-relay-example/
16 | $ API_TOKEN=abc123 yarn start
17 | ```
18 |
19 | Once your server is running, you can open http://localhost:3000.
20 |
21 | ## See also
22 |
23 | * [Ruby on Rails example using graphql-client](https://github.com/github/github-graphql-rails-example)
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 Joshua Peek
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/public/application.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 20px;
3 | padding-bottom: 20px;
4 | }
5 |
6 | .header {
7 | margin-top: 0;
8 | margin-bottom: 0;
9 | }
10 |
11 | .header h3 {
12 | margin-top: 0;
13 | margin-bottom: 0;
14 | line-height: 40px;
15 | }
16 |
17 | .octicon {
18 | vertical-align: text-top;
19 | }
20 |
21 | .repositories {
22 | width: 350px;
23 | }
24 |
25 | .star-badge {
26 | float: right;
27 | color: #777;
28 | }
29 |
30 | .star-badge .octicon {
31 | margin-left: 3px;
32 | color: #777;
33 | }
34 |
35 | .star-badge .highlight {
36 | color: #e36209;
37 | }
38 |
39 | .nav-pills .badge {
40 | background-color: #ccc;
41 | }
42 |
43 | .show-more .spinner {
44 | display: none;
45 | }
46 |
47 | .show-more.loading .spinner {
48 | display: inline-block;
49 | float: right;
50 | animation-name: spin;
51 | animation-duration: 2000ms;
52 | animation-iteration-count: infinite;
53 | animation-timing-function: linear;
54 | padding-left: 4px;
55 | }
56 |
57 | @keyframes spin {
58 | from {
59 | transform: rotate(0deg);
60 | } to {
61 | transform: rotate(360deg);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/RepositoryStar.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable jsx-a11y/anchor-is-valid */
2 |
3 | import React from 'react'
4 | import environment from './createRelayEnvironment'
5 | import {commitMutation, createFragmentContainer, graphql} from 'react-relay'
6 |
7 | function starMutation(starrableId) {
8 | const variables = {
9 | input: {
10 | starrableId
11 | }
12 | }
13 |
14 | commitMutation(environment, {
15 | variables,
16 | mutation: graphql`
17 | mutation RepositoryStarStarMutation($input: AddStarInput!) {
18 | addStar(input: $input) {
19 | starrable {
20 | ...RepositoryStar_repository
21 | }
22 | }
23 | }
24 | `
25 | })
26 | }
27 |
28 | function unstarMutation(starrableId) {
29 | const variables = {
30 | input: {
31 | starrableId
32 | }
33 | }
34 |
35 | commitMutation(environment, {
36 | variables,
37 | mutation: graphql`
38 | mutation RepositoryStarUnstarMutation($input: RemoveStarInput!) {
39 | removeStar(input: $input) {
40 | starrable {
41 | ...RepositoryStar_repository
42 | }
43 | }
44 | }
45 | `
46 | })
47 | }
48 |
49 | export default createFragmentContainer(
50 | RepositoryStar,
51 | graphql`
52 | fragment RepositoryStar_repository on Repository {
53 | id
54 | viewerHasStarred
55 | stargazers {
56 | totalCount
57 | }
58 | }
59 | `
60 | )
61 |
62 | function RepositoryStar({repository}) {
63 | const octiconClassName = repository.viewerHasStarred ? 'octicon octicon-star highlight' : 'octicon octicon-star'
64 |
65 | return (
66 |
67 | {repository.stargazers.totalCount}
68 | {
71 | e.preventDefault()
72 | repository.viewerHasStarred ? unstarMutation(repository.id) : starMutation(repository.id)
73 | }}
74 | >
75 |
76 |
77 |
78 | )
79 | }
80 |
--------------------------------------------------------------------------------
/src/Dashboard.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable jsx-a11y/anchor-is-valid */
2 |
3 | import React from 'react'
4 | import {createPaginationContainer, graphql} from 'react-relay'
5 |
6 | import RepositoryListItem from './RepositoryListItem'
7 |
8 | class Dashboard extends React.Component {
9 | loadMoreRepositories() {
10 | this.props.relay.loadMore(10, () => {})
11 | }
12 |
13 | render() {
14 | const {viewer} = this.props.data
15 |
16 | return (
17 |
18 |
19 | -
20 | Your repositories
21 | {viewer.repositories.totalCount}
22 |
23 |
24 | {viewer.repositories.edges.map(edge => )}
25 |
26 | {
29 | event.preventDefault()
30 | this.loadMoreRepositories()
31 | }}
32 | />
33 |
34 |
35 | )
36 | }
37 | }
38 |
39 | function ShowMore({repositories, onClick}) {
40 | if (repositories.pageInfo.hasNextPage) {
41 | return (
42 |
43 |
44 | Show more repositories...
45 |
46 |
47 |
48 |
49 | )
50 | } else {
51 | return
52 | }
53 | }
54 |
55 | export default createPaginationContainer(
56 | Dashboard,
57 | graphql`
58 | fragment Dashboard on Query {
59 | viewer {
60 | repositories(first: $count, after: $cursor) @connection(key: "Dashboard_repositories") {
61 | totalCount
62 | pageInfo {
63 | hasNextPage
64 | endCursor
65 | }
66 | edges {
67 | node {
68 | id
69 | ...RepositoryListItem_repository
70 | }
71 | }
72 | }
73 | }
74 | }
75 | `,
76 | {
77 | getVariables(props, {count, cursor}) {
78 | return {count, cursor}
79 | },
80 | query: graphql`
81 | query DashboardPaginationQuery($count: Int!, $cursor: String) {
82 | ...Dashboard
83 | }
84 | `
85 | }
86 | )
87 |
--------------------------------------------------------------------------------
/public/octicons.min.css:
--------------------------------------------------------------------------------
1 | @font-face{font-family:Octicons;src:url(octicons.eot?ef21c39f0ca9b1b5116e5eb7ac5eabe6);src:url(octicons.eot?#iefix) format("embedded-opentype"),url(octicons.woff2?ef21c39f0ca9b1b5116e5eb7ac5eabe6) format("woff2"),url(octicons.woff?ef21c39f0ca9b1b5116e5eb7ac5eabe6) format("woff"),url(octicons.ttf?ef21c39f0ca9b1b5116e5eb7ac5eabe6) format("truetype"),url(octicons.svg?ef21c39f0ca9b1b5116e5eb7ac5eabe6#octicons) format("svg");font-weight:400;font-style:normal}.mega-octicon,.octicon{font:normal normal normal 16px/1 Octicons;display:inline-block;text-decoration:none;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-user-select:none;-ms-user-select:none;user-select:none;speak:none}.mega-octicon{font-size:32px}.octicon-alert:before{content:"\f02d"}.octicon-arrow-down:before{content:"\f03f"}.octicon-arrow-left:before{content:"\f040"}.octicon-arrow-right:before{content:"\f03e"}.octicon-arrow-small-down:before{content:"\f0a0"}.octicon-arrow-small-left:before{content:"\f0a1"}.octicon-arrow-small-right:before{content:"\f071"}.octicon-arrow-small-up:before{content:"\f09f"}.octicon-arrow-up:before{content:"\f03d"}.octicon-beaker:before{content:"\f0dd"}.octicon-bell:before{content:"\f0de"}.octicon-bold:before{content:"\f0e2"}.octicon-book:before{content:"\f007"}.octicon-bookmark:before{content:"\f07b"}.octicon-briefcase:before{content:"\f0d3"}.octicon-broadcast:before{content:"\f048"}.octicon-browser:before{content:"\f0c5"}.octicon-bug:before{content:"\f091"}.octicon-calendar:before{content:"\f068"}.octicon-check:before{content:"\f03a"}.octicon-checklist:before{content:"\f076"}.octicon-chevron-down:before{content:"\f0a3"}.octicon-chevron-left:before{content:"\f0a4"}.octicon-chevron-right:before{content:"\f078"}.octicon-chevron-up:before{content:"\f0a2"}.octicon-circle-slash:before{content:"\f084"}.octicon-circuit-board:before{content:"\f0d6"}.octicon-clippy:before{content:"\f035"}.octicon-clock:before{content:"\f046"}.octicon-cloud-download:before{content:"\f00b"}.octicon-cloud-upload:before{content:"\f00c"}.octicon-code:before{content:"\f05f"}.octicon-comment-discussion:before{content:"\f04f"}.octicon-comment:before{content:"\f02b"}.octicon-credit-card:before{content:"\f045"}.octicon-dash:before{content:"\f0ca"}.octicon-dashboard:before{content:"\f07d"}.octicon-database:before{content:"\f096"}.octicon-desktop-download:before{content:"\f0dc"}.octicon-device-camera-video:before{content:"\f057"}.octicon-device-camera:before{content:"\f056"}.octicon-device-desktop:before{content:"\f27c"}.octicon-device-mobile:before{content:"\f038"}.octicon-diff-added:before{content:"\f06b"}.octicon-diff-ignored:before{content:"\f099"}.octicon-diff-modified:before{content:"\f06d"}.octicon-diff-removed:before{content:"\f06c"}.octicon-diff-renamed:before{content:"\f06e"}.octicon-diff:before{content:"\f04d"}.octicon-ellipses:before{content:"\f101"}.octicon-ellipsis:before{content:"\f09a"}.octicon-eye:before{content:"\f04e"}.octicon-file-binary:before{content:"\f094"}.octicon-file-code:before{content:"\f010"}.octicon-file-directory:before{content:"\f016"}.octicon-file-media:before{content:"\f012"}.octicon-file-pdf:before{content:"\f014"}.octicon-file-submodule:before{content:"\f017"}.octicon-file-symlink-directory:before{content:"\f0b1"}.octicon-file-symlink-file:before{content:"\f0b0"}.octicon-file-text:before{content:"\f011"}.octicon-file-zip:before{content:"\f013"}.octicon-file:before{content:"\f102"}.octicon-flame:before{content:"\f0d2"}.octicon-fold:before{content:"\f0cc"}.octicon-gear:before{content:"\f02f"}.octicon-gift:before{content:"\f042"}.octicon-gist-secret:before{content:"\f08c"}.octicon-gist:before{content:"\f00e"}.octicon-git-branch:before{content:"\f020"}.octicon-git-commit:before{content:"\f01f"}.octicon-git-compare:before{content:"\f0ac"}.octicon-git-merge:before{content:"\f023"}.octicon-git-pull-request:before{content:"\f009"}.octicon-globe:before{content:"\f0b6"}.octicon-grabber:before{content:"\f103"}.octicon-graph:before{content:"\f043"}.octicon-heart:before{content:"\2665"}.octicon-history:before{content:"\f07e"}.octicon-home:before{content:"\f08d"}.octicon-horizontal-rule:before{content:"\f070"}.octicon-hubot:before{content:"\f09d"}.octicon-inbox:before{content:"\f0cf"}.octicon-info:before{content:"\f059"}.octicon-issue-closed:before{content:"\f028"}.octicon-issue-opened:before{content:"\f026"}.octicon-issue-reopened:before{content:"\f027"}.octicon-italic:before{content:"\f0e4"}.octicon-jersey:before{content:"\f019"}.octicon-key:before{content:"\f049"}.octicon-keyboard:before{content:"\f00d"}.octicon-law:before{content:"\f0d8"}.octicon-light-bulb:before{content:"\f000"}.octicon-link-external:before{content:"\f07f"}.octicon-link:before{content:"\f05c"}.octicon-list-ordered:before{content:"\f062"}.octicon-list-unordered:before{content:"\f061"}.octicon-location:before{content:"\f060"}.octicon-lock:before{content:"\f06a"}.octicon-logo-gist:before{content:"\f0ad"}.octicon-logo-github:before{content:"\f092"}.octicon-mail-read:before{content:"\f03c"}.octicon-mail-reply:before{content:"\f051"}.octicon-mail:before{content:"\f03b"}.octicon-mark-github:before{content:"\f00a"}.octicon-markdown:before{content:"\f0c9"}.octicon-megaphone:before{content:"\f077"}.octicon-mention:before{content:"\f0be"}.octicon-milestone:before{content:"\f075"}.octicon-mirror:before{content:"\f024"}.octicon-mortar-board:before{content:"\f0d7"}.octicon-mute:before{content:"\f080"}.octicon-no-newline:before{content:"\f09c"}.octicon-octoface:before{content:"\f008"}.octicon-organization:before{content:"\f037"}.octicon-package:before{content:"\f0c4"}.octicon-paintcan:before{content:"\f0d1"}.octicon-pencil:before{content:"\f058"}.octicon-person:before{content:"\f018"}.octicon-pin:before{content:"\f041"}.octicon-plug:before{content:"\f0d4"}.octicon-plus-small:before{content:"\f104"}.octicon-plus:before{content:"\f05d"}.octicon-primitive-dot:before{content:"\f052"}.octicon-primitive-square:before{content:"\f053"}.octicon-pulse:before{content:"\f085"}.octicon-question:before{content:"\f02c"}.octicon-quote:before{content:"\f063"}.octicon-radio-tower:before{content:"\f030"}.octicon-reply:before{content:"\f105"}.octicon-repo-clone:before{content:"\f04c"}.octicon-repo-force-push:before{content:"\f04a"}.octicon-repo-forked:before{content:"\f002"}.octicon-repo-pull:before{content:"\f006"}.octicon-repo-push:before{content:"\f005"}.octicon-repo:before{content:"\f001"}.octicon-rocket:before{content:"\f033"}.octicon-rss:before{content:"\f034"}.octicon-ruby:before{content:"\f047"}.octicon-search:before{content:"\f02e"}.octicon-server:before{content:"\f097"}.octicon-settings:before{content:"\f07c"}.octicon-shield:before{content:"\f0e1"}.octicon-sign-in:before{content:"\f036"}.octicon-sign-out:before{content:"\f032"}.octicon-smiley:before{content:"\f0e7"}.octicon-squirrel:before{content:"\f0b2"}.octicon-star:before{content:"\f02a"}.octicon-stop:before{content:"\f08f"}.octicon-sync:before{content:"\f087"}.octicon-tag:before{content:"\f015"}.octicon-tasklist:before{content:"\f0e5"}.octicon-telescope:before{content:"\f088"}.octicon-terminal:before{content:"\f0c8"}.octicon-text-size:before{content:"\f0e3"}.octicon-three-bars:before{content:"\f05e"}.octicon-thumbsdown:before{content:"\f0db"}.octicon-thumbsup:before{content:"\f0da"}.octicon-tools:before{content:"\f031"}.octicon-trashcan:before{content:"\f0d0"}.octicon-triangle-down:before{content:"\f05b"}.octicon-triangle-left:before{content:"\f044"}.octicon-triangle-right:before{content:"\f05a"}.octicon-triangle-up:before{content:"\f0aa"}.octicon-unfold:before{content:"\f039"}.octicon-unmute:before{content:"\f0ba"}.octicon-unverified:before{content:"\f0e8"}.octicon-verified:before{content:"\f0e6"}.octicon-versions:before{content:"\f064"}.octicon-watch:before{content:"\f0e0"}.octicon-x:before{content:"\f081"}.octicon-zap:before{content:"\26a1"}
--------------------------------------------------------------------------------
/public/octicons.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
430 |
--------------------------------------------------------------------------------