├── .compilerc
├── .gitignore
├── .stylelintrc
├── .vscode
├── launch.json
└── settings.json
├── LICENSE.md
├── README.md
├── build
├── imports.css
└── index.html
├── examples
└── import-export
│ └── json-cat.js
├── package.json
├── postcss.config.js
├── screenshots
├── animation-cat.gif
└── screenshot-cat.png
├── src
├── assets
│ ├── apple-touch-icon.png
│ ├── dmg
│ │ └── background.png
│ ├── favicon.ico
│ ├── pp-logo.png
│ ├── pp-logo.png.icns
│ ├── pp-logo.png.ico
│ └── regular-icon.png
├── components
│ ├── AddCloudPeerForm.jsx
│ ├── Animation.jsx
│ ├── App.jsx
│ ├── AvatarContainer.jsx
│ ├── Bucket.jsx
│ ├── Button.jsx
│ ├── Canvas.jsx
│ ├── CellSize.jsx
│ ├── CloudPeers.jsx
│ ├── ColorPicker.jsx
│ ├── CopyCSS.jsx
│ ├── CssDisplay.jsx
│ ├── DebugInfoContainer.jsx
│ ├── Dimensions.jsx
│ ├── DownloadDrawing.jsx
│ ├── Duration.jsx
│ ├── Eraser.jsx
│ ├── Eyedropper.jsx
│ ├── Field.jsx
│ ├── Frame.jsx
│ ├── FramesHandler.jsx
│ ├── GridWrapper.jsx
│ ├── HistoryContainer.jsx
│ ├── ImportImageContainer.jsx
│ ├── LoadDrawing.jsx
│ ├── Modal.jsx
│ ├── PaletteColor.jsx
│ ├── PaletteGrid.jsx
│ ├── Picker.jsx
│ ├── PixelCanvas.jsx
│ ├── PixelCell.jsx
│ ├── PixelConflictContainer.jsx
│ ├── PixelGrid.jsx
│ ├── PresenceContainer.jsx
│ ├── Preview.jsx
│ ├── ProjectInfoContainer.jsx
│ ├── RadioSelector.jsx
│ ├── Reset.jsx
│ ├── Root.jsx
│ ├── ShareLinkContainer.jsx
│ ├── SimpleNotification.jsx
│ ├── SimpleSpinner.jsx
│ ├── TitleContainer.jsx
│ ├── Tooltip.jsx
│ ├── TwitterForm.jsx
│ ├── Version.jsx
│ ├── VersionsContainer.jsx
│ └── Window.jsx
├── css
│ ├── _base.css
│ ├── _normalize.css
│ ├── _utils.css
│ ├── _variables.css
│ ├── components
│ │ ├── _App.css
│ │ ├── _Bucket.css
│ │ ├── _Button.css
│ │ ├── _CellSize.css
│ │ ├── _CloudPeers.css
│ │ ├── _ColorPicker.css
│ │ ├── _CopyCss.css
│ │ ├── _Credits.css
│ │ ├── _CssDisplay.css
│ │ ├── _Dimensions.css
│ │ ├── _DownloadDrawing.css
│ │ ├── _Duration.css
│ │ ├── _Eraser.css
│ │ ├── _EyeDropper.css
│ │ ├── _Field.css
│ │ ├── _Frame.css
│ │ ├── _FramesHandler.css
│ │ ├── _LoadDrawing.css
│ │ ├── _Menu.css
│ │ ├── _Modal.css
│ │ ├── _NewProject.css
│ │ ├── _PaletteColor.css
│ │ ├── _PaletteGrid.css
│ │ ├── _Peer.css
│ │ ├── _Picker.css
│ │ ├── _PixelConflict.css
│ │ ├── _PixelGrid.css
│ │ ├── _RadioSelector.css
│ │ ├── _Reset.css
│ │ ├── _SaveDrawing.css
│ │ ├── _SimpleNotification.css
│ │ ├── _SimpleSpinner.css
│ │ ├── _Tooltip.css
│ │ ├── _TwitterForm.css
│ │ ├── _UndoRedo.css
│ │ └── _Version.css
│ ├── fonts
│ │ ├── _fonts.css
│ │ └── files
│ │ │ ├── FontAwesome.otf
│ │ │ ├── fontawesome-webfont.eot
│ │ │ ├── fontawesome-webfont.svg
│ │ │ ├── fontawesome-webfont.ttf
│ │ │ ├── fontawesome-webfont.woff
│ │ │ ├── fontawesome-webfont.woff2
│ │ │ ├── minecraftia-regular-webfont.eot
│ │ │ ├── minecraftia-regular-webfont.svg
│ │ │ ├── minecraftia-regular-webfont.ttf
│ │ │ ├── minecraftia-regular-webfont.woff
│ │ │ └── minecraftia-regular-webfont.woff2
│ ├── imports.css
│ ├── input
│ │ ├── _button.css
│ │ └── _inputText.css
│ └── layout
│ │ ├── _banner.css
│ │ ├── _grid.css
│ │ ├── _header.css
│ │ └── _queries.css
├── electron.js
├── index.html
├── index.jsx
├── lib
│ └── hypermerge-redux.js
├── logic
│ ├── Clock.js
│ ├── Init.js
│ ├── Keyboard.js
│ ├── Mutation.js
│ ├── Pixels.js
│ └── Versions.js
├── records
│ ├── CloudPeer.js
│ ├── Identity.js
│ ├── Peer.js
│ ├── Pixel.js
│ ├── Project.js
│ ├── State.js
│ └── Tree.js
├── store
│ ├── actions
│ │ └── actionCreators.js
│ ├── autoSave.js
│ ├── cloudPeers.js
│ ├── configureStore.js
│ ├── hypermergeHelpers.js
│ ├── openUrlHandler.js
│ ├── reducers
│ │ ├── reducer.js
│ │ └── reducerHelpers.js
│ └── whenChanged.js
└── utils
│ ├── cssParse.js
│ ├── serialization.js
│ ├── share.js
│ ├── shareLink.js
│ └── storage.js
└── yarn.lock
/.compilerc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "development": {
4 | "application/javascript": {
5 | "presets": [
6 | ["env", { "targets": { "electron": "1.6.0" } }],
7 | "react"
8 | ],
9 | "plugins": [
10 | "transform-object-rest-spread",
11 | "transform-class-properties",
12 | "transform-es2015-classes",
13 | "react-hot-loader/babel"
14 | ],
15 | "sourceMaps": "inline"
16 | }
17 | },
18 | "production": {
19 | "application/javascript": {
20 | "presets": [
21 | ["env", { "targets": { "electron": "1.6.0" } }],
22 | "react"
23 | ],
24 | "plugins": [
25 | "transform-object-rest-spread",
26 | "transform-class-properties",
27 | "transform-es2015-classes"
28 | ],
29 | "sourceMaps": "none"
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist/bundle.js
3 | npm-debug.log
4 | deploy/*
5 | Procfile
6 | config.json
7 | public
8 | routes
9 | npm-debug.log
10 | .directory
11 | /.data
12 | .env
13 | .eslintcache
14 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "stylelint-config-standard",
4 | "stylelint-config-lost"
5 | ],
6 | "rules": {
7 | "at-rule-no-unknown": [
8 | true,
9 | {
10 | "ignoreAtRules": ["mixin", "if", "extend", "/^define[a-z]*/"]
11 | }
12 | ],
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch Client 1",
11 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
12 | "program": "${workspaceFolder}/src/electron.js",
13 | "protocol": "inspector",
14 | "env": {
15 | "CLIENT_ID": "1"
16 | }
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "standard.enable": true
3 | }
4 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Javier Valencia Romero
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pixelpusher
2 |
3 | 
4 |
5 | Read the announcement blog post here:
6 |
7 | [Pixelpusher: Real-time peer-to-peer collaboration with React](https://medium.com/@pvh/pixelpusher-real-time-peer-to-peer-collaboration-with-react-7c7bc8ecbf74)
8 |
9 | ## Downloadable releases
10 |
11 | https://github.com/automerge/pixelpusher/releases
12 |
13 | A Mac .dmg and Windows installer are available. These are hand built at the moment.
14 |
15 | ## Installation from source
16 |
17 | ```bash
18 | # Install latest node. I'm using 9.2.1
19 | brew install yarn
20 | brew install openssl
21 | yarn
22 |
23 | export CPPFLAGS=-I/usr/local/opt/openssl/include
24 | export LDFLAGS=-L/usr/local/opt/openssl/lib
25 | yarn start
26 | ```
27 |
28 | You can start additional clients by setting `CLIENT_ID` (default: 0):
29 |
30 | ```
31 | CLIENT_ID=1 yarn start
32 | ```
33 |
34 | If you want to edit the CSS, for now, start the css watcher separately:
35 |
36 | ```bash
37 | yarn run css
38 | ```
39 |
40 | 
41 |
42 | ## Slack
43 |
44 | [Join the Automerge Slack community](https://communityinviter.com/apps/automerge/automerge)
45 |
46 | There is a #pixelpusher channel. Come share your artwork and ask us about the code!
47 |
--------------------------------------------------------------------------------
/build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/examples/import-export/json-cat.js:
--------------------------------------------------------------------------------
1 | export const exampleCat = {
2 | "id": "ryCp2kGsx",
3 | "frames":[
4 | {
5 | "pixels":[
6 | "#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffcdd2","#ffffff","#303f46","#303f46","#ffffff","#ffcdd2","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffcdd2","#ffcdd2","#ffffff","#ffffff","#ffcdd2","#ffcdd2","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffcdd2","#ffffff","#ffffff","#ffffff","#ffffff","#ffcdd2","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#000000","#ffffff","#ffffff","#000000","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#000000","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#607d8b","#607d8b","#607d8b","#607d8b","#607d8b","#ffffff","#ffffff","#607d8b","#607d8b","#607d8b","#ffffff","#ffffff","#607d8b","#607d8b","#607d8b","#607d8b"
7 | ],
8 | "interval":25,
9 | "id":"ryHxk7Qsx"
10 | },
11 | {
12 | "pixels":[
13 | "#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffcdd2","#ffffff","#303f46","#303f46","#ffffff","#ffcdd2","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffcdd2","#ffcdd2","#ffffff","#ffffff","#ffcdd2","#ffcdd2","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffcdd2","#ffffff","#ffffff","#ffffff","#ffffff","#ffcdd2","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#000000","#ffffff","#ffffff","#000000","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#000000","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#607d8b","#607d8b","#607d8b","#607d8b","#607d8b","#ffffff","#ffffff","#607d8b","#607d8b","#607d8b","#ffffff","#ffffff","#607d8b","#607d8b","#607d8b","#607d8b"
14 | ],
15 | "interval":50,
16 | "id":"HkZXJXmie"
17 | },
18 | {
19 | "pixels":[
20 | "#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffcdd2","#ffffff","#303f46","#303f46","#ffffff","#ffcdd2","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffcdd2","#ffcdd2","#ffffff","#ffffff","#ffcdd2","#ffcdd2","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffcdd2","#ffffff","#ffffff","#ffffff","#ffffff","#ffcdd2","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#000000","#ffffff","#ffffff","#000000","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#000000","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#607d8b","#607d8b","#607d8b","#607d8b","#607d8b","#ffffff","#ffffff","#607d8b","#607d8b","#607d8b","#ffffff","#ffffff","#607d8b","#607d8b","#607d8b","#607d8b"
21 | ],
22 | "interval":75,
23 | "id":"BykH1Qmig"
24 | },
25 | {
26 | "pixels":[
27 | "#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffcdd2","#ffffff","#303f46","#303f46","#ffffff","#ffcdd2","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffcdd2","#ffcdd2","#ffffff","#ffffff","#ffcdd2","#ffcdd2","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffcdd2","#ffffff","#ffffff","#ffffff","#ffffff","#ffcdd2","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#000000","#ffffff","#ffffff","#000000","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#000000","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#ffffff","#303f46","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#ffffff","#ffffff","#303f46","#303f46","#303f46","#303f46","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#ffffff","#303f46","#303f46","#607d8b","#607d8b","#607d8b","#607d8b","#607d8b","#ffffff","#ffffff","#607d8b","#607d8b","#607d8b","#ffffff","#ffffff","#607d8b","#607d8b","#607d8b","#607d8b"
28 | ],
29 | "interval":100,
30 | "id":"S1F3nG7sg"
31 | }
32 | ],
33 | "palette":[
34 | {"color":"#000000","id":0},{"color":"#ff0000","id":1},{"color":"#e91e63","id":2},{"color":"#9c27b0","id":3},{"color":"#673ab7","id":4},{"color":"#3f51b5","id":5},{"color":"#2196f3","id":6},{"color":"#03a9f4","id":7},{"color":"#00bcd4","id":8},{"color":"#009688","id":9},{"color":"#4caf50","id":10},{"color":"#8bc34a","id":11},{"color":"#cddc39","id":12},{"color":"#9ee07a","id":13},{"color":"#ffeb3b","id":14},{"color":"#ffc107","id":15},{"color":"#ff9800","id":16},{"color":"#ffcdd2","id":17},{"color":"#ff5722","id":18},{"color":"#795548","id":19},{"color":"#9e9e9e","id":20},{"color":"#607d8b","id":21},{"color":"#303f46","id":22},{"color":"#ffffff","id":23},{"color":"#383535","id":24},{"color":"#383534","id":25},{"color":"#383533","id":26},{"color":"#383532","id":27},{"color":"#383531","id":28},{"color":"#383530","id":29}
35 | ],
36 | "cellSize":5,
37 | "columns":16,
38 | "rows":16
39 | };
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pixelpusher",
3 | "productName": "pixelpusher",
4 | "version": "0.0.4",
5 | "description": "Draw and animate Pixel Art, then export the results to CSS, share or download them, powered by Hypermerge",
6 | "main": "src/electron.js",
7 | "scripts": {
8 | "start": "electron-forge start",
9 | "package": "electron-forge package",
10 | "make": "electron-forge make",
11 | "publish": "electron-forge publish",
12 | "css": "postcss --watch --dir=build src/css/imports.css"
13 | },
14 | "keywords": [],
15 | "author": "jeffpeterson",
16 | "config": {
17 | "forge": {
18 | "make_targets": {
19 | "win32": [
20 | "squirrel"
21 | ],
22 | "darwin": [
23 | "dmg"
24 | ],
25 | "linux": [
26 | "deb",
27 | "rpm"
28 | ]
29 | },
30 | "electronPackagerConfig": {
31 | "packageManager": "yarn",
32 | "icon": "src/assets/pp-logo.png.icns"
33 | },
34 | "electronInstallerDMG": {
35 | "title": "pixelpusher",
36 | "icon": "./src/assets/pp-logo.png.icns",
37 | "iconsize": 100,
38 | "background": "./src/assets/dmg/background.png"
39 | },
40 | "electronWinstallerConfig": {
41 | "name": "pixel_art_forge"
42 | },
43 | "electronInstallerDebian": {},
44 | "electronInstallerRedhat": {},
45 | "github_repository": {
46 | "owner": "",
47 | "name": ""
48 | },
49 | "windowsStoreConfig": {
50 | "packageName": "",
51 | "name": "pixelartforge"
52 | }
53 | }
54 | },
55 | "dependencies": {
56 | "automerge": "^0.7.0",
57 | "bs58": "^4.0.1",
58 | "classnames": "^2.2.5",
59 | "cookie-parser": "^1.4.3",
60 | "electron-compile": "^6.4.2",
61 | "electron-devtools-installer": "^2.1.0",
62 | "electron-squirrel-startup": "^1.0.0",
63 | "get-image-pixels": "^1.0.1",
64 | "hypercore": "^6.12.1",
65 | "hyperdiscovery": "^7.0.0",
66 | "hypermerge": "^0.3.2",
67 | "immutable": "^3.8.2",
68 | "js-crc": "^0.2.0",
69 | "lodash": "^4.17.4",
70 | "lost": "^8.2.0",
71 | "mkdirp": "^0.5.1",
72 | "postcss-import": "^11.0.0",
73 | "postcss-reporter": "^5.0.0",
74 | "precss": "^2.0.0",
75 | "pretty-hash": "^1.0.1",
76 | "radium": "^0.19.6",
77 | "random-access-memory": "^2.4.0",
78 | "react": "^16.1.0",
79 | "react-color": "^2.13.8",
80 | "react-custom-scrollbars": "^4.2.1",
81 | "react-dom": "^16.1.0",
82 | "react-hot-loader": "^3.1.2",
83 | "react-modal": "^3.1.2",
84 | "react-redux": "^5.0.6",
85 | "react-transition-group": "^2.2.1",
86 | "redux": "^3.7.2",
87 | "shortid": "^2.2.8"
88 | },
89 | "devDependencies": {
90 | "babel-plugin-transform-class-properties": "^6.24.1",
91 | "babel-plugin-transform-es2015-classes": "^6.24.1",
92 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
93 | "babel-preset-env": "^1.6.1",
94 | "babel-preset-react": "^6.24.1",
95 | "electron-forge": "^5.1.0",
96 | "electron-prebuilt-compile": "1.8.2",
97 | "postcss-cli": "^4.1.1",
98 | "postcss-url": "^7.3.0"
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = () => {
2 | return {
3 | plugins: [
4 | require('postcss-import')(),
5 | require('postcss-url')({
6 | url: 'inline',
7 | }),
8 | require('precss')(),
9 | require('autoprefixer')({
10 | browsers: ['last 2 versions', 'IE > 8']
11 | }),
12 | require('lost'),
13 | require('postcss-reporter')({
14 | clearMessages: true
15 | })
16 | ]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/screenshots/animation-cat.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/automerge/pixelpusher/e03d9f0a2703f1f8e60b676c5bbf2381b9f4220e/screenshots/animation-cat.gif
--------------------------------------------------------------------------------
/screenshots/screenshot-cat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/automerge/pixelpusher/e03d9f0a2703f1f8e60b676c5bbf2381b9f4220e/screenshots/screenshot-cat.png
--------------------------------------------------------------------------------
/src/assets/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/automerge/pixelpusher/e03d9f0a2703f1f8e60b676c5bbf2381b9f4220e/src/assets/apple-touch-icon.png
--------------------------------------------------------------------------------
/src/assets/dmg/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/automerge/pixelpusher/e03d9f0a2703f1f8e60b676c5bbf2381b9f4220e/src/assets/dmg/background.png
--------------------------------------------------------------------------------
/src/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/automerge/pixelpusher/e03d9f0a2703f1f8e60b676c5bbf2381b9f4220e/src/assets/favicon.ico
--------------------------------------------------------------------------------
/src/assets/pp-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/automerge/pixelpusher/e03d9f0a2703f1f8e60b676c5bbf2381b9f4220e/src/assets/pp-logo.png
--------------------------------------------------------------------------------
/src/assets/pp-logo.png.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/automerge/pixelpusher/e03d9f0a2703f1f8e60b676c5bbf2381b9f4220e/src/assets/pp-logo.png.icns
--------------------------------------------------------------------------------
/src/assets/pp-logo.png.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/automerge/pixelpusher/e03d9f0a2703f1f8e60b676c5bbf2381b9f4220e/src/assets/pp-logo.png.ico
--------------------------------------------------------------------------------
/src/assets/regular-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/automerge/pixelpusher/e03d9f0a2703f1f8e60b676c5bbf2381b9f4220e/src/assets/regular-icon.png
--------------------------------------------------------------------------------
/src/components/AddCloudPeerForm.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Field from './Field'
3 |
4 | export default class AddCloudPeerForm extends React.Component {
5 | constructor (props) {
6 | super(props)
7 | this.state = {key: ''}
8 | }
9 |
10 | click () {
11 | let key
12 | try {
13 | key = /^(dat:\/\/)?([0-9a-f]{64})$/i.exec(this.state.key)[2]
14 | if (!key) throw new Error('Missing key')
15 | this.props.onAdd(key)
16 | } catch (e) {
17 | this.setState({
18 | validationError: 'Invalid key'
19 | })
20 | }
21 | }
22 |
23 | render () {
24 | return (
25 |
26 |
Add Archiver
27 | this.setState({key, validationError: null})}
31 | />
32 | {this.state.validationError}
33 |
34 |
35 | )
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/Animation.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import radium from 'radium';
3 |
4 | const Animation = radium((props) => {
5 | const pulseKeyframes = radium.keyframes(props.boxShadow, 'pulse');
6 | const style = {
7 | position: 'absolute',
8 | animation: `x ${props.duration}s infinite`,
9 | animationName: pulseKeyframes
10 | };
11 | return (
12 |
13 | );
14 | });
15 |
16 | export default Animation;
17 |
--------------------------------------------------------------------------------
/src/components/App.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PixelCanvasContainer from './PixelCanvas';
3 | import CellSizeContainer from './CellSize';
4 | import ColorPickerContainer from './ColorPicker';
5 | import CloudPeersContainer from './CloudPeers';
6 | import ModalContainer from './Modal';
7 | import DimensionsContainer from './Dimensions';
8 | import DurationContainer from './Duration';
9 | import EraserContainer from './Eraser';
10 | import BucketContainer from './Bucket';
11 | import EyedropperContainer from './Eyedropper';
12 | import FramesHandlerContainer from './FramesHandler';
13 | import PaletteGridContainer from './PaletteGrid';
14 | import ResetContainer from './Reset';
15 | import SimpleNotificationContainer from './SimpleNotification';
16 | import SimpleSpinnerContainer from './SimpleSpinner';
17 | import PresenceContainer from './PresenceContainer';
18 | import VersionsContainer from './VersionsContainer';
19 | import DebugInfoContainer from './DebugInfoContainer';
20 | import ShareLinkContainer from './ShareLinkContainer';
21 | import ProjectInfoContainer from './ProjectInfoContainer';
22 | import ImportImageContainer from './ImportImageContainer';
23 | import TitleContainer from './TitleContainer';
24 |
25 | export default class App extends React.Component {
26 | constructor() {
27 | super();
28 | this.state = {
29 | modalType: null,
30 | modalOpen: false,
31 | helpOn: false,
32 | showCookiesBanner: false
33 | };
34 | }
35 |
36 | changeModalType(type) {
37 | this.setState({
38 | modalType: type,
39 | modalOpen: true
40 | });
41 | }
42 |
43 | closeModal() {
44 | this.setState({
45 | modalOpen: false
46 | });
47 | }
48 |
49 | hideCookiesBanner() {
50 | this.setState({
51 | showCookiesBanner: false
52 | });
53 | }
54 |
55 | toggleHelp() {
56 | this.setState({ helpOn: !this.state.helpOn });
57 | }
58 |
59 | tip(text) {
60 | return this.state.helpOn ? text : null
61 | }
62 |
63 | render() {
64 | return (
65 |
66 | {this.renderHeader()}
67 |
68 |
69 |
74 |
75 | {this.renderLeftSide()}
76 |
77 |
78 |
79 |
86 |
87 |
88 |
89 |
90 | {this.renderRightSide()}
91 |
92 |
93 |
{ this.closeModal(); }}
97 | open={() => { this.changeModalType(this.state.modalType); }}
98 | />
99 |
100 |
101 | );
102 | }
103 |
104 | renderHeader() {
105 | return (
106 |
120 | );
121 | }
122 |
123 | renderLeftSide() {
124 | return (
125 |
126 |
127 |
128 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 | Originally by JVALEN
176 |
177 |
178 |
179 |
180 |
181 | );
182 | }
183 |
184 | renderRightSide() {
185 | return (
186 |
187 |
188 |
189 | { this.changeModalType('addCloudPeer'); }
191 | } />
192 |
193 |
194 | );
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/src/components/AvatarContainer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 | import * as Versions from '../logic/Versions'
4 |
5 | import Canvas from './Canvas'
6 |
7 | class Avatar extends React.Component {
8 | render () {
9 | const {identity, avatar} = this.props
10 |
11 | const color = identity && Versions.color(identity)
12 |
13 | if (!identity) return null
14 |
15 | return (
16 |
17 | { avatar
18 | ?
19 | : null}
20 |
21 | )
22 | }
23 | }
24 |
25 | const mapStateToProps = (state, {identityId}) => {
26 | const identity = state.identities.get(identityId, null)
27 | const avatarId = identity && identity.getIn(['doc', 'avatarId'])
28 | const avatar = avatarId && state.projects.get(avatarId)
29 |
30 | return {
31 | identity,
32 | avatar
33 | }
34 | }
35 |
36 | const mapDispatchToProps = dispatch => ({
37 | dispatch
38 | })
39 |
40 | const AvatarContainer = connect(
41 | mapStateToProps,
42 | mapDispatchToProps
43 | )(Avatar)
44 |
45 | export default AvatarContainer
46 |
--------------------------------------------------------------------------------
/src/components/Bucket.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import { bindActionCreators } from 'redux';
4 | import * as actionCreators from '../store/actions/actionCreators';
5 |
6 | const Bucket = (props) => {
7 | const handleClick = () => {
8 | props.actions.setBucket();
9 | };
10 |
11 | return (
12 |