tide-commander 1.43.1 → 1.45.0

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 (46) hide show
  1. package/dist/assets/{BossLogsModal-CVg_MfdE.js → BossLogsModal-N4CnX-0x.js} +1 -1
  2. package/dist/assets/{BossSpawnModal-Bikb7hBY.js → BossSpawnModal-4CPjS2xn.js} +1 -1
  3. package/dist/assets/ControlsModal-Cr5klRxS.js +1 -0
  4. package/dist/assets/{DockerLogsModal-Cry5-Tvn.js → DockerLogsModal-D6U9obTe.js} +1 -1
  5. package/dist/assets/{EmbeddedEditor-CIhFmW0L.js → EmbeddedEditor-Bnm-9Q73.js} +1 -1
  6. package/dist/assets/{GmailOAuthSetup-Dy5O_2Fa.js → GmailOAuthSetup-GoKvyYWS.js} +1 -1
  7. package/dist/assets/{GoogleOAuthSetup-CZPe6iiU.js → GoogleOAuthSetup-D3sAGlZq.js} +1 -1
  8. package/dist/assets/{IframeModal-P4PrKR61.js → IframeModal-DQqev6_V.js} +1 -1
  9. package/dist/assets/{IntegrationsPanel-CeKz_I5C.js → IntegrationsPanel-C9hWD7mO.js} +2 -2
  10. package/dist/assets/{LogViewerModal-Cu0ojz2h.js → LogViewerModal-DB0xx-oB.js} +1 -1
  11. package/dist/assets/{MonitoringModal-Bo7YZKga.js → MonitoringModal-BspW74kP.js} +1 -1
  12. package/dist/assets/{PM2LogsModal-DjSjBB5n.js → PM2LogsModal-Cz4GZtyl.js} +1 -1
  13. package/dist/assets/{RestoreArchivedAreaModal-Cr0dVcm7.js → RestoreArchivedAreaModal-KCCSJy3B.js} +1 -1
  14. package/dist/assets/{SaveSnapshotModal-Cngm66r9.js → SaveSnapshotModal-DqZ1JedM.js} +1 -1
  15. package/dist/assets/{Scene2DCanvas-CxUD-QLv.js → Scene2DCanvas-MRhWT6EA.js} +1 -1
  16. package/dist/assets/{SceneManager-CFq_dIjf.js → SceneManager-DggU0iAh.js} +1 -1
  17. package/dist/assets/{SkillsPanel-DeNlZjhm.js → SkillsPanel-DZC0Im2z.js} +1 -1
  18. package/dist/assets/{SnapshotManager-DyGSyuTV.js → SnapshotManager-3LQMRFLh.js} +1 -1
  19. package/dist/assets/{SpawnModal-DCk35phK.js → SpawnModal-C_e6q2uv.js} +1 -1
  20. package/dist/assets/{SubordinateAssignmentModal-q4of-xB_.js → SubordinateAssignmentModal-BZdSk9WL.js} +1 -1
  21. package/dist/assets/{SupervisorPanel-B4g4RnXX.js → SupervisorPanel-CxId5lI7.js} +1 -1
  22. package/dist/assets/{TriggerManagerPanel-yKzt3ymQ.js → TriggerManagerPanel-BCzGGCPD.js} +1 -1
  23. package/dist/assets/{WorkflowEditorPanel-BEhxdbzG.js → WorkflowEditorPanel-BmNCGmxP.js} +1 -1
  24. package/dist/assets/{index-Cx3vkOU7.js → index-8WVMR823.js} +1 -1
  25. package/dist/assets/{index-3Q4Y5M5A.js → index-B8SWRPUd.js} +1 -1
  26. package/dist/assets/{index-egvzmURy.js → index-Bhb-rse9.js} +3 -3
  27. package/dist/assets/{index-Dm4Ql23t.js → index-C9B8kaWK.js} +1 -1
  28. package/dist/assets/{index-D9CfgwK1.js → index-COBkxsEU.js} +1 -1
  29. package/dist/assets/{index-CNyYl9DR.js → index-CcYTUJp1.js} +1 -1
  30. package/dist/assets/{index-Brczvyiq.js → index-CvJuv-ho.js} +2 -2
  31. package/dist/assets/{index-mlbu3rRR.js → index-Sej69v19.js} +1 -1
  32. package/dist/assets/main-6CnCKW13.js +153 -0
  33. package/dist/assets/main-DjGrMNoB.css +1 -0
  34. package/dist/assets/{web-CTzGBDAL.js → web-CrUhsC9T.js} +1 -1
  35. package/dist/assets/{web-Dp1qM35w.js → web-RTEsf66y.js} +1 -1
  36. package/dist/index.html +2 -2
  37. package/dist/src/packages/server/data/index.js +43 -0
  38. package/dist/src/packages/server/index.js +2 -0
  39. package/dist/src/packages/server/routes/agents.js +33 -0
  40. package/dist/src/packages/server/services/agent-service.js +42 -2
  41. package/dist/src/packages/server/websocket/handler.js +3 -1
  42. package/dist/src/packages/server/websocket/handlers/agent-handler.js +48 -0
  43. package/package.json +1 -1
  44. package/dist/assets/ControlsModal-CkLjXrTR.js +0 -1
  45. package/dist/assets/main-BmmvF2Ey.js +0 -153
  46. package/dist/assets/main-Chjl2UXl.css +0 -1
