honeytree 1.0.5 → 1.0.6
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/bin/honeydew.js +0 -0
- package/package.json +1 -1
- package/src/badge.js +57 -58
- package/src/plant.js +7 -0
- package/src/renderer.js +50 -3
package/bin/honeydew.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
package/src/badge.js
CHANGED
|
@@ -2,70 +2,74 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
4
|
import { readForest } from "./state.js";
|
|
5
|
+
import { buildScene, getWiltFactor } from "./renderer.js";
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const CELL = 6;
|
|
8
|
+
const BG = "#0d1117";
|
|
9
|
+
const TEXT_PAD = 18;
|
|
9
10
|
|
|
10
|
-
function
|
|
11
|
-
return Math.round(text.length * CHAR_WIDTH + PADDING * 2);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function buildMessage(forest) {
|
|
11
|
+
function buildStatsText(forest, biome) {
|
|
15
12
|
const count = forest.trees.length;
|
|
16
13
|
const streak = forest.streak || 0;
|
|
17
|
-
const
|
|
14
|
+
const wilt = getWiltFactor(forest.lastActiveDate);
|
|
18
15
|
|
|
19
|
-
|
|
16
|
+
const parts = [`${count} tree${count === 1 ? "" : "s"}`];
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
if (wilt > 0) {
|
|
19
|
+
const a = new Date(forest.lastActiveDate + "T00:00:00");
|
|
20
|
+
const b = new Date(new Date().toISOString().slice(0, 10) + "T00:00:00");
|
|
21
|
+
const idle = Math.round((b - a) / (24 * 60 * 60 * 1000));
|
|
22
|
+
parts.push(`wilting (${idle}d idle)`);
|
|
23
|
+
} else if (streak > 0) {
|
|
24
|
+
parts.push(`${streak}d streak`);
|
|
25
|
+
}
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return treePart;
|
|
27
|
+
parts.push(biome.label);
|
|
28
|
+
return parts.join(" · ");
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
function generateForestSVG(forest) {
|
|
32
|
+
const cols = Math.max(40, Math.min(forest.viewerWidth || 60, 80));
|
|
33
|
+
const { buffer, biome, sceneRows } = buildScene(forest, cols);
|
|
34
|
+
|
|
35
|
+
const artW = cols * CELL;
|
|
36
|
+
const artH = sceneRows * CELL;
|
|
37
|
+
const totalW = artW;
|
|
38
|
+
const totalH = artH + TEXT_PAD;
|
|
39
|
+
|
|
40
|
+
let rects = "";
|
|
41
|
+
for (let y = 0; y < sceneRows; y += 1) {
|
|
42
|
+
for (let x = 0; x < cols; x += 1) {
|
|
43
|
+
const cell = buffer[y][x];
|
|
44
|
+
if (!cell.color) continue;
|
|
45
|
+
rects += `<rect x="${x * CELL}" y="${y * CELL}" width="${CELL}" height="${CELL}" fill="${cell.color}"/>`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="${tw}" height="${HEIGHT}" role="img" aria-label="${label}: ${message}">
|
|
49
|
-
<title>${label}: ${message}</title>
|
|
50
|
-
<linearGradient id="s" x2="0" y2="100%">
|
|
51
|
-
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
|
52
|
-
<stop offset="1" stop-opacity=".1"/>
|
|
53
|
-
</linearGradient>
|
|
54
|
-
<clipPath id="r"><rect width="${tw}" height="${HEIGHT}" rx="3" fill="#fff"/></clipPath>
|
|
55
|
-
<g clip-path="url(#r)">
|
|
56
|
-
<rect width="${lw}" height="${HEIGHT}" fill="#555"/>
|
|
57
|
-
<rect x="${lw}" width="${mw}" height="${HEIGHT}" fill="${color}"/>
|
|
58
|
-
<rect width="${tw}" height="${HEIGHT}" fill="url(#s)"/>
|
|
59
|
-
</g>
|
|
60
|
-
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
|
|
61
|
-
<text aria-hidden="true" x="${lx}" y="15" fill="#010101" fill-opacity=".3">${label}</text>
|
|
62
|
-
<text x="${lx}" y="14">${label}</text>
|
|
63
|
-
<text aria-hidden="true" x="${mx}" y="15" fill="#010101" fill-opacity=".3">${message}</text>
|
|
64
|
-
<text x="${mx}" y="14">${message}</text>
|
|
65
|
-
</g>
|
|
49
|
+
const stats = buildStatsText(forest, biome);
|
|
50
|
+
|
|
51
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${totalW}" height="${totalH}" viewBox="0 0 ${totalW} ${totalH}">
|
|
52
|
+
<rect width="${totalW}" height="${totalH}" fill="${BG}" rx="6"/>
|
|
53
|
+
${rects}
|
|
54
|
+
<text x="${totalW / 2}" y="${artH + 13}" text-anchor="middle" fill="#8e8a84" font-family="monospace" font-size="10">${stats}</text>
|
|
66
55
|
</svg>`;
|
|
67
56
|
}
|
|
68
57
|
|
|
58
|
+
export function writeBadgeSVG(forest, outPath) {
|
|
59
|
+
fs.writeFileSync(outPath, generateForestSVG(forest));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function findBadgeFile() {
|
|
63
|
+
let dir = process.cwd();
|
|
64
|
+
const root = path.parse(dir).root;
|
|
65
|
+
while (dir !== root) {
|
|
66
|
+
const candidate = path.join(dir, "honeytree-badge.svg");
|
|
67
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
68
|
+
dir = path.dirname(dir);
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
69
73
|
export async function badge() {
|
|
70
74
|
const forest = readForest();
|
|
71
75
|
if (!forest) {
|
|
@@ -73,17 +77,12 @@ export async function badge() {
|
|
|
73
77
|
process.exit(1);
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
const label = "honeytree";
|
|
77
|
-
const message = buildMessage(forest);
|
|
78
|
-
const color = badgeColor(forest);
|
|
79
|
-
const svg = generateSVG(label, message, color);
|
|
80
|
-
|
|
81
80
|
const outPath = path.resolve("honeytree-badge.svg");
|
|
82
|
-
|
|
81
|
+
writeBadgeSVG(forest, outPath);
|
|
83
82
|
|
|
84
83
|
console.log(`Badge written to ${outPath}`);
|
|
85
84
|
console.log("");
|
|
86
|
-
console.log("Add this to your README to show your Honeytree
|
|
85
|
+
console.log("Add this to your README to show your Honeytree forest.");
|
|
87
86
|
console.log("The badge links to https://github.com/Varun2009178/honeytree");
|
|
88
87
|
console.log("");
|
|
89
88
|
console.log(
|
package/src/plant.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getSprite, TREE_TYPES } from "./sprites.js";
|
|
2
2
|
import { createEmptyForest, readForest, writeForest } from "./state.js";
|
|
3
|
+
import { findBadgeFile, writeBadgeSVG } from "./badge.js";
|
|
3
4
|
|
|
4
5
|
const MIN_GAP = 4;
|
|
5
6
|
const DEFAULT_WIDTH = 80;
|
|
@@ -98,4 +99,10 @@ export async function plant() {
|
|
|
98
99
|
forest.totalPrompts += 1;
|
|
99
100
|
|
|
100
101
|
writeForest(forest);
|
|
102
|
+
|
|
103
|
+
// Auto-refresh badge if one exists in the repo
|
|
104
|
+
try {
|
|
105
|
+
const badgePath = findBadgeFile();
|
|
106
|
+
if (badgePath) writeBadgeSVG(forest, badgePath);
|
|
107
|
+
} catch {}
|
|
101
108
|
}
|
package/src/renderer.js
CHANGED
|
@@ -7,9 +7,10 @@ const TREE_ROWS = 7;
|
|
|
7
7
|
const GROUND_ROWS = 2;
|
|
8
8
|
const SPACER_ROWS = 1;
|
|
9
9
|
const STATS_ROWS = 1;
|
|
10
|
+
const CTA_ROWS = 1;
|
|
10
11
|
|
|
11
12
|
export const SCENE_HEIGHT =
|
|
12
|
-
SKY_ROWS + TREE_ROWS + GROUND_ROWS + SPACER_ROWS + STATS_ROWS;
|
|
13
|
+
SKY_ROWS + TREE_ROWS + GROUND_ROWS + SPACER_ROWS + STATS_ROWS + CTA_ROWS;
|
|
13
14
|
|
|
14
15
|
const STATS_ACCENT = "#f5a50b";
|
|
15
16
|
const STATS_TEXT = "#8e8a84";
|
|
@@ -248,7 +249,7 @@ export function renderFrame(forest, termWidth = 80, options = {}) {
|
|
|
248
249
|
applyFog(buffer, wilt, width);
|
|
249
250
|
|
|
250
251
|
const lines = [];
|
|
251
|
-
for (let y = 0; y < SCENE_HEIGHT - SPACER_ROWS - STATS_ROWS; y += 1) {
|
|
252
|
+
for (let y = 0; y < SCENE_HEIGHT - SPACER_ROWS - STATS_ROWS - CTA_ROWS; y += 1) {
|
|
252
253
|
let line = "";
|
|
253
254
|
for (const cell of buffer[y]) {
|
|
254
255
|
if (!cell.color) {
|
|
@@ -264,10 +265,56 @@ export function renderFrame(forest, termWidth = 80, options = {}) {
|
|
|
264
265
|
|
|
265
266
|
lines.push("");
|
|
266
267
|
lines.push(buildStatsLine(forest, biome));
|
|
268
|
+
lines.push(
|
|
269
|
+
chalk.hex("#555555")(" add your forest to your README → ") +
|
|
270
|
+
chalk.hex(STATS_ACCENT)("honeytree badge"),
|
|
271
|
+
);
|
|
267
272
|
|
|
268
273
|
return lines.join("\n");
|
|
269
274
|
}
|
|
270
275
|
|
|
276
|
+
export function buildScene(forest, width) {
|
|
277
|
+
const w = Math.max(40, width);
|
|
278
|
+
const sceneRows = SKY_ROWS + TREE_ROWS + GROUND_ROWS;
|
|
279
|
+
const buffer = Array.from({ length: sceneRows }, () =>
|
|
280
|
+
Array.from({ length: w }, () => ({ char: " ", color: null })),
|
|
281
|
+
);
|
|
282
|
+
const groundStart = SKY_ROWS + TREE_ROWS;
|
|
283
|
+
const biome = getBiome(forest.trees.length);
|
|
284
|
+
const wilt = getWiltFactor(forest.lastActiveDate);
|
|
285
|
+
|
|
286
|
+
for (const star of generateStars(w, biome, 0)) {
|
|
287
|
+
if (star.y < sceneRows) {
|
|
288
|
+
buffer[star.y][star.x] = { char: star.char, color: star.color };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
for (let rowIndex = 0; rowIndex < GROUND_ROWS; rowIndex += 1) {
|
|
293
|
+
for (let x = 0; x < w; x += 1) {
|
|
294
|
+
buffer[groundStart + rowIndex][x] = { char: "█", color: biome.ground[rowIndex] };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const treeBaseY = groundStart - 1;
|
|
299
|
+
for (const tree of forest.trees) {
|
|
300
|
+
compositeSprite(buffer, getSprite(tree.type, tree.growth), tree.x, treeBaseY);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
applyFog(buffer, wilt, w);
|
|
304
|
+
|
|
305
|
+
if (wilt > 0) {
|
|
306
|
+
for (let y = SKY_ROWS; y < sceneRows; y += 1) {
|
|
307
|
+
for (let x = 0; x < w; x += 1) {
|
|
308
|
+
if (buffer[y][x].color) {
|
|
309
|
+
buffer[y][x].color = wiltColor(buffer[y][x].color, wilt);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return { buffer, biome, sceneRows };
|
|
316
|
+
}
|
|
317
|
+
|
|
271
318
|
export function renderPlainText(forest, width = 60) {
|
|
272
319
|
const w = Math.max(40, Math.min(width, 80));
|
|
273
320
|
const buffer = createBuffer(w);
|
|
@@ -290,7 +337,7 @@ export function renderPlainText(forest, width = 60) {
|
|
|
290
337
|
}
|
|
291
338
|
|
|
292
339
|
const lines = [];
|
|
293
|
-
for (let y = 0; y < SCENE_HEIGHT - SPACER_ROWS - STATS_ROWS; y += 1) {
|
|
340
|
+
for (let y = 0; y < SCENE_HEIGHT - SPACER_ROWS - STATS_ROWS - CTA_ROWS; y += 1) {
|
|
294
341
|
let line = "";
|
|
295
342
|
for (const cell of buffer[y]) {
|
|
296
343
|
line += cell.char;
|