codeling 0.1.1 → 0.1.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 (2) hide show
  1. package/package.json +14 -9
  2. package/.vite/build/main.js +0 -91
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "codeling",
3
3
  "productName": "Codeling",
4
- "version": "0.1.1",
4
+ "version": "0.1.4",
5
5
  "description": "Gamified Claude Code companion that lives in your menu bar.",
6
- "main": ".vite/build/main.js",
6
+ "main": "scripts/codeling-cli.mjs",
7
7
  "private": false,
8
8
  "author": {
9
9
  "name": "Tyler Dodd",
@@ -33,6 +33,8 @@
33
33
  "package": "electron-forge package",
34
34
  "make": "electron-forge make",
35
35
  "release": "electron-forge publish",
36
+ "prepublishOnly": "node ./scripts/npm-main-swap.mjs --swap",
37
+ "postpublish": "node ./scripts/npm-main-swap.mjs --restore",
36
38
  "lint": "tsc --noEmit",
37
39
  "test": "vitest run",
38
40
  "test:watch": "vitest",
@@ -44,31 +46,34 @@
44
46
  "stop-hook:uninstall": "node ./scripts/install-stop-hook.mjs uninstall",
45
47
  "stop-hook:status": "node ./scripts/install-stop-hook.mjs status"
46
48
  },
49
+ "dependencies": {
50
+ "@grpc/grpc-js": "^1.12.2",
51
+ "@grpc/proto-loader": "^0.7.13",
52
+ "better-sqlite3": "^11.5.0",
53
+ "express": "^4.21.1",
54
+ "menubar": "^9.5.2",
55
+ "protobufjs": "^7.4.0",
56
+ "update-electron-app": "^3.2.0"
57
+ },
47
58
  "devDependencies": {
48
59
  "@electron-forge/cli": "^7.5.0",
49
60
  "@electron-forge/maker-deb": "^7.5.0",
50
61
  "@electron-forge/maker-dmg": "^7.11.1",
51
62
  "@electron-forge/maker-rpm": "^7.5.0",
52
63
  "@electron-forge/maker-squirrel": "^7.5.0",
64
+ "@electron-forge/plugin-auto-unpack-natives": "^7.11.1",
53
65
  "@electron-forge/plugin-vite": "^7.5.0",
54
66
  "@electron-forge/publisher-github": "^7.11.1",
55
- "@grpc/grpc-js": "^1.12.2",
56
- "@grpc/proto-loader": "^0.7.13",
57
67
  "@types/better-sqlite3": "^7.6.11",
58
68
  "@types/express": "^5.0.0",
59
69
  "@types/node": "^22.7.0",
60
70
  "@types/react": "^18.3.11",
61
71
  "@types/react-dom": "^18.3.0",
62
72
  "@vitejs/plugin-react": "^4.3.2",
63
- "better-sqlite3": "^11.5.0",
64
73
  "electron": "^33.0.0",
65
- "express": "^4.21.1",
66
- "menubar": "^9.5.2",
67
- "protobufjs": "^7.4.0",
68
74
  "react": "^18.3.1",
69
75
  "react-dom": "^18.3.1",
70
76
  "typescript": "~5.6.2",
71
- "update-electron-app": "^3.2.0",
72
77
  "vite": "^5.4.8",
73
78
  "vitest": "^4.1.5"
74
79
  }
