@ideare/kit 0.1.0-next.1 → 0.1.0-next.3

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.
@@ -20,12 +20,15 @@ export type DraftInput = {
20
20
  name: string;
21
21
  /** What the business does, in the owner's words. It becomes the tagline. */
22
22
  idea: string;
23
+ /** A tagline the owner picked (an AI suggestion). It replaces the idea as the tagline only:
24
+ * the idea still says what the business does, and the look never moves with it. */
25
+ tagline?: string;
23
26
  locale: Locale;
24
27
  seed: number;
25
28
  picks: DraftPicks;
26
29
  };
27
30
  /** The longest name (the wordmark's limit) and line (the copy check's tagline limit) a draft
28
- * takes: every file the kit makes has room for them. */
31
+ * takes: every file the kit makes has room for them. A picked tagline has the line's limit. */
29
32
  export declare const DRAFT_LIMITS: {
30
33
  readonly name: 40;
31
34
  readonly idea: 60;
@@ -55,7 +58,8 @@ export declare function newDraftSeed(): number;
55
58
  export declare function draftBrand(input: DraftInput, ctx: {
56
59
  fonts: FontResolver;
57
60
  }): Promise<Brand>;
58
- /** The draft as link parameters: name, idea, locale, seed, colors, fonts and logo. */
61
+ /** The draft as link parameters: name, idea, a picked tagline, locale, seed, colors, fonts and
62
+ * logo. */
59
63
  export declare function draftParams(input: DraftInput): URLSearchParams;
60
64
  /** A draft read back from link parameters, or null when any part is missing or out of range. */
61
65
  export declare function readDraftParams(params: URLSearchParams): DraftInput | null;
@@ -4,7 +4,7 @@ import { catalogFont } from "../fonts/pick.js";
4
4
  import { iconMark, initials, matchIcons, monogramMark } from "../marks/draw.js";
5
5
  import { engineFor } from "../scene/render.js";
6
6
  /** The longest name (the wordmark's limit) and line (the copy check's tagline limit) a draft
7
- * takes: every file the kit makes has room for them. */
7
+ * takes: every file the kit makes has room for them. A picked tagline has the line's limit. */
8
8
  export const DRAFT_LIMITS = { name: 40, idea: 60 };
9
9
  /**
10
10
  * The font pairs a draft picks from. Few on purpose: a browser carries every file
@@ -199,6 +199,106 @@ function lineMood(idea) {
199
199
  const haystack = plain(idea);
200
200
  return MOOD_WORDS.find((row) => row.words.some((word) => haystack.includes(` ${word}`)));
201
201
  }
202
+ /**
203
+ * Colors a name or an idea can say, in pt-BR, en and es, as OKLCH hue ranges: "Salão Ametista"
204
+ * starts purple, "Loja Rubi" red. A trailing space means the whole word. Short words that are also
205
+ * everyday words (mar, sol, céu) are left out, so colors don't jump while a name is typed.
206
+ */
207
+ const COLOR_WORDS = [
208
+ {
209
+ hues: [18, 34],
210
+ words: [
211
+ "vermelh",
212
+ "rubi",
213
+ "ruby",
214
+ "rojo ",
215
+ "roja ",
216
+ "carmim",
217
+ "carmin",
218
+ "cereja",
219
+ "cherry",
220
+ ],
221
+ },
222
+ { hues: [24, 40], words: ["coral", "terracota", "terracotta"] },
223
+ { hues: [48, 64], words: ["laranj", "orange", "naranj", "tangerin", "damasco", "apricot"] },
224
+ {
225
+ hues: [50, 66],
226
+ words: ["marrom", "brown", "chocolat", "cacau", "cacao", "canela", "cinnamon"],
227
+ },
228
+ {
229
+ hues: [86, 104],
230
+ words: [
231
+ "amarel",
232
+ "yellow",
233
+ "amarill",
234
+ "dourad",
235
+ "dorad",
236
+ "golden",
237
+ "ouro ",
238
+ "girassol",
239
+ "sunflower",
240
+ "girasol",
241
+ ],
242
+ },
243
+ {
244
+ hues: [132, 156],
245
+ words: [
246
+ "verde",
247
+ "green",
248
+ "esmerald",
249
+ "emerald",
250
+ "menta",
251
+ "mint ",
252
+ "oliva",
253
+ "olive",
254
+ "jade",
255
+ "salvia",
256
+ "sage ",
257
+ ],
258
+ },
259
+ { hues: [180, 200], words: ["turques", "turquoise", "turquesa", "aqua "] },
260
+ {
261
+ hues: [244, 266],
262
+ words: [
263
+ "azul",
264
+ "blue",
265
+ "safira",
266
+ "sapphire",
267
+ "zafiro",
268
+ "celeste",
269
+ "anil",
270
+ "indigo",
271
+ "marinho",
272
+ "navy",
273
+ ],
274
+ },
275
+ {
276
+ hues: [296, 316],
277
+ words: [
278
+ "roxo",
279
+ "roxa",
280
+ "purple",
281
+ "morad",
282
+ "lilas",
283
+ "lilac",
284
+ "lila ",
285
+ "violet",
286
+ "ametist",
287
+ "amethyst",
288
+ "amatist",
289
+ "lavand",
290
+ "lavender",
291
+ ],
292
+ },
293
+ {
294
+ hues: [338, 356],
295
+ words: ["rosa ", "pink", "magenta", "fucsia", "fuchsia", "rubor", "blush"],
296
+ },
297
+ ];
298
+ function colorHues(text) {
299
+ const haystack = plain(text);
300
+ return COLOR_WORDS.find((row) => row.words.some((word) => haystack.includes(` ${word}`)))?.hues;
301
+ }
202
302
  /** FNV-1a, 32 bits: a stable number for a string. */
203
303
  function hash(text) {
204
304
  let h = 0x811c9dc5;
@@ -229,7 +329,8 @@ function draftColors(input) {
229
329
  const fromLine = lineMood(`${input.name} ${input.idea}`);
230
330
  const base = fromLine?.mood ?? MOODS[Math.floor(roll(input.seed, "mood") * MOODS.length)];
231
331
  const mood = base ?? "calm";
232
- const [from, to] = fromLine?.hues ?? [0, 360];
332
+ // A color the name says comes first, then one the idea says, then the business's own range.
333
+ const [from, to] = colorHues(input.name) ?? colorHues(input.idea) ?? fromLine?.hues ?? [0, 360];
233
334
  const firstHue = from + roll(input.seed, "hue") * (to - from);
234
335
  const k = input.picks.colors;
235
336
  // The business's own mood holds for two presses, then the moods take turns.
@@ -307,7 +408,9 @@ export async function draftBrand(input, ctx) {
307
408
  ink: light.onPrimary,
308
409
  });
309
410
  }
310
- const tagline = idea || name;
411
+ const picked = (input.tagline ?? "").trim().slice(0, DRAFT_LIMITS.idea);
412
+ const tagline = picked || idea || name;
413
+ const subtitle = picked || idea;
311
414
  const draft = {
312
415
  $schema: BRAND_SCHEMA_URL,
313
416
  schemaVersion: 1,
@@ -318,8 +421,9 @@ export async function draftBrand(input, ctx) {
318
421
  copy: {
319
422
  [input.locale]: {
320
423
  tagline,
321
- description: tagline,
322
- card: { title: name, ...(idea ? { subtitle: idea } : {}) },
424
+ // What the business does, which a picked tagline doesn't replace.
425
+ description: idea || tagline,
426
+ card: { title: name, ...(subtitle ? { subtitle } : {}) },
323
427
  },
324
428
  },
325
429
  mark: {
@@ -344,14 +448,18 @@ export async function draftBrand(input, ctx) {
344
448
  return parsed.brand;
345
449
  }
346
450
  const PICK_KEYS = ["colors", "fonts", "logo"];
347
- /** The draft as link parameters: name, idea, locale, seed, colors, fonts and logo. */
451
+ /** The draft as link parameters: name, idea, a picked tagline, locale, seed, colors, fonts and
452
+ * logo. */
348
453
  export function draftParams(input) {
349
454
  const params = new URLSearchParams({
350
455
  name: input.name.trim().slice(0, DRAFT_LIMITS.name),
351
456
  idea: input.idea.trim().slice(0, DRAFT_LIMITS.idea),
352
- locale: input.locale,
353
- seed: String(input.seed),
354
457
  });
458
+ const tagline = input.tagline?.trim().slice(0, DRAFT_LIMITS.idea);
459
+ if (tagline)
460
+ params.set("tagline", tagline);
461
+ params.set("locale", input.locale);
462
+ params.set("seed", String(input.seed));
355
463
  for (const key of PICK_KEYS)
356
464
  params.set(key, String(input.picks[key]));
357
465
  return params;
@@ -366,11 +474,12 @@ function count(value, max) {
366
474
  export function readDraftParams(params) {
367
475
  const name = params.get("name")?.trim() ?? "";
368
476
  const idea = params.get("idea")?.trim() ?? "";
477
+ const tagline = params.get("tagline")?.trim() ?? "";
369
478
  const locale = LOCALES.find((code) => code === params.get("locale"));
370
479
  const seed = count(params.get("seed"), 2147483647);
371
480
  if (!name || name.length > DRAFT_LIMITS.name || idea.length > DRAFT_LIMITS.idea)
372
481
  return null;
373
- if (!locale || !seed)
482
+ if (tagline.length > DRAFT_LIMITS.idea || !locale || !seed)
374
483
  return null;
375
484
  const picks = { colors: 0, fonts: 0, logo: 0 };
376
485
  for (const key of PICK_KEYS) {
@@ -379,5 +488,5 @@ export function readDraftParams(params) {
379
488
  return null;
380
489
  picks[key] = value;
381
490
  }
382
- return { name, idea, locale, seed, picks };
491
+ return { name, idea, ...(tagline ? { tagline } : {}), locale, seed, picks };
383
492
  }
@@ -23,6 +23,8 @@ export type HeadOptions = {
23
23
  base?: string;
24
24
  /** The absolute URL of the share card, like https://acme.com/og.png. */
25
25
  ogImage?: string;
26
+ /** The card's alt text when the card is a page's own (generate's `pages`), not the brand's. */
27
+ imageAlt?: string;
26
28
  locale?: Locale;
27
29
  /** The page title, when it is not the brand's name. */
28
30
  title?: string;
@@ -247,7 +247,7 @@ export function headTags(brand, options = {}) {
247
247
  `<meta property="og:description" content="${escapeHtml(copy.description)}">`,
248
248
  ];
249
249
  if (options.ogImage) {
250
- tags.push(`<meta property="og:image" content="${escapeHtml(options.ogImage)}">`, '<meta property="og:image:width" content="1200">', '<meta property="og:image:height" content="630">', `<meta property="og:image:alt" content="${escapeHtml(ogAlt(brand, options.locale))}">`, '<meta name="twitter:card" content="summary_large_image">');
250
+ tags.push(`<meta property="og:image" content="${escapeHtml(options.ogImage)}">`, '<meta property="og:image:width" content="1200">', '<meta property="og:image:height" content="630">', `<meta property="og:image:alt" content="${escapeHtml(options.imageAlt ?? ogAlt(brand, options.locale))}">`, '<meta name="twitter:card" content="summary_large_image">');
251
251
  }
252
252
  return tags;
253
253
  }
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** The kit's version, kept equal to package.json's (src/brand/schema.test.ts checks it). */
2
- export declare const VERSION = "0.1.0-next.1";
2
+ export declare const VERSION = "0.1.0-next.3";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** The kit's version, kept equal to package.json's (src/brand/schema.test.ts checks it). */
2
- export const VERSION = "0.1.0-next.1";
2
+ export const VERSION = "0.1.0-next.3";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ideare/kit",
3
- "version": "0.1.0-next.1",
3
+ "version": "0.1.0-next.3",
4
4
  "description": "The brand kit engine behind ideare: brand.json, file generation, checks and the scene renderer.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://ideare.app",