File size: 2,701 Bytes
064bfd6
 
 
 
 
 
 
 
 
 
 
 
97289d7
064bfd6
 
 
 
97289d7
064bfd6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import { c as _c } from "react/compiler-runtime";
import { feature } from 'bun:bundle';
import React, { useEffect } from 'react';
import { useNotifications } from '../context/notifications.js';
import { Text } from '../ink.js';
import { getGlobalConfig } from '../utils/config.js';
import { getRainbowColor } from '../utils/thinking.js';

// Local date, not UTC — 24h rolling wave across timezones. Sustained Twitter
// buzz instead of a single UTC-midnight spike, gentler on soul-gen load.
// Teaser window: April 1-7, 2026 only. Command stays live forever after.
export function isBuddyTeaserWindow(): boolean {
  if ((process.env.USER_TYPE) === 'ant') return true;
  const d = new Date();
  return d.getFullYear() === 2026 && d.getMonth() === 3 && d.getDate() <= 7;
}
export function isBuddyLive(): boolean {
  if ((process.env.USER_TYPE) === 'ant') return true;
  const d = new Date();
  return d.getFullYear() > 2026 || d.getFullYear() === 2026 && d.getMonth() >= 3;
}
function RainbowText(t0) {
  const $ = _c(2);
  const {
    text
  } = t0;
  let t1;
  if ($[0] !== text) {
    t1 = <>{[...text].map(_temp)}</>;
    $[0] = text;
    $[1] = t1;
  } else {
    t1 = $[1];
  }
  return t1;
}

// Rainbow /buddy teaser shown on startup when no companion hatched yet.
// Idle presence and reactions are handled by CompanionSprite directly.
function _temp(ch, i) {
  return <Text key={i} color={getRainbowColor(i)}>{ch}</Text>;
}
export function useBuddyNotification() {
  const $ = _c(4);
  const {
    addNotification,
    removeNotification
  } = useNotifications();
  let t0;
  let t1;
  if ($[0] !== addNotification || $[1] !== removeNotification) {
    t0 = () => {
      if (!feature("BUDDY")) {
        return;
      }
      const config = getGlobalConfig();
      if (config.companion || !isBuddyTeaserWindow()) {
        return;
      }
      addNotification({
        key: "buddy-teaser",
        jsx: <RainbowText text="/buddy" />,
        priority: "immediate",
        timeoutMs: 15000
      });
      return () => removeNotification("buddy-teaser");
    };
    t1 = [addNotification, removeNotification];
    $[0] = addNotification;
    $[1] = removeNotification;
    $[2] = t0;
    $[3] = t1;
  } else {
    t0 = $[2];
    t1 = $[3];
  }
  useEffect(t0, t1);
}
export function findBuddyTriggerPositions(text: string): Array<{
  start: number;
  end: number;
}> {
  if (!feature('BUDDY')) return [];
  const triggers: Array<{
    start: number;
    end: number;
  }> = [];
  const re = /\/buddy\b/g;
  let m: RegExpExecArray | null;
  while ((m = re.exec(text)) !== null) {
    triggers.push({
      start: m.index,
      end: m.index + m[0].length
    });
  }
  return triggers;
}