habithive-code 0.2.0 → 0.2.1
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/README.md +21 -0
- package/package.json +1 -1
- package/src/logo.js +55 -12
package/README.md
CHANGED
|
@@ -10,6 +10,11 @@ two things effortless:
|
|
|
10
10
|
> them from their official sources under their own licenses. All credit for those
|
|
11
11
|
> projects belongs to their authors (see [Credits](#credits)).
|
|
12
12
|
|
|
13
|
+
## Prerequisites
|
|
14
|
+
|
|
15
|
+
- **Node.js ≥ 18**
|
|
16
|
+
- Internet access to fetch `opencode` and `oh-my-openagent` on first `habithive setup` (nothing else is required up front)
|
|
17
|
+
|
|
13
18
|
## Install
|
|
14
19
|
|
|
15
20
|
```sh
|
|
@@ -17,6 +22,22 @@ npm i -g habithive-code
|
|
|
17
22
|
habithive setup # full stack (below). add --minimal to skip the extras
|
|
18
23
|
```
|
|
19
24
|
|
|
25
|
+
### Clone from source (GitLab)
|
|
26
|
+
|
|
27
|
+
For development, or if you'd rather run from a checkout than the published package:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
git clone https://repository.hclhealthcare.in/hive/habit-hive-code.git
|
|
31
|
+
cd habit-hive-code
|
|
32
|
+
npm test # sanity check — expect "16 passed, 0 failed"
|
|
33
|
+
node bin/habithive.mjs status # smoke test without installing anything globally
|
|
34
|
+
npm i -g . # optional: makes `habithive` available globally from this checkout
|
|
35
|
+
habithive setup # wire opencode + oh-my-opencode + the habithive plugin
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Cloning requires a GitLab account with access to the `hive` group on
|
|
39
|
+
`repository.hclhealthcare.in` — ask your GitLab admin to add you if you don't have it yet.
|
|
40
|
+
|
|
20
41
|
`habithive setup` sets up, from each tool's **official source** (nothing but the two
|
|
21
42
|
skills is bundled):
|
|
22
43
|
|
package/package.json
CHANGED
package/src/logo.js
CHANGED
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
// habithive-code — home-screen wordmark in opencode's TUI.
|
|
2
2
|
//
|
|
3
|
-
// Renders a big <ascii_font> "HABITHIVE" (block font,
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
3
|
+
// Renders a big <ascii_font> "HABITHIVE" (block font: white fill, black outline —
|
|
4
|
+
// same monochrome, high-contrast look as opencode's own wordmark) into the
|
|
5
|
+
// `home_logo` slot, with a slow breathing-brightness animation. A @opentui/core
|
|
6
|
+
// renderable is a valid slot node, so there's no JSX build step. We probe-construct
|
|
7
|
+
// once at register time (inside try/catch, OUTSIDE opencode's render loop) so any
|
|
8
|
+
// failure aborts gracefully instead of crashing the home screen — the slot is only
|
|
9
|
+
// registered if the probe succeeds.
|
|
8
10
|
|
|
9
|
-
const TEXT = "habithive";
|
|
10
|
-
const FONT = "block";
|
|
11
|
-
|
|
11
|
+
const TEXT = "habithive"; // ascii_font uppercases it
|
|
12
|
+
const FONT = "block"; // tiny | slick | block | huge | shade | grid | pallet
|
|
13
|
+
// `block` renders two color layers: [fill, outline]. White-on-black, opencode-style.
|
|
14
|
+
const FILL = "#ffffff";
|
|
15
|
+
const OUTLINE = "#000000";
|
|
16
|
+
const PULSE_MS = 90; // animation frame interval
|
|
17
|
+
const PULSE_STEPS = 40; // full breathing cycle length
|
|
18
|
+
|
|
19
|
+
// Brightness for step i of a triangle-wave breathing cycle, as a hex gray "#rrggbb".
|
|
20
|
+
function breathGray(i) {
|
|
21
|
+
const t = i / PULSE_STEPS; // 0..1
|
|
22
|
+
const tri = t < 0.5 ? t * 2 : (1 - t) * 2; // 0 -> 1 -> 0
|
|
23
|
+
const v = Math.round(150 + tri * 105); // 150..255, never fully dims out
|
|
24
|
+
const hex = v.toString(16).padStart(2, "0");
|
|
25
|
+
return `#${hex}${hex}${hex}`;
|
|
26
|
+
}
|
|
12
27
|
|
|
13
28
|
export async function registerLogo(api) {
|
|
14
29
|
try {
|
|
@@ -18,11 +33,15 @@ export async function registerLogo(api) {
|
|
|
18
33
|
try { rctx = (rnd && rnd.renderContext) || (rnd && rnd.root && rnd.root.ctx) || null; } catch {}
|
|
19
34
|
if (!rctx) return;
|
|
20
35
|
|
|
21
|
-
let AsciiFont;
|
|
22
|
-
try {
|
|
36
|
+
let AsciiFont, RenderableEvents;
|
|
37
|
+
try {
|
|
38
|
+
const mod = await import("@opentui/core");
|
|
39
|
+
AsciiFont = mod.ASCIIFontRenderable;
|
|
40
|
+
RenderableEvents = mod.RenderableEvents;
|
|
41
|
+
} catch { return; }
|
|
23
42
|
if (typeof AsciiFont !== "function") return;
|
|
24
43
|
|
|
25
|
-
const make = () => new AsciiFont(rctx, { text: TEXT, font: FONT, color:
|
|
44
|
+
const make = () => new AsciiFont(rctx, { text: TEXT, font: FONT, color: [FILL, OUTLINE] });
|
|
26
45
|
|
|
27
46
|
// Prove construction works before registering (a throw here is caught → no crash).
|
|
28
47
|
try {
|
|
@@ -30,7 +49,31 @@ export async function registerLogo(api) {
|
|
|
30
49
|
if (probe && typeof probe.destroyRecursively === "function") probe.destroyRecursively();
|
|
31
50
|
} catch { return; }
|
|
32
51
|
|
|
33
|
-
api.slots.register({
|
|
52
|
+
api.slots.register({
|
|
53
|
+
slots: {
|
|
54
|
+
home_logo: () => {
|
|
55
|
+
const node = make();
|
|
56
|
+
try {
|
|
57
|
+
let step = 0;
|
|
58
|
+
const timer = setInterval(() => {
|
|
59
|
+
try {
|
|
60
|
+
step = (step + 1) % PULSE_STEPS;
|
|
61
|
+
node.color = [breathGray(step), OUTLINE];
|
|
62
|
+
} catch { clearInterval(timer); }
|
|
63
|
+
}, PULSE_MS);
|
|
64
|
+
const stop = () => clearInterval(timer);
|
|
65
|
+
if (RenderableEvents && typeof node.once === "function") {
|
|
66
|
+
node.once(RenderableEvents.DESTROYED, stop);
|
|
67
|
+
} else if (typeof node.on === "function") {
|
|
68
|
+
node.on("destroyed", stop);
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
// animation is best-effort; a static logo still renders if this fails
|
|
72
|
+
}
|
|
73
|
+
return node;
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
});
|
|
34
77
|
} catch {
|
|
35
78
|
// never break the home screen
|
|
36
79
|
}
|