@@ -1 +1 @@
1
- import{bx as a}from"./main-BmmvF2Ey.js";import{ImpactStyle as i,NotificationType as r}from"./index-Brczvyiq.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class u extends a{constructor(){super(...arguments),this.selectionStarted=!1}async impact(t){const e=this.patternForImpact(t==null?void 0:t.style);this.vibrateWithPattern(e)}async notification(t){const e=this.patternForNotification(t==null?void 0:t.type);this.vibrateWithPattern(e)}async vibrate(t){const e=(t==null?void 0:t.duration)||300;this.vibrateWithPattern([e])}async selectionStart(){this.selectionStarted=!0}async selectionChanged(){this.selectionStarted&&this.vibrateWithPattern([70])}async selectionEnd(){this.selectionStarted=!1}patternForImpact(t=i.Heavy){return t===i.Medium?[43]:t===i.Light?[20]:[61]}patternForNotification(t=r.Success){return t===r.Warning?[30,40,30,50,60]:t===r.Error?[27,45,50]:[35,65,21]}vibrateWithPattern(t){if(navigator.vibrate)navigator.vibrate(t);else throw this.unavailable("Browser does not support the vibrate API")}}export{u as HapticsWeb};
1
+ import{by as a}from"./main-6CnCKW13.js";import{ImpactStyle as i,NotificationType as r}from"./index-CvJuv-ho.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class u extends a{constructor(){super(...arguments),this.selectionStarted=!1}async impact(t){const e=this.patternForImpact(t==null?void 0:t.style);this.vibrateWithPattern(e)}async notification(t){const e=this.patternForNotification(t==null?void 0:t.type);this.vibrateWithPattern(e)}async vibrate(t){const e=(t==null?void 0:t.duration)||300;this.vibrateWithPattern([e])}async selectionStart(){this.selectionStarted=!0}async selectionChanged(){this.selectionStarted&&this.vibrateWithPattern([70])}async selectionEnd(){this.selectionStarted=!1}patternForImpact(t=i.Heavy){return t===i.Medium?[43]:t===i.Light?[20]:[61]}patternForNotification(t=r.Success){return t===r.Warning?[30,40,30,50,60]:t===r.Error?[27,45,50]:[35,65,21]}vibrateWithPattern(t){if(navigator.vibrate)navigator.vibrate(t);else throw this.unavailable("Browser does not support the vibrate API")}}export{u as HapticsWeb};
@@ -1 +1 @@
1
- import{bx as s}from"./main-BmmvF2Ey.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class f extends s{constructor(){super(...arguments),this.pending=[],this.deliveredNotifications=[],this.hasNotificationSupport=()=>{if(!("Notification"in window)||!Notification.requestPermission)return!1;if(Notification.permission!=="granted")try{new Notification("")}catch(i){if(i instanceof Error&&i.name==="TypeError")return!1}return!0}}async getDeliveredNotifications(){const i=[];for(const t of this.deliveredNotifications){const e={title:t.title,id:parseInt(t.tag),body:t.body};i.push(e)}return{notifications:i}}async removeDeliveredNotifications(i){for(const t of i.notifications){const e=this.deliveredNotifications.find(n=>n.tag===String(t.id));e==null||e.close(),this.deliveredNotifications=this.deliveredNotifications.filter(()=>!e)}}async removeAllDeliveredNotifications(){for(const i of this.deliveredNotifications)i.close();this.deliveredNotifications=[]}async createChannel(){throw this.unimplemented("Not implemented on web.")}async deleteChannel(){throw this.unimplemented("Not implemented on web.")}async listChannels(){throw this.unimplemented("Not implemented on web.")}async schedule(i){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");for(const t of i.notifications)this.sendNotification(t);return{notifications:i.notifications.map(t=>({id:t.id}))}}async getPending(){return{notifications:this.pending}}async registerActionTypes(){throw this.unimplemented("Not implemented on web.")}async cancel(i){this.pending=this.pending.filter(t=>!i.notifications.find(e=>e.id===t.id))}async areEnabled(){const{display:i}=await this.checkPermissions();return{value:i==="granted"}}async changeExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async checkExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async requestPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(await Notification.requestPermission())}}async checkPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(Notification.permission)}}transformNotificationPermission(i){switch(i){case"granted":return"granted";case"denied":return"denied";default:return"prompt"}}sendPending(){var i;const t=[],e=new Date().getTime();for(const n of this.pending)!((i=n.schedule)===null||i===void 0)&&i.at&&n.schedule.at.getTime()<=e&&(this.buildNotification(n),t.push(n));this.pending=this.pending.filter(n=>!t.find(o=>o===n))}sendNotification(i){var t;if(!((t=i.schedule)===null||t===void 0)&&t.at){const e=i.schedule.at.getTime()-new Date().getTime();this.pending.push(i),setTimeout(()=>{this.sendPending()},e);return}this.buildNotification(i)}buildNotification(i){const t=new Notification(i.title,{body:i.body,tag:String(i.id)});return t.addEventListener("click",this.onClick.bind(this,i),!1),t.addEventListener("show",this.onShow.bind(this,i),!1),t.addEventListener("close",()=>{this.deliveredNotifications=this.deliveredNotifications.filter(()=>!this)},!1),this.deliveredNotifications.push(t),t}onClick(i){const t={actionId:"tap",notification:i};this.notifyListeners("localNotificationActionPerformed",t)}onShow(i){this.notifyListeners("localNotificationReceived",i)}}export{f as LocalNotificationsWeb};
1
+ import{by as s}from"./main-6CnCKW13.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class f extends s{constructor(){super(...arguments),this.pending=[],this.deliveredNotifications=[],this.hasNotificationSupport=()=>{if(!("Notification"in window)||!Notification.requestPermission)return!1;if(Notification.permission!=="granted")try{new Notification("")}catch(i){if(i instanceof Error&&i.name==="TypeError")return!1}return!0}}async getDeliveredNotifications(){const i=[];for(const t of this.deliveredNotifications){const e={title:t.title,id:parseInt(t.tag),body:t.body};i.push(e)}return{notifications:i}}async removeDeliveredNotifications(i){for(const t of i.notifications){const e=this.deliveredNotifications.find(n=>n.tag===String(t.id));e==null||e.close(),this.deliveredNotifications=this.deliveredNotifications.filter(()=>!e)}}async removeAllDeliveredNotifications(){for(const i of this.deliveredNotifications)i.close();this.deliveredNotifications=[]}async createChannel(){throw this.unimplemented("Not implemented on web.")}async deleteChannel(){throw this.unimplemented("Not implemented on web.")}async listChannels(){throw this.unimplemented("Not implemented on web.")}async schedule(i){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");for(const t of i.notifications)this.sendNotification(t);return{notifications:i.notifications.map(t=>({id:t.id}))}}async getPending(){return{notifications:this.pending}}async registerActionTypes(){throw this.unimplemented("Not implemented on web.")}async cancel(i){this.pending=this.pending.filter(t=>!i.notifications.find(e=>e.id===t.id))}async areEnabled(){const{display:i}=await this.checkPermissions();return{value:i==="granted"}}async changeExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async checkExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async requestPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(await Notification.requestPermission())}}async checkPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(Notification.permission)}}transformNotificationPermission(i){switch(i){case"granted":return"granted";case"denied":return"denied";default:return"prompt"}}sendPending(){var i;const t=[],e=new Date().getTime();for(const n of this.pending)!((i=n.schedule)===null||i===void 0)&&i.at&&n.schedule.at.getTime()<=e&&(this.buildNotification(n),t.push(n));this.pending=this.pending.filter(n=>!t.find(o=>o===n))}sendNotification(i){var t;if(!((t=i.schedule)===null||t===void 0)&&t.at){const e=i.schedule.at.getTime()-new Date().getTime();this.pending.push(i),setTimeout(()=>{this.sendPending()},e);return}this.buildNotification(i)}buildNotification(i){const t=new Notification(i.title,{body:i.body,tag:String(i.id)});return t.addEventListener("click",this.onClick.bind(this,i),!1),t.addEventListener("show",this.onShow.bind(this,i),!1),t.addEventListener("close",()=>{this.deliveredNotifications=this.deliveredNotifications.filter(()=>!this)},!1),this.deliveredNotifications.push(t),t}onClick(i){const t={actionId:"tap",notification:i};this.notifyListeners("localNotificationActionPerformed",t)}onShow(i){this.notifyListeners("localNotificationReceived",i)}}export{f as LocalNotificationsWeb};
package/dist/index.html CHANGED
@@ -22,11 +22,11 @@
22
22
  <link rel="icon" type="image/png" sizes="16x16" href="/assets/icons/favicon-16x16.png" />
23
23
  <link rel="apple-touch-icon" sizes="180x180" href="/assets/icons/apple-touch-icon.png" />
24
24
  <title>Tide Commander</title>
25
- <script type="module" crossorigin src="/assets/main-BmmvF2Ey.js"></script>
25
+ <script type="module" crossorigin src="/assets/main-6CnCKW13.js"></script>
26
26
  <link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
27
27
  <link rel="modulepreload" crossorigin href="/assets/vendor-react--Eh9ivFN.js">
28
28
  <link rel="modulepreload" crossorigin href="/assets/vendor-three-Chj50gSY.js">
29
- <link rel="stylesheet" crossorigin href="/assets/main-Chjl2UXl.css">
29
+ <link rel="stylesheet" crossorigin href="/assets/main-DjGrMNoB.css">
30
30
  </head>
31
31
  <body>
32
32
  <div id="app"></div>
@@ -27,9 +27,11 @@ const SKILLS_FILE = path.join(DATA_DIR, 'skills.json');
27
27
  const CUSTOM_CLASSES_FILE = path.join(DATA_DIR, 'custom-agent-classes.json');
28
28
  const RUNNING_PROCESSES_FILE = path.join(DATA_DIR, 'running-processes.json');
29
29
  const SECRETS_FILE = path.join(DATA_DIR, 'secrets.json');
30
+ const SESSION_HISTORY_FILE = path.join(DATA_DIR, 'session-history.json');
30
31
  const AREA_LOGOS_DIR = path.join(DATA_DIR, 'area-logos');
31
32
  // Maximum history entries per agent
32
33
  const MAX_HISTORY_PER_AGENT = 50;
34
+ const MAX_SESSION_HISTORY_PER_AGENT = 100;
33
35
  const MAX_DELEGATION_HISTORY_PER_BOSS = 100;
34
36
  // Ensure data directory exists
