fraim-hub 2.0.225 → 2.0.227

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/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "fraim-hub",
3
- "version": "2.0.225",
3
+ "version": "2.0.227",
4
4
  "description": "FRAIM Hub local companion package.",
5
- "main": "index.js",
6
5
  "bin": {
7
6
  "fraim-hub": "bin/fraim-hub.js"
8
7
  },
8
+ "main": "dist/src/ai-hub/desktop-main.js",
9
+ "scripts": {
10
+ "build:windows-installer": "npx tsx ../../scripts/build-windows-hub-installer.ts"
11
+ },
9
12
  "files": [
10
13
  "dist/src/ai-hub/",
11
14
  "dist/src/api/admin/payments.js",
@@ -80,20 +83,82 @@
80
83
  "publishConfig": {
81
84
  "access": "public"
82
85
  },
86
+ "build": {
87
+ "appId": "ai.fraim.hub",
88
+ "productName": "FRAIM Hub",
89
+ "artifactName": "FRAIM-Hub-${version}-${os}-${arch}.${ext}",
90
+ "directories": {
91
+ "output": "../../release/fraim-hub"
92
+ },
93
+ "files": [
94
+ "dist/src/ai-hub/**",
95
+ "dist/src/api/**",
96
+ "dist/src/cli/**",
97
+ "dist/src/config/**",
98
+ "dist/src/core/**",
99
+ "dist/src/db/**",
100
+ "dist/src/first-run/**",
101
+ "dist/src/fraim/**",
102
+ "dist/src/local-mcp-server/**",
103
+ "dist/src/middleware/**",
104
+ "dist/src/models/**",
105
+ "dist/src/routes/**",
106
+ "dist/src/services/**",
107
+ "dist/src/types/**",
108
+ "dist/src/utils/**",
109
+ "extensions/office-word/**",
110
+ "public/ai-hub/**",
111
+ "public/first-run/**",
112
+ "public/portfolio/**",
113
+ "package.json"
114
+ ],
115
+ "publish": {
116
+ "provider": "github",
117
+ "owner": "mathursrus",
118
+ "repo": "FRAIM"
119
+ },
120
+ "win": {
121
+ "target": [
122
+ "nsis",
123
+ "portable"
124
+ ]
125
+ },
126
+ "nsis": {
127
+ "oneClick": false,
128
+ "perMachine": false,
129
+ "allowToChangeInstallationDirectory": true
130
+ },
131
+ "mac": {
132
+ "target": [
133
+ "dmg",
134
+ "zip"
135
+ ],
136
+ "hardenedRuntime": true,
137
+ "gatekeeperAssess": false
138
+ },
139
+ "linux": {
140
+ "target": [
141
+ "AppImage",
142
+ "deb"
143
+ ],
144
+ "category": "Utility"
145
+ }
146
+ },
83
147
  "engines": {
84
148
  "node": ">=16.0.0"
85
149
  },
86
150
  "dependencies": {
87
151
  "@octokit/rest": "^22.0.1",
88
- "adm-zip": "^0.5.16",
152
+ "adm-zip": "^0.6.0",
89
153
  "axios": "^1.7.0",
90
154
  "chalk": "4.1.2",
91
155
  "commander": "^14.0.2",
92
156
  "cors": "^2.8.5",
93
157
  "dotenv": "^16.4.7",
94
158
  "electron": "^41.2.2",
159
+ "electron-updater": "^6.8.9",
95
160
  "express": "^5.2.1",
96
- "fraim": "2.0.225",
161
+ "fraim": "2.0.227",
97
162
  "mongodb": "^7.0.0",
98
163
  "node-cron": "4.2.1",
99
164
  "node-edge-tts": "^1.2.10",
@@ -3407,14 +3407,21 @@ const COACH_DROPDOWN_PAIRS = [
3407
3407
  ['conviction-dropdown', 'conviction-btn'],
3408
3408
  ];
3409
3409
 
3410
+ function ensureCoachDropdownPortal(dropdown) {
3411
+ if (!dropdown || dropdown.parentElement === document.body) return;
3412
+ document.body.appendChild(dropdown);
3413
+ }
3414
+
3410
3415
  function positionCoachDropdown(dropdown, btn) {
3411
3416
  if (!dropdown || !btn || dropdown.hidden) return;
3412
3417
  const margin = 6;
3413
3418
  const viewportPad = 8;
3419
+ ensureCoachDropdownPortal(dropdown);
3414
3420
  const rect = btn.getBoundingClientRect();
3415
3421
  const minWidth = Math.max(200, Math.ceil(rect.width));
3416
3422
  dropdown.style.minWidth = minWidth + 'px';
3417
3423
  dropdown.style.maxWidth = Math.max(220, window.innerWidth - viewportPad * 2) + 'px';
3424
+ dropdown.style.maxHeight = '';
3418
3425
  dropdown.style.visibility = 'hidden';
3419
3426
  dropdown.style.transform = 'none';
3420
3427
  const menuRect = dropdown.getBoundingClientRect();
@@ -3424,13 +3431,17 @@ function positionCoachDropdown(dropdown, btn) {
3424
3431
  const wouldOverflowRight = rect.left + menuWidth > window.innerWidth - viewportPad;
3425
3432
  const idealLeft = wouldOverflowRight ? rect.right - menuWidth : rect.left;
3426
3433
  const left = Math.min(Math.max(viewportPad, idealLeft), viewportMaxLeft);
3427
- const bottomTop = rect.bottom + margin;
3428
- const top = bottomTop + menuHeight <= window.innerHeight - viewportPad
3429
- ? bottomTop
3430
- : Math.max(viewportPad, rect.top - menuHeight - margin);
3434
+ const availableBelow = Math.max(0, window.innerHeight - rect.bottom - margin - viewportPad);
3435
+ const availableAbove = Math.max(0, rect.top - margin - viewportPad);
3436
+ const opensBelow = availableBelow >= Math.min(menuHeight, 160) || availableBelow >= availableAbove;
3437
+ const availableSpace = opensBelow ? availableBelow : availableAbove;
3438
+ const renderedHeight = Math.min(menuHeight, availableSpace || menuHeight);
3439
+ const top = opensBelow
3440
+ ? Math.min(rect.bottom + margin, window.innerHeight - renderedHeight - viewportPad)
3441
+ : Math.max(viewportPad, rect.top - renderedHeight - margin);
3431
3442
  dropdown.style.left = Math.round(left) + 'px';
3432
3443
  dropdown.style.top = Math.round(top) + 'px';
3433
- dropdown.style.maxHeight = Math.max(120, window.innerHeight - top - viewportPad) + 'px';
3444
+ dropdown.style.maxHeight = Math.max(80, Math.floor(availableSpace || (window.innerHeight - viewportPad * 2))) + 'px';
3434
3445
  dropdown.style.visibility = '';
3435
3446
  }
3436
3447
 
@@ -7088,22 +7099,20 @@ function wireEvents() {
7088
7099
  }
7089
7100
 
7090
7101
  // R5: quick-access coaching buttons.
7091
- // Clicking a button populates the textarea with "{$fraim|/fraim} {job-id}" so the
7092
- // user can see exactly what will be sent and append their own context before hitting
7093
- // Send. The server still receives coachingJobId out-of-band for job routing.
7094
- if (els['quick-coach-btns']) {
7095
- els['quick-coach-btns'].addEventListener('click', (e) => {
7096
- const btn = e.target.closest('.quick-coach-btn');
7097
- if (!btn) return;
7098
- const jobId = btn.dataset.job;
7099
- if (!jobId) return;
7100
- const label = btn.textContent.trim();
7101
- const invocation = `${fraimPrefix()} ${jobId}`;
7102
- setPendingCoachingJob(jobId, label, invocation);
7103
- closeAllCoachDropdowns();
7104
- els['coach-text'].focus();
7105
- });
7106
- }
7102
+ // Clicking a button populates the textarea with "{$fraim|/fraim} {job-id}" so the
7103
+ // user can see exactly what will be sent and append their own context before hitting
7104
+ // Send. The server still receives coachingJobId out-of-band for job routing.
7105
+ function handleCoachingJobPick(btn) {
7106
+ if (!btn) return false;
7107
+ const jobId = btn.dataset.job;
7108
+ if (!jobId) return false;
7109
+ const label = btn.textContent.trim();
7110
+ const invocation = `${fraimPrefix()} ${jobId}`;
7111
+ setPendingCoachingJob(jobId, label, invocation);
7112
+ closeAllCoachDropdowns();
7113
+ els['coach-text'].focus();
7114
+ return true;
7115
+ }
7107
7116
 
7108
7117
  // R4/R5/R6 — coaching grouped dropdowns: toggle on click, keyboard-focus on Enter/Space/ArrowDown.
7109
7118
  function wireCoachDropdownBtn(btnId, dropdownId) {
@@ -7124,12 +7133,14 @@ function wireEvents() {
7124
7133
  }
7125
7134
  });
7126
7135
  }
7127
- for (const [dropdownId, btnId] of COACH_DROPDOWN_PAIRS) wireCoachDropdownBtn(btnId, dropdownId);
7136
+ for (const [dropdownId, btnId] of COACH_DROPDOWN_PAIRS) wireCoachDropdownBtn(btnId, dropdownId);
7128
7137
  document.addEventListener('click', (e) => {
7138
+ const jobBtn = e.target.closest('.coach-dropdown-menu .quick-coach-btn[data-job], #quick-coach-btns .quick-coach-btn[data-job]');
7139
+ if (handleCoachingJobPick(jobBtn)) return;
7129
7140
  const openDropdown = COACH_DROPDOWN_PAIRS
7130
7141
  .some(([id]) => { const d = els[id] || document.getElementById(id); return d && !d.hidden; });
7131
7142
  if (!openDropdown) return;
7132
- const inDropdown = e.target.closest('.coach-dropdown');
7143
+ const inDropdown = e.target.closest('.coach-dropdown, .coach-dropdown-menu');
7133
7144
  if (!inDropdown) closeAllCoachDropdowns();
7134
7145
  });
7135
7146
  window.addEventListener('resize', positionOpenCoachDropdowns);