├── .github
└── workflows
│ └── deploy.yml
├── .gitignore
├── .pre-commit-config.yaml
├── Makefile
├── README.md
├── color-code.py
├── color_code.tmpl
├── demo.py
├── index.css
├── index.tmpl
├── requirements.txt
└── setup.cfg
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | on:
2 | pull_request:
3 | push:
4 | branches: [main]
5 |
6 | jobs:
7 | pr:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v1
11 | - uses: actions/setup-python@v1
12 | - run: pip install virtualenv
13 | - run: make
14 | - run: make push
15 | env:
16 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | /color_code.py
3 | /index.html
4 | /index.py
5 | /out
6 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/pre-commit/pre-commit-hooks
3 | rev: v4.5.0
4 | hooks:
5 | - id: trailing-whitespace
6 | - id: end-of-file-fixer
7 | - id: check-yaml
8 | - id: debug-statements
9 | - id: double-quote-string-fixer
10 | - id: name-tests-test
11 | - id: requirements-txt-fixer
12 | - repo: https://github.com/asottile/reorder-python-imports
13 | rev: v3.12.0
14 | hooks:
15 | - id: reorder-python-imports
16 | args: [--py38-plus, --add-import, 'from __future__ import annotations']
17 | - repo: https://github.com/asottile/add-trailing-comma
18 | rev: v3.1.0
19 | hooks:
20 | - id: add-trailing-comma
21 | - repo: https://github.com/asottile/pyupgrade
22 | rev: v3.15.0
23 | hooks:
24 | - id: pyupgrade
25 | args: [--py38-plus]
26 | - repo: https://github.com/hhatto/autopep8
27 | rev: v2.0.4
28 | hooks:
29 | - id: autopep8
30 | - repo: https://github.com/PyCQA/flake8
31 | rev: 6.1.0
32 | hooks:
33 | - id: flake8
34 | - repo: https://github.com/pre-commit/mirrors-mypy
35 | rev: v1.6.0
36 | hooks:
37 | - id: mypy
38 | - repo: https://github.com/asottile/cheetah_lint
39 | rev: v1.4.0
40 | hooks:
41 | - id: cheetah-reorder-imports
42 | - id: cheetah-flake
43 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: all
2 | all: demo
3 |
4 | venv: requirements.txt
5 | rm -rf venv
6 | virtualenv venv -ppython3
7 | venv/bin/pip install -rrequirements.txt
8 |
9 | %.py: %.tmpl venv
10 | venv/bin/cheetah-compile $<
11 |
12 | .PHONY: demo
13 | demo: color_code.py index.py
14 | venv/bin/python demo.py
15 |
16 | .PHONY: push
17 | push: venv
18 | venv/bin/markdown-to-presentation push index.html index.css out
19 |
20 | .PHONY: clean
21 | clean:
22 | rm -rf venv color_code.py index.html out *.pyc
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/asottile/color-code/actions)
2 | [](https://results.pre-commit.ci/latest/github/asottile/color-code/main)
3 |
4 | color-code
5 | ==========
6 |
7 | This was a derpy project made for hackathon 19 (the "color code" hackathon).
8 |
9 | It encodes any file into colored rectangles.
10 |
11 | You can see some examples on [github pages](https://asottile.github.io/color-code)
12 |
13 | Usage:
14 |
15 | ```
16 | make
17 | . venv/bin/activate
18 | ./color-code.py FILENAME OUTPUT --name NAME [--widths]
19 | ```
20 |
--------------------------------------------------------------------------------
/color-code.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from __future__ import annotations
3 |
4 | import argparse
5 | import subprocess
6 | import sys
7 |
8 |
9 | def main() -> int:
10 | parser = argparse.ArgumentParser()
11 | parser.add_argument('filename')
12 | parser.add_argument('output')
13 | parser.add_argument('--widths', action='store_true')
14 | parser.add_argument('--name', required=True)
15 | args = parser.parse_args()
16 | args.name = args.name or args.filename
17 |
18 | with open(args.output, 'wb') as output:
19 | return subprocess.call(
20 | (sys.executable, '-m', 'color_code'),
21 | env={
22 | 'FILENAME': args.filename, 'WIDTHS': str(args.widths),
23 | 'NAME': args.name,
24 | },
25 | stdout=output,
26 | )
27 |
28 |
29 | if __name__ == '__main__':
30 | raise SystemExit(main())
31 |
--------------------------------------------------------------------------------
/color_code.tmpl:
--------------------------------------------------------------------------------
1 | #import codecs
2 |
3 |
4 |
5 |
6 |
7 | $NAME as colors
8 |
20 |
21 |
22 | #py use_widths = $WIDTHS == 'True'
23 | #py width = 50
24 | #with open($FILENAME, 'rb') as f:
25 | #while True:
26 | #py triplet = codecs.encode(f.read(3), 'hex')
27 | #if not triplet:
28 | #break
29 | #end if
30 | #if use_widths:
31 | #py width = ord(f.read(1) or b'\x00') + 20
32 | #end if
33 |
34 | #end while
35 | #end with
36 |
37 |
38 |
--------------------------------------------------------------------------------
/demo.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import copyreg
4 | import json
5 | import os.path
6 | import random
7 | import subprocess
8 | import sys
9 | import tempfile
10 | import urllib.request
11 |
12 |
13 | DOWNLOAD = (
14 | (
15 | 'dumb-init',
16 | 'https://github.com/Yelp/dumb-init/releases/download/v1.0.0/'
17 | 'dumb-init_1.0.0_amd64',
18 | ),
19 | (
20 | 'pip_faster.py',
21 | 'https://raw.githubusercontent.com/Yelp/pip-faster/'
22 | '275da8/pip_faster.py',
23 | ),
24 | (
25 | 'yelp.com-logo.png',
26 | 'https://s3-media2.fl.yelpcdn.com/assets/srv0/yelp_styleguide/'
27 | 'e0bcd848f6ae/assets/img/logos/header_logo.png',
28 | ),
29 | (
30 | 'pre-commit-favicon',
31 | 'https://raw.githubusercontent.com/pre-commit/pre-commit.github.io/'
32 | '8135597/favicon.ico',
33 | ),
34 | )
35 |
36 | ON_DISK: tuple[str, ...] = (
37 | '/bin/echo',
38 | '/bin/cat',
39 | # https://github.com/python/mypy/issues/9726
40 | copyreg.__cached__, # type: ignore
41 | )
42 |
43 |
44 | def to_filename(name: str) -> str:
45 | return os.path.basename(name)
46 |
47 |
48 | def run(filename: str, output_name: str) -> None:
49 | print(f'Making {output_name}')
50 | out_fname = to_filename(output_name)
51 | subprocess.check_call((
52 | sys.executable, 'color-code.py', '--name', output_name,
53 | filename, f'out/{out_fname}.html',
54 | ))
55 | subprocess.check_call((
56 | sys.executable, 'color-code.py', '--name', output_name, '--widths',
57 | filename, f'out/{out_fname}_widths.html',
58 | ))
59 |
60 |
61 | def main() -> int:
62 | os.makedirs('out', exist_ok=True)
63 | with tempfile.TemporaryDirectory() as tmpdir:
64 | path = os.path.join(tmpdir, 'tmpfile')
65 | for name, url in DOWNLOAD:
66 | resp = urllib.request.urlopen(url)
67 | with open(path, 'wb') as f:
68 | f.write(resp.read())
69 | run(path, name)
70 |
71 | random.seed(0)
72 | some_bytes = bytes(random.getrandbits(8) for _ in range(2400))
73 | with open(path, 'wb') as f:
74 | f.write(some_bytes)
75 | run(path, 'random')
76 |
77 | for filename in ON_DISK:
78 | run(filename, filename)
79 |
80 | names = [name for name, _ in DOWNLOAD] + list(ON_DISK) + ['random']
81 | names_fnames = [
82 | (name.rpartition('/')[2], to_filename(name)) for name in names
83 | ]
84 | with open('index.html', 'wb') as f:
85 | subprocess.check_call(
86 | (sys.executable, 'index.py'),
87 | stdout=f,
88 | env={'NAMES_FNAMES': json.dumps(names_fnames)},
89 | )
90 |
91 | return 0
92 |
93 |
94 | if __name__ == '__main__':
95 | raise SystemExit(main())
96 |
--------------------------------------------------------------------------------
/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | height: 100%;
3 | width: 100%;
4 | margin: 0;
5 | padding: 0;
6 | overflow: hidden;
7 | font-family: monospace;
8 | }
9 |
10 | h1, p {
11 | color: #333;
12 | }
13 |
14 | h1 {
15 | font-size: 48px;
16 | font-weight: bold;
17 | background: linear-gradient(
18 | to right,
19 | salmon,
20 | tomato,
21 | coral,
22 | gold,
23 | darkseagreen,
24 | lightblue,
25 | violet,
26 | indigo
27 | );
28 | -webkit-background-clip: text;
29 | -webkit-text-fill-color: transparent;
30 | }
31 |
32 | main {
33 | height: 100%;
34 | width: 100%;
35 | }
36 |
37 | .square {
38 | border-radius: 3px;
39 | display: inline-block;
40 | margin: 1px;
41 | height: 36px;
42 | opacity: 0.6;
43 | cursor: pointer;
44 | }
45 |
46 | .square:hover {
47 | opacity: 1;
48 | }
49 |
50 | .wrapper {
51 | display: flex;
52 | flex-flow: row nowrap;
53 | overflow: hidden;
54 | align-content: flex-start;
55 | }
56 |
57 | .wrapper a {
58 | flex-grow: 1;
59 | }
60 |
61 | .box {
62 | flex-basis: 150px;
63 | }
64 |
65 | .box, .inner-box {
66 | box-shadow: 2px 2px rgba(0, 0, 0, 0.3);
67 | cursor: pointer;
68 | opacity: 0.7;
69 | min-height: 35px;
70 | height: 6vh;
71 | max-height: 45px;
72 | margin: 2px;
73 | border-radius: 4px;
74 | }
75 |
76 | .box:hover, .inner-box:hover {
77 | box-shadow: none;
78 | opacity: 1;
79 | }
80 |
81 | .weird-middle {
82 | display: flex;
83 | flex-flow: row nowrap;
84 | }
85 |
86 | .info {
87 | margin: 2px;
88 | border-radius: 4px;
89 | text-align: center;
90 | flex-basis: 700px;
91 | }
92 |
93 | .info-wrapper {
94 | max-width: 90%;
95 | margin: auto;
96 |
97 | }
98 |
99 | .inner-column {
100 | display: flex;
101 | flex-flow: column nowrap;
102 | flex-grow: 1;
103 | }
104 |
105 | .inner-wrapper {
106 | display: flex;
107 | flex-flow: row nowrap;
108 | align-items: flex-start;
109 | }
110 |
111 | .inner-wrapper a {
112 | flex-grow: 1;
113 | }
114 |
115 | @media only screen and (max-width: 768px) and (min-width: 481px) {
116 | .info {
117 | flex-basis: 450px;
118 | }
119 |
120 | h1 {
121 | font-size: 36px;
122 | }
123 | }
124 |
125 | @media only screen and (max-width: 480px) {
126 | .info {
127 | flex-basis: 100%;
128 | }
129 |
130 | .inner-column {
131 | display: none !important;
132 | }
133 |
134 | h1 {
135 | font-size: 28px;
136 | }
137 |
138 | .wrapper:last-child {
139 | display: none;
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/index.tmpl:
--------------------------------------------------------------------------------
1 | #import codecs
2 | #import json
3 | #import random
4 | #import struct
5 |
6 |
7 | #py random.seed(0)
8 |
9 |
10 |
11 |
12 |
13 | Color Code
14 |
15 |
16 |
17 |
18 | $self._render_top_bottom(4)
19 |
20 | $self._render_info_sides()
21 |
22 |
23 |
color code
24 |
25 | This is a digital art program made for yelp's hackathon 19 (theme: "color code"). It encodes any file into colored rectangles by converting every 3 bytes of the file into a color and optionally, the 4th byte into a width.
26 |
27 |
View on GitHub
28 |
29 | Examples:
30 | #py names_fnames = json.loads($NAMES_FNAMES)
31 | #for name, fname in names_fnames[:-1]:
32 | $name
33 | (w/o widths ) *
34 | #end for
35 | $names_fnames[-1][0]
36 | (w/o widths )
37 |
38 |
39 |
40 | $self._render_info_sides()
41 |
42 | $self._render_top_bottom(20)
43 |
44 |
45 |
46 |
47 |
48 | #def _rect(minsize, maxsize)
49 | #py width = random.randint(minsize, maxsize)
50 | #py color = codecs.encode(struct.pack(b'I', random.getrandbits(24)), 'hex')[:6]
51 | #py names_fnames = json.loads($NAMES_FNAMES)
52 | #py rand = random.choice(names_fnames)[1] + random.choice(('_widths', ''))
53 |
54 |
55 |
56 | #end def
57 |
58 |
59 | #def _render_top_bottom(num)
60 |
61 | #for _ in range($num)
62 |
63 | #for _ in range(random.randint(5, 11))
64 | $self._rect(10, 170)
65 | #end for
66 |
67 | #end for
68 |
69 | #end def
70 |
71 | #def _render_info_sides()
72 |
73 | #for _ in range(8)
74 |
75 | #for _ in range(random.randint(2, 5))
76 | $self._rect(20, 100)
77 | #end for
78 |
79 | #end for
80 |
81 | #end def
82 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | markdown-to-presentation>=0.0.28
2 | pre-commit
3 | yelp-cheetah
4 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [mypy]
2 | check_untyped_defs = true
3 | disallow_any_generics = true
4 | disallow_incomplete_defs = true
5 | disallow_untyped_defs = true
6 | warn_redundant_casts = true
7 | warn_unused_ignores = true
8 |
9 | [mypy-testing.*]
10 | disallow_untyped_defs = false
11 |
12 | [mypy-tests.*]
13 | disallow_untyped_defs = false
14 |
--------------------------------------------------------------------------------