diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7938765..f833627 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,73 +1,89 @@ name: build on: - push: - paths-ignore: - - "**.md" - - "**.bbcode" - - LICENSE - branches: [main, dev] - pull_request: - paths-ignore: - - "**.md" - - "**.bbcode" - - LICENSE - branches: [main] + push: + paths-ignore: + - "**.md" + - "**.bbcode" + - LICENSE + branches: [main, dev] + pull_request: + paths-ignore: + - "**.md" + - "**.bbcode" + - LICENSE + branches: [main] jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install build dependencies (apt) - run: sudo apt install -y libx11-dev libxcursor-dev libpng-dev - continue-on-error: false - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: Caching yarn packages - uses: actions/cache@v2 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: Set Up NodeJS 12.x - uses: actions/setup-node@v1 - with: - node-version: "12.x" - - name: Caching pip packages - uses: actions/cache@v2 - id: pip-cache # use this to check for `cache-hit` (`steps.pip-cache.outputs.cache-hit != 'true'`) - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} - restore-keys: | - ${{ runner.os }}-pip- - - name: Set up Python 3.8 - uses: actions/setup-python@v2 - with: - python-version: "3.8" - - name: Generating `GoogleDot` Cursor Theme - run: make - continue-on-error: false + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install build dependencies (apt) + run: sudo apt install -y libx11-dev libxcursor-dev libpng-dev + continue-on-error: false + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: Caching yarn packages + uses: actions/cache@v2 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: Set Up NodeJS 12.x + uses: actions/setup-node@v1 + with: + node-version: "12.x" + - name: Caching pip packages + uses: actions/cache@v2 + id: pip-cache # use this to check for `cache-hit` (`steps.pip-cache.outputs.cache-hit != 'true'`) + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: "3.8" + - name: Generating `GoogleDot` Cursor Theme + run: make + continue-on-error: false - - name: Compressing UNIX theme - run: tar -cvzf GoogleDot.tar.gz themes/GoogleDot + - name: Compressing UNIX theme + run: | + tar -cvzf GoogleDot-Blue.tar.gz themes/GoogleDot-Blue + tar -cvzf GoogleDot-Black.tar.gz themes/GoogleDot-Blue - - name: Uploading `bitmaps` artifact - uses: actions/upload-artifact@v2 - with: - name: bitmaps - path: bitmaps/* - - name: Uploading `GoogleDot` UNIX Theme artifact - uses: actions/upload-artifact@v2 - with: - name: GoogleDot - path: GoogleDot.tar.gz - - name: Uploading `GoogleDot` Windows Theme artifact - uses: actions/upload-artifact@v2 - with: - name: GoogleDot_Windows - path: themes/GoogleDot_Windows/* + - name: Uploading `bitmaps` artifact + uses: actions/upload-artifact@v2 + with: + name: bitmaps + path: bitmaps/* + + - name: Uploading `GoogleDot-Blue` UNIX Theme artifact + uses: actions/upload-artifact@v2 + with: + name: GoogleDot-Blue + path: GoogleDot-Blue.tar.gz + + - name: Uploading `GoogleDot-Black` UNIX Theme artifact + uses: actions/upload-artifact@v2 + with: + name: GoogleDot-Black + path: GoogleDot-Black.tar.gz + + - name: Uploading `GoogleDot-Blue` Windows Theme artifact + uses: actions/upload-artifact@v2 + with: + name: GoogleDot-Blue-Windows + path: themes/GoogleDot-Blue-Windows/* + + - name: Uploading `GoogleDot-Black` Windows Theme artifact + uses: actions/upload-artifact@v2 + with: + name: GoogleDot-Black-Windows + path: themes/GoogleDot-Black-Windows/* diff --git a/CHANGELOG.md b/CHANGELOG.md index 5054963..0b541a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,31 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +### Added + +- GoogleDot Black version +- separated 'core' module in bitmapper +- Makefile updated for GoogleDot +- GoogleDot Black CI supports + +### Changed + +- All `.svg` formated with `xmllint` +- `.svg` colors changed to key colors (Green & Blue) +- Multiple cursors supports in `builder` +- Dynamic theme-name & comments inside `builder/build.py` + ## [v1.0.1] - 17 Feb 2021 ### Added -- Figma file added inside **README.md** -- Organized build & bitmaps source code -- Add **Make** compatibility -- Only build options added in `Makefile` -- Relinked few svg files for Windows cursors purpose -- New build docs +- Figma file added inside **README.md** +- Organized build & bitmaps source code +- Add **Make** compatibility +- Only build options added in `Makefile` +- Relinked few svg files for Windows cursors purpose +- New build docs ### Changed -- Fixed some linting problems of builder using `pylint` -- Reduced Package Size -- Customize sizes from `make` -- CI workflow `build` compatibility with `make` commands +- Fixed some linting problems of builder using `pylint` +- Reduced Package Size +- Customize sizes from `make` +- CI workflow `build` compatibility with `make` commands ## [v1.0.0] - 27 Oct 2020 ### Added -- Initial release 🎊 -- Logo and badges -- CI/CD Pipelines +- Initial release 🎊 +- Logo and badges +- CI/CD Pipelines [unreleased]: https://github.com/ful1e5/Google_Cursor/compare/v1.0.1...main [v1.0.1]: https://github.com/ful1e5/Google_Cursor/compare/v1.0.0...v1.0.1 diff --git a/PLING.bbcode b/PLING.bbcode index 777f068..a2b8bfc 100644 --- a/PLING.bbcode +++ b/PLING.bbcode @@ -3,26 +3,27 @@ [/b][i]Get latest build[/i] @[b][url=https://github.com/ful1e5/Google_Cursor/actions]GitHub Actions[/url][/b] [i]Release Notification[/i] at [b][url=https://twitter.com/ful1e5]Twitter[/url][/b](@ful1e5) +For [i]Customizing Size[/i] check [b][url=https://github.com/ful1e5/Google_Cursor#manual-build]README.md[/url][/b] [b]Linux/X11 installation[/b] Get the latest stable Linux release from the [b][url=https://www.pling.com/p/1215613#files-panel]Pling[/url][/b]. Unpack [b].tar.gz[/b] file and follow these [b]commands[/b]. [b]Install[/b] [b]For all user:[/b] -[code]sudo mv GoogleDot /usr/share/icons[/code] +[code]sudo mv GoogleDot-* /usr/share/icons[/code] [b]For local user:[/b] -[code]mv GoogleDot ~/.icons[/code] +[code]mv GoogleDot-* ~/.icons[/code] [b]Uninstall[/b] [b]From all user:[/b] -[code]sudo rm -r /usr/share/icons/GoogleDot[/code] +[code]sudo rm -r /usr/share/icons/GoogleDot-*[/code] [b]From local user:[/b] -[code]rm -r ~/.icons/GoogleDot[/code] +[code]rm -r ~/.icons/GoogleDot-*[/code] [b]Window installation[/b] [list=1] - [*]unzip [b]GoogleDot_Windows.zip[/b] file[/*] - [*]Open [b]GoogleDot_Windows/[/b] in Explorer, and [b]right-click[/b] on [b]install.inf[/b].[/*] + [*]unzip [b].zip[/b] file[/*] + [*]Open [b]unziped directory[/b] in Explorer, and [b]right-click[/b] on [b]install.inf[/b].[/*] [*]Click 'Install' from the context menu, and authorise the modifications to your system.[/*] [*]Open [i]Control Panel > Personalisation and Appearance > Change mouse pointers[/i], and select [b]GoogleDot Cursors[/b].[/*] [*]Click '[b]Apply[/b]'.[/*] @@ -37,4 +38,4 @@ Get the latest stable Linux release from the [b][url=https://www.pling.com/p/121 [/list] [b]License & Terms[/b] -'[b]GoogleDot[/b]' Cursor Theme is available under the terms of the [b]GPL-3.0[/b] license. \ No newline at end of file +'[b]GoogleDot[/b]' Cursor Theme is available under the terms of the [b]GPL-3.0[/b] license. diff --git a/README.md b/README.md index 51e50c9..6dda419 100644 --- a/README.md +++ b/README.md @@ -106,9 +106,15 @@ Cursor theme inspired on **google material design** for `Windows` and `Linux` wi

