#!/usr/bin/env python3
"""
# Script ob-colors
## Metadata
**Kind**: #obsidian/ob-script
**Language**: #python
**Parent**:: [[Obsidian Chore Scripts]], [[Executable Markdown File]]
## Synopsis
Dump palettes saved in macOS color picker.
Example:: [[Palette - Obsidian]]
## References
- JXA Cookbook Authors. (2017). _[Using Objective C (ObjC) with JXA](https://github.com/JXA-Cookbook/JXA-Cookbook/wiki/Using-Objective-C-(ObjC)-with-JXA)_. GitHub.
- Apple Developer Authors. (2019). _[NSColorList](https://developer.apple.com/documentation/appkit/nscolorlist?language=objc)_. Apple Developer Documentation.
- [[Wikipedia Authors - Luma (Video) (Highlights)]]
## Script
```python
# """
import filecmp
import json
import os
import re
import subprocess
import tempfile
from pathlib import Path
JXA_SCRIPT = '''
ObjC.import("AppKit");
function hex(nsfloat) {
return Math.round(ObjC.unwrap(nsfloat) * 255).toString(16).toUpperCase().padStart(2, '0');
}
function rgba(nscolor) {
const r = hex(nscolor.redComponent);
const g = hex(nscolor.greenComponent);
const b = hex(nscolor.blueComponent);
const a = hex(nscolor.alphaComponent);
return `${r}${g}${b}${a == 'FF' ? '' : a}`;
}
let palettesJSON = {};
for (const palette of ObjC.unwrap($.NSColorList.availableColorLists)) {
const name = ObjC.unwrap(palette.name);
const keys = ObjC.unwrap(palette.allKeys);
let paletteJSON = {};
for (const key of keys) {
const color = palette.colorWithKey(key).colorUsingColorSpace($.NSColorSpace.genericRGBColorSpace);
paletteJSON[ObjC.unwrap(key).replace('\\x00', '')] = rgba(color);
}
palettesJSON[name] = paletteJSON;
}
JSON.stringify(palettesJSON, null, 2)
'''
INVALID_CHARS_RE = re.compile(r'[\[\]\'?*#:||,\\/:" ~]+')
ESCAPE_MD_RE = re.compile(r'[#]')
def safe_palette_name(name):
return INVALID_CHARS_RE.sub(' ', name).strip()
def safe_color_name(name):
return ESCAPE_MD_RE.sub(r'\\\g<0>', name)
def auto_color(rgba):
r = int(rgba[0:2], 16)
g = int(rgba[2:4], 16)
b = int(rgba[4:6], 16)
brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b
if len(rgba) == 8:
brightness = brightness * int(rgba[6:8], 16) / 255
if brightness > 144:
return 'black'
return 'white'
palettes = json.loads(
subprocess.check_output(
['osascript', '-l', 'JavaScript', '-e', JXA_SCRIPT],
stderr=subprocess.DEVNULL,
encoding='utf-8'
)
)
export_dir = Path.home() / 'Dropbox' / 'Brain' / 'robot' / 'Color Palettes'
for name, palette in palettes.items():
name = safe_palette_name(name)
tmp_md_fd, tmp_md_path = tempfile.mkstemp(suffix='.md', text=True)
with os.fdopen(tmp_md_fd, 'w') as md_io:
print(f'# Palette - {name}\n\n', file=md_io)
print('**Kind**:: #color-palette', file=md_io)
print('**Source**:: #from/macos-colors', file=md_io)
print('**Generated by**:: [[ob-colors]]', file=md_io)
print('\n| Name | Color |\n| --- | --- |', file=md_io)
for color_name, color in palette.items():
colorized_rgb = f'<code style="color:{auto_color(color)}; background:#{color}"> {color} </code>'
print(
f'| {safe_color_name(color_name)} | {colorized_rgb} |', file=md_io)
md_file = export_dir / f'Palette - {name}.md'
tmp_md_path = Path(tmp_md_path)
if not md_file.exists() or not filecmp.cmp(tmp_md_path, md_file, shallow=False):
if name != "System" or not md_file.exists():
print('New: {}'.format(md_file.name))
os.replace(tmp_md_path, md_file)
else:
# print('Same: {}'.format(md_file.name))
tmp_md_path.unlink()
"""
# vim: ft=python
```
"""