@@ -1,91 +0,0 @@
1
- "use strict";const u=require("electron"),_t=require("menubar"),E=require("node:path"),T=require("node:fs"),yt=require("better-sqlite3"),bt=require("node:events"),Tt=require("node:assert"),St=require("node:os"),vt=require("node:util"),wt=require("@grpc/grpc-js"),Rt=require("@grpc/proto-loader"),kt=require("protobufjs"),te=require("express"),Be=require("node:fs/promises");function We(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const n in e)if(n!=="default"){const s=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,s.get?s:{enumerable:!0,get:()=>e[n]})}}return t.default=e,Object.freeze(t)}const ne=We(wt),Lt=We(Rt),Ot=32,Ge=5,qe=1e3,R={wizard:{label:"Wizard",tier:"common",priceBits:200},slime:{label:"Slime",tier:"common",priceBits:200},bat:{label:"Bat",tier:"common",priceBits:200},rat:{label:"Rat",tier:"common",priceBits:200},mushroom:{label:"Mushroom",tier:"common",priceBits:200},skeleton:{label:"Skeleton",tier:"common",priceBits:200},goblin:{label:"Goblin",tier:"common",priceBits:200},flying_eye:{label:"Flying Eye",tier:"uncommon",priceBits:400},fire_worm:{label:"Fire Worm",tier:"uncommon",priceBits:400},mimic:{label:"Mimic",tier:"rare",priceBits:800},evil_wizard:{label:"Evil Wizard",tier:"rare",priceBits:800},apprentice_wizard:{label:"Apprentice Wizard",tier:"rare",priceBits:800},martial_hero:{label:"Martial Hero",tier:"legendary",priceBits:1500},martial_hero_2:{label:"Martial Hero II",tier:"legendary",priceBits:1500}},At=`CREATE TABLE IF NOT EXISTS pet (
2
- id INTEGER PRIMARY KEY CHECK (id = 1),
3
- species TEXT NOT NULL,
4
- name TEXT NOT NULL,
5
- evolution_stage INTEGER NOT NULL DEFAULT 0,
6
- level INTEGER NOT NULL DEFAULT 1,
7
- xp INTEGER NOT NULL DEFAULT 0,
8
- bits INTEGER NOT NULL DEFAULT 0,
9
- created_at INTEGER NOT NULL
10
- );
11
-
12
- CREATE TABLE IF NOT EXISTS unlocks (
13
- item_id TEXT PRIMARY KEY,
14
- category TEXT NOT NULL,
15
- acquired_via TEXT NOT NULL,
16
- acquired_at INTEGER NOT NULL,
17
- equipped INTEGER NOT NULL DEFAULT 0
18
- );
19
-
20
- CREATE TABLE IF NOT EXISTS sessions (
21
- session_id TEXT PRIMARY KEY,
22
- started_at INTEGER NOT NULL,
23
- last_seen_at INTEGER NOT NULL,
24
- message_count INTEGER NOT NULL DEFAULT 0,
25
- input_tokens INTEGER NOT NULL DEFAULT 0,
26
- output_tokens INTEGER NOT NULL DEFAULT 0,
27
- cache_read_tokens INTEGER NOT NULL DEFAULT 0,
28
- cache_creation_tokens INTEGER NOT NULL DEFAULT 0,
29
- cost_usd REAL NOT NULL DEFAULT 0,
30
- stop_event_count INTEGER NOT NULL DEFAULT 0
31
- );
32
-
33
- CREATE TABLE IF NOT EXISTS otel_events (
34
- id INTEGER PRIMARY KEY AUTOINCREMENT,
35
- signal_type TEXT NOT NULL,
36
- transport TEXT NOT NULL,
37
- received_at INTEGER NOT NULL,
38
- payload TEXT NOT NULL
39
- );
40
- CREATE INDEX IF NOT EXISTS idx_otel_events_received_at ON otel_events(received_at);
41
-
42
- CREATE TABLE IF NOT EXISTS spin_state (
43
- id INTEGER PRIMARY KEY CHECK (id = 1),
44
- spins_available INTEGER NOT NULL DEFAULT 0,
45
- messages_since_last_spin INTEGER NOT NULL DEFAULT 0,
46
- spin_threshold INTEGER NOT NULL DEFAULT 50
47
- );
48
-
49
- CREATE TABLE IF NOT EXISTS achievements (
50
- id TEXT PRIMARY KEY,
51
- earned_at INTEGER NOT NULL
52
- );
53
-
54
- CREATE TABLE IF NOT EXISTS daily_activity (
55
- date TEXT PRIMARY KEY -- YYYY-MM-DD in local time
56
- );
57
-
58
- CREATE TABLE IF NOT EXISTS meta (
59
- key TEXT PRIMARY KEY,
60
- value TEXT NOT NULL
61
- );
62
- `;let k=null;function f(){if(k)return k;const e=E.join(u.app.getPath("userData"),"codeling.db");return k=new yt(e),k.pragma("journal_mode = WAL"),k.pragma("foreign_keys = ON"),k.exec(At),Nt(k),Mt(k),ge(k),k}function Nt(e){const t=e.pragma("table_info(sessions)").map(n=>n.name);t.includes("cost_usd")||e.exec("ALTER TABLE sessions ADD COLUMN cost_usd REAL NOT NULL DEFAULT 0"),t.includes("stop_event_count")||e.exec("ALTER TABLE sessions ADD COLUMN stop_event_count INTEGER NOT NULL DEFAULT 0")}function Mt(e){const t=Object.keys(R),n=new Set(t),s=e.prepare("SELECT species FROM pet WHERE id = 1").get();if(s&&!n.has(s.species)){const c=t[Math.floor(Math.random()*t.length)];e.prepare("UPDATE pet SET species = ? WHERE id = 1").run(c),console.log(`[migrate] active species '${s.species}' no longer valid; switched to '${c}'`)}const o=e.prepare("SELECT item_id FROM unlocks WHERE category = 'species'").all();for(const c of o){if(!c.item_id.startsWith("species:"))continue;const r=c.item_id.slice(8);n.has(r)||(e.prepare("DELETE FROM unlocks WHERE item_id = ?").run(c.item_id),console.log(`[migrate] dropped stale species unlock '${c.item_id}'`))}const i=e.prepare("SELECT item_id FROM unlocks WHERE category = 'animation'").all();for(const c of i){const r=c.item_id.startsWith("anim:")?c.item_id.slice(5):null;if(!r)continue;const l=r.indexOf(":");if(l<0)continue;const p=r.slice(0,l);n.has(p)||(e.prepare("DELETE FROM unlocks WHERE item_id = ?").run(c.item_id),console.log(`[migrate] dropped stale animation unlock '${c.item_id}'`))}const a=e.prepare("SELECT key FROM meta WHERE key LIKE 'home_animation:%'").all();for(const c of a){const r=c.key.slice(15);n.has(r)||e.prepare("DELETE FROM meta WHERE key = ?").run(c.key)}}function ge(e){const t=Date.now(),n=Object.keys(R),s=n[Math.floor(Math.random()*n.length)],o=R[s].label;e.prepare("INSERT OR IGNORE INTO pet (id, species, name, created_at) VALUES (1, ?, ?, ?)").run(s,o,t),e.prepare("INSERT OR IGNORE INTO spin_state (id) VALUES (1)").run(),e.prepare(`INSERT OR IGNORE INTO unlocks (item_id, category, acquired_via, acquired_at)
63
- SELECT 'species:' || species, 'species', 'starter', created_at
64
- FROM pet WHERE id = 1`).run()}function It(){k&&(k.close(),k=null)}class Ut extends bt.EventEmitter{emit(t,...n){return super.emit(t,...n)}on(t,n){return super.on(t,n)}off(t,n){return super.off(t,n)}}const L=new Ut;function Z(e=new Date){const t=e.getFullYear(),n=String(e.getMonth()+1).padStart(2,"0"),s=String(e.getDate()).padStart(2,"0");return`${t}-${n}-${s}`}function ye(e,t){const n=new Date(`${e}T00:00:00`);return n.setDate(n.getDate()+t),Z(n)}function Ye(){f().prepare("INSERT OR IGNORE INTO daily_activity (date) VALUES (?)").run(Z())}function Xe(){const e=f().prepare("SELECT date FROM daily_activity").all(),t=new Set(e.map(o=>o.date));let n=Z();if(!t.has(n)&&(n=ye(n,-1),!t.has(n)))return 0;let s=0;for(;t.has(n);)s+=1,n=ye(n,-1);return s}const be=Object.keys(R).length,ze=[{id:"first_message",label:"First word",description:"Send your first message",tier:"bronze",check:e=>e.totals.messages>=1},{id:"msg_100",label:"Chatterbox",description:"100 messages",tier:"silver",check:e=>e.totals.messages>=100},{id:"msg_1000",label:"Loquacious",description:"1,000 messages",tier:"gold",check:e=>e.totals.messages>=1e3},{id:"level_5",label:"Apprentice",description:"Reach level 5",tier:"bronze",check:e=>e.pet.level>=5},{id:"level_25",label:"Adept",description:"Reach level 25",tier:"silver",check:e=>e.pet.level>=25},{id:"level_100",label:"Master",description:"Reach level 100",tier:"gold",check:e=>e.pet.level>=100},{id:"unlock_species_2",label:"Collector",description:"Own 2 species",tier:"bronze",check:e=>e.unlockCounts.species>=2},{id:"unlock_species_5",label:"Menagerie",description:"Own 5 species",tier:"silver",check:e=>e.unlockCounts.species>=5},{id:"unlock_species_all",label:"Completionist",description:`Own all ${be} species`,tier:"gold",check:e=>e.unlockCounts.species>=be},{id:"first_animation",label:"Bringing it to life",description:"Unlock your first animation",tier:"bronze",check:e=>e.unlockCounts.animation>=1},{id:"animation_collector_10",label:"Animator",description:"Unlock 10 animations",tier:"silver",check:e=>e.unlockCounts.animation>=10},{id:"animation_master_25",label:"Master Animator",description:"Unlock 25 animations",tier:"gold",check:e=>e.unlockCounts.animation>=25},{id:"first_upgrade",label:"Power play",description:"Buy your first upgrade",tier:"silver",check:e=>e.unlockCounts.upgrade>=1},{id:"spend_1usd",label:"Tokens count",description:"$1 of session cost",tier:"bronze",check:e=>e.totals.costUsd>=1},{id:"spend_10usd",label:"Heavy lifting",description:"$10 of session cost",tier:"silver",check:e=>e.totals.costUsd>=10},{id:"streak_3",label:"Warming up",description:"3-day streak",tier:"bronze",check:e=>e.streakDays>=3},{id:"streak_7",label:"Habit",description:"7-day streak",tier:"silver",check:e=>e.streakDays>=7},{id:"streak_30",label:"Devout",description:"30-day streak",tier:"gold",check:e=>e.streakDays>=30}];function Ct(){const e=f(),t=e.prepare("SELECT level FROM pet WHERE id = 1").get(),n=e.prepare(`SELECT
65
- COALESCE(SUM(message_count), 0) AS messages,
66
- COALESCE(SUM(output_tokens), 0) AS output_tokens,
67
- COALESCE(SUM(cost_usd), 0) AS cost_usd
68
- FROM sessions`).get(),s=e.prepare(`SELECT
69
- COALESCE(SUM(CASE WHEN category = 'species' THEN 1 ELSE 0 END), 0) AS species,
70
- COALESCE(SUM(CASE WHEN category = 'upgrade' THEN 1 ELSE 0 END), 0) AS upgrade,
71
- COALESCE(SUM(CASE WHEN category = 'animation' THEN 1 ELSE 0 END), 0) AS animation
72
- FROM unlocks`).get();return{pet:{level:(t==null?void 0:t.level)??1},totals:{messages:(n==null?void 0:n.messages)??0,outputTokens:(n==null?void 0:n.output_tokens)??0,costUsd:(n==null?void 0:n.cost_usd)??0},unlockCounts:{species:(s==null?void 0:s.species)??0,upgrade:(s==null?void 0:s.upgrade)??0,animation:(s==null?void 0:s.animation)??0},streakDays:Xe()}}function X(e=!1){const t=f(),n=t.prepare("SELECT id FROM achievements").all(),s=new Set(n.map(r=>r.id)),o=Ct(),i=t.prepare("INSERT OR IGNORE INTO achievements (id, earned_at) VALUES (?, ?)"),a=Date.now(),c=[];for(const r of ze)s.has(r.id)||r.check(o)&&(i.run(r.id,a),c.push(r));if(!e)for(const r of c)L.emit("achievement:earned",{id:r.id,label:r.label,description:r.description,tier:r.tier,earnedAt:a})}var Te=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},z={},$=1e3,P=$*60,F=P*60,C=F*24,Dt=C*7,xt=C*365.25,$t=function(e,t){t=t||{};var n=typeof e;if(n==="string"&&e.length>0)return Pt(e);if(n==="number"&&isFinite(e))return t.long?jt(e):Ft(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))};function Pt(e){if(e=String(e),!(e.length>100)){var t=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(t){var n=parseFloat(t[1]),s=(t[2]||"ms").toLowerCase();switch(s){case"years":case"year":case"yrs":case"yr":case"y":return n*xt;case"weeks":case"week":case"w":return n*Dt;case"days":case"day":case"d":return n*C;case"hours":case"hour":case"hrs":case"hr":case"h":return n*F;case"minutes":case"minute":case"mins":case"min":case"m":return n*P;case"seconds":case"second":case"secs":case"sec":case"s":return n*$;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return n;default:return}}}}function Ft(e){var t=Math.abs(e);return t>=C?Math.round(e/C)+"d":t>=F?Math.round(e/F)+"h":t>=P?Math.round(e/P)+"m":t>=$?Math.round(e/$)+"s":e+"ms"}function jt(e){var t=Math.abs(e);return t>=C?V(e,t,C,"day"):t>=F?V(e,t,F,"hour"):t>=P?V(e,t,P,"minute"):t>=$?V(e,t,$,"second"):e+" ms"}function V(e,t,n,s){var o=t>=n*1.5;return Math.round(e/n)+" "+s+(o?"s":"")}var Ht=qt,Bt=/^(?:\w+:)?\/\/(\S+)$/,Wt=/^localhost[\:?\d]*(?:[^\:?\d]\S*)?$/,Gt=/^[^\s\.]+\.\S{2,}$/;function qt(e){if(typeof e!="string")return!1;var t=e.match(Bt);if(!t)return!1;var n=t[1];return n?!!(Wt.test(n)||Gt.test(n)):!1}var Yt=Ht,Xt=/(?:(?:[^:]+:)?[/][/])?(?:.+@)?([^/]+)([/][^?#]+)/,zt=function(e,t){var n={};if(t=t||{},!e||(e.url&&(e=e.url),typeof e!="string"))return null;var s=e.match(/^([\w-_]+)\/([\w-_\.]+)(?:#([\w-_\.]+))?$/),o=e.match(/^github:([\w-_]+)\/([\w-_\.]+)(?:#([\w-_\.]+))?$/),i=e.match(/^git@[\w-_\.]+:([\w-_]+)\/([\w-_\.]+)$/);if(s)n.user=s[1],n.repo=s[2],n.branch=s[3]||"master",n.host="github.com";else if(o)n.user=o[1],n.repo=o[2],n.branch=o[3]||"master",n.host="github.com";else if(i)n.user=i[1],n.repo=i[2].replace(/\.git$/i,""),n.branch="master",n.host="github.com";else{if(e=e.replace(/^git\+/,""),!Yt(e))return null;var a=e.match(Xt)||[],c=a[1],r=a[2];if(!c||c!=="github.com"&&c!=="www.github.com"&&!t.enterprise)return null;var l=r.match(/^\/([\w-_]+)\/([\w-_\.]+)(\/tree\/[\%\w-_\.\/]+)?(\/blob\/[\%\w-_\.\/]+)?/);if(!l)return null;if(n.user=l[1],n.repo=l[2].replace(/\.git$/i,""),n.host=c||"github.com",l[3]&&/^\/tree\/master\//.test(l[3]))n.branch="master",n.path=l[3].replace(/\/$/,"");else if(l[3]){var p=l[3].replace(/^\/tree\//,"").match(/[\%\w-_.]*\/?[\%\w-_]+/);n.branch=p&&p[0]}else if(l[4]){var p=l[4].replace(/^\/blob\//,"").match(/[\%\w-_.]*\/?[\%\w-_]+/);n.branch=p&&p[0]}else n.branch="master"}return n.host==="github.com"?n.apiHost="api.github.com":n.apiHost=n.host+"/api/v3",n.tarball_url="https://"+n.apiHost+"/repos/"+n.user+"/"+n.repo+"/tarball/"+n.branch,n.clone_url="https://"+n.host+"/"+n.user+"/"+n.repo,n.branch==="master"?(n.https_url="https://"+n.host+"/"+n.user+"/"+n.repo,n.travis_url="https://travis-ci.org/"+n.user+"/"+n.repo,n.zip_url="https://"+n.host+"/"+n.user+"/"+n.repo+"/archive/master.zip"):(n.https_url="https://"+n.host+"/"+n.user+"/"+n.repo+"/blob/"+n.branch,n.travis_url="https://travis-ci.org/"+n.user+"/"+n.repo+"?branch="+n.branch,n.zip_url="https://"+n.host+"/"+n.user+"/"+n.repo+"/archive/"+n.branch+".zip"),n.path&&(n.https_url+=n.path),n.api_url="https://"+n.apiHost+"/repos/"+n.user+"/"+n.repo,n};const Vt="update-electron-app",Kt="3.2.0",Jt={name:Vt,version:Kt};var j=Te&&Te.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(z,"__esModule",{value:!0});var Ve=z.UpdateSourceType=void 0,Qt=z.updateElectronApp=rn;z.makeUserNotifier=Ke;const ue=j($t),Zt=j(zt),O=j(Tt),en=j(T),Se=j(St),tn=j(E),nn=vt,w=u;var U;(function(e){e[e.ElectronPublicUpdateService=0]="ElectronPublicUpdateService",e[e.StaticStorage=1]="StaticStorage"})(U||(Ve=z.UpdateSourceType=U={}));const ve=Jt,sn=(0,nn.format)("%s/%s (%s: %s)",ve.name,ve.version,Se.default.platform(),Se.default.arch()),on=["darwin","win32"],we=e=>{try{const{protocol:t}=new URL(e);return t==="https:"}catch{return!1}};function rn(e={}){const t=cn(e);if(!w.app.isPackaged){const n="update-electron-app config looks good; aborting updates since app is in development mode";e.logger?e.logger.log(n):console.log(n);return}w.app.isReady()?Re(t):w.app.on("ready",()=>Re(t))}function Re(e){const{updateSource:t,updateInterval:n,logger:s}=e;if(!on.includes(process==null?void 0:process.platform)){c(`Electron's autoUpdater does not support the '${process.platform}' platform. Ref: https://www.electronjs.org/docs/latest/api/auto-updater#platform-notices`);return}let o,i="default";switch(t.type){case U.ElectronPublicUpdateService:{const r=process.windowsStore?"/msix":"";o=`${t.host}/${t.repo}/${process.platform}-${process.arch}${r}/${w.app.getVersion()}`;break}case U.StaticStorage:{o=t.baseUrl,process.platform==="darwin"&&(o+="/RELEASES.json",i="json");break}}const a={"User-Agent":sn};function c(...r){s.log(...r)}c("feedURL",o),c("requestHeaders",a),w.autoUpdater.setFeedURL({url:o,headers:a,serverType:i}),w.autoUpdater.on("error",r=>{c("updater error"),c(r)}),w.autoUpdater.on("checking-for-update",()=>{c("checking-for-update")}),w.autoUpdater.on("update-available",()=>{c("update-available; downloading...")}),w.autoUpdater.on("update-not-available",()=>{c("update-not-available")}),e.notifyUser&&w.autoUpdater.on("update-downloaded",(r,l,p,d,g)=>{c("update-downloaded",[r,l,p,d,g]),typeof e.onNotifyUser!="function"?((0,O.default)(e.onNotifyUser===void 0,"onNotifyUser option must be a callback function or undefined"),c("update-downloaded: notifyUser is true, opening default dialog"),e.onNotifyUser=Ke()):c("update-downloaded: notifyUser is true, running custom onNotifyUser callback"),e.onNotifyUser({event:r,releaseNotes:l,releaseDate:d,releaseName:p,updateURL:g})}),w.autoUpdater.checkForUpdates(),setInterval(()=>{w.autoUpdater.checkForUpdates()},(0,ue.default)(n))}function Ke(e){const n=Object.assign({},{title:"Application Update",detail:"A new version has been downloaded. Restart the application to apply the updates.",restartButtonText:"Restart",laterButtonText:"Later"},e);return s=>{const{releaseNotes:o,releaseName:i}=s,{title:a,restartButtonText:c,laterButtonText:r,detail:l}=n,p={type:"info",buttons:[c,r],title:a,message:process.platform==="win32"?o:i,detail:l};w.dialog.showMessageBox(p).then(({response:d})=>{d===0&&w.autoUpdater.quitAndInstall()})}}function an(){var e;const t=en.default.readFileSync(tn.default.join(w.app.getAppPath(),"package.json")),n=JSON.parse(t.toString()),s=((e=n.repository)===null||e===void 0?void 0:e.url)||n.repository,o=(0,Zt.default)(s);return(0,O.default)(o,"repo not found. Add repository string to your app's package.json file"),`${o.user}/${o.repo}`}function cn(e){var t;const n={host:"https://update.electronjs.org",updateInterval:"10 minutes",logger:console,notifyUser:!0},{host:s,updateInterval:o,logger:i,notifyUser:a,onNotifyUser:c}=Object.assign({},n,e);let r=e.updateSource;switch(r||(r={type:U.ElectronPublicUpdateService,repo:e.repo||an(),host:s}),r.type){case U.ElectronPublicUpdateService:{(0,O.default)((t=r.repo)===null||t===void 0?void 0:t.includes("/"),"repo is required and should be in the format `owner/repo`"),r.host||(r.host=s),(0,O.default)(r.host&&we(r.host),"host must be a valid HTTPS URL");break}case U.StaticStorage:{(0,O.default)(r.baseUrl&&we(r.baseUrl),"baseUrl must be a valid HTTPS URL");break}}return(0,O.default)(typeof o=="string"&&o.match(/^\d+/),"updateInterval must be a human-friendly string interval like `20 minutes`"),(0,O.default)((0,ue.default)(o)>=5*60*1e3,"updateInterval must be `5 minutes` or more"),(0,O.default)((0,ue.default)(o)<2**31,"updateInterval must fit in a signed 32-bit integer"),(0,O.default)(i&&typeof i.log,"function"),{updateSource:r,updateInterval:o,logger:i,notifyUser:a,onNotifyUser:c}}const ln="auto_update_enabled";function un(){try{const e=f().prepare("SELECT value FROM meta WHERE key = ?").get(ln);return e?e.value==="1":!0}catch{return!0}}function pn(){if(!u.app.isPackaged){console.log("[updater] skipped — dev mode");return}if(!un()){console.log("[updater] skipped — disabled in meta");return}try{Qt({updateSource:{type:Ve.ElectronPublicUpdateService,repo:"tdodd777/Codeling"},updateInterval:"10 minutes",logger:{log:(...e)=>console.log("[updater]",...e),info:(...e)=>console.log("[updater]",...e),warn:(...e)=>console.warn("[updater]",...e),error:(...e)=>console.error("[updater]",...e)},notifyUser:!0})}catch(e){console.error("[updater] init failed",e)}}const se="last_summary_date";function ke(e){const t=new Date(e);return t.setHours(0,0,0,0),t.getTime()}function dn(e){const t=f().prepare("SELECT value FROM meta WHERE key = ?").get(e);return t==null?void 0:t.value}function Le(e,t){f().prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run(e,t)}function fn(){const e=new Date,t=ke(e),n=new Date(e);n.setDate(n.getDate()-1);const s=ke(n),o=f().prepare(`SELECT
73
- COUNT(*) AS sessions,
74
- COALESCE(SUM(message_count), 0) AS messages,
75
- COALESCE(SUM(cost_usd), 0) AS cost
76
- FROM sessions
77
- WHERE last_seen_at >= ? AND last_seen_at < ?`).get(s,t);return{sessions:(o==null?void 0:o.sessions)??0,messages:(o==null?void 0:o.messages)??0,costUsd:(o==null?void 0:o.cost)??0}}function Oe(e){const t=[];if(t.push(`${e.sessions} session${e.sessions===1?"":"s"}`),t.push(`${e.messages} message${e.messages===1?"":"s"}`),e.costUsd>0){const n=e.costUsd>=1?e.costUsd.toFixed(2):e.costUsd.toFixed(4).replace(/0+$/,"").replace(/\.$/,"");t.push(`$${n}`)}return t.join(" · ")}function Je(){const e=Z();if(dn(se)===e)return!1;const t=fn();return t.messages===0&&t.sessions===0?(Le(se,e),!1):(u.Notification.isSupported()&&new u.Notification({title:"Yesterday in Codeling",body:Oe(t),silent:!0}).show(),console.log(`[daily-summary] ${Oe(t)}`),Le(se,e),!0)}const mn=new Set(["message_count","input_tokens","output_tokens","cache_read_tokens","cache_creation_tokens","cost_usd"]);function M(){const e=f().prepare("SELECT * FROM pet WHERE id = 1").get();if(!e)throw new Error("pet row missing — seed did not run");return{species:e.species,name:e.name,level:e.level,xp:e.xp,bits:e.bits,createdAt:e.created_at}}const Qe="home_animation:";function gn(e){const t=f().prepare("SELECT value FROM meta WHERE key = ?").get(`${Qe}${e}`);return(t==null?void 0:t.value)??"idle"}function En(e,t){f().prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run(`${Qe}${e}`,t)}function hn(e){if(!(e in R))return{error:"not-owned"};const t=f();if(!t.prepare("SELECT item_id FROM unlocks WHERE category = 'species' AND item_id = ?").get(`species:${e}`))return{error:"not-owned"};t.prepare("UPDATE pet SET species = ? WHERE id = 1").run(e);const s=M();return{ok:!0,species:e,name:s.name}}function _n(e){if(!Number.isFinite(e)||!Number.isInteger(e))throw new Error("not-integer");if(e<Ge||e>qe)throw new Error("out-of-range");return f().prepare("UPDATE spin_state SET spin_threshold = ? WHERE id = 1").run(e),e}function yn(){const e=f();e.transaction(()=>{e.exec("DELETE FROM otel_events"),e.exec("DELETE FROM achievements"),e.exec("DELETE FROM daily_activity"),e.exec("DELETE FROM meta"),e.exec("DELETE FROM unlocks"),e.exec("DELETE FROM sessions"),e.exec("DELETE FROM spin_state"),e.exec("DELETE FROM pet"),ge(e)})()}function bn(e){const t=e.trim();if(t.length===0)throw new Error("empty-name");if(t.length>Ot)throw new Error("name-too-long");return f().prepare("UPDATE pet SET name = ? WHERE id = 1").run(t),t}function Tn(){const e=f().prepare("SELECT * FROM spin_state WHERE id = 1").get();if(!e)throw new Error("spin_state row missing — seed did not run");return{spinsAvailable:e.spins_available,messagesSinceLastSpin:e.messages_since_last_spin,spinThreshold:e.spin_threshold}}function Sn(){const e=f().prepare(`SELECT
78
- COALESCE(SUM(message_count), 0) AS total_messages,
79
- COALESCE(SUM(input_tokens), 0) AS total_input,
80
- COALESCE(SUM(output_tokens), 0) AS total_output,
81
- COALESCE(SUM(cache_read_tokens), 0) AS total_cache_read,
82
- COALESCE(SUM(cache_creation_tokens), 0) AS total_cache_create,
83
- COALESCE(SUM(cost_usd), 0) AS total_cost,
84
- COUNT(*) AS session_count
85
- FROM sessions`).get();return{totalMessages:(e==null?void 0:e.total_messages)??0,totalInputTokens:(e==null?void 0:e.total_input)??0,totalOutputTokens:(e==null?void 0:e.total_output)??0,totalCacheReadTokens:(e==null?void 0:e.total_cache_read)??0,totalCacheCreationTokens:(e==null?void 0:e.total_cache_create)??0,totalCostUsd:(e==null?void 0:e.total_cost)??0,sessionCount:(e==null?void 0:e.session_count)??0}}function vn(){return f().prepare("SELECT item_id, category, acquired_via, acquired_at FROM unlocks ORDER BY acquired_at DESC").all().map(t=>{let n=t.item_id,s="common";if(t.category==="species"&&t.item_id.startsWith("species:")){const o=t.item_id.slice(8),i=R[o];i&&(n=i.label,s=i.tier)}return{itemId:t.item_id,category:t.category,acquiredVia:t.acquired_via,acquiredAt:t.acquired_at,label:n,tier:s}})}function wn(){const e=f().prepare("SELECT id, earned_at FROM achievements").all(),t=new Map(e.map(n=>[n.id,n.earned_at]));return ze.map(n=>{const s=t.get(n.id);return{id:n.id,label:n.label,description:n.description,tier:n.tier,earned:s!==void 0,earnedAt:s}})}function Rn(e,t,n){f().prepare("INSERT INTO otel_events (signal_type, transport, received_at, payload) VALUES (?, ?, ?, ?)").run(e,t,Date.now(),JSON.stringify(n))}function kn(e){if(e.length===0)return;const t=f(),n=t.prepare("INSERT OR IGNORE INTO sessions (session_id, started_at, last_seen_at) VALUES (?, ?, ?)"),s=t.prepare("UPDATE sessions SET last_seen_at = MAX(last_seen_at, ?) WHERE session_id = ?"),o=new Map;function i(c){let r=o.get(c);return r||(r=t.prepare(`UPDATE sessions
86
- SET ${c} = ${c} + ?,
87
- last_seen_at = MAX(last_seen_at, ?)
88
- WHERE session_id = ?`),o.set(c,r)),r}t.transaction(c=>{for(const r of c)if(n.run(r.sessionId,r.timestampMs,r.timestampMs),r.field&&r.delta&&r.delta!==0){if(!mn.has(r.field))continue;i(r.field).run(r.delta,r.timestampMs,r.sessionId)}else s.run(r.timestampMs,r.sessionId)})(e)}const Ze=["xpPerMessage","xpPerOutputTokens","bitsPerMessage","bitsPerOutputTokens"],Ln={xpPerMessage:10,xpPerOutputTokens:100,bitsPerMessage:5,bitsPerOutputTokens:1e3},pe={xpPerMessage:{min:0,max:1e3},xpPerOutputTokens:{min:1,max:1e6},bitsPerMessage:{min:0,max:1e3},bitsPerOutputTokens:{min:1,max:1e6}},Q={xpForLevel:e=>e*100};function et(e){return`economy:${e}`}function ee(){const t=f().prepare("SELECT key, value FROM meta WHERE key LIKE 'economy:%'").all(),n=new Map(t.map(o=>[o.key,o.value])),s={};for(const o of Ze){const i=n.get(et(o)),a=i!==void 0?Number(i):NaN;s[o]=Number.isFinite(a)?a:Ln[o]}return s}function On(e,t){if(!Ze.includes(e))throw new Error("unknown-key");if(!Number.isInteger(t))throw new Error("not-integer");const{min:n,max:s}=pe[e];if(t<n||t>s)throw new Error("out-of-range");return f().prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run(et(e),String(t)),ee()}function An(){return f().prepare("DELETE FROM meta WHERE key LIKE 'economy:%'").run(),ee()}function tt(e){const t={xpGained:0,bitsGained:0,levelsGained:0,spinsGranted:0,changed:!1};if(e.messages<=0&&e.outputTokens<=0)return t;const n=ee(),s=e.messages*n.xpPerMessage+Math.floor(e.outputTokens/n.xpPerOutputTokens);let o=e.messages*n.bitsPerMessage+Math.floor(e.outputTokens/n.bitsPerOutputTokens);if(s===0&&o===0&&e.messages===0)return t;const i=f();return i.transaction(()=>{!!i.prepare("SELECT item_id FROM unlocks WHERE category = 'upgrade' AND item_id = 'bit_multiplier_2x' LIMIT 1").get()&&(o*=2);const r=i.prepare("SELECT level, xp, bits FROM pet WHERE id = 1").get();if(!r)throw new Error("pet row missing");let l=r.level,p=r.xp+s,d=0;for(;p>=Q.xpForLevel(l)&&(p-=Q.xpForLevel(l),l+=1,d+=1,!(d>100)););const g=r.bits+o;if(i.prepare("UPDATE pet SET level = ?, xp = ?, bits = ? WHERE id = 1").run(l,p,g),t.xpGained=s,t.bitsGained=o,t.levelsGained=d,e.messages>0){const b=i.prepare("SELECT spins_available, messages_since_last_spin, spin_threshold FROM spin_state WHERE id = 1").get();if(!b)throw new Error("spin_state row missing");let m=b.messages_since_last_spin+e.messages,y=0;for(;m>=b.spin_threshold&&(m-=b.spin_threshold,y+=1,!(y>100)););i.prepare("UPDATE spin_state SET spins_available = spins_available + ?, messages_since_last_spin = ? WHERE id = 1").run(y,m),t.spinsGranted=y}t.changed=!0})(),t}let oe=!1;const Nn="codeling:update";function S(){oe||(oe=!0,setImmediate(()=>{oe=!1;for(const e of u.BrowserWindow.getAllWindows())e.isDestroyed()||e.webContents.send(Nn)}))}let re=null;function nt(){return u.app.isPackaged?E.join(process.resourcesPath,"proto"):E.join(u.app.getAppPath(),"proto")}async function Mn(){if(re)return re;const e=nt(),t=new kt.Root;return t.resolvePath=(n,s)=>E.isAbsolute(s)?s:s.startsWith("opentelemetry/")?E.join(e,s):s,await t.load(["opentelemetry/proto/collector/trace/v1/trace_service.proto","opentelemetry/proto/collector/metrics/v1/metrics_service.proto","opentelemetry/proto/collector/logs/v1/logs_service.proto"].map(n=>E.join(e,n)),{keepCase:!0}),re=t,t}async function In(){const e=await Mn(),t=e.lookupType("opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest"),n=e.lookupType("opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest"),s=e.lookupType("opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest"),o={longs:Number,enums:String,bytes:String,defaults:!1};return{decodeTraces:i=>t.toObject(t.decode(i),o),decodeMetrics:i=>n.toObject(n.decode(i),o),decodeLogs:i=>s.toObject(s.decode(i),o)}}function Un(){return nt()}function _(e,...t){if(!e||typeof e!="object")return;const n=e;for(const s of t){const o=n[s];if(o!=null)return o}}function Cn(e){if(!e)return;const t=_(e,"string_value","stringValue");if(t!==void 0)return t;const n=_(e,"int_value","intValue");if(n!==void 0)return Number(n);const s=_(e,"double_value","doubleValue");if(s!==void 0)return s;const o=_(e,"bool_value","boolValue");if(o!==void 0)return o}function q(e,t){if(Array.isArray(e)){for(const n of e)if((n==null?void 0:n.key)===t)return Cn(n.value)}}function Dn(e){const t=_(e,"as_int","asInt");if(t!==void 0)return Number(t);const n=_(e,"as_double","asDouble");return n!==void 0?n:0}function Ee(e){if(e==null)return Date.now();const t=Number(e);return!Number.isFinite(t)||t<=0?Date.now():Math.round(t/1e6)}const xn={input:"input_tokens",output:"output_tokens",cacheRead:"cache_read_tokens",cacheCreation:"cache_creation_tokens"};function $n(e){const t=[],n=_(e,"resource_metrics","resourceMetrics")??[];for(const s of n){const o=_(s,"scope_metrics","scopeMetrics")??[];for(const i of o){const a=_(i,"metrics")??[];for(const c of a){const r=_(c,"name");if(r!=="claude_code.token.usage"&&r!=="claude_code.cost.usage")continue;const l=_(c,"sum");if(!l)continue;if(Number(_(l,"aggregation_temporality","aggregationTemporality")??0)===2){console.warn(`[otel] skipping ${r}: CUMULATIVE temporality not supported (DELTA expected)`);continue}const d=_(l,"data_points","dataPoints")??[];for(const g of d){const b=g,m=_(b,"attributes"),y=String(q(m,"session.id")??"");if(!y)continue;let v;if(r==="claude_code.token.usage"){const ht=String(q(m,"type")??"");v=xn[ht]}else v="cost_usd";if(!v)continue;const A=Dn(b);if(A<=0)continue;const Et=Ee(_(b,"time_unix_nano","timeUnixNano"));t.push({sessionId:y,timestampMs:Et,field:v,delta:A})}}}}return t}function Pn(e){const t=[],n=_(e,"resource_logs","resourceLogs")??[];for(const s of n){const o=_(s,"scope_logs","scopeLogs")??[];for(const i of o){const a=_(i,"log_records","logRecords")??[];for(const c of a){const r=c,l=_(r,"attributes"),p=String(q(l,"session.id")??"");if(!p)continue;const d=Ee(_(r,"time_unix_nano","timeUnixNano"));String(q(l,"event.name")??"")==="user_prompt"?t.push({sessionId:p,timestampMs:d,field:"message_count",delta:1}):t.push({sessionId:p,timestampMs:d})}}}return t}function Fn(e){const t=[],n=_(e,"resource_spans","resourceSpans")??[];for(const s of n){const o=_(s,"scope_spans","scopeSpans")??[];for(const i of o){const a=_(i,"spans")??[];for(const c of a){const r=c,l=_(r,"attributes"),p=String(q(l,"session.id")??"");if(!p)continue;const d=Ee(_(r,"end_time_unix_nano","endTimeUnixNano"));t.push({sessionId:p,timestampMs:d})}}}return t}function x(e,t,n){Rn(e,t,n);let s=[];switch(e){case"metric":s=$n(n);break;case"log":s=Pn(n);break;case"trace":s=Fn(n);break}kn(s);const o=jn(s);o.message_count>0&&(Ye(),Je());const i=tt({messages:o.message_count,outputTokens:o.output_tokens});(i.changed||s.length>0)&&X();const a=Bn(e,n),c=Hn(o),r=i.changed?` | +xp=${i.xpGained} +bits=${i.bitsGained}`+(i.levelsGained?` +levels=${i.levelsGained}`:"")+(i.spinsGranted?` +spins=${i.spinsGranted}`:""):"";console.log(`[otel:${t}] ${e} ${a}${c}${r}`),s.length>0&&S()}function jn(e){const t=new Set,n={sessions:0,message_count:0,input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_creation_tokens:0,cost_usd:0};for(const s of e)t.add(s.sessionId),s.field&&s.delta&&(n[s.field]+=s.delta);return n.sessions=t.size,n}function Hn(e){if(e.sessions===0)return"";const t=[`sessions=${e.sessions}`];for(const[n,s]of Object.entries(e)){if(n==="sessions"||s===0)continue;const o=n==="cost_usd"?`$${s.toFixed(4)}`:s;t.push(`+${n}=${o}`)}return` → ${t.join(" ")}`}function Bn(e,t){if(!t||typeof t!="object")return"";const n=t;switch(e){case"trace":{const s=n.resourceSpans??n.resource_spans,o=ie(s,"scopeSpans","scope_spans","spans");return`resourceSpans=${(s==null?void 0:s.length)??0} spans=${o}`}case"metric":{const s=n.resourceMetrics??n.resource_metrics,o=ie(s,"scopeMetrics","scope_metrics","metrics");return`resourceMetrics=${(s==null?void 0:s.length)??0} metrics=${o}`}case"log":{const s=n.resourceLogs??n.resource_logs,o=ie(s,"scopeLogs","scope_logs","logRecords","log_records");return`resourceLogs=${(s==null?void 0:s.length)??0} records=${o}`}}}function ie(e,t,n,...s){if(!e)return 0;let o=0;for(const i of e){if(!i||typeof i!="object")continue;const a=i,c=a[t]??a[n];if(c)for(const r of c){if(!r||typeof r!="object")continue;const l=r;for(const p of s){const d=l[p];Array.isArray(d)&&(o+=d.length)}}}return o}const Wn=4317,Ae="127.0.0.1",Gn=["opentelemetry/proto/collector/trace/v1/trace_service.proto","opentelemetry/proto/collector/metrics/v1/metrics_service.proto","opentelemetry/proto/collector/logs/v1/logs_service.proto"];async function qn(){const e=Un(),t=Lt.loadSync(Gn.map(o=>E.join(e,o)),{keepCase:!0,longs:Number,enums:String,bytes:String,defaults:!1,oneofs:!0,includeDirs:[e]}),n=ne.loadPackageDefinition(t),s=new ne.Server;return s.addService(n.opentelemetry.proto.collector.trace.v1.TraceService.service,{Export:(o,i)=>{try{x("trace","grpc",o.request),i(null,{partialSuccess:{}})}catch(a){i(a)}}}),s.addService(n.opentelemetry.proto.collector.metrics.v1.MetricsService.service,{Export:(o,i)=>{try{x("metric","grpc",o.request),i(null,{partialSuccess:{}})}catch(a){i(a)}}}),s.addService(n.opentelemetry.proto.collector.logs.v1.LogsService.service,{Export:(o,i)=>{try{x("log","grpc",o.request),i(null,{partialSuccess:{}})}catch(a){i(a)}}}),new Promise((o,i)=>{s.bindAsync(`${Ae}:${Wn}`,ne.ServerCredentials.createInsecure(),(a,c)=>{if(a)return i(a);console.log(`[otel:grpc] listening on ${Ae}:${c}`),o(s)})})}function Yn(e){const t=typeof e.session_id=="string"?e.session_id:"";if(!t)return{error:"no-session-id"};const n=f(),s=Date.now();let o=0;return n.transaction(()=>{n.prepare("INSERT OR IGNORE INTO sessions (session_id, started_at, last_seen_at) VALUES (?, ?, ?)").run(t,s,s),n.prepare(`UPDATE sessions
89
- SET stop_event_count = stop_event_count + 1,
90
- last_seen_at = MAX(last_seen_at, ?)
91
- WHERE session_id = ?`).run(s,t);const a=n.prepare("SELECT message_count, stop_event_count FROM sessions WHERE session_id = ?").get(t);a&&a.stop_event_count>a.message_count&&(o=a.stop_event_count-a.message_count,n.prepare("UPDATE sessions SET message_count = ? WHERE session_id = ?").run(a.stop_event_count,t))})(),o>0&&(Ye(),tt({messages:o,outputTokens:0}),X(),S()),{ok:!0,messagesAdded:o}}const Ne=4318,Me="127.0.0.1";async function Xn(){const e=await In(),t=te();return t.use(te.raw({type:"application/x-protobuf",limit:"10mb"})),t.use(te.json({type:"application/json",limit:"10mb"})),t.post("/v1/traces",(n,s)=>{try{const o=Buffer.isBuffer(n.body)?e.decodeTraces(n.body):n.body;x("trace","http",o),s.set("Content-Type","application/x-protobuf").status(200).send(Buffer.alloc(0))}catch(o){console.error("[otel:http] trace decode failed",o),s.status(400).end()}}),t.post("/v1/metrics",(n,s)=>{try{const o=Buffer.isBuffer(n.body)?e.decodeMetrics(n.body):n.body;x("metric","http",o),s.set("Content-Type","application/x-protobuf").status(200).send(Buffer.alloc(0))}catch(o){console.error("[otel:http] metric decode failed",o),s.status(400).end()}}),t.post("/v1/logs",(n,s)=>{try{const o=Buffer.isBuffer(n.body)?e.decodeLogs(n.body):n.body;x("log","http",o),s.set("Content-Type","application/x-protobuf").status(200).send(Buffer.alloc(0))}catch(o){console.error("[otel:http] log decode failed",o),s.status(400).end()}}),t.post("/codeling/stop-hook",(n,s)=>{try{const o=typeof n.body=="object"&&n.body!==null?n.body:{},i=Yn(o);if("error"in i){s.status(400).json(i);return}const a=typeof o.session_id=="string"?o.session_id:"?";console.log(`[stop-hook] sid=${a.slice(0,8)} +msg=${i.messagesAdded}`),s.status(204).end()}catch(o){console.error("[stop-hook] failed",o),s.status(500).end()}}),new Promise((n,s)=>{const o=t.listen(Ne,Me,()=>{console.log(`[otel:http] listening on http://${Me}:${Ne}`),n(o)});o.on("error",s)})}const st="telemetry_enabled";let W=null,G=null,ae=!1,ce=!1;function ot(){const e=f().prepare("SELECT value FROM meta WHERE key = ?").get(st);return e?e.value==="1":!0}function zn(e){f().prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run(st,e?"1":"0")}function he(){return!!W||!!G}async function rt(){if(!(he()||ae)){ae=!0;try{W=await Xn()}catch(e){console.error("[otel:http] failed to start",e)}try{G=await qn()}catch(e){console.error("[otel:grpc] failed to start",e)}ae=!1}}async function Vn(){if(!ce){if(ce=!0,W){const e=W;W=null,await new Promise(t=>{let n=!1;const s=()=>{n||(n=!0,t())};e.close(()=>s());const o=setTimeout(s,1e3);typeof o.unref=="function"&&o.unref()}),console.log("[otel:http] stopped")}if(G){const e=G;G=null,await new Promise(t=>{let n=!1;const s=()=>{n||(n=!0,t())};e.tryShutdown(()=>s());const o=setTimeout(()=>{try{e.forceShutdown()}catch{}s()},1e3);typeof o.unref=="function"&&o.unref()}),console.log("[otel:grpc] stopped")}ce=!1}}async function Kn(e){return zn(e),e?await rt():await Vn(),he()}const it="popout:bounds",D={x:100,y:100,width:480,height:720};let B=null,h=null;function Jn(e){B=e;const t=e.mb.tray;t&&(t.removeAllListeners("click"),t.on("click",()=>{var n;if(de()&&h&&!h.isDestroyed()){h.isMinimized()&&h.restore(),h.show(),h.focus();return}(n=e.mb.window)!=null&&n.isVisible()?e.mb.hideWindow():e.mb.showWindow()})),e.mb.on("after-show",()=>{de()&&(e.mb.hideWindow(),h&&!h.isDestroyed()&&(h.show(),h.focus()))})}function de(){return!!h&&!h.isDestroyed()}function Qn(){if(!B)return{error:"not-initialized"};if(B.mb.hideWindow(),h&&!h.isDestroyed())return h.isMinimized()&&h.restore(),h.show(),h.focus(),S(),{ok:!0};const e=es();return h=new u.BrowserWindow({x:e.x,y:e.y,width:e.width,height:e.height,minWidth:380,minHeight:480,title:"Codeling",autoHideMenuBar:!0,webPreferences:{preload:B.preloadPath,contextIsolation:!0,nodeIntegration:!1,sandbox:!1}}),h.loadURL(B.rendererUrl),h.on("close",()=>{h&&!h.isDestroyed()&&ts(h.getBounds())}),h.on("closed",()=>{h=null,S()}),S(),{ok:!0}}function Zn(){h&&!h.isDestroyed()&&h.close()}function es(){const e=f().prepare("SELECT value FROM meta WHERE key = ?").get(it);if(!e)return D;try{const t=JSON.parse(e.value);return{x:Number.isFinite(t.x)?t.x:D.x,y:Number.isFinite(t.y)?t.y:D.y,width:Number.isFinite(t.width)&&t.width>=380?t.width:D.width,height:Number.isFinite(t.height)&&t.height>=480?t.height:D.height}}catch{return D}}function ts(e){f().prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run(it,JSON.stringify(e))}const fe=1,Ie=["pet","sessions","unlocks","spin_state","achievements","daily_activity","meta"];function I(e){return f().prepare(`SELECT * FROM ${e}`).all()}function ns(){return{version:fe,exportedAt:Date.now(),pet:I("pet"),sessions:I("sessions"),unlocks:I("unlocks"),spin_state:I("spin_state"),achievements:I("achievements"),daily_activity:I("daily_activity"),meta:I("meta")}}async function ss(){const e=`codeling-save-${new Date().toISOString().slice(0,10)}.json`,t=await u.dialog.showSaveDialog({title:"Export Codeling save",defaultPath:e,filters:[{name:"Codeling save",extensions:["json"]}]});if(t.canceled||!t.filePath)return{error:"cancelled"};try{const n=JSON.stringify(ns(),null,2);return await Be.writeFile(t.filePath,n,"utf8"),{ok:!0,path:t.filePath}}catch(n){return{error:"write-failed",detail:n.message}}}function os(e){const t=f();t.transaction(()=>{t.exec("DELETE FROM otel_events");for(const s of Ie)t.exec(`DELETE FROM ${s}`);for(const s of Ie){const o=e[s];if(!Array.isArray(o)||o.length===0)continue;const i=t.pragma(`table_info(${s})`).map(c=>c.name),a=new Set(i);for(const c of o){const r=Object.keys(c).filter(d=>a.has(d));if(r.length===0)continue;const l=r.map(()=>"?").join(", ");t.prepare(`INSERT OR REPLACE INTO ${s} (${r.join(", ")}) VALUES (${l})`).run(...r.map(d=>c[d]))}}ge(t)})()}async function rs(){const e=await u.dialog.showOpenDialog({title:"Import Codeling save",filters:[{name:"Codeling save",extensions:["json"]}],properties:["openFile"]});if(e.canceled||e.filePaths.length===0)return{error:"cancelled"};const t=e.filePaths[0];let n;try{n=await Be.readFile(t,"utf8")}catch(i){return{error:"read-failed",detail:i.message}}let s;try{s=JSON.parse(n)}catch(i){return{error:"invalid-format",detail:i.message}}if(!s||typeof s!="object")return{error:"invalid-format",detail:"not an object"};const o=s;return typeof o.version!="number"?{error:"invalid-format",detail:"missing version"}:o.version>fe?{error:"unsupported-version",detail:`save v${o.version} > app v${fe}`}:(os(o),{ok:!0,path:t})}const le={n:"north",north:"north",ne:"northeast",northeast:"northeast","north-east":"northeast",e:"east",east:"east",se:"southeast",southeast:"southeast","south-east":"southeast",s:"south",south:"south",sw:"southwest",southwest:"southwest","south-west":"southwest",w:"west",west:"west",nw:"northwest",northwest:"northwest","north-west":"northwest"};function is(e){const t=e.replace(/-[a-f0-9]{6,}$/i,"").toLowerCase(),n=new Set([t]);return/idle|breath/.test(t)&&n.add("idle"),/run/.test(t)&&n.add("run"),/walk/.test(t)&&n.add("walk"),/attack|cast|fight|hit/.test(t)&&n.add("attack"),[...n]}function me(e){const t=e.replace(/-[a-f0-9]{6,}$/i,"").toLowerCase();return/idle|breath/.test(t)?"idle":/^run/.test(t)?"run":/^walk/.test(t)?"walk":/^(attack|cast|fight|hit)$/.test(t)?"attack":t}function at(){return u.app.isPackaged?E.join(process.resourcesPath,"assets","sprites"):E.join(u.app.getAppPath(),"assets","sprites")}function ct(e){return{dir:E.join(at(),e),urlSegments:["./sprites",e]}}function H(e,...t){return[...e,...t].join("/")}function N(e){try{return T.statSync(e).isDirectory()?T.readdirSync(e):[]}catch{return[]}}function K(e){const t=e.match(/_(\d+)\.png$/i);return t&&t[1]?parseInt(t[1],10):-1}function Ue(e){let t=0;for(const n of Object.values(e))t+=(n==null?void 0:n.length)??0;return t}function Ce(e,t){return e?Ue(t)>Ue(e):!0}function as(e){const t=E.join(at(),e,"background.png");try{if(T.statSync(t).isFile())return["./sprites",e,"background.png"].join("/")}catch{}}function lt(e){const{dir:t}=ct(e),n=new Set;for(const s of N(E.join(t,"animations"))){const o=E.join(t,"animations",s);try{if(!T.statSync(o).isDirectory())continue}catch{continue}n.add(me(s))}if(!T.existsSync(E.join(t,"animations"))){for(const s of N(t))if(!(s==="rotations"||s==="metadata.json"))try{T.statSync(E.join(t,s)).isDirectory()&&n.add(me(s))}catch{}}return[...n]}function cs(e,t){var r;const{dir:n,urlSegments:s}=ct(e),o={static:H(s,"rotations","south.png"),animations:{}},i=as(e);if(i&&(o.background=i),!T.existsSync(n))return console.warn(`[sprites] no sprite directory for ${e} at ${n}`),o;const a=l=>{if(!t)return!0;const p=me(l);return p==="idle"?!0:t.has(p)};for(const l of N(E.join(n,"rotations"))){if(!l.toLowerCase().endsWith(".png"))continue;const p=l.replace(/\.png$/i,"").toLowerCase(),d=le[p];if(!d)continue;d==="south"&&(o.static=H(s,"rotations",l));const g=(r=o.animations).static??(r.static={});g[d]=[H(s,"rotations",l)]}for(const l of N(E.join(n,"animations"))){const p=E.join(n,"animations",l);if(!T.statSync(p).isDirectory()||!a(l))continue;const d={};for(const g of N(p)){const b=E.join(p,g);if(!T.statSync(b).isDirectory())continue;const m=le[g.toLowerCase()];if(!m)continue;const y=N(b).filter(v=>v.toLowerCase().endsWith(".png")).sort((v,A)=>K(v)-K(A)).map(v=>H(s,"animations",l,g,v));y.length>0&&(d[m]=y)}if(Object.keys(d).length>0)for(const g of is(l))Ce(o.animations[g],d)&&(o.animations[g]=d)}for(const l of N(n)){if(l==="rotations"||l==="animations"||l==="metadata.json")continue;const p=E.join(n,l);if(!T.statSync(p).isDirectory()||!a(l))continue;const d={};for(const g of N(p)){if(!g.toLowerCase().endsWith(".png"))continue;const b=g.replace(/\.png$/i,"").match(/^([a-z-]+?)(?:_(\d+))?$/i);if(!b||!b[1])continue;const m=le[b[1].toLowerCase()];m&&(d[m]??(d[m]=[])).push(H(s,l,g))}for(const g of Object.keys(d))d[g].sort((b,m)=>K(b)-K(m));if(Object.keys(d).length>0){const g=l.toLowerCase();Ce(o.animations[g],d)&&(o.animations[g]=d)}}const c=Object.entries(o.animations).map(([l,p])=>`${l}(${Object.entries(p).map(([d,g])=>`${d}=${g.length}`).join(",")})`).join(" ");return console.log(`[sprites] ${e}: ${c||"(only static fallback)"}`),o}const Y="anim:";function ls(e,t){return`${Y}${e}:${t}`}function _e(e){if(!e.startsWith(Y))return null;const t=e.slice(Y.length),n=t.indexOf(":");if(n<0)return null;const s=t.slice(0,n),o=t.slice(n+1);return!(s in R)||o.length===0?null:{species:s,name:o}}function ut(e){return e==="idle"?0:e==="walk"||e==="run"?50:e==="attack"?150:300}function pt(e){return e==="idle"?1:e==="walk"||e==="run"?5:e==="attack"?15:25}function us(e,t){return lt(e).map(n=>({id:ls(e,n),species:e,name:n,priceBits:ut(n),levelRequired:pt(n),owned:n==="idle"||t.has(n)})).sort(ps)}function ps(e,t){return e.name==="idle"&&t.name!=="idle"?-1:t.name==="idle"&&e.name!=="idle"?1:e.priceBits!==t.priceBits?e.priceBits-t.priceBits:e.name.localeCompare(t.name)}function ds(){const e=f(),t=e.prepare("SELECT item_id FROM unlocks WHERE category = 'species'").all().map(o=>o.item_id.slice(8)).filter(o=>o in R),n=new Map;for(const o of e.prepare("SELECT item_id FROM unlocks WHERE category = 'animation'").all()){const i=_e(o.item_id);if(!i)continue;let a=n.get(i.species);a||(a=new Set,n.set(i.species,a)),a.add(i.name)}const s={};for(const o of t)s[o]=us(o,n.get(o)??new Set);return s}function De(e){const t=f().prepare("SELECT item_id FROM unlocks WHERE category = 'animation' AND item_id LIKE ?").all(`${Y}${e}:%`),n=new Set;for(const s of t){const o=_e(s.item_id);o&&n.add(o.name)}return n}function fs(e){const t=_e(e);if(!t||!f().prepare("SELECT item_id FROM unlocks WHERE category = 'species' AND item_id = ?").get(`species:${t.species}`)||!lt(t.species).includes(t.name))return null;const s=ut(t.name);let o="common";return s>=300?o="rare":s>=150?o="uncommon":s>0&&(o="common"),{id:e,kind:"animation",species:t.species,name:t.name,priceBits:s,levelRequired:pt(t.name),label:ms(t.species,t.name),tier:o}}function ms(e,t){const n=R[e].label,s=t.charAt(0).toUpperCase()+t.slice(1);return`${n} — ${s}`}const gs=Object.entries(R).map(([e,t])=>({id:`species:${e}`,kind:"species",tier:t.tier,priceBits:t.priceBits,label:t.label,species:e})),Es=[{id:"bit_multiplier_2x",kind:"upgrade",tier:"rare",priceBits:500,label:"2× Bits",description:"Doubles bits earned from messages and tokens. Permanent.",effect:"bit_multiplier_2x"}],dt=[...gs,...Es];function hs(e){return dt.find(t=>t.id===e)}function _s(e){return e.startsWith(Y)?fs(e):hs(e)??null}function ys(e){const t=_s(e);if(!t)return{error:"unknown-item"};const n=f();let s={error:"unknown-item"};return n.transaction(()=>{if(n.prepare("SELECT item_id FROM unlocks WHERE item_id = ?").get(t.id)){s={error:"already-owned"};return}const a=n.prepare("SELECT level, bits FROM pet WHERE id = 1").get();if(!a)throw new Error("pet row missing");if(t.kind==="animation"&&a.level<t.levelRequired){s={error:"level-locked",required:t.levelRequired,current:a.level};return}if(a.bits<t.priceBits){s={error:"insufficient",bits:a.bits,price:t.priceBits};return}n.prepare("UPDATE pet SET bits = bits - ? WHERE id = 1").run(t.priceBits),n.prepare("INSERT INTO unlocks (item_id, category, acquired_via, acquired_at) VALUES (?, ?, 'shop', ?)").run(t.id,t.kind,Date.now()),s={ok:!0,itemId:t.id,category:t.kind,bitsRemaining:a.bits-t.priceBits,pricePaid:t.priceBits}})(),"ok"in s&&X(),s}const J=[{id:"bits_small",kind:"bits",tier:"common",weight:40,amount:25,label:"+25 bits"},{id:"bits_medium",kind:"bits",tier:"uncommon",weight:18,amount:75,label:"+75 bits"},{id:"bits_large",kind:"bits",tier:"rare",weight:6,amount:200,label:"+200 bits"},{id:"bits_jackpot",kind:"bits",tier:"legendary",weight:1,amount:1e3,label:"+1000 bits"},{id:"xp_small",kind:"xp",tier:"uncommon",weight:12,amount:50,label:"+50 XP"},{id:"xp_large",kind:"xp",tier:"rare",weight:3,amount:200,label:"+200 XP"},{id:"species_token",kind:"species_token",tier:"legendary",weight:1,label:"New Species!"}],xe=500;function bs(e=Math.random){const t=J.reduce((s,o)=>s+o.weight,0);let n=e()*t;for(const s of J)if(n-=s.weight,n<0)return s;return J[J.length-1]}function Ts(e){const t=f();let n={error:"no-spins"};return t.transaction(()=>{const o=t.prepare("SELECT spins_available FROM spin_state WHERE id = 1").get();if(!o||o.spins_available<=0){n={error:"no-spins"};return}t.prepare("UPDATE spin_state SET spins_available = spins_available - 1 WHERE id = 1").run();const i=o.spins_available-1,a=bs(e),c={id:a.id,kind:a.kind,tier:a.tier,label:a.label};if(a.kind==="bits"){t.prepare("UPDATE pet SET bits = bits + ? WHERE id = 1").run(a.amount),n={reward:c,applied:{kind:"bits",amount:a.amount},spinsRemaining:i};return}if(a.kind==="xp"){const m=t.prepare("SELECT level, xp FROM pet WHERE id = 1").get();if(!m)throw new Error("pet row missing");let y=m.level,v=m.xp+a.amount,A=0;for(;v>=Q.xpForLevel(y)&&(v-=Q.xpForLevel(y),y+=1,A+=1,!(A>100)););t.prepare("UPDATE pet SET level = ?, xp = ? WHERE id = 1").run(y,v),n={reward:c,applied:{kind:"xp",amount:a.amount,levelsGained:A},spinsRemaining:i};return}const r=t.prepare("SELECT item_id FROM unlocks WHERE category = 'species'").all(),l=new Set(r.map(m=>m.item_id.slice(8))),p=Object.keys(R).filter(m=>!l.has(m));if(p.length===0){t.prepare("UPDATE pet SET bits = bits + ? WHERE id = 1").run(xe),n={reward:c,applied:{kind:"bits",amount:xe,consolationFor:"species_token"},spinsRemaining:i};return}const d=Math.random,g=Math.min(p.length-1,Math.floor(d()*p.length)),b=p[g];t.prepare("INSERT OR IGNORE INTO unlocks (item_id, category, acquired_via, acquired_at) VALUES (?, 'species', 'spin', ?)").run(`species:${b}`,Date.now()),n={reward:c,applied:{kind:"species",species:b},spinsRemaining:i}})(),"error"in n||X(),n}function Ss(){u.ipcMain.handle("codeling:getPet",()=>M()),u.ipcMain.handle("codeling:getSpinState",()=>Tn()),u.ipcMain.handle("codeling:getStats",()=>Sn()),u.ipcMain.handle("codeling:getSprites",(e,t)=>{const n=De(t);return cs(t,n)}),u.ipcMain.handle("codeling:getAnimationsCatalog",()=>ds()),u.ipcMain.handle("codeling:getHomeAnimation",(e,t)=>t in R?gn(t):"idle"),u.ipcMain.handle("codeling:setHomeAnimation",(e,t,n)=>t in R?typeof n!="string"||n.length===0?{error:"not-owned"}:n!=="idle"&&!De(t).has(n)?{error:"not-owned"}:(En(t,n),S(),{ok:!0,name:n}):{error:"not-owned"}),u.ipcMain.handle("codeling:getUnlocks",()=>vn()),u.ipcMain.handle("codeling:getShopItems",()=>{const e=new Set(f().prepare("SELECT item_id FROM unlocks").all().map(t=>t.item_id));return dt.filter(t=>!e.has(t.id)).map(t=>({id:t.id,kind:t.kind,priceBits:t.priceBits,label:t.label,description:t.description,tier:t.tier}))}),u.ipcMain.handle("codeling:spin",()=>{const e=Ts();return"error"in e||S(),e}),u.ipcMain.handle("codeling:purchase",(e,t)=>{const n=ys(t);return"ok"in n&&S(),n}),u.ipcMain.handle("codeling:setSpinThreshold",(e,t)=>{const n=typeof t=="number"?t:Number(t);try{const s=_n(n);return S(),{ok:!0,value:s}}catch(s){const o=s.message;if(o==="not-integer"||o==="out-of-range")return{error:o,min:Ge,max:qe};throw s}}),u.ipcMain.handle("codeling:getEconomyRules",()=>({rules:ee(),bounds:pe})),u.ipcMain.handle("codeling:setEconomyRule",(e,t,n)=>{const s=typeof n=="number"?n:Number(n);try{const o=On(t,s);return S(),{ok:!0,rules:o}}catch(o){const i=o.message;if(i==="unknown-key"||i==="not-integer"||i==="out-of-range")return{error:i,bounds:pe[t]};throw o}}),u.ipcMain.handle("codeling:resetEconomyRules",()=>{const e=An();return S(),{rules:e}}),u.ipcMain.handle("codeling:setActiveSpecies",(e,t)=>{const n=String(t);if(!(n in R))return{error:"not-owned"};const s=hn(n);return"ok"in s&&(L.emit("pet:species-changed",{species:n}),S()),s}),u.ipcMain.handle("codeling:resetSave",()=>{yn(),L.emit("pet:reset");try{L.emit("pet:renamed",{name:M().name})}catch{}return S(),{ok:!0}}),u.ipcMain.handle("codeling:getReceiverInfo",()=>({http:"http://127.0.0.1:4318",grpc:"http://127.0.0.1:4317"})),u.ipcMain.handle("codeling:getTelemetryEnabled",()=>({enabled:ot(),running:he()})),u.ipcMain.handle("codeling:setTelemetryEnabled",async(e,t)=>{const n=await Kn(!!t);return S(),{enabled:!!t,running:n}}),u.ipcMain.handle("codeling:getAchievements",()=>wn()),u.ipcMain.handle("codeling:getStreak",()=>Xe()),u.ipcMain.handle("codeling:getAutoLaunch",()=>u.app.getLoginItemSettings().openAtLogin),u.ipcMain.handle("codeling:setAutoLaunch",(e,t)=>(u.app.setLoginItemSettings({openAtLogin:!!t}),u.app.getLoginItemSettings().openAtLogin)),u.ipcMain.handle("codeling:openPopout",()=>Qn()),u.ipcMain.handle("codeling:closePopout",()=>(Zn(),{ok:!0})),u.ipcMain.handle("codeling:isPopoutOpen",()=>de()),u.ipcMain.handle("codeling:exportSave",()=>ss()),u.ipcMain.handle("codeling:importSave",async()=>{const e=await rs();if("ok"in e){L.emit("pet:reset");try{L.emit("pet:renamed",{name:M().name})}catch{}S()}return e}),u.ipcMain.handle("codeling:renamePet",(e,t)=>{if(typeof t!="string")return{error:"empty-name"};try{const n=bn(t);return L.emit("pet:renamed",{name:n}),S(),{ok:!0,name:n}}catch(n){const s=n.message;if(s==="empty-name"||s==="name-too-long")return{error:s};throw n}})}const vs=40,$e=4,ws={wizard:.55,slime:.85,flying_eye:.9,bat:.7,mimic:.65,evil_wizard:.5,fire_worm:.8,martial_hero:.5,martial_hero_2:.5,apprentice_wizard:.5,goblin:.55,skeleton:.5,mushroom:.65,rat:.7};u.app.requestSingleInstanceLock()||(u.app.quit(),process.exit(0));function ft(){return u.app.isPackaged?E.join(process.resourcesPath,"assets"):E.join(u.app.getAppPath(),"assets")}function Pe(e,t=16,n=1){const{width:s,height:o}=e.getSize();if(s===0||o===0)return e;const i=e.toBitmap();let a=s,c=o,r=-1,l=-1;for(let m=0;m<o;m++)for(let y=0;y<s;y++)(i[(m*s+y)*4+3]??0)>t&&(y<a&&(a=y),y>r&&(r=y),m<c&&(c=m),m>l&&(l=m));if(r<0)return e;const p=Math.max(0,a-n),d=Math.max(0,c-n),g=Math.min(s-p,r-a+1+2*n),b=Math.min(o-d,l-c+1+2*n);return e.crop({x:p,y:d,width:g,height:b})}function Rs(e,t){const{width:n,height:s}=e.getSize();if(n===0||s===0)return e;const o=Math.max(1,Math.round(s*t));return e.crop({x:0,y:0,width:n,height:o})}function mt(e,t){const n=ws[t]??.55,s=Pe(Rs(Pe(e),n));if(process.platform!=="win32")return s;const{width:o,height:i}=s.getSize(),a=vs/Math.max(o,i,1);return s.resize({width:Math.max(1,Math.round(o*a)),height:Math.max(1,Math.round(i*a)),quality:"best"})}function gt(e){return E.join(ft(),"sprites",e)}function Fe(){try{const n=M(),s=gt(n.species),o=E.join(s,"rotations","south.png");if(T.existsSync(o))return mt(u.nativeImage.createFromPath(o),n.species)}catch{}const e=process.platform==="darwin"?"tray-icon-Template.png":"tray-icon.png",t=E.join(ft(),e);return T.existsSync(t)?u.nativeImage.createFromPath(t):u.nativeImage.createEmpty()}function ks(e){const t=E.join(gt(e),"animations");if(!T.existsSync(t))return[];const n=T.readdirSync(t).find(i=>/idle|breath/i.test(i));if(!n)return[];const s=E.join(t,n,"south");return T.existsSync(s)?T.readdirSync(s).filter(i=>i.toLowerCase().endsWith(".png")).sort().map(i=>mt(u.nativeImage.createFromPath(E.join(s,i)),e)):[]}function je(){return`file://${E.join(__dirname,"../renderer/main_window/index.html")}`}function He(){return E.join(__dirname,"preload.js")}async function Ls(){var c;await u.app.whenReady(),process.platform==="darwin"&&((c=u.app.dock)==null||c.hide()),f(),X(!0),Je(),Ss(),ot()?rt():console.log("[otel] receivers not started — telemetry disabled in Settings"),pn();let e="Codeling";try{e=`Codeling — ${M().name}`}catch{}const t=_t.menubar({index:je(),icon:Fe(),tooltip:e,showDockIcon:!1,preloadWindow:!0,browserWindow:{width:380,height:560,transparent:!1,resizable:!1,webPreferences:{preload:He(),contextIsolation:!0,nodeIntegration:!1,sandbox:!1}}});t.on("ready",()=>{var r;console.log("[codeling] menubar ready"),u.app.isPackaged||(r=t.window)==null||r.webContents.openDevTools({mode:"detach"}),o(),Jn({mb:t,rendererUrl:je(),preloadPath:He()})}),t.on("after-create-window",()=>{var r;u.app.isPackaged||(r=t.window)==null||r.webContents.openDevTools({mode:"detach"})});let n=null,s=null;function o(){n&&(clearInterval(n),n=null);let r;try{r=M().species}catch{return}t.tray&&!t.tray.isDestroyed()&&t.tray.setImage(Fe()),s=r;const l=ks(r);if(l.length<=1)return;console.log(`[tray] animating ${l.length} idle frames at ${$e} fps (${r})`);let p=0;n=setInterval(()=>{if(!t.tray||t.tray.isDestroyed()){n&&(clearInterval(n),n=null);return}t.tray.setImage(l[p%l.length]),p++},Math.round(1e3/$e))}const a=setInterval(()=>{let r;try{r=M().species}catch{return}s!==null&&r!==s&&(console.log(`[tray] species drifted ${s} → ${r}; refreshing`),o())},5e3);typeof a.unref=="function"&&a.unref(),L.on("pet:species-changed",r=>{console.log(`[tray] active species changed → ${r.species}; refreshing`),o()}),L.on("pet:reset",()=>{console.log("[tray] save reset; refreshing tray"),o()}),L.on("pet:renamed",r=>{t.tray&&!t.tray.isDestroyed()&&t.tray.setToolTip(`Codeling — ${r.name}`)}),L.on("achievement:earned",r=>{console.log(`[achievement] earned ${r.id}: ${r.label}`),u.Notification.isSupported()&&new u.Notification({title:`Achievement: ${r.label}`,body:r.description,silent:!1}).show()}),u.app.on("before-quit",()=>{n&&clearInterval(n),clearInterval(a)})}u.app.on("before-quit",()=>{It()});Ls().catch(e=>{console.error("[codeling] bootstrap failed",e),u.app.quit()});