├── .npmrc ├── .gitattributes ├── .gitignore ├── test.tsx.snap ├── screenshot.png ├── .editorconfig ├── tsconfig.json ├── .github └── workflows │ └── main.yml ├── test.tsx.md ├── test.tsx ├── license ├── readme.md ├── package.json └── source └── index.tsx /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn.lock 3 | /dist 4 | -------------------------------------------------------------------------------- /test.tsx.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/ink-gradient/HEAD/test.tsx.snap -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sindresorhus/ink-gradient/HEAD/screenshot.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.yml] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@sindresorhus/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "include": [ 7 | "source" 8 | ], 9 | "ts-node": { 10 | "transpileOnly": true, 11 | "files": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | test: 7 | name: Node.js ${{ matrix.node-version }} 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | node-version: 13 | - 18 14 | - 16 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: actions/setup-node@v3 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - run: npm install 21 | - run: npm test 22 | -------------------------------------------------------------------------------- /test.tsx.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test.tsx` 2 | 3 | The actual snapshot is saved in `test.tsx.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## render 8 | 9 | > Snapshot 1 10 | 11 | `██╗ ██╗ ███╗ ██╗ ██╗ ██████╗ ██████╗ ██████╗ ███╗ ██╗ ███████╗␊ 12 | ██║ ██║ ████╗ ██║ ██║ ██╔════╝ ██╔═══██╗ ██╔══██╗ ████╗ ██║ ██╔════╝␊ 13 | ██║ ██║ ██╔██╗ ██║ ██║ ██║ ██║ ██║ ██████╔╝ ██╔██╗ ██║ ███████╗␊ 14 | ██║ ██║ ██║╚██╗██║ ██║ ██║ ██║ ██║ ██╔══██╗ ██║╚██╗██║ ╚════██║␊ 15 | ╚██████╔╝ ██║ ╚████║ ██║ ╚██████╗ ╚██████╔╝ ██║ ██║ ██║ ╚████║ ███████║` 16 | -------------------------------------------------------------------------------- /test.tsx: -------------------------------------------------------------------------------- 1 | import process from 'node:process'; 2 | import React from 'react'; 3 | import test from 'ava'; 4 | import {Text} from 'ink'; 5 | import {render} from 'ink-testing-library'; 6 | import stripAnsi from 'strip-ansi'; 7 | import Gradient from './source/index.js'; 8 | 9 | test('render', t => { 10 | process.env.FORCE_COLOR = 1; 11 | 12 | const text = ` 13 | ██╗ ██╗ ███╗ ██╗ ██╗ ██████╗ ██████╗ ██████╗ ███╗ ██╗ ███████╗ 14 | ██║ ██║ ████╗ ██║ ██║ ██╔════╝ ██╔═══██╗ ██╔══██╗ ████╗ ██║ ██╔════╝ 15 | ██║ ██║ ██╔██╗ ██║ ██║ ██║ ██║ ██║ ██████╔╝ ██╔██╗ ██║ ███████╗ 16 | ██║ ██║ ██║╚██╗██║ ██║ ██║ ██║ ██║ ██╔══██╗ ██║╚██╗██║ ╚════██║ 17 | ╚██████╔╝ ██║ ╚████║ ██║ ╚██████╗ ╚██████╔╝ ██║ ██║ ██║ ╚████║ ███████║ 18 | `.trim().split('\n').map(line => line.trimStart()).join('\n'); 19 | 20 | const {lastFrame} = render( 21 | 22 | {text} 23 | , 24 | ); 25 | 26 | console.log(lastFrame()); 27 | t.snapshot(stripAnsi(lastFrame())); 28 | 29 | delete process.env.FORCE_COLOR; 30 | }); 31 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Sindre Sorhus (https://sindresorhus.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # ink-gradient 2 | 3 | > Gradient color component for [Ink](https://github.com/vadimdemedes/ink) 4 | 5 | ![](screenshot.png) 6 | 7 | ## Install 8 | 9 | ```sh 10 | npm install ink-gradient 11 | ``` 12 | 13 | ## Usage 14 | 15 | ```js 16 | import React from 'react'; 17 | import {render} from 'ink'; 18 | import Gradient from 'ink-gradient'; 19 | import BigText from 'ink-big-text'; 20 | 21 | render( 22 | 23 | 24 | 25 | ); 26 | ``` 27 | 28 | ## API 29 | 30 | ### `` 31 | 32 | It accepts a string or Ink component as `children`. For example, [``](https://github.com/vadimdemedes/ink#box). 33 | 34 | #### Props 35 | 36 | ##### name 37 | 38 | Type: `string` 39 | 40 | The name of a [built-in gradient](https://github.com/bokub/gradient-string#available-built-in-gradients). 41 | 42 | Mutually exclusive with `colors`. 43 | 44 | ##### colors 45 | 46 | Type: `string[] | object[]` 47 | 48 | [Colors to use to make the gradient.](https://github.com/bokub/gradient-string#initialize-a-gradient) 49 | 50 | Mutually exclusive with `name`. 51 | 52 | ## Related 53 | 54 | - [ink-big-text](https://github.com/sindresorhus/ink-big-text) - Awesome text component for Ink 55 | - [ink-link](https://github.com/sindresorhus/ink-link) - Link component for Ink 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ink-gradient", 3 | "version": "3.0.0", 4 | "description": "Gradient color component for Ink", 5 | "license": "MIT", 6 | "repository": "sindresorhus/ink-gradient", 7 | "funding": "https://github.com/sponsors/sindresorhus", 8 | "author": { 9 | "name": "Sindre Sorhus", 10 | "email": "sindresorhus@gmail.com", 11 | "url": "https://sindresorhus.com" 12 | }, 13 | "type": "module", 14 | "exports": { 15 | "types": "./dist/index.d.ts", 16 | "default": "./dist/index.js" 17 | }, 18 | "engines": { 19 | "node": ">=16" 20 | }, 21 | "scripts": { 22 | "pretest": "npm run build", 23 | "prepublish": "npm run build", 24 | "test": "xo && ava", 25 | "build": "tsc" 26 | }, 27 | "files": [ 28 | "dist" 29 | ], 30 | "keywords": [ 31 | "ink-component", 32 | "ink", 33 | "component", 34 | "gradient", 35 | "gradients", 36 | "color", 37 | "colors", 38 | "rainbow", 39 | "style", 40 | "styles", 41 | "react", 42 | "jsx", 43 | "terminal", 44 | "term", 45 | "console", 46 | "command-line" 47 | ], 48 | "dependencies": { 49 | "@types/gradient-string": "^1.1.2", 50 | "gradient-string": "^2.0.2", 51 | "prop-types": "^15.8.1", 52 | "strip-ansi": "^7.1.0" 53 | }, 54 | "peerDependencies": { 55 | "ink": ">=4" 56 | }, 57 | "devDependencies": { 58 | "@sindresorhus/tsconfig": "^3.0.1", 59 | "@types/react": "^18.2.10", 60 | "ava": "^5.3.0", 61 | "eslint-config-xo-react": "^0.27.0", 62 | "eslint-plugin-react": "^7.32.2", 63 | "eslint-plugin-react-hooks": "^4.6.0", 64 | "ink": "^4.2.0", 65 | "ink-testing-library": "^3.0.0", 66 | "react": "^18.2.0", 67 | "ts-node": "^10.9.1", 68 | "typescript": "^5.1.3", 69 | "xo": "^0.54.2" 70 | }, 71 | "ava": { 72 | "color": true, 73 | "extensions": { 74 | "ts": "module", 75 | "tsx": "module" 76 | }, 77 | "nodeArguments": [ 78 | "--loader=ts-node/esm" 79 | ] 80 | }, 81 | "xo": { 82 | "extends": [ 83 | "xo-react" 84 | ] 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /source/index.tsx: -------------------------------------------------------------------------------- 1 | import React, {type FC as ReactFC, type ReactNode} from 'react'; 2 | import {Transform} from 'ink'; 3 | import PropTypes, {type Validator} from 'prop-types'; 4 | import gradientString, {type Gradient as GradientStringType} from 'gradient-string'; 5 | import stripAnsi from 'strip-ansi'; 6 | 7 | export type GradientName = 8 | | 'cristal' 9 | | 'teen' 10 | | 'mind' 11 | | 'morning' 12 | | 'vice' 13 | | 'passion' 14 | | 'fruit' 15 | | 'instagram' 16 | | 'atlas' 17 | | 'retro' 18 | | 'summer' 19 | | 'pastel' 20 | | 'rainbow'; 21 | 22 | export type GradientColors = Array>; 23 | 24 | export type Props = { 25 | readonly children: ReactNode; 26 | 27 | /** 28 | The name of a [built-in gradient](https://github.com/bokub/gradient-string#available-built-in-gradients). 29 | 30 | Mutually exclusive with `colors`. 31 | */ 32 | readonly name?: GradientName; 33 | 34 | /** 35 | [Colors to use to make the gradient.](https://github.com/bokub/gradient-string#initialize-a-gradient) 36 | 37 | Mutually exclusive with `name`. 38 | */ 39 | readonly colors?: GradientColors; 40 | }; 41 | 42 | /** 43 | @example 44 | ``` 45 | import React from 'react'; 46 | import {render} from 'ink'; 47 | import Gradient from 'ink-gradient'; 48 | import BigText from 'ink-big-text'; 49 | 50 | render( 51 | 52 | 53 | 54 | ); 55 | ``` 56 | */ 57 | const Gradient: ReactFC = props => { // eslint-disable-line react/function-component-definition 58 | if (props.name && props.colors) { 59 | throw new Error('The `name` and `colors` props are mutually exclusive'); 60 | } 61 | 62 | let gradient: GradientStringType; 63 | if (props.name) { 64 | gradient = gradientString[props.name]; 65 | } else if (props.colors) { 66 | // eslint-disable-next-line @typescript-eslint/no-unsafe-argument 67 | gradient = gradientString(props.colors as any); // TODO: Make stronger type. 68 | } else { 69 | throw new Error('Either `name` or `colors` prop must be provided'); 70 | } 71 | 72 | const applyGradient = (text: string) => gradient.multiline(stripAnsi(text)); 73 | 74 | return {props.children}; 75 | }; 76 | 77 | Gradient.propTypes = { 78 | children: PropTypes.oneOfType([ 79 | PropTypes.arrayOf(PropTypes.node), 80 | PropTypes.node, 81 | ]).isRequired, 82 | name: PropTypes.oneOf([ 83 | 'cristal', 84 | 'teen', 85 | 'mind', 86 | 'morning', 87 | 'vice', 88 | 'passion', 89 | 'fruit', 90 | 'instagram', 91 | 'atlas', 92 | 'retro', 93 | 'summer', 94 | 'pastel', 95 | 'rainbow', 96 | ]), 97 | colors: PropTypes.arrayOf( 98 | PropTypes.oneOfType([ 99 | PropTypes.string, 100 | PropTypes.object, 101 | ]), 102 | ) as Validator, 103 | }; 104 | 105 | export default Gradient; 106 | --------------------------------------------------------------------------------