logomarks 0.0.0-stage → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/LICENSE +21 -0
- package/README.md +56 -2
- package/bin/logomarks.js +4 -0
- package/data/fonts/BricolageGrotesque-SemiBold.ttf +0 -0
- package/data/fonts/Cagliostro-Regular.ttf +0 -0
- package/data/fonts/Fraunces-SemiBold.ttf +0 -0
- package/data/fonts/IBMPlexMono-Regular.ttf +0 -0
- package/data/fonts/IBMPlexMono-SemiBold.ttf +0 -0
- package/data/fonts/InstrumentSans-SemiBold.ttf +0 -0
- package/data/fonts/InstrumentSerif-Regular.ttf +0 -0
- package/data/fonts/Inter-SemiBold.ttf +0 -0
- package/data/fonts/Limelight-Regular.ttf +0 -0
- package/data/fonts/OFL.txt +103 -0
- package/data/fonts/Shrikhand-Regular.ttf +0 -0
- package/data/fonts/SpaceGrotesk-SemiBold.ttf +0 -0
- package/data/index.bin +0 -0
- package/docs/api.md +31 -0
- package/docs/commands.md +412 -0
- package/docs/editing.md +61 -0
- package/docs/mcp.md +45 -0
- package/package.json +94 -4
- package/src/brand/brand.d.ts +23 -0
- package/src/brand/brand.js +29 -0
- package/src/brand/fonts.d.ts +61 -0
- package/src/brand/fonts.js +93 -0
- package/src/brand/mark.d.ts +53 -0
- package/src/brand/mark.js +151 -0
- package/src/checks/finding.d.ts +56 -0
- package/src/checks/finding.js +57 -0
- package/src/checks/fit.d.ts +5 -0
- package/src/checks/fit.js +46 -0
- package/src/checks/index.d.ts +33 -0
- package/src/checks/index.js +57 -0
- package/src/checks/specks.d.ts +5 -0
- package/src/checks/specks.js +38 -0
- package/src/checks/widths.d.ts +7 -0
- package/src/checks/widths.js +48 -0
- package/src/cli/args.d.ts +20 -0
- package/src/cli/args.js +35 -0
- package/src/cli/commands/check.d.ts +10 -0
- package/src/cli/commands/check.js +66 -0
- package/src/cli/commands/combine.d.ts +10 -0
- package/src/cli/commands/combine.js +54 -0
- package/src/cli/commands/cover.d.ts +10 -0
- package/src/cli/commands/cover.js +53 -0
- package/src/cli/commands/edit.d.ts +10 -0
- package/src/cli/commands/edit.js +70 -0
- package/src/cli/commands/find.d.ts +10 -0
- package/src/cli/commands/find.js +61 -0
- package/src/cli/commands/get.d.ts +10 -0
- package/src/cli/commands/get.js +57 -0
- package/src/cli/commands/glitch.d.ts +10 -0
- package/src/cli/commands/glitch.js +57 -0
- package/src/cli/commands/index.d.ts +15 -0
- package/src/cli/commands/index.js +24 -0
- package/src/cli/commands/kit.d.ts +10 -0
- package/src/cli/commands/kit.js +66 -0
- package/src/cli/commands/mcp.d.ts +9 -0
- package/src/cli/commands/mcp.js +36 -0
- package/src/cli/commands/mockup.d.ts +10 -0
- package/src/cli/commands/mockup.js +61 -0
- package/src/cli/commands/parts.d.ts +10 -0
- package/src/cli/commands/parts.js +44 -0
- package/src/cli/commands/sheet.d.ts +10 -0
- package/src/cli/commands/sheet.js +66 -0
- package/src/cli/commands/similar.d.ts +10 -0
- package/src/cli/commands/similar.js +111 -0
- package/src/cli/commands/social.d.ts +10 -0
- package/src/cli/commands/social.js +65 -0
- package/src/cli/commands/specks.d.ts +20 -0
- package/src/cli/commands/specks.js +103 -0
- package/src/cli/edited.d.ts +6 -0
- package/src/cli/edited.js +32 -0
- package/src/cli/files.d.ts +4 -0
- package/src/cli/files.js +27 -0
- package/src/cli/help.d.ts +20 -0
- package/src/cli/help.js +55 -0
- package/src/cli/index.d.ts +10 -0
- package/src/cli/index.js +104 -0
- package/src/cli/options.d.ts +30 -0
- package/src/cli/options.js +155 -0
- package/src/cli/report.d.ts +4 -0
- package/src/cli/report.js +28 -0
- package/src/cli/style.d.ts +4 -0
- package/src/cli/style.js +34 -0
- package/src/cover/index.d.ts +49 -0
- package/src/cover/index.js +63 -0
- package/src/edit/combine.d.ts +31 -0
- package/src/edit/combine.js +103 -0
- package/src/edit/doc.d.ts +34 -0
- package/src/edit/doc.js +96 -0
- package/src/edit/glitch.d.ts +48 -0
- package/src/edit/glitch.js +43 -0
- package/src/edit/index.d.ts +8 -0
- package/src/edit/index.js +11 -0
- package/src/edit/ops.d.ts +51 -0
- package/src/edit/ops.js +90 -0
- package/src/edit/preview.d.ts +8 -0
- package/src/edit/preview.js +51 -0
- package/src/find/index.d.ts +54 -0
- package/src/find/index.js +88 -0
- package/src/index.d.ts +30 -0
- package/src/index.js +28 -0
- package/src/kit/index.d.ts +26 -0
- package/src/kit/index.js +31 -0
- package/src/mcp/content.d.ts +30 -0
- package/src/mcp/content.js +56 -0
- package/src/mcp/edited.d.ts +5 -0
- package/src/mcp/edited.js +25 -0
- package/src/mcp/index.d.ts +41 -0
- package/src/mcp/index.js +72 -0
- package/src/mcp/inputs.d.ts +46 -0
- package/src/mcp/inputs.js +122 -0
- package/src/mcp/preview.d.ts +4 -0
- package/src/mcp/preview.js +24 -0
- package/src/mcp/tools/check.d.ts +15 -0
- package/src/mcp/tools/check.js +47 -0
- package/src/mcp/tools/combine.d.ts +15 -0
- package/src/mcp/tools/combine.js +42 -0
- package/src/mcp/tools/cover.d.ts +20 -0
- package/src/mcp/tools/cover.js +46 -0
- package/src/mcp/tools/edit.d.ts +21 -0
- package/src/mcp/tools/edit.js +73 -0
- package/src/mcp/tools/find.d.ts +14 -0
- package/src/mcp/tools/find.js +36 -0
- package/src/mcp/tools/get.d.ts +15 -0
- package/src/mcp/tools/get.js +44 -0
- package/src/mcp/tools/glitch.d.ts +12 -0
- package/src/mcp/tools/glitch.js +39 -0
- package/src/mcp/tools/index.d.ts +4 -0
- package/src/mcp/tools/index.js +23 -0
- package/src/mcp/tools/kit.d.ts +28 -0
- package/src/mcp/tools/kit.js +51 -0
- package/src/mcp/tools/mockup.d.ts +22 -0
- package/src/mcp/tools/mockup.js +51 -0
- package/src/mcp/tools/sheet.d.ts +15 -0
- package/src/mcp/tools/sheet.js +43 -0
- package/src/mcp/tools/similar.d.ts +13 -0
- package/src/mcp/tools/similar.js +42 -0
- package/src/mcp/tools/social.d.ts +5 -0
- package/src/mcp/tools/social.js +68 -0
- package/src/mockup/index.d.ts +52 -0
- package/src/mockup/index.js +152 -0
- package/src/raster/render.d.ts +30 -0
- package/src/raster/render.js +100 -0
- package/src/rules.d.ts +73 -0
- package/src/rules.js +139 -0
- package/src/sheet/card.d.ts +20 -0
- package/src/sheet/card.js +190 -0
- package/src/sheet/draw.d.ts +26 -0
- package/src/sheet/draw.js +94 -0
- package/src/sheet/grid.d.ts +2 -0
- package/src/sheet/grid.js +32 -0
- package/src/sheet/index.d.ts +31 -0
- package/src/sheet/index.js +47 -0
- package/src/sheet/theme.d.ts +8 -0
- package/src/sheet/theme.js +45 -0
- package/src/similar/folder.d.ts +20 -0
- package/src/similar/folder.js +66 -0
- package/src/similar/nearest.d.ts +32 -0
- package/src/similar/nearest.js +60 -0
- package/src/similar/signature.d.ts +12 -0
- package/src/similar/signature.js +42 -0
- package/src/similar/store.d.ts +67 -0
- package/src/similar/store.js +88 -0
- package/src/util/color.d.ts +2 -0
- package/src/util/color.js +23 -0
- package/src/util/files.d.ts +8 -0
- package/src/util/files.js +60 -0
- package/src/util/round.d.ts +2 -0
- package/src/util/round.js +14 -0
- package/src/util/version.d.ts +2 -0
- package/src/util/version.js +10 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module logomarks/cli/commands
|
|
3
|
+
*
|
|
4
|
+
* The verbs, by name.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as check from './check.js';
|
|
8
|
+
import * as similar from './similar.js';
|
|
9
|
+
import * as specks from './specks.js';
|
|
10
|
+
import * as sheet from './sheet.js';
|
|
11
|
+
import * as get from './get.js';
|
|
12
|
+
import * as kit from './kit.js';
|
|
13
|
+
import * as mockup from './mockup.js';
|
|
14
|
+
import * as cover from './cover.js';
|
|
15
|
+
import * as parts from './parts.js';
|
|
16
|
+
import * as glitch from './glitch.js';
|
|
17
|
+
import * as edit from './edit.js';
|
|
18
|
+
import * as combine from './combine.js';
|
|
19
|
+
import * as social from './social.js';
|
|
20
|
+
import * as find from './find.js';
|
|
21
|
+
import * as mcp from './mcp.js';
|
|
22
|
+
|
|
23
|
+
/** Every verb the CLI runs. */
|
|
24
|
+
export const COMMANDS = { check, similar, specks, sheet, find, parts, glitch, edit, combine, get, kit, social, mockup, cover, mcp };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ArgSpec, Parsed } from '../args.js';
|
|
2
|
+
import type { IO } from '../index.js';
|
|
3
|
+
import type { CommandHelp } from '../help.js';
|
|
4
|
+
|
|
5
|
+
/** What `kit` takes. */
|
|
6
|
+
export const spec: ArgSpec;
|
|
7
|
+
/** What `logomarks kit --help` prints. */
|
|
8
|
+
export const help: CommandHelp;
|
|
9
|
+
/** Runs `kit`; resolves to its exit code (0). */
|
|
10
|
+
export function run(args: Parsed, io: IO): Promise<number>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module logomarks/cli/commands/kit
|
|
3
|
+
*
|
|
4
|
+
* `logomarks kit <slug|file.svg> --name "Brand" -o dir/`: the brand kit
|
|
5
|
+
* (`@logomarks/brand-kit`) written to a folder.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { kit } from '../../kit/index.js';
|
|
10
|
+
import { slugify } from '../../brand/mark.js';
|
|
11
|
+
import { brandArgs, packArgs, asUsage, writeFiles } from '../options.js';
|
|
12
|
+
import { style } from '../style.js';
|
|
13
|
+
import { FONTS } from '../../brand/fonts.js';
|
|
14
|
+
import { STYLE_IDS, DEFAULT_STYLES } from '@logomarks/brand-kit';
|
|
15
|
+
import { wrapList } from '../help.js';
|
|
16
|
+
|
|
17
|
+
/** What `kit` takes. */
|
|
18
|
+
export const spec = { flags: ['json', 'animated', 'no-social'], options: { out: 'o', name: '', color: '', font: '', tagline: '', url: '', handle: '', styles: '', platforms: '' } };
|
|
19
|
+
|
|
20
|
+
/** `logomarks kit --help`. */
|
|
21
|
+
/** @type {import('../help.js').CommandHelp} */
|
|
22
|
+
export const help = {
|
|
23
|
+
summary: 'Build a brand kit on a mark and write it to a folder.',
|
|
24
|
+
usage: ['<mark> --name "Brand" [-o dir/] [options]'],
|
|
25
|
+
about: [
|
|
26
|
+
'Writes mark/ (SVG on light, dark and colour; PNG 64–2048), favicon/ (.ico, favicon.svg,',
|
|
27
|
+
'touch and maskable icons, manifest, head.html), lockup/, colors/ (CSS, a Tailwind theme',
|
|
28
|
+
'and tokens: a 50–950 OKLCH scale with WCAG pairings), social/, brand-sheet.png and .svg,',
|
|
29
|
+
'brand.json and README.md. Look at brand-sheet.png before handing the kit over.',
|
|
30
|
+
],
|
|
31
|
+
options: [
|
|
32
|
+
['--name <text>', "The brand's name. Default: the mark's name."],
|
|
33
|
+
['--color <hex>', 'The brand colour, e.g. #ff5c3a. Default #d4ff3a.'],
|
|
34
|
+
['--font <id>', `The typeface for the name (SIL OFL). Default inter.\n${wrapList(Object.keys(FONTS), 48)}.`],
|
|
35
|
+
['--tagline <text>', 'A short line under the name on social images and the brand sheet.'],
|
|
36
|
+
['--url <url>', "The brand's website, for the brand sheet and email signature."],
|
|
37
|
+
['--handle <@name>', 'The social handle. Default: @ and the name.'],
|
|
38
|
+
['--styles <ids>', `Social styles: ${STYLE_IDS.join(', ')}.\nDefault: ${DEFAULT_STYLES.join(', ')}.`],
|
|
39
|
+
['--platforms <ids>', 'Social images by id or platform, e.g. instagram,x-header.\nDefault: every one.'],
|
|
40
|
+
['--animated', 'Also write animated SVGs for avatars, stories and banners.'],
|
|
41
|
+
['--no-social', 'Leave the social pack out.'],
|
|
42
|
+
['-o, --out <dir>', 'The folder to write. Default <name>-kit.'],
|
|
43
|
+
],
|
|
44
|
+
examples: ["logomarks kit hex-gem --name \"Hexa Labs\" --color '#ff5c3a' --font space-grotesk -o hexa/", 'logomarks kit mark.svg --name Acme --no-social'],
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Runs `kit`.
|
|
49
|
+
*
|
|
50
|
+
* @param {import('../args.js').Parsed} args The parsed arguments.
|
|
51
|
+
* @param {import('../index.js').IO} io Where output goes.
|
|
52
|
+
* @returns {Promise<number>} The exit code: 0.
|
|
53
|
+
*/
|
|
54
|
+
export async function run(args, io) {
|
|
55
|
+
const brand = brandArgs(args);
|
|
56
|
+
const dir = args.options.out ?? `${slugify(brand.name) || 'brand'}-kit`;
|
|
57
|
+
const k = await asUsage(() => kit({ ...brand, ...packArgs(args), social: !args.flags.has('no-social') }));
|
|
58
|
+
const n = writeFiles(dir, k.files);
|
|
59
|
+
if (args.flags.has('json')) io.out(JSON.stringify({ ok: true, dir, ...k.brand }, null, 2));
|
|
60
|
+
else {
|
|
61
|
+
io.out(`${style.green('✓')} wrote ${style.bold(`${n} files`)} to ${style.bold(dir)}: ${brand.name} in ${k.brand.colors.primary}, ${k.brand.font.name}, on ${brand.mark.name}.`);
|
|
62
|
+
io.out(style.dim(' mark/ favicon/ lockup/ colors/ social/ brand.json README.md'));
|
|
63
|
+
io.out(` Look at ${style.bold(path.join(dir, 'brand-sheet.png'))}.`);
|
|
64
|
+
}
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ArgSpec } from '../args.js';
|
|
2
|
+
import type { CommandHelp } from '../help.js';
|
|
3
|
+
|
|
4
|
+
/** What `mcp` takes: nothing. */
|
|
5
|
+
export const spec: ArgSpec;
|
|
6
|
+
/** What `logomarks mcp --help` prints. */
|
|
7
|
+
export const help: CommandHelp;
|
|
8
|
+
/** Starts the MCP server on stdio; resolves to 0 once it is connected. */
|
|
9
|
+
export function run(): Promise<number>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module logomarks/cli/commands/mcp
|
|
3
|
+
*
|
|
4
|
+
* `logomarks mcp`: the MCP server on stdio, for Claude Code, Codex, Cursor
|
|
5
|
+
* and any MCP client. Nothing is written to stdout but the protocol.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** What `mcp` takes. */
|
|
9
|
+
export const spec = { flags: [], options: {} };
|
|
10
|
+
|
|
11
|
+
/** `logomarks mcp --help`. */
|
|
12
|
+
/** @type {import('../help.js').CommandHelp} */
|
|
13
|
+
export const help = {
|
|
14
|
+
summary: 'Run the MCP server on stdio, for Claude Code, Codex, Cursor and other agents.',
|
|
15
|
+
usage: [''],
|
|
16
|
+
about: [
|
|
17
|
+
'Every verb as an MCP tool, run locally; nothing leaves the machine. Add it to an agent:',
|
|
18
|
+
' claude mcp add logomarks -- npx -y logomarks mcp',
|
|
19
|
+
' codex mcp add logomarks -- npx -y logomarks mcp',
|
|
20
|
+
'Or install the Claude Code plugin, which adds the skills too:',
|
|
21
|
+
' /plugin marketplace add logomarks/skills',
|
|
22
|
+
' /plugin install logomarks@logomarks',
|
|
23
|
+
],
|
|
24
|
+
json: false,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Runs `mcp`: starts the server and returns; the process lives until the client hangs up.
|
|
29
|
+
*
|
|
30
|
+
* @returns {Promise<number>} The exit code: 0.
|
|
31
|
+
*/
|
|
32
|
+
export async function run() {
|
|
33
|
+
const { serve } = await import('../../mcp/index.js');
|
|
34
|
+
await serve();
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ArgSpec, Parsed } from '../args.js';
|
|
2
|
+
import type { IO } from '../index.js';
|
|
3
|
+
import type { CommandHelp } from '../help.js';
|
|
4
|
+
|
|
5
|
+
/** What `mockup` takes. */
|
|
6
|
+
export const spec: ArgSpec;
|
|
7
|
+
/** What `logomarks mockup --help` prints. */
|
|
8
|
+
export const help: CommandHelp;
|
|
9
|
+
/** Runs `mockup`; resolves to its exit code (0). */
|
|
10
|
+
export function run(args: Parsed, io: IO): Promise<number>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module logomarks/cli/commands/mockup
|
|
3
|
+
*
|
|
4
|
+
* `logomarks mockup <slug|file> --object card -o out.png`: the brand on a
|
|
5
|
+
* 2D object. `-o` ending in `.svg` writes the layered SVG instead.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { mockup, OBJECTS } from '../../mockup/index.js';
|
|
9
|
+
import { UsageError } from '../args.js';
|
|
10
|
+
import { markArg, colorArg, fontArg, intArg, asUsage, writeFile } from '../options.js';
|
|
11
|
+
import { style } from '../style.js';
|
|
12
|
+
import { FONTS } from '../../brand/fonts.js';
|
|
13
|
+
import { wrapList } from '../help.js';
|
|
14
|
+
|
|
15
|
+
/** What `mockup` takes. */
|
|
16
|
+
export const spec = { flags: ['json'], options: { out: 'o', object: '', name: '', color: '', font: '', layout: '', surface: '', view: '', base: '', size: '', seed: '' } };
|
|
17
|
+
|
|
18
|
+
/** `logomarks mockup --help`. */
|
|
19
|
+
/** @type {import('../help.js').CommandHelp} */
|
|
20
|
+
export const help = {
|
|
21
|
+
summary: 'Put a brand on an object and write the image.',
|
|
22
|
+
usage: ['<mark> --object <object> [-o file.png] [options]'],
|
|
23
|
+
about: [
|
|
24
|
+
`Objects: ${wrapList(Object.keys(OBJECTS), 80)}.`,
|
|
25
|
+
'Garments, mugs and bags carry the mark; prints carry the lockup on the brand colour.',
|
|
26
|
+
],
|
|
27
|
+
options: [
|
|
28
|
+
['--object <id>', 'The object (required).'],
|
|
29
|
+
['--name <text>', "The brand's name. Default: the mark's name."],
|
|
30
|
+
['--color <hex>', 'The brand colour, e.g. #ff5c3a. Default #d4ff3a.'],
|
|
31
|
+
['--font <id>', `The typeface for the name (SIL OFL). Default inter.\n${wrapList(Object.keys(FONTS), 48)}.`],
|
|
32
|
+
['--layout <id>', "One of the object's layouts (a wrong one lists them)."],
|
|
33
|
+
['--surface <id>', 'What it lies on: oak, concrete, linen, paper, light, dark or a hex.'],
|
|
34
|
+
['--view <front | angle>', "A screen's or a sign's view."],
|
|
35
|
+
['--base <colour>', "The object's own colour: white, black, navy or a hex."],
|
|
36
|
+
['--size <px>', 'The longest side, 200–4000. Default 1600.'],
|
|
37
|
+
['--seed <text>', "Drives every random choice. Default: the mark's slug."],
|
|
38
|
+
['-o, --out <file>', 'Write .png, or .svg for the layered SVG. Default <object>.png.'],
|
|
39
|
+
],
|
|
40
|
+
examples: ['logomarks mockup hex-gem --name "Hexa Labs" --object card -o card.png', 'logomarks mockup mark.svg --object tee --base black -o tee.png'],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Runs `mockup`.
|
|
45
|
+
*
|
|
46
|
+
* @param {import('../args.js').Parsed} args The parsed arguments.
|
|
47
|
+
* @param {import('../index.js').IO} io Where output goes.
|
|
48
|
+
* @returns {Promise<number>} The exit code: 0.
|
|
49
|
+
*/
|
|
50
|
+
export async function run(args, io) {
|
|
51
|
+
const mark = markArg(args.positionals);
|
|
52
|
+
const { object, out: file, name, layout, surface, view, base, seed } = args.options;
|
|
53
|
+
if (!object || !(object in OBJECTS)) throw new UsageError(`--object is one of ${Object.keys(OBJECTS).join(', ')}`);
|
|
54
|
+
const out = file ?? `${object}.png`;
|
|
55
|
+
const options = { mark, object, name: name?.trim() || undefined, color: colorArg(args.options.color), font: fontArg(args.options.font), layout, surface, view, base, seed, size: intArg('size', args.options.size, 200, 4000) };
|
|
56
|
+
const m = await asUsage(() => mockup(options));
|
|
57
|
+
writeFile(out, out.toLowerCase().endsWith('.svg') ? m.svg : m.png);
|
|
58
|
+
if (args.flags.has('json')) io.out(JSON.stringify({ ok: true, file: out, object, width: m.width, height: m.height }, null, 2));
|
|
59
|
+
else io.out(`${style.green('✓')} wrote ${style.bold(out)}: ${options.name ?? mark.name} on a ${object}, ${m.width} × ${m.height}.`);
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ArgSpec, Parsed } from '../args.js';
|
|
2
|
+
import type { IO } from '../index.js';
|
|
3
|
+
import type { CommandHelp } from '../help.js';
|
|
4
|
+
|
|
5
|
+
/** What `parts` takes. */
|
|
6
|
+
export const spec: ArgSpec;
|
|
7
|
+
/** What `logomarks parts --help` prints. */
|
|
8
|
+
export const help: CommandHelp;
|
|
9
|
+
/** Runs `parts`; resolves to its exit code. */
|
|
10
|
+
export function run(args: Parsed, io: IO): Promise<number>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module logomarks/cli/commands/parts
|
|
3
|
+
*
|
|
4
|
+
* `logomarks parts <slug|file.svg>`: the parts by index, as `edit --ops`
|
|
5
|
+
* names them; `-o parts.png` draws them numbered.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { markParts } from '../../edit/ops.js';
|
|
9
|
+
import { partsPreview } from '../../edit/preview.js';
|
|
10
|
+
import { markArg, writeFile } from '../options.js';
|
|
11
|
+
import { style, pad } from '../style.js';
|
|
12
|
+
|
|
13
|
+
/** What `parts` takes. */
|
|
14
|
+
export const spec = { flags: ['json'], options: { out: 'o' } };
|
|
15
|
+
|
|
16
|
+
/** `logomarks parts --help`. */
|
|
17
|
+
/** @type {import('../help.js').CommandHelp} */
|
|
18
|
+
export const help = {
|
|
19
|
+
summary: "List a mark's parts by index, as edit --ops names them.",
|
|
20
|
+
usage: ['<mark> [-o parts.png]'],
|
|
21
|
+
about: ['A mark is a live slug or name, or the path to your own .svg (fitted to the grid first).'],
|
|
22
|
+
options: [['-o, --out <file.png>', 'Also draw the mark with every part boxed and numbered.']],
|
|
23
|
+
examples: ['logomarks parts quad-cove', 'logomarks parts mark.svg -o parts.png'],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Runs `parts`.
|
|
28
|
+
*
|
|
29
|
+
* @param {import('../args.js').Parsed} args The parsed arguments.
|
|
30
|
+
* @param {import('../index.js').IO} io Where output goes.
|
|
31
|
+
* @returns {Promise<number>} The exit code: 0.
|
|
32
|
+
*/
|
|
33
|
+
export async function run(args, io) {
|
|
34
|
+
const mark = markArg(args.positionals);
|
|
35
|
+
const list = markParts(mark.svg);
|
|
36
|
+
if (args.options.out) writeFile(args.options.out, await partsPreview(mark.svg, list));
|
|
37
|
+
if (args.flags.has('json')) io.out(JSON.stringify({ count: list.length, parts: list, ...(args.options.out && { file: args.options.out }) }, null, 2));
|
|
38
|
+
else {
|
|
39
|
+
io.out(`${style.bold(mark.name)}: ${list.length} part${list.length === 1 ? '' : 's'}`);
|
|
40
|
+
for (const p of list) io.out(` ${pad(String(p.index), 4)}${style.dim('box')} [${p.box.join(', ')}] ${style.dim('area')} ${p.area}${p.holes ? ` ${p.holes} hole${p.holes > 1 ? 's' : ''}` : ''}${p.tone ? ' tone 2' : ''}`);
|
|
41
|
+
if (args.options.out) io.out(` Numbered: ${style.bold(args.options.out)}`);
|
|
42
|
+
}
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ArgSpec, Parsed } from '../args.js';
|
|
2
|
+
import type { IO } from '../index.js';
|
|
3
|
+
import type { CommandHelp } from '../help.js';
|
|
4
|
+
|
|
5
|
+
/** What `sheet` takes. */
|
|
6
|
+
export const spec: ArgSpec;
|
|
7
|
+
/** What `logomarks sheet --help` prints. */
|
|
8
|
+
export const help: CommandHelp;
|
|
9
|
+
/** Runs `sheet`; resolves to its exit code (0 pass, 1 fail). */
|
|
10
|
+
export function run(args: Parsed, io: IO): Promise<number>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module logomarks/cli/commands/sheet
|
|
3
|
+
*
|
|
4
|
+
* `logomarks sheet <file.svg…> -o sheet.png`: a review card per mark, for
|
|
5
|
+
* an AI or a person to look at.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import { sheet } from '../../sheet/index.js';
|
|
10
|
+
import { gridSheet } from '../../sheet/grid.js';
|
|
11
|
+
import { svgFiles, slugOf } from '../files.js';
|
|
12
|
+
import { style } from '../style.js';
|
|
13
|
+
|
|
14
|
+
/** What `sheet` takes. */
|
|
15
|
+
export const spec = { flags: ['json', 'strict', 'plain'], options: { out: 'o', ignore: '', cols: '' } };
|
|
16
|
+
|
|
17
|
+
/** `logomarks sheet --help`. */
|
|
18
|
+
/** @type {import('../help.js').CommandHelp} */
|
|
19
|
+
export const help = {
|
|
20
|
+
summary: 'Draw review cards for marks, as one PNG.',
|
|
21
|
+
usage: ['<file.svg | folder>… [-o sheet.png] [options]'],
|
|
22
|
+
about: [
|
|
23
|
+
'Each card: the mark large on dark and light with its findings boxed and numbered, at 32',
|
|
24
|
+
'and 16 px, and beside its three nearest live marks. Exit 1 when any mark fails a check or',
|
|
25
|
+
'is a near-copy.',
|
|
26
|
+
],
|
|
27
|
+
options: [
|
|
28
|
+
['-o, --out <file.png>', 'The PNG to write. Default sheet.png.'],
|
|
29
|
+
['--strict', 'Count warnings as failures too.'],
|
|
30
|
+
['--ignore <ids>', 'Leave these finding ids out.'],
|
|
31
|
+
['--plain', 'A plain labelled grid instead of review cards.'],
|
|
32
|
+
['--cols <n>', 'Columns in the plain grid. Default 8.'],
|
|
33
|
+
],
|
|
34
|
+
examples: ['logomarks sheet out/*.svg -o sheet.png', 'logomarks sheet out/ --plain --cols 6 -o batch.png'],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Runs `sheet`.
|
|
39
|
+
*
|
|
40
|
+
* @param {import('../args.js').Parsed} args The parsed arguments.
|
|
41
|
+
* @param {import('../index.js').IO} io Where output goes.
|
|
42
|
+
* @returns {Promise<number>} The exit code: 1 when any mark fails its check or is a near-copy of a live mark.
|
|
43
|
+
*/
|
|
44
|
+
export async function run(args, io) {
|
|
45
|
+
const files = svgFiles(args.positionals);
|
|
46
|
+
const out = args.options.out ?? 'sheet.png';
|
|
47
|
+
const marks = files.map((file) => ({ name: slugOf(file), svg: fs.readFileSync(file, 'utf8') }));
|
|
48
|
+
if (args.flags.has('plain')) {
|
|
49
|
+
fs.writeFileSync(out, await gridSheet(marks, { cols: Number(args.options.cols ?? 8) || 8 }));
|
|
50
|
+
io.out(`${style.green('✓')} wrote ${style.bold(out)}: ${marks.length} mark${marks.length === 1 ? '' : 's'} in a plain grid.`);
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
const s = await sheet(marks, { strict: args.flags.has('strict'), ignore: (args.options.ignore ?? '').split(',').filter(Boolean) });
|
|
54
|
+
fs.writeFileSync(out, s.png);
|
|
55
|
+
const rows = s.marks.map((m, i) => ({
|
|
56
|
+
file: files[i],
|
|
57
|
+
ok: m.report.ok,
|
|
58
|
+
errors: m.report.errors,
|
|
59
|
+
warnings: m.report.warnings,
|
|
60
|
+
nearest: m.matches.map(({ slug, score, verdict }) => ({ slug, score, verdict })),
|
|
61
|
+
}));
|
|
62
|
+
const failing = rows.filter((r) => !r.ok || r.nearest.some((n) => n.verdict === 'near-copy')).length;
|
|
63
|
+
if (args.flags.has('json')) io.out(JSON.stringify({ ok: !failing, file: out, marks: rows }, null, 2));
|
|
64
|
+
else io.out(`${failing ? style.red('✗') : style.green('✓')} wrote ${style.bold(out)}: ${rows.length} card${rows.length === 1 ? '' : 's'}${failing ? `, ${failing} failing or a near-copy` : ''}. Look at it.`);
|
|
65
|
+
return failing ? 1 : 0;
|
|
66
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ArgSpec, Parsed } from '../args.js';
|
|
2
|
+
import type { IO } from '../index.js';
|
|
3
|
+
import type { CommandHelp } from '../help.js';
|
|
4
|
+
|
|
5
|
+
/** What `similar` takes. */
|
|
6
|
+
export const spec: ArgSpec;
|
|
7
|
+
/** What `logomarks similar --help` prints. */
|
|
8
|
+
export const help: CommandHelp;
|
|
9
|
+
/** Runs `similar`; resolves to its exit code (0 pass, 1 fail). */
|
|
10
|
+
export function run(args: Parsed, io: IO): Promise<number>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module logomarks/cli/commands/similar
|
|
3
|
+
*
|
|
4
|
+
* `logomarks similar <file.svg…>`: the nearest live marks, with scores.
|
|
5
|
+
* Over the house threshold a match is too close (look at the two side by
|
|
6
|
+
* side: solid shapes often share an outline); over the near-copy threshold
|
|
7
|
+
* the mark fails. `--against <dir>` compares with a folder of SVGs instead of
|
|
8
|
+
* the bundled index (the site's svg folder while a batch is drawn), `--each`
|
|
9
|
+
* with the other candidates too, and `--all <data dir>` writes "More like this"
|
|
10
|
+
* for a whole set (similar.json).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { nearestAll } from '@logomarks/core/similar';
|
|
16
|
+
import { similar } from '../../similar/nearest.js';
|
|
17
|
+
import { loadIndex } from '../../similar/store.js';
|
|
18
|
+
import { folderIndex, indexOf, joinIndexes } from '../../similar/folder.js';
|
|
19
|
+
import { outlineCoverage } from '../../similar/signature.js';
|
|
20
|
+
import { slugOf } from '../files.js';
|
|
21
|
+
import { SIMILAR, LETTERS } from '../../rules.js';
|
|
22
|
+
import { UsageError } from '../args.js';
|
|
23
|
+
import { svgFiles } from '../files.js';
|
|
24
|
+
import { style, pad } from '../style.js';
|
|
25
|
+
|
|
26
|
+
/** What `similar` takes. */
|
|
27
|
+
export const spec = { flags: ['json', 'letters', 'each'], options: { top: '', against: '', all: '', out: 'o' } };
|
|
28
|
+
|
|
29
|
+
/** `logomarks similar --help`. */
|
|
30
|
+
/** @type {import('../help.js').CommandHelp} */
|
|
31
|
+
export const help = {
|
|
32
|
+
summary: 'Find the live marks nearest to yours, with scores.',
|
|
33
|
+
usage: ['<file.svg | folder>… [options]', '--all <data dir> [-o similar.json]'],
|
|
34
|
+
about: [
|
|
35
|
+
`Compares outlines, rotations and mirrors included. ${SIMILAR.tooClose} and up is too close (compare`,
|
|
36
|
+
`the two by eye); ${SIMILAR.nearCopy} and up is a near-copy. Exit 1 when any mark is a near-copy.`,
|
|
37
|
+
],
|
|
38
|
+
options: [
|
|
39
|
+
['--top <n>', 'How many matches per mark. Default 5.'],
|
|
40
|
+
['--letters', `Compare a badge's knocked-out letter instead of its outline\n(${LETTERS.tooClose} too close, ${LETTERS.nearCopy} a near-copy).`],
|
|
41
|
+
['--against <dir>', 'Compare with a folder of SVGs instead of the bundled live set.'],
|
|
42
|
+
['--each', 'Compare the candidates with each other too (marked *).'],
|
|
43
|
+
['--all <data dir>', 'Write every mark\'s 8 nearest for a whole set ("More like this"),\nfrom its marks.json, descriptions.json and svg/.'],
|
|
44
|
+
['-o, --out <file>', 'Where --all writes. Default: stdout.'],
|
|
45
|
+
],
|
|
46
|
+
examples: ['logomarks similar mark.svg', 'logomarks similar out/ --top 3 --each', 'logomarks similar badge.svg --letters --json'],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** Verdict labels for the readable output. */
|
|
50
|
+
const LABEL = { 'near-copy': style.red('near-copy'), 'too-close': style.yellow('too close'), ok: style.dim('ok ') };
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Runs `similar`.
|
|
54
|
+
*
|
|
55
|
+
* @param {import('../args.js').Parsed} args The parsed arguments.
|
|
56
|
+
* @param {import('../index.js').IO} io Where output goes.
|
|
57
|
+
* @returns {Promise<number>} The exit code: 1 when any mark is a near-copy of a live one.
|
|
58
|
+
*/
|
|
59
|
+
export async function run(args, io) {
|
|
60
|
+
if (args.options.all) return all(args.options.all, args.options.out, io);
|
|
61
|
+
const files = svgFiles(args.positionals);
|
|
62
|
+
const top = Number(args.options.top ?? 5);
|
|
63
|
+
if (!Number.isInteger(top) || top < 1) throw new UsageError('--top takes a whole number');
|
|
64
|
+
const letters = args.flags.has('letters');
|
|
65
|
+
const base = args.options.against ? await folderIndex(args.options.against, { letters }) : loadIndex();
|
|
66
|
+
const index = args.flags.has('each') && files.length > 1 ? joinIndexes(base, await indexOf(files, { letters, candidate: true })) : base;
|
|
67
|
+
const results = [];
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
const matches = await similar(fs.readFileSync(file, 'utf8'), { top, letters, index, exclude: args.flags.has('each') ? slugOf(file) : undefined });
|
|
70
|
+
results.push({ file, ok: !matches.some((m) => m.verdict === 'near-copy'), tooClose: matches.some((m) => m.verdict !== 'ok'), matches });
|
|
71
|
+
}
|
|
72
|
+
const copies = results.filter((r) => !r.ok).length;
|
|
73
|
+
const t = letters ? LETTERS : SIMILAR;
|
|
74
|
+
if (args.flags.has('json')) {
|
|
75
|
+
io.out(JSON.stringify({ ok: !copies, thresholds: { tooClose: t.tooClose, nearCopy: t.nearCopy }, live: base.marks.length, built: base.built, marks: results }, null, 2));
|
|
76
|
+
} else {
|
|
77
|
+
for (const { file, ok, tooClose, matches } of results) {
|
|
78
|
+
io.out(`${!ok ? style.red('✗') : tooClose ? style.yellow('!') : style.green('✓')} ${style.bold(file)}`);
|
|
79
|
+
if (!matches.length) io.out(style.dim(' no knocked-out letter (holes) to compare'));
|
|
80
|
+
for (const m of matches) io.out(` ${m.score.toFixed(3)} ${LABEL[m.verdict]} ${pad(m.slug + (m.candidate ? ' *' : ''), 28)} ${style.dim(m.candidate ? 'another candidate' : m.name)}`);
|
|
81
|
+
}
|
|
82
|
+
const close = results.filter((r) => r.ok && r.tooClose).length;
|
|
83
|
+
io.out(`\n${results.length - copies} of ${results.length} not a near-copy of the ${base.marks.length} ${args.options.against ? 'marks' : 'live marks'}${index !== base ? ' or each other' : ''}${close ? `; ${close} too close, look at them side by side` : ''} (too close ≥ ${t.tooClose}, near-copy ≥ ${t.nearCopy})`);
|
|
84
|
+
}
|
|
85
|
+
return copies ? 1 : 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* `similar --all <data dir>`: every mark's 8 nearest ("More like this"), by overlap of 48 px renders nudged for the
|
|
90
|
+
* same style and shared moods, from the folder's marks.json, descriptions.json and svg/.
|
|
91
|
+
*
|
|
92
|
+
* @param {string} dir The data folder.
|
|
93
|
+
* @param {string | undefined} out Where to write the JSON (default: stdout).
|
|
94
|
+
* @param {import('../index.js').IO} io Where output goes.
|
|
95
|
+
* @returns {Promise<number>} The exit code.
|
|
96
|
+
*/
|
|
97
|
+
async function all(dir, out, io) {
|
|
98
|
+
const read = (/** @type {string} */ f) => JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8'));
|
|
99
|
+
/** @type {{ slug: string }[]} */
|
|
100
|
+
const marks = read('marks.json');
|
|
101
|
+
/** @type {Record<string, { style?: string, mood?: string[] }>} */
|
|
102
|
+
const desc = fs.existsSync(path.join(dir, 'descriptions.json')) ? read('descriptions.json') : {};
|
|
103
|
+
const set = [];
|
|
104
|
+
for (const m of marks) set.push({ slug: m.slug, alpha: await outlineCoverage(fs.readFileSync(path.join(dir, 'svg', `${m.slug}.svg`), 'utf8')), style: desc[m.slug]?.style, mood: desc[m.slug]?.mood });
|
|
105
|
+
const json = JSON.stringify(nearestAll(set)) + '\n';
|
|
106
|
+
if (out) {
|
|
107
|
+
fs.writeFileSync(out, json);
|
|
108
|
+
io.out(`similar for ${set.length} marks → ${out}`);
|
|
109
|
+
} else io.out(json.trimEnd());
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ArgSpec, Parsed } from '../args.js';
|
|
2
|
+
import type { IO } from '../index.js';
|
|
3
|
+
import type { CommandHelp } from '../help.js';
|
|
4
|
+
|
|
5
|
+
/** What `social` takes. */
|
|
6
|
+
export const spec: ArgSpec;
|
|
7
|
+
/** What `logomarks social --help` prints. */
|
|
8
|
+
export const help: CommandHelp;
|
|
9
|
+
/** Runs `social`; resolves to its exit code. */
|
|
10
|
+
export function run(args: Parsed, io: IO): Promise<number>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module logomarks/cli/commands/social
|
|
3
|
+
*
|
|
4
|
+
* `logomarks social <slug|file.svg> --name "Brand" -o dir/`: the social pack
|
|
5
|
+
* (`@logomarks/brand-kit`): every platform image in each style, the email
|
|
6
|
+
* signature and the Open Graph card, written to a folder.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { social } from '../../kit/index.js';
|
|
11
|
+
import { slugify } from '../../brand/mark.js';
|
|
12
|
+
import { brandArgs, packArgs, asUsage, writeFiles } from '../options.js';
|
|
13
|
+
import { style } from '../style.js';
|
|
14
|
+
import { FONTS } from '../../brand/fonts.js';
|
|
15
|
+
import { STYLE_IDS, DEFAULT_STYLES } from '@logomarks/brand-kit';
|
|
16
|
+
import { wrapList } from '../help.js';
|
|
17
|
+
|
|
18
|
+
/** What `social` takes. */
|
|
19
|
+
export const spec = { flags: ['json', 'animated'], options: { out: 'o', name: '', color: '', font: '', tagline: '', url: '', handle: '', styles: '', platforms: '' } };
|
|
20
|
+
|
|
21
|
+
/** `logomarks social --help`. */
|
|
22
|
+
/** @type {import('../help.js').CommandHelp} */
|
|
23
|
+
export const help = {
|
|
24
|
+
summary: "Draw the brand for every social platform at its size, and write it to a folder.",
|
|
25
|
+
usage: ['<mark> --name "Brand" [-o dir/] [options]'],
|
|
26
|
+
about: [
|
|
27
|
+
'Every platform image with the logo inside its safe zone, in each style, as',
|
|
28
|
+
'<style>/<id>.png and .svg, plus an email signature, the Open Graph card and a README.',
|
|
29
|
+
],
|
|
30
|
+
options: [
|
|
31
|
+
['--name <text>', "The brand's name. Default: the mark's name."],
|
|
32
|
+
['--color <hex>', 'The brand colour, e.g. #ff5c3a. Default #d4ff3a.'],
|
|
33
|
+
['--font <id>', `The typeface for the name (SIL OFL). Default inter.\n${wrapList(Object.keys(FONTS), 48)}.`],
|
|
34
|
+
['--tagline <text>', 'A short line under the name on social images and the brand sheet.'],
|
|
35
|
+
['--url <url>', "The brand's website, for the brand sheet and email signature."],
|
|
36
|
+
['--handle <@name>', 'The social handle. Default: @ and the name.'],
|
|
37
|
+
['--styles <ids>', `Social styles: ${STYLE_IDS.join(', ')}.\nDefault: ${DEFAULT_STYLES.join(', ')}.`],
|
|
38
|
+
['--platforms <ids>', 'Social images by id or platform, e.g. instagram,x-header.\nDefault: every one.'],
|
|
39
|
+
['--animated', 'Also write animated SVGs for avatars, stories and banners.'],
|
|
40
|
+
['-o, --out <dir>', 'The folder to write. Default <name>-social.'],
|
|
41
|
+
],
|
|
42
|
+
examples: ['logomarks social hex-gem --name "Hexa Labs" --platforms instagram,linkedin --styles bold', 'logomarks social mark.svg --name Acme --animated -o acme-social/'],
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Runs `social`.
|
|
47
|
+
*
|
|
48
|
+
* @param {import('../args.js').Parsed} args The parsed arguments.
|
|
49
|
+
* @param {import('../index.js').IO} io Where output goes.
|
|
50
|
+
* @returns {Promise<number>} The exit code: 0.
|
|
51
|
+
*/
|
|
52
|
+
export async function run(args, io) {
|
|
53
|
+
const brand = brandArgs(args);
|
|
54
|
+
const dir = args.options.out ?? `${slugify(brand.name) || 'brand'}-social`;
|
|
55
|
+
const pack = await asUsage(() => social({ ...brand, ...packArgs(args) }));
|
|
56
|
+
const files = [...pack.files.keys()];
|
|
57
|
+
writeFiles(dir, pack.files);
|
|
58
|
+
if (args.flags.has('json')) io.out(JSON.stringify({ ok: true, dir, count: files.length, files }, null, 2));
|
|
59
|
+
else {
|
|
60
|
+
const images = files.filter((f) => f.endsWith('.png'));
|
|
61
|
+
io.out(`${style.green('✓')} wrote ${style.bold(`${files.length} files`)} to ${style.bold(dir)}: ${images.length} images of ${brand.name}.`);
|
|
62
|
+
io.out(` Sizes and sources: ${style.bold(path.join(dir, 'README.md'))}.`);
|
|
63
|
+
}
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Box } from '@logomarks/core/geometry';
|
|
2
|
+
import type { ArgSpec, Parsed } from '../args.js';
|
|
3
|
+
import type { IO } from '../index.js';
|
|
4
|
+
import type { CommandHelp } from '../help.js';
|
|
5
|
+
|
|
6
|
+
/** One mark's small parts, as `specks` reports them. */
|
|
7
|
+
export interface MarkSpecks {
|
|
8
|
+
slug: string;
|
|
9
|
+
/** Why it was read from the raster, or couldn't be read. */
|
|
10
|
+
note: string;
|
|
11
|
+
raster: boolean;
|
|
12
|
+
parts: { kind: 'piece' | 'hole' | 'mark'; area: number; width: number; box: Box | number[]; raster?: boolean; skip: string }[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** What `specks` takes. */
|
|
16
|
+
export const spec: ArgSpec;
|
|
17
|
+
/** What `logomarks specks --help` prints. */
|
|
18
|
+
export const help: CommandHelp;
|
|
19
|
+
/** Runs `specks`; resolves to 1 when anything is reported. */
|
|
20
|
+
export function run(args: Parsed, io: IO): Promise<number>;
|