├── .gitignore ├── .npmignore ├── .travis.yml ├── README.md ├── hyperambient.gif ├── index.js ├── package.json └── preinstall.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | hyperambient.gif 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HyperAmbient - A HyperTerm theme that responds to ambient light changes 2 | 3 | [![Build Status](https://travis-ci.org/jamox/hyperambient.svg?branch=master)](https://travis-ci.org/jamox/hyperambient) 4 | [![npm version](https://badge.fury.io/js/hyperambient.svg)](https://badge.fury.io/js/hyperambient) 5 | 6 | ![Demo](https://cdn.rawgit.com/jamox/hyperambient/master/hyperambient.gif) 7 | 8 | > Moving from shadows to outdoors and back, the camera adjusted for the changes in lightning conditions - however, you may notice the color scheme changing from dark to light and back. 9 | 10 | Due to changing lightning conditions using just one color scheme rarely suffices. Especially when moving from indoors to sunny outdoors a dark color scheme that looked incredible inside is almost unreadable. 11 | Thus the terminal color scheme should react to ambient light changes! 12 | 13 | Let me introduce HyperAmbient - the HyperTerm theme that that changes the color scheme to fit the current ambient lighting conditions. 14 | 15 | The theme toggles between [hyperterm-solarized-light](https://www.npmjs.com/package/hyperterm-solarized-light) and [hyperterm-solarized-dark](https://www.npmjs.com/package/hyperterm-solarized-dark) 16 | 17 | NOTE: Currently underlying implementation for accessing ambient light sensor data only works in OSX. 18 | 19 | ## How install 20 | 21 | Install HyperTerm and add hyperambient to plugins in ~/.hyperterm.js. 22 | ```json 23 | plugins: ["hyperambient"], 24 | ``` 25 | 26 | HyperTerm should automatically load the plugin. If it does not, perform a "Full Reload" for the change to take effect. 27 | 28 | ## How to use 29 | 30 | It automatically checks for ambient light changes and changes color shceme if neccesary - keep on using hyperterm for a while when ambient lightning condition change. 31 | 32 | Should you want the changes to reflect immediately you'll need to make a Full Reload to all plugins (shift + cmd + R) 33 | 34 | ### Customizing 35 | 36 | Should you want to use alternative HyperTerm themes for light and dark ambient lighting conditions, you may define the themes in `~/.hyperambient.json` like this: 37 | 38 | ```javascript 39 | { 40 | "config": { 41 | "darkTheme": "hyperterm-gruvbox-dark", 42 | "lightTheme": "hyperterm-one-light" 43 | } 44 | } 45 | ``` 46 | 47 | For this change to take effect, you may need to run the Plugins -> Update All **twice**, and ignore possible errors after the first run. 48 | The update process may take a while and there is no clear progress bar. So be patient. 49 | 50 | Also, after modifying the `~/.hyperambient.json` Plugins -> Update All may need to be run **twice**! 51 | 52 | ### Troubleshooting 53 | 54 | * The ambient light sensor is located next to facetime camera on most mac modelst 55 | * It takes a few event for the change to take place, try hitting enter for 50 times 56 | * Checkout DevTools log for possible ambient light readings. 57 | * You might not exeed the threshold on which the color scheme is changed. 58 | 59 | ## TODO 60 | 61 | - [x] Automatically change color scheme when lightning conditions changes 62 | - [x] Make themes configurable 63 | - [ ] Make threshold configurable 64 | 65 | -------------------------------------------------------------------------------- /hyperambient.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamo/hyperambient/009165f59db6fbe7b66c65ecab20b5af459a0f56/hyperambient.gif -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { homedir } = require('os') 4 | const { readFileSync, accessSync } = require('fs') 5 | const { resolve } = require('path') 6 | 7 | const ambient = require('ambientlight') 8 | 9 | const configfile = resolve(homedir(), '.hyperambient.json') 10 | var ambientConfig = { config: {} } 11 | try { 12 | accessSync(configfile) 13 | ambientConfig = JSON.parse(readFileSync(configfile, 'utf-8')) 14 | } catch (e) { 15 | // NOP 16 | } 17 | 18 | const light = require(ambientConfig.config.lightTheme || 'hyperterm-solarized-light') 19 | const dark = require(ambientConfig.config.darkTheme || 'hyperterm-solarized-dark') 20 | 21 | // Selected by taking multiple measurements from multiple inside and outside by 22 | // using the ambientlight library 23 | const threshold = 15000000 24 | 25 | exports.decorateConfig = (config) => { 26 | const lux = ambient() 27 | 28 | if (lux > threshold) { 29 | return light.decorateConfig(config) 30 | } 31 | return dark.decorateConfig(config) 32 | } 33 | 34 | var ambientCounter = 0 35 | exports.middleware = (store) => (next) => (action) => { 36 | ambientCounter += 1 37 | if (ambientCounter > 100) { 38 | ambientCounter = 0 39 | store.dispatch({ 40 | type: 'UPDATE_THEME', 41 | config: config.getConfig() 42 | }) 43 | } 44 | next(action) 45 | } 46 | 47 | exports.reduceUI = (state, action) => { 48 | switch (action.type) { 49 | case 'UPDATE_THEME': 50 | const lux = ambient() 51 | const oldReading = state.hyperambient || 0 52 | const isBetween = Math.min(lux, oldReading) <= threshold && threshold <= Math.max(lux, oldReading) 53 | console.log(`Ambient light: ${lux}`) 54 | 55 | if (!isBetween) { 56 | // No need to change the theme 57 | return state.set('hyperambient', lux) 58 | } 59 | 60 | var theme 61 | if (lux > threshold) { 62 | theme = light.decorateConfig(action.config) 63 | } else { 64 | theme = dark.decorateConfig(action.config) 65 | } 66 | 67 | return state.set('foregroundColor', theme.foregroundColor) 68 | .set('backgroundColor', theme.backgroundColor) 69 | .set('borderColor', theme.borderColor) 70 | .set('cursorColor', theme.cursorColor) 71 | .set('colors', theme.colors) 72 | .set('termCSS', `${config.termCSS || ''} ${theme.termCSS}`) 73 | .set('css', `${config.css || ''} ${theme.css}`) 74 | .set('hyperambient', lux) 75 | } 76 | return state 77 | } 78 | 79 | exports.mapTermsState = (state, map) => { 80 | return Object.assign(map, { 81 | hyperambient: state.ui.hyperambient 82 | }) 83 | } 84 | 85 | exports.getTermProps = (uid, parentProps, props) => { 86 | return Object.assign(props, { 87 | hyperambient: parentProps.hyperambient 88 | }) 89 | } 90 | 91 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hyperambient", 3 | "version": "0.4.0", 4 | "author": "@jamox", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/jamox/hyperambient.git" 8 | }, 9 | "main": "index.js", 10 | "bugs": { 11 | "url": "https://github.com/jamox/hyperambient/issues" 12 | }, 13 | "scripts": { 14 | "test": "eslint index.js preinstall.js", 15 | "preinstall": "node preinstall.js" 16 | }, 17 | "licenses": [ 18 | { 19 | "type": "MIT", 20 | "url": "http://opensource.org/licenses/MIT" 21 | } 22 | ], 23 | "keywords": [ 24 | "hyperterm", 25 | "ambient light" 26 | ], 27 | "description": "A Hyperterm theme that reacts to ambient light", 28 | "dependencies": { 29 | "ambientlight": "^0.1.2", 30 | "hyperterm-solarized-dark": "^0.1.6", 31 | "hyperterm-solarized-light": "^0.1.1" 32 | }, 33 | "devDependencies": { 34 | "eslint": "^3.1.1", 35 | "eslint-config-standard": "^5.3.5", 36 | "eslint-plugin-promise": "^2.0.0", 37 | "eslint-plugin-standard": "^2.0.0" 38 | }, 39 | "eslintConfig": { 40 | "extends": "standard", 41 | "rules": { 42 | "yoda": 0, 43 | "semi": [ 44 | 2, 45 | "never" 46 | ], 47 | "no-undef": 0, 48 | "no-unused-vars": 2, 49 | "no-extra-semi": 2, 50 | "semi-spacing": [ 51 | 2, 52 | { 53 | "before": false, 54 | "after": true 55 | } 56 | ] 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /preinstall.js: -------------------------------------------------------------------------------- 1 | const { homedir } = require('os') 2 | const { readFileSync, writeFileSync, accessSync } = require('fs') 3 | const { resolve } = require('path') 4 | 5 | const configfile = resolve(homedir(), '.hyperambient.json') 6 | 7 | try { 8 | accessSync((configfile)) 9 | } catch (e) { 10 | process.exit(0) 11 | } 12 | 13 | const ambientConfig = JSON.parse(readFileSync(configfile, 'utf-8')) 14 | 15 | const packageJson = require('./package') 16 | packageJson.dependencies[ambientConfig.config.lightTheme] = 'latest' 17 | packageJson.dependencies[ambientConfig.config.darkTheme] = 'latest' 18 | 19 | console.log(packageJson.dependencies) 20 | writeFileSync('package.json', JSON.stringify(packageJson, null, 2) + '\n') 21 | 22 | --------------------------------------------------------------------------------