- +
- GoogleDot Cursors 🍭 + Blue GoogleDot Cursors 🍭 +

+ +

+ +
+ BlackBlue GoogleDot Cursors 🍭

### Manual Install @@ -120,16 +126,16 @@ Cursor theme inspired on **google material design** for `Windows` and `Linux` wi tar -xvf GoogleDot.tar.gz # For local users -mv GoogleDot ~/.icons/ +mv GoogleDot-* ~/.icons/ # For all users -sudo mv GoogleDot /usr/share/icons/ +sudo mv GoogleDot-* /usr/share/icons/ ``` #### Windows -1. unzip `GoogleDot_Windows.zip` file -2. Open `GoogleDot_Windows/` in Explorer, and **right click** on `install.inf`. +1. unzip `.zip` file +2. Open unziped directory in Explorer, and **right click** on `install.inf`. 3. Click 'Install' from the context menu, and authorize the modifications to your system. 4. Open _Control Panel > Personalization and Appearance > Change mouse pointers_, and select **GoogleDot Cursors**. 5. Click '**Apply**'. @@ -140,9 +146,9 @@ sudo mv GoogleDot /usr/share/icons/ ## External Libraries -- libxcursor -- libx11 -- libpng (<=1.6) +- libxcursor +- libx11 +- libpng (<=1.6) #### Install External Libraries @@ -173,41 +179,41 @@ sudo dnf install libX11-devel libXcursor-devel libpng-devel ## Build Dependencies -- [gcc](https://gcc.gnu.org/install/) -- [make](https://www.gnu.org/software/make/) -- [nodejs](https://nodejs.org/en/) (<=12.x.x) -- [yarn](https://classic.yarnpkg.com/en/docs/install/) -- [python](https://www.python.org/downloads/) (<=3.8) -- [pip3](https://pip.pypa.io/en/stable/installing/) +- [gcc](https://gcc.gnu.org/install/) +- [make](https://www.gnu.org/software/make/) +- [nodejs](https://nodejs.org/en/) (<=12.x.x) +- [yarn](https://classic.yarnpkg.com/en/docs/install/) +- [python](https://www.python.org/downloads/) (<=3.8) +- [pip3](https://pip.pypa.io/en/stable/installing/) ### Node Packages -- [puppeteer](https://www.npmjs.com/package/puppeteer) -- [pngjs](https://www.npmjs.com/package/pngjs) -- [pixelmatch](https://www.npmjs.com/package/pixelmatch) +- [puppeteer](https://www.npmjs.com/package/puppeteer) +- [pngjs](https://www.npmjs.com/package/pngjs) +- [pixelmatch](https://www.npmjs.com/package/pixelmatch) ### PyPi Packages -- [clickgen](https://pypi.org/project/clickgen/s) +- [clickgen](https://pypi.org/project/clickgen/s) ## Build Dependencies -- [gcc](https://gcc.gnu.org/install/) -- [make](https://www.gnu.org/software/make/) -- [nodejs](https://nodejs.org/en/) (<=12.x.x) -- [yarn](https://classic.yarnpkg.com/en/docs/install/) -- [python](https://www.python.org/downloads/) (<=3.8) -- [pip3](https://pip.pypa.io/en/stable/installing/) +- [gcc](https://gcc.gnu.org/install/) +- [make](https://www.gnu.org/software/make/) +- [nodejs](https://nodejs.org/en/) (<=12.x.x) +- [yarn](https://classic.yarnpkg.com/en/docs/install/) +- [python](https://www.python.org/downloads/) (<=3.8) +- [pip3](https://pip.pypa.io/en/stable/installing/) ### Node Packages -- [puppeteer](https://www.npmjs.com/package/puppeteer) -- [pngjs](https://www.npmjs.com/package/pngjs) -- [pixelmatch](https://www.npmjs.com/package/pixelmatch) +- [puppeteer](https://www.npmjs.com/package/puppeteer) +- [pngjs](https://www.npmjs.com/package/pngjs) +- [pixelmatch](https://www.npmjs.com/package/pixelmatch) ### PyPi Packages -- [clickgen](https://pypi.org/project/clickgen/s) +- [clickgen](https://pypi.org/project/clickgen/s) ## Build From Scratch diff --git a/bitmapper/package.json b/bitmapper/package.json index ea0355d..6df6d10 100644 --- a/bitmapper/package.json +++ b/bitmapper/package.json @@ -1,6 +1,6 @@ { "name": "google_cursor", - "version": "1.0.1", + "version": "1.1.0", "description": "🍭 Cursor theme inspired on Google", "main": "index.js", "repository": "git@github.com:ful1e5/Google_Cursor.git", diff --git a/bitmapper/src/config.ts b/bitmapper/src/config.ts index 6ab4744..e56a352 100644 --- a/bitmapper/src/config.ts +++ b/bitmapper/src/config.ts @@ -1,23 +1,29 @@ -import path from "path"; -import { readdirSync, existsSync } from "fs"; +import { Colors } from "./core/types"; -// Directory resolve -const projectRoot = path.resolve(__dirname, "../../"); - -const outDir = path.resolve(projectRoot, "bitmaps"); -const staticSvgDir = path.resolve(projectRoot, "svg", "static"); -const animatedSvgDir = path.resolve(projectRoot, "svg", "animated"); - -// Generate a svg list -if (!existsSync(staticSvgDir) || !existsSync(animatedSvgDir)) { - throw new Error("svg directory not found"); +interface Config { + themeName: string; + color: Colors; } -const staticCursors = readdirSync(staticSvgDir).map((f) => - path.resolve(staticSvgDir, f) -); -const animatedCursors = readdirSync(animatedSvgDir).map((f) => - path.resolve(animatedSvgDir, f) -); +const black = "#000000"; +const blue = "#4285F4"; +const white = "#FFFFFF"; + +const config: Config[] = [ + { + themeName: "GoogleDot-Blue", + color: { + base: blue, + outline: white, + }, + }, + { + themeName: "GoogleDot-Black", + color: { + base: black, + outline: white, + }, + }, +]; -export { staticCursors, animatedCursors, outDir }; +export { config }; diff --git a/bitmapper/src/core/BitmapsGenerator.ts b/bitmapper/src/core/BitmapsGenerator.ts new file mode 100644 index 0000000..7a017ba --- /dev/null +++ b/bitmapper/src/core/BitmapsGenerator.ts @@ -0,0 +1,155 @@ +import fs from "fs"; +import path from "path"; + +import puppeteer, { Browser, ElementHandle, Page } from "puppeteer"; + +import { frameNumber } from "./util/frameNumber"; +import { matchImages } from "./util/matchImages"; +import { toHTML } from "./util/toHTML"; + +class BitmapsGenerator { + /** + * Generate Png files from svg code. + * @param themeName Give name, So all bitmaps files are organized in one directory. + * @param bitmapsDir `absolute` or `relative` path, Where `.png` files will store. + */ + constructor(private bitmapsDir: string) { + this.bitmapsDir = path.resolve(bitmapsDir); + this.createDir(this.bitmapsDir); + } + + /** + * Create directory if it doesn't exists. + * @param dirPath directory `absolute` path. + */ + private createDir(dirPath: string) { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + } + } + + /** + * Prepare headless browser. + */ + public async getBrowser(): Promise { + return await puppeteer.launch({ + ignoreDefaultArgs: ["--no-sandbox"], + headless: true, + }); + } + + private async getSvgElement( + page: Page, + content: string + ): Promise> { + if (!content) { + throw new Error(`${content} File Read error`); + } + + const html = toHTML(content); + await page.setContent(html, { timeout: 0 }); + + const svg = await page.$("#container svg"); + + if (!svg) { + throw new Error("svg element not found!"); + } + return svg; + } + + public async generateStatic(browser: Browser, content: string, key: string) { + const page = await browser.newPage(); + const svg = await this.getSvgElement(page, content); + + const out = path.resolve(this.bitmapsDir, `${key}.png`); + + await svg.screenshot({ omitBackground: true, path: out }); + await page.close(); + } + + private async screenshot( + element: ElementHandle + ): Promise { + const buffer = await element.screenshot({ + encoding: "binary", + omitBackground: true, + }); + + if (!buffer) { + throw new Error("SVG element screenshot not working"); + } + return buffer; + } + + private async stopAnimation(page: Page) { + const client = await page.target().createCDPSession(); + await client.send("Animation.setPlaybackRate", { + playbackRate: 0, + }); + } + + private async resumeAnimation(page: Page, playbackRate: number) { + const client = await page.target().createCDPSession(); + await client.send("Animation.setPlaybackRate", { + playbackRate, + }); + } + + private async saveFrameImage(key: string, frame: Buffer | string) { + const out_path = path.resolve(this.bitmapsDir, key); + fs.writeFileSync(out_path, frame); + } + + public async generateAnimated( + browser: Browser, + content: string, + key: string, + options?: { + playbackRate?: number; + diff?: number; + frameLimit?: number; + framePadding?: number; + } + ) { + const opt = Object.assign( + { playbackRate: 0.1, diff: 0, frameLimit: 300, framePadding: 4 }, + options + ); + + const page = await browser.newPage(); + const svg = await this.getSvgElement(page, content); + await this.stopAnimation(page); + + let index = 1; + let breakRendering = false; + let prevImg: Buffer | string; + + // Rendering frames till `imgN` matched to `imgN-1` (When Animation is done) + while (!breakRendering) { + if (index > opt.frameLimit) { + throw new Error("Reached the frame limit."); + } + + await this.resumeAnimation(page, opt.playbackRate); + const img: string | Buffer = await this.screenshot(svg); + await this.stopAnimation(page); + + if (index > 1) { + // @ts-ignore + const diff = matchImages(prevImg, img); + if (diff <= opt.diff) { + breakRendering = !breakRendering; + } + } + const number = frameNumber(index, opt.framePadding); + const frame = `${key}-${number}.png`; + + this.saveFrameImage(frame, img); + + prevImg = img; + ++index; + } + await page.close(); + } +} +export { BitmapsGenerator }; diff --git a/bitmapper/src/core/SVGHandler/SvgDirectoryParser.ts b/bitmapper/src/core/SVGHandler/SvgDirectoryParser.ts new file mode 100644 index 0000000..65756da --- /dev/null +++ b/bitmapper/src/core/SVGHandler/SvgDirectoryParser.ts @@ -0,0 +1,77 @@ +import fs from "fs"; +import path from "path"; + +interface Svg { + key: string; + content: string; +} + +class SvgDirectoryParser { + /** + * Manage and Parse SVG file path in `absolute` fashion. + * This Parser look svg files as below fashion: + * ` + * <@svgDir>/static + * <@svgDir>/animated + * ` + * @param svgDir is relative/absolute path, Where `SVG` files are stored. + */ + semiAnimated: boolean = false; + constructor(private svgDir: string) { + if (!fs.existsSync(this.svgDir)) { + throw new Error(`SVG files not found in ${this.svgDir}`); + } + } + + private readData(f: string): Svg { + const content = fs.readFileSync(f, "utf-8"); + const key = path.basename(f, ".svg"); + return { content, key }; + } + + /** + * Return absolute paths array of SVG files data located inside '@svgDir/static' + */ + public getStatic(): Svg[] { + const staticDir = path.resolve(this.svgDir, "static"); + + if (!fs.existsSync(staticDir)) { + console.log(`${this.svgDir} contains semi-animated .svg files`); + this.semiAnimated = true; + return []; + } else { + const svgs = fs + .readdirSync(staticDir) + .map((f) => this.readData(path.resolve(staticDir, f))); + + if (svgs.length == 0) { + throw new Error("Static Cursors directory is empty"); + } + return svgs; + } + } + /** + * Return absolute paths array of SVG files data located inside '@svgDir/animated' + */ + public getAnimated(): Svg[] { + const animatedDir = path.resolve(this.svgDir, "animated"); + + if (!fs.existsSync(animatedDir)) { + throw new Error("Animated Cursors directory not found"); + } + + const svgs = fs + .readdirSync(animatedDir) + .map((f) => this.readData(path.resolve(animatedDir, f))); + + if (svgs.length == 0 && this.semiAnimated) { + throw new Error( + `Can't parse svg directory ${this.svgDir} as semi-animated theme` + ); + } + + return svgs; + } +} + +export { SvgDirectoryParser }; diff --git a/bitmapper/src/core/SVGHandler/colorSvg.ts b/bitmapper/src/core/SVGHandler/colorSvg.ts new file mode 100644 index 0000000..148dcd2 --- /dev/null +++ b/bitmapper/src/core/SVGHandler/colorSvg.ts @@ -0,0 +1,52 @@ +import { Colors } from "../types"; + +/** + * Default Key Colors for generating colored svg. + * base="#00FF00" (Green) + * outline="#0000FF" (Blue) + * watch.background="#FF0000" (Red) + * */ +const defaultKeyColors: Colors = { + base: "#00FF00", + outline: "#0000FF", + watch: { + background: "#FF0000", + }, +}; + +/** + * Customize colors of svg code. + * @param {string} content SVG code. + * @param {Colors} colors Customize colors. + * @param {Colors} [keys] Colors Key, That was written SVG code. + * @returns {string} SVG code with colors. + */ +const colorSvg = ( + content: string, + colors: Colors, + keys: Colors = defaultKeyColors +): string => { + content = content + .replace(new RegExp(keys.base, "ig"), colors.base) + .replace(new RegExp(keys.outline, "ig"), colors.outline); + + try { + // === trying to replace `watch` color === + + if (!colors.watch?.background) { + throw new Error(""); + } + const { background: b } = colors.watch; + content = content.replace(new RegExp(keys.watch!.background, "ig"), b); // Watch Background + } catch (error) { + // === on error => replace `watch` color as `base` === + + content = content.replace( + new RegExp(keys.watch!.background, "ig"), + colors.base + ); + } + return content; +}; + +export { colorSvg }; diff --git a/bitmapper/src/core/SVGHandler/index.ts b/bitmapper/src/core/SVGHandler/index.ts new file mode 100644 index 0000000..308a433 --- /dev/null +++ b/bitmapper/src/core/SVGHandler/index.ts @@ -0,0 +1,4 @@ +import { colorSvg } from "./colorSvg"; +import { SvgDirectoryParser } from "./SvgDirectoryParser"; + +export { colorSvg, SvgDirectoryParser }; diff --git a/bitmapper/src/core/index.ts b/bitmapper/src/core/index.ts new file mode 100644 index 0000000..2fe958b --- /dev/null +++ b/bitmapper/src/core/index.ts @@ -0,0 +1,4 @@ +import { BitmapsGenerator } from "./BitmapsGenerator"; +import * as SVGHandler from "./SVGHandler"; + +export { BitmapsGenerator, SVGHandler }; diff --git a/bitmapper/src/core/types.ts b/bitmapper/src/core/types.ts new file mode 100644 index 0000000..cd5ddb3 --- /dev/null +++ b/bitmapper/src/core/types.ts @@ -0,0 +1,20 @@ +/** + * Hex Colors in string Format. + * + * `Example: `"#FFFFFF" + */ +type HexColor = string; + +/** + * @Colors expect `base`, `outline` & `watch-background` colors in **HexColor** Format. + * @default background is `base` color. + */ +type Colors = { + base: HexColor; + outline: HexColor; + watch?: { + background: HexColor; + }; +}; + +export { Colors }; diff --git a/bitmapper/src/utils/frameNumber.ts b/bitmapper/src/core/util/frameNumber.ts similarity index 100% rename from bitmapper/src/utils/frameNumber.ts rename to bitmapper/src/core/util/frameNumber.ts diff --git a/bitmapper/src/utils/matchImages.ts b/bitmapper/src/core/util/matchImages.ts similarity index 94% rename from bitmapper/src/utils/matchImages.ts rename to bitmapper/src/core/util/matchImages.ts index e1a2844..625d2bd 100644 --- a/bitmapper/src/utils/matchImages.ts +++ b/bitmapper/src/core/util/matchImages.ts @@ -6,6 +6,6 @@ export const matchImages = (img1: Buffer, img2: Buffer): number => { const { data: imgNData } = PNG.sync.read(img2); return Pixelmatch(img1Data, imgNData, null, width, height, { - threshold: 0.001, + threshold: 0.1, }); }; diff --git a/bitmapper/src/utils/toHTML.ts b/bitmapper/src/core/util/toHTML.ts similarity index 89% rename from bitmapper/src/utils/toHTML.ts rename to bitmapper/src/core/util/toHTML.ts index 36fbbf4..aecb3d7 100644 --- a/bitmapper/src/utils/toHTML.ts +++ b/bitmapper/src/core/util/toHTML.ts @@ -16,4 +16,4 @@ export const template = ` `; export const toHTML = (svgData: string): string => - template.replace("", svgData); + template.replace("", svgData); diff --git a/bitmapper/src/index.ts b/bitmapper/src/index.ts index d51f0f5..75dbfdc 100644 --- a/bitmapper/src/index.ts +++ b/bitmapper/src/index.ts @@ -1,127 +1,37 @@ -import fs from "fs"; import path from "path"; -import puppeteer, { ElementHandle, Page } from "puppeteer"; -import { animatedCursors, outDir, staticCursors } from "./config"; -import { frameNumber } from "./utils/frameNumber"; -import { matchImages } from "./utils/matchImages"; -import { toHTML } from "./utils/toHTML"; +import { BitmapsGenerator, SVGHandler } from "./core"; +import { config } from "./config"; -const getSVGElement = async (page: Page) => { - const svg = await page.$("#container svg"); - - if (!svg) { - throw new Error("svg element not found!"); - } - return svg; -}; - -const screenshot = async (element: ElementHandle): Promise => { - return element.screenshot({ - omitBackground: true, - encoding: "binary", - }); -}; - -const stopAnimation = async (page: Page) => { - // @ts-ignore - await page._client.send("Animation.setPlaybackRate", { - playbackRate: 0, - }); -}; - -const resumeAnimation = async (page: Page, playbackRate: number = 0.1) => { - // @ts-ignore - await page._client.send("Animation.setPlaybackRate", { - playbackRate, - }); -}; - -const saveFrameImage = (key: string, frame: Buffer) => { - const out_path = path.resolve(outDir, key); - fs.writeFileSync(out_path, frame, { encoding: "binary" }); -}; +const root = path.resolve(__dirname, "../../"); +const svgDir = path.resolve(root, "svg"); const main = async () => { - const browser = await puppeteer.launch({ - ignoreDefaultArgs: ["--single-process", "--no-sandbox"], - headless: true, - }); + for (const { themeName, color } of config) { + console.log("=>", themeName); - if (!fs.existsSync(outDir)) { - fs.mkdirSync(outDir); - } else { - throw new Error(`out directory '${outDir}' already exists.`); - } + const bitmapsDir = path.resolve(root, "bitmaps", themeName); + const svg = new SVGHandler.SvgDirectoryParser(svgDir); - for (const svgFilePath of staticCursors) { - const svgData = fs.readFileSync(svgFilePath, "utf-8"); - if (!svgData) { - throw new Error(`${svgFilePath} File Read error`); - } - - const page = await browser.newPage(); - const html = toHTML(svgData); - - await page.setContent(html); - const svg = await getSVGElement(page); + const png = new BitmapsGenerator(bitmapsDir); + const browser = await png.getBrowser(); - const key = `${path.basename(svgFilePath, ".svg")}.png`; - const out = path.join(outDir, key); + for (let { key, content } of svg.getStatic()) { + console.log(" -> Saving", key, "..."); - console.log("Saving", key, "..."); - await svg.screenshot({ omitBackground: true, path: out }); - await page.close(); - } - - for (const svgFilePath of animatedCursors) { - const svgData = fs.readFileSync(svgFilePath, "utf8"); - if (!svgData) { - throw new Error(`${svgFilePath} File Read error`); + content = SVGHandler.colorSvg(content, color); + await png.generateStatic(browser, content, key); } - const page = await browser.newPage(); - const html = toHTML(svgData); - - await page.setContent(html); - const svg = await getSVGElement(page); - await stopAnimation(page); - - let index = 1; - const frameLimit = 300; - let breakRendering = false; - let prevImg: Buffer; - - // Rendering frames till `imgN` matched to `imgN-1` (When Animation is done) - while (!breakRendering) { - if (index > frameLimit) { - throw new Error("Reached the frame limit."); - } - - resumeAnimation(page); - const img = await screenshot(svg); - stopAnimation(page); - - if (index > 1) { - // @ts-ignore - const diff = matchImages(prevImg, img); - if (diff <= 100) { - breakRendering = !breakRendering; - } - } - const frame = frameNumber(index, 3); - const key = `${path.basename(svgFilePath, ".svg")}-${frame}.png`; - - console.log("Saving", key, "..."); - saveFrameImage(key, img); + for (let { key, content } of svg.getAnimated()) { + console.log(" -> Saving", key, "..."); - prevImg = img; - ++index; + content = SVGHandler.colorSvg(content, color); + await png.generateAnimated(browser, content, key); } - await page.close(); + await browser.close(); } - await browser.close(); }; main(); diff --git a/builder/Makefile b/builder/Makefile index 4ee31b6..11b0def 100644 --- a/builder/Makefile +++ b/builder/Makefile @@ -1,8 +1,9 @@ - -all: clean setup build +bitmaps_dir = "../bitmaps" .PHONY: all +all: clean setup build + .ONESHELL: SHELL:=/bin/bash @@ -25,11 +26,14 @@ setup: setup.py @. venv/bin/activate; python3 setup.py install --record files.txt build: setup build.py - @. venv/bin/activate; python3 build.py --xsizes $(X_SIZES) --win-size $(WIN_SIZE) --win-canvas-size $(WIN_CANVAS_SIZE) + @. venv/bin/activate; python3 build.py -p "$(bitmaps_dir)/GoogleDot-Blue" --xsizes $(X_SIZES) --win-size $(WIN_SIZE) --win-canvas-size $(WIN_CANVAS_SIZE) + @. venv/bin/activate; python3 build.py -p "$(bitmaps_dir)/GoogleDot-Black" --xsizes $(X_SIZES) --win-size $(WIN_SIZE) --win-canvas-size $(WIN_CANVAS_SIZE) build_unix: setup build.py - @. venv/bin/activate; python3 build.py unix --xsizes $(X_SIZES) + @. venv/bin/activate; python3 build.py unix -p "$(bitmaps_dir)/GoogleDot-Blue" --xsizes $(X_SIZES) + @. venv/bin/activate; python3 build.py unix -p "$(bitmaps_dir)/GoogleDot-Black" --xsizes $(X_SIZES) build_windows: setup build.py - @. venv/bin/activate; python3 build.py windows --win-size $(WIN_SIZE) --win-canvas-size $(WIN_CANVAS_SIZE) + @. venv/bin/activate; python3 build.py windows -p "$(bitmaps_dir)/GoogleDot-Blue" --win-size $(WIN_SIZE) --win-canvas-size $(WIN_CANVAS_SIZE) + @. venv/bin/activate; python3 build.py windows -p "$(bitmaps_dir)/GoogleDot-Black" --win-size $(WIN_SIZE) --win-canvas-size $(WIN_CANVAS_SIZE) diff --git a/builder/build.py b/builder/build.py index 376e787..e3e17ac 100644 --- a/builder/build.py +++ b/builder/build.py @@ -5,7 +5,7 @@ from pathlib import Path from gbpkg.configure import get_config -from gbpkg.generator import xbuild, wbuild, build +from gbpkg.generator import Info, build, wbuild, xbuild parser = argparse.ArgumentParser( prog="google_dot_builder", @@ -95,9 +95,17 @@ args = parser.parse_args() bitmaps_dir = Path(args.png_dir) +name = bitmaps_dir.stem -x_out_dir = Path(args.out_dir) / "GoogleDot" -win_out_dir = Path(args.out_dir) / "GoogleDot_Windows" +comments = { + "GoogleDot-Blue": "Blue cursor theme inspired on Google", + "GoogleDot-Black": "Black cursor theme inspired on Google", +} + +x_out_dir = Path(args.out_dir) / name +win_out_dir = Path(args.out_dir) / f"{name}-Windows" + +print(f"Getting '{name}' bitmaps ready for build...") config = get_config( bitmaps_dir, @@ -106,9 +114,11 @@ win_size=args.win_size, ) +info = Info(name=name, comment=comments.get(name, f"{name} Cursors")) + if args.platform == "unix": - xbuild(config, x_out_dir) + xbuild(config, x_out_dir, info) elif args.platform == "windows": - wbuild(config, win_out_dir) + wbuild(config, win_out_dir, info) else: - build(config, x_out_dir, win_out_dir) + build(config, x_out_dir, win_out_dir, info) diff --git a/builder/gbpkg/configure.py b/builder/gbpkg/configure.py index f818382..0e7309d 100644 --- a/builder/gbpkg/configure.py +++ b/builder/gbpkg/configure.py @@ -4,10 +4,8 @@ from typing import Any, Dict, Tuple, TypeVar from clickgen.util import PNGProvider - from gbpkg.constants import WIN_CURSORS_CFG, WIN_DELAY, X_CURSORS_CFG, X_DELAY - X = TypeVar("X") diff --git a/builder/gbpkg/constants.py b/builder/gbpkg/constants.py index 0c727c9..1a8db05 100644 --- a/builder/gbpkg/constants.py +++ b/builder/gbpkg/constants.py @@ -4,8 +4,6 @@ from typing import Dict # Info -THEME_NAME = "GoogleDot" -COMMENT = "Cursor theme inspired on Google" AUTHOR = "Kaiz Khatri" URL = "https://github.com/ful1e5/Google_Cursor" diff --git a/builder/gbpkg/generator.py b/builder/gbpkg/generator.py index c5fd773..c82830d 100644 --- a/builder/gbpkg/generator.py +++ b/builder/gbpkg/generator.py @@ -2,20 +2,21 @@ # -*- coding: utf-8 -*- from pathlib import Path -from typing import Any, Dict +from typing import Any, Dict, NamedTuple from clickgen.builders import WindowsCursor, XCursor from clickgen.core import CursorAlias from clickgen.packagers import WindowsPackager, XPackager - -from gbpkg.constants import AUTHOR, COMMENT, THEME_NAME, URL +from gbpkg.constants import AUTHOR, URL from gbpkg.symlinks import add_missing_xcursor -def xbuild( - config: Dict[str, Dict[str, Any]], - x_out_dir: Path, -) -> None: +class Info(NamedTuple): + name: str + comment: str + + +def xbuild(config: Dict[str, Dict[str, Any]], x_out_dir: Path, info: Info) -> None: """Build `GoogleDot` cursor theme for only `X11`(UNIX) platform. ``` @@ -24,6 +25,7 @@ def xbuild( :x_out_dir: (Path) Path to the output directory, Where the `X11` cursor theme package will generate. It also creates a directory if not exists. + :info: (Dict) Content theme name & comment ``` """ @@ -39,10 +41,10 @@ def xbuild( XCursor.create(x_cfg, x_out_dir) add_missing_xcursor(x_out_dir / "cursors") - XPackager(x_out_dir, THEME_NAME, COMMENT) + XPackager(x_out_dir, info.name, info.comment) -def wbuild(config: Dict[str, Dict[str, Any]], win_out_dir: Path) -> None: +def wbuild(config: Dict[str, Dict[str, Any]], win_out_dir: Path, info: Info) -> None: """Build `GoogleDot` cursor theme for only `Windows` platforms. ``` @@ -51,6 +53,7 @@ def wbuild(config: Dict[str, Dict[str, Any]], win_out_dir: Path) -> None: :win_out_dir: (Path) Path to the output directory, Where the `Windows` cursor theme package will generate. It also creates a directory if not exists. + :info: (Dict) Content theme name & comment ``` """ @@ -76,11 +79,11 @@ def wbuild(config: Dict[str, Dict[str, Any]], win_out_dir: Path) -> None: print(f"Building '{win_cfg.stem}' Windows Cursor...") WindowsCursor.create(win_cfg, win_out_dir) - WindowsPackager(win_out_dir, THEME_NAME, COMMENT, AUTHOR, URL) + WindowsPackager(win_out_dir, info.name, info.comment, AUTHOR, URL) def build( - config: Dict[str, Dict[str, Any]], x_out_dir: Path, win_out_dir: Path + config: Dict[str, Dict[str, Any]], x_out_dir: Path, win_out_dir: Path, info: Info ) -> None: """Build `GoogleDot` cursor theme for `X11` & `Windows` platforms. @@ -94,6 +97,7 @@ def build( :win_out_dir: (Path) Path to the output directory, Where the `Windows` cursor theme package will generate. It also creates a directory if not exists. + :info: (Dict) Content theme name & comment ``` """ @@ -125,6 +129,6 @@ def win_build(item: Dict[str, Any], alias: CursorAlias) -> None: win_build(item, alias) add_missing_xcursor(x_out_dir / "cursors") - XPackager(x_out_dir, THEME_NAME, COMMENT) + XPackager(x_out_dir, info.name, info.comment) - WindowsPackager(win_out_dir, THEME_NAME, COMMENT, AUTHOR, URL) + WindowsPackager(win_out_dir, info.name, info.comment, AUTHOR, URL) diff --git a/builder/gbpkg/symlinks.py b/builder/gbpkg/symlinks.py index e00a340..b745d69 100644 --- a/builder/gbpkg/symlinks.py +++ b/builder/gbpkg/symlinks.py @@ -55,19 +55,9 @@ def add_missing_xcursor(directory) -> None: "diamond_cross", "tcross", "color-picker", - # crosshair "crosshair", ], }, - # { - # "src": "crossed_circle", - # "links": [ - # "03b6e0fcb3499374a867c041f52298f0", - # "not-allowed", - # "forbidden", - # "circle", - # ], - # }, { "src": "dnd_no_drop", "links": [ @@ -96,7 +86,6 @@ def add_missing_xcursor(directory) -> None: "links": [ "arrow", "default", - # center_ptr symlinks "center_ptr", ], }, @@ -109,7 +98,6 @@ def add_missing_xcursor(directory) -> None: "progress", ], }, - # {"src": "left_side", "links": ["w-resize", "right_side", "e-resize"]}, { "src": "link", "links": [ @@ -159,7 +147,6 @@ def add_missing_xcursor(directory) -> None: "size-hor", "size_hor", "split_h", - # left_side symlinks "left_side", "w-resize", "right_side", @@ -181,14 +168,12 @@ def add_missing_xcursor(directory) -> None: "size_ver", "split_v", "v_double_arrow", - # top_side symlinks "top_side", "s-resize", "n-resize", "bottom_side", ], }, - # {"src": "top_side", "links": ["s-resize", "n-resize", "bottom_side"]}, {"src": "wait", "links": ["watch"]}, {"src": "X_cursor", "links": ["pirate", "x-cursor"]}, {"src": "xterm", "links": ["ibeam", "text"]}, diff --git a/builder/setup.py b/builder/setup.py index 2ddf303..b0e6efb 100644 --- a/builder/setup.py +++ b/builder/setup.py @@ -5,7 +5,7 @@ setup( name="gbpkg", - version="1.0.1", + version="1.1.0", description="Generate 'GoogleDot' cursor theme from PNGs file", url="https://github.com/ful1e5/Google_Cursor", packages=["gbpkg"], diff --git a/svg/animated/wait.svg b/svg/animated/wait.svg index 9e14168..674cf1e 100644 --- a/svg/animated/wait.svg +++ b/svg/animated/wait.svg @@ -1,122 +1,47 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/svg/static/X_cursor.svg b/svg/static/X_cursor.svg index 055e57e..6f0b91a 100644 --- a/svg/static/X_cursor.svg +++ b/svg/static/X_cursor.svg @@ -1,18 +1,7 @@ - - - - - + + + + + + diff --git a/svg/static/bottom_left_corner.svg b/svg/static/bottom_left_corner.svg index 6c36adf..f7871a3 100644 --- a/svg/static/bottom_left_corner.svg +++ b/svg/static/bottom_left_corner.svg @@ -1,66 +1,24 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/bottom_right_corner.svg b/svg/static/bottom_right_corner.svg index 1b7f868..acd7df3 100644 --- a/svg/static/bottom_right_corner.svg +++ b/svg/static/bottom_right_corner.svg @@ -1,66 +1,24 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/bottom_tee.svg b/svg/static/bottom_tee.svg index 9c4fb6a..06e5c90 100644 --- a/svg/static/bottom_tee.svg +++ b/svg/static/bottom_tee.svg @@ -1,63 +1,23 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/context-menu.svg b/svg/static/context-menu.svg index 816edb3..a400e19 100644 --- a/svg/static/context-menu.svg +++ b/svg/static/context-menu.svg @@ -1,55 +1,25 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/copy.svg b/svg/static/copy.svg index 09ab8f4..eb5c423 100644 --- a/svg/static/copy.svg +++ b/svg/static/copy.svg @@ -1,98 +1,36 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/cross.svg b/svg/static/cross.svg index a584ef2..c878905 100644 --- a/svg/static/cross.svg +++ b/svg/static/cross.svg @@ -1,52 +1,22 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/dnd_no_drop.svg b/svg/static/dnd_no_drop.svg index f1ea5b3..a9e6a3a 100644 --- a/svg/static/dnd_no_drop.svg +++ b/svg/static/dnd_no_drop.svg @@ -1,56 +1,23 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/dotbox.svg b/svg/static/dotbox.svg index 3420bb1..c4d5f42 100644 --- a/svg/static/dotbox.svg +++ b/svg/static/dotbox.svg @@ -1,56 +1,26 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/hand1.svg b/svg/static/hand1.svg index 7386e1d..c76d2ee 100644 --- a/svg/static/hand1.svg +++ b/svg/static/hand1.svg @@ -1,52 +1,22 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/hand2.svg b/svg/static/hand2.svg index bbada2f..52350ce 100644 --- a/svg/static/hand2.svg +++ b/svg/static/hand2.svg @@ -1,52 +1,22 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/left_ptr.svg b/svg/static/left_ptr.svg index a00f8ed..6a7b4d7 100644 --- a/svg/static/left_ptr.svg +++ b/svg/static/left_ptr.svg @@ -1,52 +1,22 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/left_tee.svg b/svg/static/left_tee.svg index 8b397e7..3def7fe 100644 --- a/svg/static/left_tee.svg +++ b/svg/static/left_tee.svg @@ -1,63 +1,23 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/link.svg b/svg/static/link.svg index 872bbd7..3708037 100644 --- a/svg/static/link.svg +++ b/svg/static/link.svg @@ -1,60 +1,24 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/ll_angle.svg b/svg/static/ll_angle.svg index 723060c..9f0830a 100644 --- a/svg/static/ll_angle.svg +++ b/svg/static/ll_angle.svg @@ -1,58 +1,22 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/lr_angle.svg b/svg/static/lr_angle.svg index b7c2803..6e03ff6 100644 --- a/svg/static/lr_angle.svg +++ b/svg/static/lr_angle.svg @@ -1,58 +1,22 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/move.svg b/svg/static/move.svg index 6dc9c12..c292faf 100644 --- a/svg/static/move.svg +++ b/svg/static/move.svg @@ -1,64 +1,22 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/plus.svg b/svg/static/plus.svg index 5b7e06d..2e0a00e 100644 --- a/svg/static/plus.svg +++ b/svg/static/plus.svg @@ -1,56 +1,23 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/question_arrow.svg b/svg/static/question_arrow.svg index 5a3d143..8dfced7 100644 --- a/svg/static/question_arrow.svg +++ b/svg/static/question_arrow.svg @@ -1,56 +1,23 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/right_tee.svg b/svg/static/right_tee.svg index cc12c73..af5f7d6 100644 --- a/svg/static/right_tee.svg +++ b/svg/static/right_tee.svg @@ -1,68 +1,28 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/sb_down_arrow.svg b/svg/static/sb_down_arrow.svg index b060888..a9910dc 100644 --- a/svg/static/sb_down_arrow.svg +++ b/svg/static/sb_down_arrow.svg @@ -1,63 +1,27 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/sb_h_double_arrow.svg b/svg/static/sb_h_double_arrow.svg index 0f7fcda..784160a 100644 --- a/svg/static/sb_h_double_arrow.svg +++ b/svg/static/sb_h_double_arrow.svg @@ -1,66 +1,24 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/sb_left_arrow.svg b/svg/static/sb_left_arrow.svg index e295161..37ab699 100644 --- a/svg/static/sb_left_arrow.svg +++ b/svg/static/sb_left_arrow.svg @@ -1,63 +1,27 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/sb_right_arrow.svg b/svg/static/sb_right_arrow.svg index e6577ba..fc01d2d 100644 --- a/svg/static/sb_right_arrow.svg +++ b/svg/static/sb_right_arrow.svg @@ -1,63 +1,27 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/sb_up_arrow.svg b/svg/static/sb_up_arrow.svg index 9102a76..65a2dd3 100644 --- a/svg/static/sb_up_arrow.svg +++ b/svg/static/sb_up_arrow.svg @@ -1,63 +1,27 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/sb_v_double_arrow.svg b/svg/static/sb_v_double_arrow.svg index 348711c..2dcb28c 100644 --- a/svg/static/sb_v_double_arrow.svg +++ b/svg/static/sb_v_double_arrow.svg @@ -1,66 +1,24 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/top_tee.svg b/svg/static/top_tee.svg index 6e18b7d..6562ba3 100644 --- a/svg/static/top_tee.svg +++ b/svg/static/top_tee.svg @@ -1,63 +1,23 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/ul_angle.svg b/svg/static/ul_angle.svg index 2933bae..83afe97 100644 --- a/svg/static/ul_angle.svg +++ b/svg/static/ul_angle.svg @@ -1,58 +1,22 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/ur_angle.svg b/svg/static/ur_angle.svg index f7811c8..aa990e4 100644 --- a/svg/static/ur_angle.svg +++ b/svg/static/ur_angle.svg @@ -1,58 +1,22 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/vertical-text.svg b/svg/static/vertical-text.svg index 3d5f002..2c4de17 100644 --- a/svg/static/vertical-text.svg +++ b/svg/static/vertical-text.svg @@ -1,19 +1,8 @@ - - - - - - + + + + + + + diff --git a/svg/static/wayland-cursor.svg b/svg/static/wayland-cursor.svg index 4bdfce9..40a0fc6 100644 --- a/svg/static/wayland-cursor.svg +++ b/svg/static/wayland-cursor.svg @@ -1,19 +1,8 @@ - - - - - - + + + + + + + diff --git a/svg/static/xterm.svg b/svg/static/xterm.svg index d2d933d..de97f4d 100644 --- a/svg/static/xterm.svg +++ b/svg/static/xterm.svg @@ -1,19 +1,8 @@ - - - - - - + + + + + + + diff --git a/svg/static/zoom-in.svg b/svg/static/zoom-in.svg index 6181608..6f3667b 100644 --- a/svg/static/zoom-in.svg +++ b/svg/static/zoom-in.svg @@ -1,56 +1,23 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/svg/static/zoom-out.svg b/svg/static/zoom-out.svg index e9c39b3..7d20317 100644 --- a/svg/static/zoom-out.svg +++ b/svg/static/zoom-out.svg @@ -1,56 +1,23 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + +