habithive-code 0.2.2 → 0.2.4

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.
Files changed (3) hide show
  1. package/README.md +23 -0
  2. package/package.json +1 -1
  3. package/src/logo.js +34 -20
package/README.md CHANGED
@@ -113,6 +113,29 @@ launch (habithive reopens your session automatically after a change).
113
113
  Tested against: **opencode 1.18.x**, **oh-my-openagent 4.19.x** (pinned in the installer).
114
114
  opencode auto-updates its own binary; if a release changes internals, open an issue.
115
115
 
116
+ ## Migrating from the previous package name
117
+
118
+ `habithive-code` is a rebrand of an earlier package that used a different name and
119
+ plugin entry. `habithive setup` only manages its own plugin entry, so if the old
120
+ package is still installed or wired into your opencode config, both load together
121
+ and you'll see two wordmarks stacked on the home screen. Fix it once with:
122
+
123
+ ```sh
124
+ npm rm -g maliv-code
125
+ rm -rf ~/.maliv
126
+ node -e "
127
+ const fs=require('fs'), os=require('os'), path=require('path');
128
+ const p=path.join(os.homedir(),'.config','opencode','tui.json');
129
+ const c=JSON.parse(fs.readFileSync(p,'utf8'));
130
+ c.plugin=(c.plugin||[]).filter(e=>!String(e).includes('maliv'));
131
+ fs.writeFileSync(p, JSON.stringify(c,null,2)+'\n');
132
+ console.log('cleaned:', p);
133
+ "
134
+ habithive setup
135
+ ```
136
+ This is a one-time cleanup — anyone installing `habithive-code` fresh, who never had
137
+ the old package, can skip this section entirely.
138
+
116
139
  ## Uninstall
117
140
 
118
141
  ```sh
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "habithive-code",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "A control layer for opencode: one-command install, live simple/complex mode switching, and a model tree for oh-my-opencode agents.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/logo.js CHANGED
@@ -1,18 +1,21 @@
1
1
  // habithive-code — home-screen wordmark in opencode's TUI.
2
2
  //
3
- // Renders a big <ascii_font> "HABIT HIVE" (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.
3
+ // Renders a two-line mark into the `home_logo` slot:
4
+ // HIVE <- big <ascii_font> (block font: white
5
+ // HABIT Intelligent Value Engineering fill, black outline), animated
6
+ // <- small caption underneath
7
+ // A @opentui/core renderable is a valid slot node, so there's no JSX build step.
8
+ // We probe-construct once at register time (inside try/catch, OUTSIDE opencode's
9
+ // render loop) so any failure aborts gracefully instead of crashing the home
10
+ // screen — the slot is only registered if the probe succeeds.
10
11
 
11
- const TEXT = "habit hive"; // ascii_font uppercases it
12
- const FONT = "block"; // tiny | slick | block | huge | shade | grid | pallet
12
+ const TITLE_TEXT = "hive"; // ascii_font uppercases it
13
+ const FONT = "block"; // tiny | slick | block | huge | shade | grid | pallet
13
14
  // `block` renders two color layers: [fill, outline]. White-on-black, opencode-style.
14
15
  const FILL = "#ffffff";
15
16
  const OUTLINE = "#000000";
17
+ const CAPTION_TEXT = "HABIT Intelligent Value Engineering";
18
+ const CAPTION_COLOR = "#999999";
16
19
  const PULSE_MS = 90; // animation frame interval
17
20
  const PULSE_STEPS = 40; // full breathing cycle length
18
21
 
@@ -33,44 +36,55 @@ export async function registerLogo(api) {
33
36
  try { rctx = (rnd && rnd.renderContext) || (rnd && rnd.root && rnd.root.ctx) || null; } catch {}
34
37
  if (!rctx) return;
35
38
 
36
- let AsciiFont, RenderableEvents;
39
+ let AsciiFont, TextRenderable, BoxRenderable, RenderableEvents;
37
40
  try {
38
41
  const mod = await import("@opentui/core");
39
42
  AsciiFont = mod.ASCIIFontRenderable;
43
+ TextRenderable = mod.TextRenderable;
44
+ BoxRenderable = mod.BoxRenderable;
40
45
  RenderableEvents = mod.RenderableEvents;
41
46
  } catch { return; }
42
- if (typeof AsciiFont !== "function") return;
47
+ if (typeof AsciiFont !== "function" || typeof TextRenderable !== "function" || typeof BoxRenderable !== "function") return;
43
48
 
44
- const make = () => new AsciiFont(rctx, { text: TEXT, font: FONT, color: [FILL, OUTLINE] });
49
+ const makeTitle = () => new AsciiFont(rctx, { text: TITLE_TEXT, font: FONT, color: [FILL, OUTLINE] });
50
+ const makeCaption = () => new TextRenderable(rctx, { content: CAPTION_TEXT, fg: CAPTION_COLOR });
51
+ const make = () => {
52
+ const box = new BoxRenderable(rctx, { flexDirection: "column", alignItems: "center", rowGap: 1 });
53
+ const title = makeTitle();
54
+ const caption = makeCaption();
55
+ box.add(title);
56
+ box.add(caption);
57
+ return { box, title };
58
+ };
45
59
 
46
60
  // Prove construction works before registering (a throw here is caught → no crash).
47
61
  try {
48
62
  const probe = make();
49
- if (probe && typeof probe.destroyRecursively === "function") probe.destroyRecursively();
63
+ if (probe.box && typeof probe.box.destroyRecursively === "function") probe.box.destroyRecursively();
50
64
  } catch { return; }
51
65
 
52
66
  api.slots.register({
53
67
  slots: {
54
68
  home_logo: () => {
55
- const node = make();
69
+ const { box, title } = make();
56
70
  try {
57
71
  let step = 0;
58
72
  const timer = setInterval(() => {
59
73
  try {
60
74
  step = (step + 1) % PULSE_STEPS;
61
- node.color = [breathGray(step), OUTLINE];
75
+ title.color = [breathGray(step), OUTLINE];
62
76
  } catch { clearInterval(timer); }
63
77
  }, PULSE_MS);
64
78
  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);
79
+ if (RenderableEvents && typeof box.once === "function") {
80
+ box.once(RenderableEvents.DESTROYED, stop);
81
+ } else if (typeof box.on === "function") {
82
+ box.on("destroyed", stop);
69
83
  }
70
84
  } catch {
71
85
  // animation is best-effort; a static logo still renders if this fails
72
86
  }
73
- return node;
87
+ return box;
74
88
  },
75
89
  },
76
90
  });