35
37
  function ensureDataDir() {
@@ -134,6 +136,9 @@ function toStoredAgents(agents) {
134
136
  lastAssignedTask: agent.lastAssignedTask,
135
137
  lastAssignedTaskTime: agent.lastAssignedTaskTime,
136
138
  taskLabel: agent.taskLabel,
139
+ trackingStatus: agent.trackingStatus,
140
+ trackingStatusDetail: agent.trackingStatusDetail,
141
+ trackingStatusTimestamp: agent.trackingStatusTimestamp,
137
142
  isBoss: agent.isBoss,
138
143
  subordinateIds: agent.subordinateIds,
139
144
  bossId: agent.bossId,
@@ -780,3 +785,41 @@ export function deleteQueryHistory(buildingId) {
780
785
  log.error(` Failed to delete query history for building ${buildingId}:`, err);
781
786
  }
782
787
  }
788
+ export function loadSessionHistory() {
789
+ ensureDataDir();
790
+ const data = safeReadJsonSync(SESSION_HISTORY_FILE, 'Session history');
791
+ if (data?.histories) {
792
+ log.log(` Loaded session history for ${Object.keys(data.histories).length} agents`);
793
+ return new Map(Object.entries(data.histories));
794
+ }
795
+ return new Map();
796
+ }
797
+ export function saveSessionHistory(histories) {
798
+ ensureDataDir();
799
+ try {
800
+ atomicWriteJsonSync(SESSION_HISTORY_FILE, {
801
+ histories: Object.fromEntries(histories),
802
+ savedAt: Date.now(),
803
+ version: '1.0.0',
804
+ });
805
+ }
806
+ catch (err) {
807
+ log.error(' Failed to save session history:', err);
808
+ }
809
+ }
810
+ export function addSessionHistoryEntry(histories, agentId, entry) {
811
+ let agentHistory = histories.get(agentId);
812
+ if (!agentHistory) {
813
+ agentHistory = [];
814
+ histories.set(agentId, agentHistory);
815
+ }
816
+ // Add to beginning (most recent first)
817
+ agentHistory.unshift(entry);
818
+ // Trim to max entries
819
+ if (agentHistory.length > MAX_SESSION_HISTORY_PER_AGENT) {
820
+ agentHistory.pop();
821
+ }
822
+ }
823
+ export function getSessionHistoryForAgent(histories, agentId) {
824
+ return histories.get(agentId) || [];
825
+ }
@@ -59,6 +59,7 @@ async function main() {
59
59
  eventRetentionService.init();
60
60
  // Initialize services
61
61
  agentService.initAgents();
62
+ agentService.initSessionHistory();
62
63
  runtimeService.init();
63
64
  supervisorService.init();
64
65
  bossService.init();
@@ -209,6 +210,7 @@ async function main() {
209
210
  buildingService.stopTerminalStatusPolling();
210
211
  buildingService.cleanupAllTerminals();
211
212
  await runtimeService.shutdown();
213
+ agentService.shutdownSessionHistory();
212
214
  agentService.flushPersistAgents();
213
215
  closeEventDb();
214
216
  wss.clients.forEach((client) => client.terminate());
@@ -9,6 +9,7 @@ import * as path from 'path';
9
9
  import * as os from 'os';
10
10
  import { agentService, runtimeService, bossMessageService } from '../services/index.js';
11
11
  import { getClaudeProjectDir, loadAreas, saveAreas } from '../data/index.js';
12
+ import { loadSession } from '../claude/session-loader.js';
12
13
  import { getAllCustomClasses } from '../services/custom-class-service.js';
13
14
  // Session listing is done inline for performance
14
15
  import { createLogger } from '../utils/logger.js';
@@ -586,6 +587,38 @@ router.get('/:id/history', async (req, res) => {
586
587
  res.status(500).json({ error: err.message });
587
588
  }
588
589
  });
590
+ // GET /api/agents/:id/session-history - Get archived session history for an agent
591
+ router.get('/:id/session-history', (_req, res) => {
592
+ try {
593
+ const entries = agentService.getAgentSessionHistory(_req.params.id);
594
+ res.json({ entries });
595
+ }
596
+ catch (err) {
597
+ log.error(' Failed to load session history:', err);
598
+ res.status(500).json({ error: err.message });
599
+ }
600
+ });
601
+ // GET /api/agents/:id/session-preview/:sessionId - Preview messages from an archived session
602
+ router.get('/:id/session-preview/:sessionId', async (req, res) => {
603
+ try {
604
+ const agent = agentService.getAgent(req.params.id);
605
+ if (!agent) {
606
+ res.status(404).json({ error: 'Agent not found' });
607
+ return;
608
+ }
609
+ const limit = parseInt(req.query.limit) || 30;
610
+ const history = await loadSession(agent.cwd, req.params.sessionId, limit, 0);
611
+ if (!history) {
612
+ res.status(404).json({ error: 'Session not found' });
613
+ return;
614
+ }
615
+ res.json({ messages: history.messages, totalCount: history.totalCount });
616
+ }
617
+ catch (err) {
618
+ log.error(' Failed to load session preview:', err);
619
+ res.status(500).json({ error: err.message });
620
+ }
621
+ });
589
622
  // GET /api/agents/:id/search - Search conversation history
590
623
  router.get('/:id/search', async (req, res) => {
591
624
  try {
@@ -5,8 +5,8 @@
5
5
  import * as fs from 'fs';
6
6
  import * as os from 'os';
7
7
  import * as path from 'path';
8
- import { loadAgents, saveAgents, saveAgentsAsync, getDataDir, loadAreas, saveAreas } from '../data/index.js';
9
- import { listSessions, getSessionSummary, loadSession, loadToolHistory, searchSession, } from '../claude/session-loader.js';
8
+ import { loadAgents, saveAgents, saveAgentsAsync, getDataDir, loadAreas, saveAreas, loadSessionHistory, saveSessionHistory, addSessionHistoryEntry, getSessionHistoryForAgent } from '../data/index.js';
9
+ import { listSessions, getSessionSummary, getProjectDir, loadSession, loadToolHistory, searchSession, } from '../claude/session-loader.js';
10
10
  import { loadSubagentHistory } from '../claude/subagent-history-loader.js';
11
11
  import { logger, generateId } from '../utils/index.js';
12
12
  const log = logger.agent;
@@ -650,5 +650,45 @@ Please acknowledge this update and continue with your work.
650
650
  `;
651
651
  }
652
652
  // ============================================================================
653
+ // Session History
654
+ // ============================================================================
655
+ let sessionHistories = new Map();
656
+ export function initSessionHistory() {
657
+ sessionHistories = loadSessionHistory();
658
+ log.log?.(` Loaded session history for ${sessionHistories.size} agents`);
659
+ }
660
+ export function shutdownSessionHistory() {
661
+ saveSessionHistory(sessionHistories);
662
+ }
663
+ /**
664
+ * Archive the current session for an agent before it gets cleared.
665
+ * Call this before setting sessionId to undefined.
666
+ */
667
+ export function archiveCurrentSession(agentId) {
668
+ const agent = agents.get(agentId);
669
+ if (!agent || !agent.sessionId)
670
+ return;
671
+ const entry = {
672
+ sessionId: agent.sessionId,
673
+ summary: agent.taskLabel || agent.lastAssignedTask || agent.currentTask || 'No description',
674
+ startedAt: agent.createdAt,
675
+ endedAt: Date.now(),
676
+ };
677
+ addSessionHistoryEntry(sessionHistories, agentId, entry);
678
+ saveSessionHistory(sessionHistories);
679
+ log.log?.(`Archived session ${agent.sessionId} for agent ${agent.name}`);
680
+ }
681
+ export function getAgentSessionHistory(agentId) {
682
+ const agent = agents.get(agentId);
683
+ const entries = getSessionHistoryForAgent(sessionHistories, agentId);
684
+ if (!agent)
685
+ return entries;
686
+ const projectDir = getProjectDir(agent.cwd);
687
+ return entries.map((entry) => ({
688
+ ...entry,
689
+ fileExists: fs.existsSync(path.join(projectDir, `${entry.sessionId}.jsonl`)),
690
+ }));
691
+ }
692
+ // ============================================================================
653
693
  // Utilities
654
694
  // ============================================================================
@@ -9,7 +9,7 @@ import { logger } from '../utils/index.js';
9
9
  import { setNotificationBroadcast, setExecBroadcast, setFocusAgentBroadcast, setAgentsBroadcast, setTriggerBroadcast } from '../routes/index.js';
10
10
  import { validateWebSocketAuth, isAuthEnabled } from '../auth/index.js';
11
11
  import { incrementWsSent, incrementWsReceived, setWsClientsCount } from '../routes/perf.js';
12
- import { handleSpawnAgent, handleKillAgent, handleStopAgent, handleClearContext, handleCollapseContext, handleRequestContextStats, handleMoveAgent, handleRemoveAgent, handleRenameAgent, handleUpdateAgentProperties, handleCreateDirectory, handleReattachAgent, } from './handlers/agent-handler.js';
12
+ import { handleSpawnAgent, handleKillAgent, handleStopAgent, handleClearContext, handleRestoreSession, handleRequestSessionHistory, handleCollapseContext, handleRequestContextStats, handleMoveAgent, handleRemoveAgent, handleRenameAgent, handleUpdateAgentProperties, handleCreateDirectory, handleReattachAgent, } from './handlers/agent-handler.js';
13
13
  import { handleCreateSkill, handleUpdateSkill, handleDeleteSkill, handleAssignSkill, handleUnassignSkill, handleRequestAgentSkills, } from './handlers/skill-handler.js';
14
14
  import { handleSpawnBossAgent, handleAssignSubordinates, handleRemoveSubordinate, handleSendBossCommand, handleRequestDelegationHistory, } from './handlers/boss-handler.js';
15
15
  import { handleCreateCustomAgentClass, handleUpdateCustomAgentClass, handleDeleteCustomAgentClass, } from './handlers/custom-class-handler.js';
@@ -138,6 +138,8 @@ const messageHandlers = {
138
138
  kill_agent: handleKillAgent,
139
139
  stop_agent: handleStopAgent,
140
140
  clear_context: handleClearContext,
141
+ restore_session: handleRestoreSession,
142
+ request_session_history: handleRequestSessionHistory,
141
143
  collapse_context: handleCollapseContext,
142
144
  create_directory: handleCreateDirectory,
143
145
  remove_agent: handleRemoveAgent,
@@ -151,6 +151,8 @@ export async function handleStopAgent(ctx, payload) {
151
151
  export async function handleClearContext(ctx, payload) {
152
152
  const agent = agentService.getAgent(payload.agentId);
153
153
  log.log(`Agent ${agent?.name || payload.agentId}: User requested context clear`);
154
+ // Archive the current session before clearing
155
+ agentService.archiveCurrentSession(payload.agentId);
154
156
  await runtimeService.stopAgent(payload.agentId);
155
157
  agentService.updateAgent(payload.agentId, {
156
158
  status: 'idle',
@@ -170,6 +172,47 @@ export async function handleClearContext(ctx, payload) {
170
172
  ctx.sendActivity(payload.agentId, 'Context cleared - new session on next command');
171
173
  log.log(`Agent ${agent?.name || payload.agentId}: Context cleared, session reset`);
172
174
  }
175
+ /**
176
+ * Handle restore_session message - restores a previous session for an agent
177
+ */
178
+ export async function handleRestoreSession(ctx, payload) {
179
+ const agent = agentService.getAgent(payload.agentId);
180
+ if (!agent) {
181
+ ctx.sendError(`Agent not found: ${payload.agentId}`);
182
+ return;
183
+ }
184
+ log.log(`Agent ${agent.name}: Restoring session ${payload.sessionId}`);
185
+ // Archive the current session first (if any)
186
+ agentService.archiveCurrentSession(payload.agentId);
187
+ // Stop any running process
188
+ await runtimeService.stopAgent(payload.agentId);
189
+ // Set the restored session ID
190
+ agentService.updateAgent(payload.agentId, {
191
+ status: 'idle',
192
+ currentTask: undefined,
193
+ taskLabel: undefined,
194
+ currentTool: undefined,
195
+ sessionId: payload.sessionId,
196
+ tokensUsed: 0,
197
+ contextUsed: 0,
198
+ contextStats: undefined,
199
+ });
200
+ ctx.sendActivity(payload.agentId, `Session restored - will resume on next command`);
201
+ log.log(`Agent ${agent.name}: Session restored to ${payload.sessionId}`);
202
+ }
203
+ /**
204
+ * Handle request_session_history - returns archived sessions for an agent
205
+ */
206
+ export function handleRequestSessionHistory(ctx, payload) {
207
+ const entries = agentService.getAgentSessionHistory(payload.agentId);
208
+ ctx.sendToClient({
209
+ type: 'session_history',
210
+ payload: {
211
+ agentId: payload.agentId,
212
+ entries,
213
+ },
214
+ });
215
+ }
173
216
  /**
174
217
  * Handle collapse_context message - sends /compact command to collapse context
175
218
  */
@@ -707,6 +750,9 @@ export async function handleUpdateAgentProperties(ctx, payload) {
707
750
  }
708
751
  agentUpdates.cwd = updates.cwd;
709
752
  }
753
+ if (updates.shortcut !== undefined) {
754
+ agentUpdates.shortcut = updates.shortcut;
755
+ }
710
756
  // Apply agent property updates if any
711
757
  // agentService.updateAgent tracks pending property changes for notification on next command
712
758
  if (Object.keys(agentUpdates).length > 0) {
@@ -806,6 +852,8 @@ export async function handleUpdateAgentProperties(ctx, payload) {
806
852
  // Claude sessions are tied to the directory they were created in
807
853
  if (cwdChanged && sessionId) {
808
854
  log.log(`Agent ${agent.name}: Working directory changed to ${updates.cwd}, clearing session (cwd change requires new session)`);
855
+ // Archive the current session before clearing
856
+ agentService.archiveCurrentSession(agentId);
809
857
  try {
810
858
  // Stop the current Claude process
811
859
  await runtimeService.stopAgent(agentId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tide-commander",
3
- "version": "1.43.1",
3
+ "version": "1.45.0",
4
4
  "description": "Visual multi-agent orchestrator and manager for Claude Code with 3D/2D interface",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1 +0,0 @@
1
- import{u as S,b9 as Y,r as m,ba as X,b8 as w,j as e,f as _,bb as W,bc as Z,z as U,h as H,s as y,bd as z,be as A}from"./main-BmmvF2Ey.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";function q({shortcut:t,onUpdate:a}){const{t:s}=S(["config","terminal"]),o=Y(),[l,h]=m.useState(!1),[i,j]=m.useState(null),f=m.useRef(null),b=m.useMemo(()=>i?X(o,i,t.id,t.context):[],[i,o,t.id,t.context]);m.useEffect(()=>{if(!l)return;const p=c=>{if(c.preventDefault(),c.stopPropagation(),c.key==="Escape"&&!c.ctrlKey&&!c.altKey&&!c.shiftKey&&!c.metaKey){h(!1),j(null);return}if(["Control","Alt","Shift","Meta"].includes(c.key))return;let v=c.key;c.code.startsWith("Key")&&c.code.length===4?v=c.code.charAt(3).toLowerCase():c.code.startsWith("Digit")&&c.code.length===6?v=c.code.charAt(5):c.code==="Space"&&(v="Space");const x={key:v,modifiers:{ctrl:c.ctrlKey,alt:c.altKey,shift:c.shiftKey,meta:c.metaKey}};j(x)};return document.addEventListener("keydown",p,!0),()=>document.removeEventListener("keydown",p,!0)},[l]),m.useEffect(()=>{if(!l)return;const p=c=>{f.current&&!f.current.contains(c.target)&&(i&&b.length===0&&a(i),h(!1),j(null))};return document.addEventListener("mousedown",p),()=>document.removeEventListener("mousedown",p)},[l,i,b,a]);const k=()=>{l?(i&&b.length===0&&a(i),h(!1),j(null)):(h(!0),j(null))},g=p=>{p.stopPropagation(),a({key:"",modifiers:{}})};let d;return l?i?d=w({...t,...i}):d=s("config:shortcuts.pressKeys"):t.key?d=w(t):d=s("config:shortcuts.notSet"),e.jsxs("div",{className:"key-capture-container",children:[e.jsx("button",{ref:f,className:`key-capture-input ${l?"capturing":""} ${b.length>0?"conflict":""} ${t.enabled?"":"disabled"}`,onClick:k,title:s(l?"config:shortcuts.pressKeysOrClick":"config:shortcuts.clickToChange"),children:e.jsx("span",{className:"key-capture-value",children:d})}),t.key&&!l&&e.jsx("button",{className:"key-capture-clear",onClick:g,title:s("config:shortcuts.clearShortcut"),children:"×"}),b.length>0&&e.jsx("div",{className:"key-capture-conflict",children:s("config:shortcuts.conflictsWith",{names:b.map(p=>s(`terminal:controls.shortcuts.${p.id}.name`,{defaultValue:p.name})).join(", ")})})]})}const V={global:"terminal:controls.contextGlobal",commander:"terminal:controls.contextCommander",toolbox:"terminal:controls.contextToolbox"},Q={global:"terminal:controls.contextGlobalDesc",commander:"terminal:controls.contextCommanderDesc",toolbox:"terminal:controls.contextToolboxDesc"},R={camera:["camera-pan","camera-orbit","camera-rotate","camera-zoom","camera-tilt"],interaction:["primary-action","selection-box","context-menu","move-command"]},J={camera:"terminal:controls.cameraControls",interaction:"terminal:controls.interaction"};function oe({isOpen:t,onClose:a}){const{t:s}=S(["terminal","common"]),o=_(),l=W(),h=Z(),[i,j]=m.useState("keyboard"),[f,b]=m.useState(""),[k,g]=m.useState("all"),[d,p]=m.useState(!1),[c,v]=m.useState(null),x=U.useRef(null),{handleMouseDown:D,handleClick:M}=H(a);if(m.useEffect(()=>{if(!t||d)return;const r=n=>{n.key==="Escape"&&(n.preventDefault(),n.stopPropagation(),a())};return document.addEventListener("keydown",r,!0),()=>document.removeEventListener("keydown",r,!0)},[t,a,d]),m.useEffect(()=>{t||(b(""),p(!1),v(null))},[t]),m.useEffect(()=>{if(!t||!d)return;const r=n=>{if(n.key==="Escape"){n.preventDefault(),n.stopPropagation(),p(!1),v(null);return}if(["Control","Alt","Shift","Meta"].includes(n.key))return;n.preventDefault(),n.stopPropagation();let u=n.key;n.code.startsWith("Key")&&n.code.length===4?u=n.code.charAt(3).toLowerCase():n.code.startsWith("Digit")&&n.code.length===6?u=n.code.charAt(5):n.code==="Space"&&(u="Space"),v({key:u,modifiers:{ctrl:n.ctrlKey,alt:n.altKey,shift:n.shiftKey,meta:n.metaKey}})};return document.addEventListener("keydown",r,!0),()=>document.removeEventListener("keydown",r,!0)},[t,d]),!t)return null;const E=o.shortcuts.filter(r=>{const n=s(`terminal:controls.shortcuts.${r.id}.name`,{defaultValue:r.name}),u=s(`terminal:controls.shortcuts.${r.id}.description`,{defaultValue:r.description});if(d&&c){const O=C=>C===" "||C==="Space"?"Space":C.length===1?C.toLowerCase():C,$=O(r.key),B=O(c.key);if(!(B.length===1&&/^[a-zA-Z]$/.test($)?`Key${$.toUpperCase()}`==`Key${B.toUpperCase()}`:$===B))return!1;const K=r.modifiers,T=c.modifiers;return(K.ctrl||!1)===(T.ctrl||!1)&&(K.alt||!1)===(T.alt||!1)&&(K.shift||!1)===(T.shift||!1)&&(K.meta||!1)===(T.meta||!1)}if(!f)return!0;const N=f.toLowerCase();return n.toLowerCase().includes(N)||u.toLowerCase().includes(N)||w(r).toLowerCase().includes(N)}),L=E.reduce((r,n)=>(r[n.context]||(r[n.context]=[]),r[n.context].push(n),r),{}),G=(r,n)=>{y.updateShortcut(r,n)},F=()=>{i==="keyboard"?confirm(s("terminal:controls.confirmResetKeyboard"))&&y.resetShortcuts():i==="mouse"?confirm(s("terminal:controls.confirmResetMouse"))&&y.resetMouseControls():i==="trackpad"&&confirm(s("terminal:controls.confirmResetTrackpad"))&&y.resetMouseControls()},P=["global","commander","toolbox"],I=l.bindings.reduce((r,n)=>(R.camera.includes(n.action)?r.camera.push(n):R.interaction.includes(n.action)&&r.interaction.push(n),r),{camera:[],interaction:[]});return e.jsx("div",{className:"shortcuts-modal-overlay",onMouseDown:D,onClick:M,children:e.jsxs("div",{className:"shortcuts-modal controls-modal",children:[e.jsxs("div",{className:"shortcuts-modal-header",children:[e.jsxs("div",{className:"shortcuts-modal-title",children:[e.jsxs("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[e.jsx("circle",{cx:"12",cy:"12",r:"3"}),e.jsx("path",{d:"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"})]}),e.jsx("span",{children:s("terminal:controls.title")})]}),e.jsx("button",{className:"shortcuts-modal-close",onClick:a,children:"×"})]}),e.jsxs("div",{className:"controls-main-tabs",children:[e.jsxs("button",{className:`controls-main-tab ${i==="keyboard"?"active":""}`,onClick:()=>j("keyboard"),children:[e.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[e.jsx("rect",{x:"2",y:"4",width:"20",height:"16",rx:"2"}),e.jsx("path",{d:"M6 8h.01M10 8h.01M14 8h.01M18 8h.01M6 12h.01M18 12h.01M8 16h8"})]}),s("terminal:controls.keyboard")]}),e.jsxs("button",{className:`controls-main-tab ${i==="mouse"?"active":""}`,onClick:()=>j("mouse"),children:[e.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[e.jsx("rect",{x:"6",y:"3",width:"12",height:"18",rx:"6"}),e.jsx("line",{x1:"12",y1:"7",x2:"12",y2:"11"})]}),s("terminal:controls.mouse")]}),e.jsxs("button",{className:`controls-main-tab ${i==="trackpad"?"active":""}`,onClick:()=>j("trackpad"),children:[e.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[e.jsx("rect",{x:"2",y:"4",width:"20",height:"16",rx:"2"}),e.jsx("circle",{cx:"12",cy:"12",r:"3"})]}),s("terminal:controls.trackpad")]})]}),i==="keyboard"&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"shortcuts-modal-toolbar",children:[d?e.jsxs("div",{className:"shortcuts-search find-by-shortcut-active",children:[e.jsxs("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[e.jsx("rect",{x:"2",y:"4",width:"20",height:"16",rx:"2"}),e.jsx("path",{d:"M6 8h.01M10 8h.01M14 8h.01M18 8h.01M6 12h.01M18 12h.01M8 16h8"})]}),c?e.jsxs("span",{className:"find-by-shortcut-display",children:[w({key:c.key,modifiers:c.modifiers}),e.jsx("button",{className:"shortcuts-search-clear",onClick:()=>v(null),children:"×"})]}):e.jsx("span",{className:"find-by-shortcut-prompt",children:s("terminal:controls.pressKeyCombination")})]}):e.jsxs("div",{className:"shortcuts-search",children:[e.jsxs("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[e.jsx("circle",{cx:"11",cy:"11",r:"8"}),e.jsx("path",{d:"M21 21l-4.35-4.35"})]}),e.jsx("input",{ref:x,type:"text",placeholder:s("terminal:controls.searchShortcuts"),value:f,onChange:r=>b(r.target.value),autoFocus:!0}),f&&e.jsx("button",{className:"shortcuts-search-clear",onClick:()=>b(""),children:"×"})]}),e.jsx("button",{className:`shortcuts-find-by-key-btn ${d?"active":""}`,onClick:()=>{p(!d),v(null),b("")},title:s(d?"terminal:controls.switchToTextSearch":"terminal:controls.findByPressingKeys"),children:e.jsxs("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[e.jsx("rect",{x:"2",y:"4",width:"20",height:"16",rx:"2"}),e.jsx("path",{d:"M6 8h.01M10 8h.01M14 8h.01M18 8h.01M6 12h.01M18 12h.01M8 16h8"})]})}),e.jsx("button",{className:"shortcuts-reset-all-btn",onClick:F,children:s("common:buttons.reset")})]}),e.jsxs("div",{className:"shortcuts-context-tabs",children:[e.jsxs("button",{className:`shortcuts-context-tab ${k==="all"?"active":""}`,onClick:()=>g("all"),children:[s("common:labels.all"),e.jsx("span",{className:"shortcuts-context-tab-count",children:E.length})]}),P.map(r=>{var u;const n=((u=L[r])==null?void 0:u.length)||0;return e.jsxs("button",{className:`shortcuts-context-tab ${k===r?"active":""}`,onClick:()=>g(r),children:[s(V[r]),e.jsx("span",{className:"shortcuts-context-tab-count",children:n})]},r)})]}),e.jsxs("div",{className:"shortcuts-modal-content",children:[P.map(r=>{const n=L[r]||[];return n.length===0||k!=="all"&&k!==r?null:e.jsxs("div",{className:"shortcuts-context-group",children:[k==="all"&&e.jsxs("div",{className:"shortcuts-context-header",children:[e.jsx("span",{className:"shortcuts-context-label",children:s(V[r])}),e.jsx("span",{className:"shortcuts-context-description",children:s(Q[r])})]}),e.jsx("div",{className:"shortcuts-grid",children:n.map(u=>e.jsxs("div",{className:"shortcut-item",children:[e.jsxs("div",{className:"shortcut-item-info",children:[e.jsx("span",{className:"shortcut-item-name",children:s(`terminal:controls.shortcuts.${u.id}.name`,{defaultValue:u.name})}),e.jsx("span",{className:"shortcut-item-description",children:s(`terminal:controls.shortcuts.${u.id}.description`,{defaultValue:u.description})})]}),e.jsx(q,{shortcut:u,onUpdate:N=>G(u.id,N)})]},u.id))})]},r)}),E.length===0&&e.jsx("div",{className:"shortcuts-empty",children:d?c?e.jsx("p",{children:s("terminal:controls.noShortcutBoundTo",{keys:w({key:c.key,modifiers:c.modifiers})})}):e.jsx("p",{children:s("terminal:controls.pressKeyCombinationToFind")}):e.jsx("p",{children:s("terminal:controls.noShortcutsFound",{query:f})})})]})]}),i==="mouse"&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"shortcuts-modal-toolbar",children:[e.jsx("span",{className:"mouse-controls-subtitle",children:s("terminal:controls.cameraAndInteraction")}),e.jsx("button",{className:"shortcuts-reset-all-btn",onClick:F,children:s("common:buttons.reset")})]}),e.jsxs("div",{className:"shortcuts-modal-content mouse-controls-content",children:[e.jsx("div",{className:"mouse-controls-bindings",children:Object.entries(I).map(([r,n])=>e.jsxs("div",{className:"shortcuts-context-group",children:[e.jsx("div",{className:"shortcuts-context-header",children:e.jsx("span",{className:"shortcuts-context-label",children:s(J[r])})}),e.jsx("div",{className:"shortcuts-grid",children:n.map(u=>e.jsx(ee,{binding:u},u.id))})]},r))}),e.jsxs("div",{className:"shortcuts-context-group",children:[e.jsxs("div",{className:"shortcuts-context-header",children:[e.jsx("span",{className:"shortcuts-context-label",children:s("terminal:controls.sensitivity")}),e.jsx("span",{className:"shortcuts-context-description",children:s("terminal:controls.adjustCameraSpeed")})]}),e.jsx(te,{sensitivity:l.sensitivity})]})]})]}),i==="trackpad"&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"shortcuts-modal-toolbar",children:[e.jsx("span",{className:"mouse-controls-subtitle",children:s("terminal:controls.trackpadGestureSettings")}),e.jsx("button",{className:"shortcuts-reset-all-btn",onClick:F,children:s("common:buttons.reset")})]}),e.jsx("div",{className:"shortcuts-modal-content trackpad-controls-content",children:e.jsx(se,{config:h})})]}),e.jsx("div",{className:"shortcuts-modal-footer",children:e.jsx("span",{className:"shortcuts-modal-hint",children:s(i==="keyboard"?d?"terminal:controls.hintFindByShortcut":"terminal:controls.hintKeyboard":i==="mouse"?"terminal:controls.hintMouse":"terminal:controls.hintTrackpad")})})]})})}function ee({binding:t}){const{t:a}=S(["terminal"]),s=W(),[o,l]=m.useState(!1),[h,i]=m.useState(null);m.useEffect(()=>{if(!o)return;const d=x=>{x.preventDefault(),x.stopPropagation();const M={0:"left",1:"middle",2:"right",3:"back",4:"forward"}[x.button];M&&i({button:M,modifiers:{ctrl:x.ctrlKey,alt:x.altKey,shift:x.shiftKey,meta:x.metaKey}})},p=x=>{x.key==="Escape"&&(x.preventDefault(),x.stopPropagation(),l(!1),i(null))},c=()=>{h&&z(s.bindings,h,t.id).length===0&&y.updateMouseBinding(t.id,{button:h.button,modifiers:h.modifiers}),l(!1),i(null)},v=setTimeout(()=>{document.addEventListener("mousedown",d,!0),document.addEventListener("keydown",p,!0),document.addEventListener("click",c,!0)},100);return()=>{clearTimeout(v),document.removeEventListener("mousedown",d,!0),document.removeEventListener("keydown",p,!0),document.removeEventListener("click",c,!0)}},[o,h,t.id,s.bindings]);const j=h?z(s.bindings,h,t.id):[],f=a(`terminal:controls.mouseBindings.${t.id}.name`,{defaultValue:t.name}),b=a(`terminal:controls.mouseBindings.${t.id}.description`,{defaultValue:t.description}),k=d=>a(`terminal:controls.mouseButtons.${d}`,{defaultValue:d}),g=h?A({...t,...h},k):A(t,k);return e.jsxs("div",{className:`shortcut-item ${t.enabled?"":"disabled"}`,children:[e.jsxs("div",{className:"shortcut-item-info",children:[e.jsx("span",{className:"shortcut-item-name",children:f}),e.jsx("span",{className:"shortcut-item-description",children:b})]}),e.jsxs("div",{className:"key-capture-container",children:[e.jsx("button",{className:`key-capture-input ${o?"capturing":""} ${j.length>0?"conflict":""}`,onClick:()=>l(!0),children:o?h?e.jsx("span",{className:"key-capture-value",style:{color:"#f1fa8c"},children:g}):e.jsx("span",{style:{color:"#6272a4",fontStyle:"italic"},children:a("terminal:controls.clickToCapture")}):e.jsx("span",{className:"key-capture-value",children:g})}),j.length>0&&e.jsxs("span",{className:"key-capture-conflict",children:[a("terminal:controls.conflicts"),": ",j.map(d=>a(`terminal:controls.mouseBindings.${d.id}.name`,{defaultValue:d.name})).join(", ")]})]})]})}function te({sensitivity:t}){const{t:a}=S(["terminal"]),s=m.useCallback((o,l)=>{y.updateCameraSensitivity({[o]:l})},[]);return e.jsxs("div",{className:"sensitivity-inline-settings",children:[e.jsxs("div",{className:"sensitivity-sliders",children:[e.jsxs("div",{className:"sensitivity-slider-row",children:[e.jsx("label",{children:a("terminal:controls.panSpeed")}),e.jsx("input",{type:"range",min:"0.1",max:"3",step:"0.1",value:t.panSpeed,onChange:o=>s("panSpeed",parseFloat(o.target.value))}),e.jsxs("span",{className:"sensitivity-slider-value",children:[t.panSpeed.toFixed(1),"x"]})]}),e.jsxs("div",{className:"sensitivity-slider-row",children:[e.jsx("label",{children:a("terminal:controls.orbitSpeed")}),e.jsx("input",{type:"range",min:"0.1",max:"3",step:"0.1",value:t.orbitSpeed,onChange:o=>s("orbitSpeed",parseFloat(o.target.value))}),e.jsxs("span",{className:"sensitivity-slider-value",children:[t.orbitSpeed.toFixed(1),"x"]})]}),e.jsxs("div",{className:"sensitivity-slider-row",children:[e.jsx("label",{children:a("terminal:controls.zoomSpeed")}),e.jsx("input",{type:"range",min:"0.1",max:"3",step:"0.1",value:t.zoomSpeed,onChange:o=>s("zoomSpeed",parseFloat(o.target.value))}),e.jsxs("span",{className:"sensitivity-slider-value",children:[t.zoomSpeed.toFixed(1),"x"]})]}),e.jsxs("div",{className:"sensitivity-slider-row",children:[e.jsx("label",{children:a("terminal:controls.smoothing")}),e.jsx("input",{type:"range",min:"0",max:"1",step:"0.1",value:t.smoothing,onChange:o=>s("smoothing",parseFloat(o.target.value))}),e.jsx("span",{className:"sensitivity-slider-value",children:t.smoothing.toFixed(1)})]})]}),e.jsxs("div",{className:"sensitivity-checkboxes-inline",children:[e.jsxs("label",{className:"sensitivity-checkbox-inline",children:[e.jsx("input",{type:"checkbox",checked:t.invertPanX,onChange:o=>s("invertPanX",o.target.checked)}),e.jsx("span",{children:a("terminal:controls.invertPanX")})]}),e.jsxs("label",{className:"sensitivity-checkbox-inline",children:[e.jsx("input",{type:"checkbox",checked:t.invertPanY,onChange:o=>s("invertPanY",o.target.checked)}),e.jsx("span",{children:a("terminal:controls.invertPanY")})]}),e.jsxs("label",{className:"sensitivity-checkbox-inline",children:[e.jsx("input",{type:"checkbox",checked:t.invertOrbitX,onChange:o=>s("invertOrbitX",o.target.checked)}),e.jsx("span",{children:a("terminal:controls.invertOrbitX")})]}),e.jsxs("label",{className:"sensitivity-checkbox-inline",children:[e.jsx("input",{type:"checkbox",checked:t.invertOrbitY,onChange:o=>s("invertOrbitY",o.target.checked)}),e.jsx("span",{children:a("terminal:controls.invertOrbitY")})]})]})]})}function se({config:t}){const{t:a}=S(["terminal"]),s=m.useCallback((l,h)=>{y.updateTrackpadConfig({[l]:h})},[]),o=m.useCallback((l,h)=>{y.updateTrackpadConfig({sensitivity:{[l]:h}})},[]);return e.jsxs("div",{className:"trackpad-settings",children:[e.jsxs("div",{className:"shortcuts-context-group",children:[e.jsxs("div",{className:"shortcuts-context-header",children:[e.jsx("span",{className:"shortcuts-context-label",children:a("terminal:controls.trackpadGestures")}),e.jsx("span",{className:"shortcuts-context-description",children:a("terminal:controls.enableTrackpadSupport")})]}),e.jsx("div",{className:"trackpad-toggle-row",children:e.jsxs("label",{className:"trackpad-toggle",children:[e.jsx("input",{type:"checkbox",checked:t.enabled,onChange:l=>s("enabled",l.target.checked)}),e.jsx("span",{className:"trackpad-toggle-label",children:a("terminal:controls.enableTrackpadGestures")})]})})]}),e.jsxs("div",{className:"shortcuts-context-group",children:[e.jsxs("div",{className:"shortcuts-context-header",children:[e.jsx("span",{className:"shortcuts-context-label",children:a("terminal:controls.gestureControls")}),e.jsx("span",{className:"shortcuts-context-description",children:a("terminal:controls.enableDisableGestures")})]}),e.jsxs("div",{className:"trackpad-gestures-grid",children:[e.jsxs("label",{className:`trackpad-gesture-item ${t.enabled?"":"disabled"}`,children:[e.jsx("input",{type:"checkbox",checked:t.pinchToZoom,onChange:l=>s("pinchToZoom",l.target.checked),disabled:!t.enabled}),e.jsxs("div",{className:"trackpad-gesture-info",children:[e.jsx("span",{className:"trackpad-gesture-name",children:a("terminal:controls.pinchToZoom")}),e.jsx("span",{className:"trackpad-gesture-desc",children:a("terminal:controls.pinchToZoomDesc")})]})]}),e.jsxs("label",{className:`trackpad-gesture-item ${t.enabled?"":"disabled"}`,children:[e.jsx("input",{type:"checkbox",checked:t.twoFingerPan,onChange:l=>s("twoFingerPan",l.target.checked),disabled:!t.enabled}),e.jsxs("div",{className:"trackpad-gesture-info",children:[e.jsx("span",{className:"trackpad-gesture-name",children:a("terminal:controls.twoFingerPan")}),e.jsx("span",{className:"trackpad-gesture-desc",children:a("terminal:controls.twoFingerPanDesc")})]})]}),e.jsxs("label",{className:`trackpad-gesture-item ${t.enabled?"":"disabled"}`,children:[e.jsx("input",{type:"checkbox",checked:t.shiftTwoFingerOrbit,onChange:l=>s("shiftTwoFingerOrbit",l.target.checked),disabled:!t.enabled}),e.jsxs("div",{className:"trackpad-gesture-info",children:[e.jsx("span",{className:"trackpad-gesture-name",children:a("terminal:controls.shiftTwoFingerOrbit")}),e.jsx("span",{className:"trackpad-gesture-desc",children:a("terminal:controls.shiftTwoFingerOrbitDesc")})]})]})]})]}),e.jsxs("div",{className:"shortcuts-context-group",children:[e.jsxs("div",{className:"shortcuts-context-header",children:[e.jsx("span",{className:"shortcuts-context-label",children:a("terminal:controls.sensitivity")}),e.jsx("span",{className:"shortcuts-context-description",children:a("terminal:controls.adjustGestureSensitivity")})]}),e.jsxs("div",{className:"trackpad-sensitivity-sliders",children:[e.jsxs("div",{className:`trackpad-slider-row ${!t.enabled||!t.pinchToZoom?"disabled":""}`,children:[e.jsx("label",{children:a("terminal:controls.zoom")}),e.jsx("input",{type:"range",min:"0.1",max:"3",step:"0.1",value:t.sensitivity.zoom,onChange:l=>o("zoom",parseFloat(l.target.value)),disabled:!t.enabled||!t.pinchToZoom}),e.jsxs("span",{className:"trackpad-slider-value",children:[t.sensitivity.zoom.toFixed(1),"x"]})]}),e.jsxs("div",{className:`trackpad-slider-row ${!t.enabled||!t.twoFingerPan?"disabled":""}`,children:[e.jsx("label",{children:a("terminal:controls.pan")}),e.jsx("input",{type:"range",min:"0.1",max:"3",step:"0.1",value:t.sensitivity.pan,onChange:l=>o("pan",parseFloat(l.target.value)),disabled:!t.enabled||!t.twoFingerPan}),e.jsxs("span",{className:"trackpad-slider-value",children:[t.sensitivity.pan.toFixed(1),"x"]})]}),e.jsxs("div",{className:`trackpad-slider-row ${!t.enabled||!t.shiftTwoFingerOrbit?"disabled":""}`,children:[e.jsx("label",{children:a("terminal:controls.orbit")}),e.jsx("input",{type:"range",min:"0.1",max:"3",step:"0.1",value:t.sensitivity.orbit,onChange:l=>o("orbit",parseFloat(l.target.value)),disabled:!t.enabled||!t.shiftTwoFingerOrbit}),e.jsxs("span",{className:"trackpad-slider-value",children:[t.sensitivity.orbit.toFixed(1),"x"]})]})]})]})]})}export{oe as ControlsModal};