@kafca/agentdock 0.1.18 → 0.1.20

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 (54) hide show
  1. package/dist/renderer/assets/{Badge-BIuWuNsC.js → Badge-C1f8JpRb.js} +1 -1
  2. package/dist/renderer/assets/Chat-S50iFg3I.js +2 -0
  3. package/dist/renderer/assets/{ChatMarkdown-C-BlK1yX.js → ChatMarkdown-VWL9Vnpz.js} +4 -4
  4. package/dist/renderer/assets/{Config-DSqG53SF.js → Config-Yc9ExUI6.js} +1 -1
  5. package/dist/renderer/assets/{CronList-BP6eOFIl.js → CronList-DTboWazi.js} +1 -1
  6. package/dist/renderer/assets/{Dashboard-yNvLuZ9h.js → Dashboard-CzXCvZVR.js} +2 -2
  7. package/dist/renderer/assets/{EmptyState-DrCleE-v.js → EmptyState-9FbY2o6z.js} +1 -1
  8. package/dist/renderer/assets/{KnowledgeDetail-DTpTUVcH.js → KnowledgeDetail-D_BDc4Zt.js} +1 -1
  9. package/dist/renderer/assets/{KnowledgeHome-gN27MIm8.js → KnowledgeHome-DsUA_BCR.js} +1 -1
  10. package/dist/renderer/assets/{Logs-DSEpn2aP.js → Logs-DDAmR67f.js} +1 -1
  11. package/dist/renderer/assets/{Modal-DRn2QI8p.js → Modal-Bt5VBC6S.js} +1 -1
  12. package/dist/renderer/assets/{Page-DFPQdZab.js → Page-Da_Kc6Qm.js} +1 -1
  13. package/dist/renderer/assets/{ProjectDetail-CPAlBCe1.js → ProjectDetail-B4Ps80yv.js} +1 -1
  14. package/dist/renderer/assets/{ProjectList-D1YqnA_p.js → ProjectList-BWTP9npz.js} +1 -1
  15. package/dist/renderer/assets/{Select-BOoDNxoe.js → Select-DWs3hVUh.js} +1 -1
  16. package/dist/renderer/assets/SessionChat-DOhUnOfH.js +1 -0
  17. package/dist/renderer/assets/{SessionList-EwaQ7I9m.js → SessionList-ChZYFXuo.js} +1 -1
  18. package/dist/renderer/assets/{ThreadChat-rmnoSpy7.js → ThreadChat-nhnikHnk.js} +5 -5
  19. package/dist/renderer/assets/Workspace-pq_JYdTN.js +14 -0
  20. package/dist/renderer/assets/{arrow-left-DLijH4Ma.js → arrow-left-CR39hNvq.js} +1 -1
  21. package/dist/renderer/assets/{book-open-C39iVsTl.js → book-open-Bot2WKl8.js} +1 -1
  22. package/dist/renderer/assets/{check-CIzPQt9l.js → check-Drbl2nU3.js} +1 -1
  23. package/dist/renderer/assets/{circle-C22V7KHC.js → circle-BXFfAwnA.js} +1 -1
  24. package/dist/renderer/assets/{desktop-SfDg5_P-.js → desktop-5k3W66fH.js} +1 -1
  25. package/dist/renderer/assets/{funnel-Uqe7wSsV.js → funnel-BNKYan_N.js} +1 -1
  26. package/dist/renderer/assets/{heart-BAV26w-r.js → heart-wUOSwQE-.js} +1 -1
  27. package/dist/renderer/assets/index-3CzgyQYB.css +11 -0
  28. package/dist/renderer/assets/{index-HCsfse2M.js → index-CmOphqrf.js} +1 -1
  29. package/dist/renderer/assets/{index-bNsLuLje.js → index-DT5la73R.js} +2 -2
  30. package/dist/renderer/assets/{pencil-DrFGidyy.js → pencil-D7-8ggue.js} +1 -1
  31. package/dist/renderer/assets/{play-CUr5dfBa.js → play-BCYOAfet.js} +1 -1
  32. package/dist/renderer/assets/{plug-CLHivmxo.js → plug-Dsik74Yl.js} +1 -1
  33. package/dist/renderer/assets/{plus-B96DRLtO.js → plus-CKrU26_l.js} +1 -1
  34. package/dist/renderer/assets/projects-CGDtNb0M.js +1 -0
  35. package/dist/renderer/assets/{save-BGOA2LdX.js → save-q7M2_eAl.js} +1 -1
  36. package/dist/renderer/assets/{search-bWm0yagY.js → search-BptY4_MT.js} +1 -1
  37. package/dist/renderer/assets/{server-wa53brxF.js → server-DSSEF0D8.js} +1 -1
  38. package/dist/renderer/assets/{session-utils-7J-omgrc.js → session-utils-BFuqPZ5x.js} +1 -1
  39. package/dist/renderer/assets/{sessions-BIMMb8YC.js → sessions-CrWo3g3u.js} +1 -1
  40. package/dist/renderer/assets/{shield-check-MBIcswd2.js → shield-check-CCU722RQ.js} +1 -1
  41. package/dist/renderer/assets/{trash-2-CEov4VLH.js → trash-2-DaeeiV8x.js} +1 -1
  42. package/dist/renderer/assets/{user-B93Dje9X.js → user-Bkz8J7Yw.js} +1 -1
  43. package/dist/renderer/assets/{wifi-off-cCUI9WRl.js → wifi-off-P8xdIOHU.js} +1 -1
  44. package/dist/renderer/index.html +2 -2
  45. package/dist-electron/electron/local-core-refactor.test.js +426 -0
  46. package/dist-electron/electron/plugin-kernel.test.js +34 -0
  47. package/dist-electron/services/local-ai-core/src/gateway/local-core-lark-gateway.js +216 -50
  48. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +34 -0
  49. package/package.json +1 -1
  50. package/dist/renderer/assets/Chat-bI6xVd_L.js +0 -2
  51. package/dist/renderer/assets/SessionChat-BoqnI_Sb.js +0 -1
  52. package/dist/renderer/assets/Workspace-CIoiGaPJ.js +0 -14
  53. package/dist/renderer/assets/index-CVrF_DwQ.css +0 -11
  54. package/dist/renderer/assets/projects-DC4slOOB.js +0 -1
@@ -40,9 +40,9 @@ const desktop_js_1 = require("../../../../shared/desktop.js");
40
40
  const PAIRING_EXPIRY_MS = 10 * 60 * 1000;
41
41
  class LocalCoreLarkGateway extends node_events_1.EventEmitter {
42
42
  options;
43
- // Lark card action callbacks are unreliable in current WS-only setup (code 200340).
44
- // Keep permission approval on explicit text commands: allow all / allow / deny.
45
- enableCardActions = false;
43
+ // Lark returns 200340 when card action events are not enabled in the app's
44
+ // event subscription. Keep card actions opt-in so text approval always works.
45
+ defaultCardActionsEnabled = false;
46
46
  // Keep permission state in a dedicated card to avoid mixing order in the main reply card.
47
47
  mirrorPermissionStateInMainCard = false;
48
48
  runtime = new Map();
@@ -79,6 +79,7 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
79
79
  status: 'disabled',
80
80
  connected: false,
81
81
  appId: binding.appId,
82
+ cardActionsEnabled: binding.cardActionsEnabled,
82
83
  });
83
84
  }
84
85
  continue;
@@ -272,7 +273,7 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
272
273
  this.options.store.clearPlatformThreadMessageId(route.workspaceId, route.chatId, route.platformUserId);
273
274
  }
274
275
  if (event.type === 'buttons') {
275
- const permissionCard = this.renderPermissionCard(turn, event);
276
+ const permissionCard = this.renderPermissionCard(turn, event, Boolean(state.cardActionsEnabled));
276
277
  if (permissionCard.text || permissionCard.buttonRows.length > 0) {
277
278
  if (!turn.permissionMessageId) {
278
279
  const createdId = await this.sendTextAsCard(state, route.chatId, permissionCard.text, permissionCard.buttonRows, sessionKey, binding.thread_id);
@@ -290,30 +291,35 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
290
291
  return;
291
292
  }
292
293
  this.consumeBridgeEvent(turn, event);
293
- const rendered = this.renderTurnCard(turn);
294
+ const rendered = this.renderBridgeEventMessage(turn, event);
294
295
  if (!rendered.text && rendered.buttonRows.length === 0) {
295
296
  this.options.log?.(`localcore-lark bridge event produced empty render for sessionKey=${sessionKey} type=${event.type}`);
296
297
  return;
297
298
  }
299
+ const existingMessageId = this.getRenderedMessageId(turn, rendered);
298
300
  const shouldThrottle = event.type === 'update_message' &&
299
- turn.messageId &&
300
- Date.now() - turn.lastPatchedAt < 900;
301
- this.options.log?.(`localcore-lark bridge event type=${event.type} sessionKey=${sessionKey} hasMessageId=${Boolean(turn.messageId)} throttle=${shouldThrottle}`);
301
+ rendered.isFinal &&
302
+ existingMessageId &&
303
+ Date.now() - (turn.lastPatchedAtByMessageId[existingMessageId] || 0) < 900;
304
+ this.options.log?.(`localcore-lark bridge event type=${event.type} sessionKey=${sessionKey} hasMessageId=${Boolean(existingMessageId)} throttle=${shouldThrottle}`);
302
305
  if (shouldThrottle) {
303
306
  return;
304
307
  }
305
- if (!turn.messageId) {
308
+ if (!existingMessageId) {
306
309
  const createdId = await this.sendTextAsCard(state, route.chatId, rendered.text, rendered.buttonRows, sessionKey, binding.thread_id);
307
310
  if (createdId) {
308
- turn.messageId = createdId;
309
- this.options.store.updatePlatformThreadMessageId(route.workspaceId, route.chatId, route.platformUserId, createdId);
311
+ this.setRenderedMessageId(turn, rendered, createdId);
312
+ if (rendered.isFinal) {
313
+ this.options.store.updatePlatformThreadMessageId(route.workspaceId, route.chatId, route.platformUserId, createdId);
314
+ }
310
315
  this.options.log?.(`localcore-lark sent new card message ${createdId} for sessionKey=${sessionKey}`);
311
316
  }
312
317
  return;
313
318
  }
314
- await this.patchTextCard(state, turn.messageId, rendered.text, rendered.buttonRows, sessionKey, binding.thread_id);
319
+ await this.patchTextCard(state, existingMessageId, rendered.text, rendered.buttonRows, sessionKey, binding.thread_id);
315
320
  turn.lastPatchedAt = Date.now();
316
- this.options.log?.(`localcore-lark patched card message ${turn.messageId} for sessionKey=${sessionKey}`);
321
+ turn.lastPatchedAtByMessageId[existingMessageId] = turn.lastPatchedAt;
322
+ this.options.log?.(`localcore-lark patched card message ${existingMessageId} for sessionKey=${sessionKey}`);
317
323
  }
318
324
  catch (error) {
319
325
  this.options.log?.(`localcore-lark bridge send failed for sessionKey=${sessionKey}: ${error instanceof Error ? error.message : String(error)}`);
@@ -496,10 +502,12 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
496
502
  status: 'starting',
497
503
  connected: false,
498
504
  appId: binding.appId,
505
+ cardActionsEnabled: binding.cardActionsEnabled,
499
506
  };
500
507
  this.runtime.set(binding.workspaceId, status);
501
508
  this.notifyRuntimeStateChanged();
502
509
  try {
510
+ this.options.log?.(`localcore-lark starting workspace=${binding.workspaceId} app=${this.maskLarkAppId(binding.appId)} cardActions=${binding.cardActionsEnabled}`);
503
511
  const mod = await this.getLarkModule();
504
512
  status.client = new mod.Client({
505
513
  appId: binding.appId,
@@ -514,8 +522,11 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
514
522
  });
515
523
  status.eventDispatcher.register({
516
524
  'im.message.receive_v1': async (data) => {
517
- this.options.log?.(`localcore-lark received im.message.receive_v1 for ${binding.workspaceId}`);
518
- await this.handleMessageEvent(binding.workspaceId, data);
525
+ this.options.log?.(`localcore-lark received im.message.receive_v1 for ${binding.workspaceId}: ${this.summarizeLarkEvent(data)}`);
526
+ void this.handleMessageEvent(binding.workspaceId, data).catch((error) => {
527
+ this.options.log?.(`localcore-lark inbound message failed for ${binding.workspaceId}: ${error instanceof Error ? error.message : String(error)}`);
528
+ });
529
+ return {};
519
530
  },
520
531
  'card.action.trigger': async (data) => {
521
532
  this.options.log?.(`localcore-lark received card.action.trigger for ${binding.workspaceId}`);
@@ -523,12 +534,15 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
523
534
  return {};
524
535
  },
525
536
  });
537
+ this.options.log?.(`localcore-lark event dispatcher registered for ${binding.workspaceId}`);
526
538
  status.wsClient = new mod.WSClient({
527
539
  appId: binding.appId,
528
540
  appSecret: binding.appSecret,
529
541
  domain: mod.Domain.Feishu,
530
542
  loggerLevel: mod.LoggerLevel.info,
531
543
  });
544
+ this.attachWsDiagnostics(binding.workspaceId, status.wsClient);
545
+ this.options.log?.(`localcore-lark ws starting for ${binding.workspaceId}`);
532
546
  await status.wsClient.start({
533
547
  eventDispatcher: status.eventDispatcher,
534
548
  });
@@ -536,6 +550,7 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
536
550
  status.connected = true;
537
551
  status.connectedAt = new Date().toISOString();
538
552
  status.lastError = undefined;
553
+ this.options.log?.(`localcore-lark ws ready for ${binding.workspaceId}`);
539
554
  }
540
555
  catch (error) {
541
556
  status.status = 'error';
@@ -550,10 +565,13 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
550
565
  if (!state) {
551
566
  return;
552
567
  }
568
+ this.options.log?.(`localcore-lark stopping workspace=${workspaceId} status=${state.status}`);
553
569
  try {
554
570
  await state.wsClient?.stop?.();
571
+ this.options.log?.(`localcore-lark stopped workspace=${workspaceId}`);
555
572
  }
556
- catch {
573
+ catch (error) {
574
+ this.options.log?.(`localcore-lark stop failed for ${workspaceId}: ${error instanceof Error ? error.message : String(error)}`);
557
575
  // Best effort: current SDK versions may not implement stop().
558
576
  }
559
577
  this.runtime.set(workspaceId, {
@@ -562,6 +580,7 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
562
580
  status: 'stopped',
563
581
  connected: false,
564
582
  appId: state.appId,
583
+ cardActionsEnabled: state.cardActionsEnabled,
565
584
  });
566
585
  this.notifyRuntimeStateChanged();
567
586
  }
@@ -579,6 +598,7 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
579
598
  : data;
580
599
  const message = payload?.message;
581
600
  const sender = payload?.sender;
601
+ this.options.log?.(`localcore-lark handling message event for ${workspaceId}: ${this.summarizeLarkPayload(payload)}`);
582
602
  if (!message || !sender) {
583
603
  this.options.log?.(`localcore-lark ignored event without message/sender for ${workspaceId}: ${JSON.stringify(Object.keys(data || {}))}`);
584
604
  return;
@@ -592,13 +612,13 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
592
612
  }
593
613
  const text = String(parsedContent.text || '').trim();
594
614
  if (!text) {
595
- this.options.log?.(`localcore-lark ignored non-text message for ${workspaceId}: ${String(message.message_type || 'unknown')}`);
615
+ this.options.log?.(`localcore-lark ignored non-text message for ${workspaceId}: type=${String(message.message_type || 'unknown')} contentKeys=${JSON.stringify(Object.keys(parsedContent))}`);
596
616
  return;
597
617
  }
598
618
  const platformUserId = String(sender.sender_id?.user_id || sender.sender_id?.open_id || '').trim();
599
619
  const chatId = String(message.chat_id || platformUserId).trim();
600
620
  if (!platformUserId || !chatId) {
601
- this.options.log?.(`localcore-lark ignored message without sender/chat for ${workspaceId}`);
621
+ this.options.log?.(`localcore-lark ignored message without sender/chat for ${workspaceId}: senderKeys=${JSON.stringify(Object.keys(sender?.sender_id || {}))} chat=${String(message.chat_id || '')}`);
602
622
  return;
603
623
  }
604
624
  this.options.log?.(`localcore-lark inbound message for ${workspaceId}: chat=${chatId} user=${platformUserId} text=${JSON.stringify(text.slice(0, 120))}`);
@@ -613,6 +633,67 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
613
633
  messageId: String(message.message_id || (0, node_crypto_1.randomUUID)()),
614
634
  });
615
635
  }
636
+ attachWsDiagnostics(workspaceId, wsClient) {
637
+ const on = typeof wsClient?.on === 'function' ? wsClient.on.bind(wsClient) : null;
638
+ if (!on) {
639
+ this.options.log?.(`localcore-lark ws diagnostics unavailable for ${workspaceId}: client has no on()`);
640
+ return;
641
+ }
642
+ for (const eventName of ['open', 'connect', 'connected', 'ready', 'close', 'closed', 'disconnect', 'error', 'reconnect']) {
643
+ try {
644
+ on(eventName, (...args) => {
645
+ this.options.log?.(`localcore-lark ws event ${eventName} for ${workspaceId}: ${this.summarizeWsArgs(args)}`);
646
+ });
647
+ }
648
+ catch (error) {
649
+ this.options.log?.(`localcore-lark ws diagnostic hook failed for ${workspaceId} event=${eventName}: ${error instanceof Error ? error.message : String(error)}`);
650
+ }
651
+ }
652
+ }
653
+ summarizeLarkEvent(data) {
654
+ const payload = (data?.event && typeof data.event === 'object')
655
+ ? data.event
656
+ : data;
657
+ return this.summarizeLarkPayload(payload);
658
+ }
659
+ summarizeLarkPayload(payload) {
660
+ const message = payload?.message || {};
661
+ const sender = payload?.sender || {};
662
+ const senderId = sender?.sender_id || {};
663
+ const content = typeof message.content === 'string' ? message.content : '';
664
+ return [
665
+ `message=${String(message.message_id || '') || 'missing'}`,
666
+ `type=${String(message.message_type || '') || 'missing'}`,
667
+ `chat=${String(message.chat_id || '') || 'missing'}`,
668
+ `sender=${String(senderId.user_id || senderId.open_id || '') || 'missing'}`,
669
+ `contentBytes=${Buffer.byteLength(content, 'utf8')}`,
670
+ `keys=${JSON.stringify(Object.keys(payload || {}))}`,
671
+ ].join(' ');
672
+ }
673
+ summarizeWsArgs(args) {
674
+ if (args.length === 0) {
675
+ return 'no-args';
676
+ }
677
+ return args.map((arg) => {
678
+ if (arg instanceof Error) {
679
+ return `${arg.name}: ${arg.message}`;
680
+ }
681
+ if (typeof arg === 'string') {
682
+ return arg.slice(0, 200);
683
+ }
684
+ if (arg && typeof arg === 'object') {
685
+ return JSON.stringify(Object.keys(arg));
686
+ }
687
+ return String(arg);
688
+ }).join(' ');
689
+ }
690
+ maskLarkAppId(appId) {
691
+ const value = String(appId || '').trim();
692
+ if (value.length <= 8) {
693
+ return value ? '***' : '';
694
+ }
695
+ return `${value.slice(0, 6)}...${value.slice(-4)}`;
696
+ }
616
697
  collectBindings(config) {
617
698
  const projects = Array.isArray(config?.projects) ? config.projects : [];
618
699
  return projects.flatMap((project) => {
@@ -633,6 +714,10 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
633
714
  verificationToken: String(platform.options.verification_token || '').trim(),
634
715
  autoApprove: String(platform.options.auto_approve || '').trim().toLowerCase() === 'true'
635
716
  || platform.options.auto_approve === true,
717
+ cardActionsEnabled: String(platform.options.card_actions || platform.options.enable_card_actions || '').trim().toLowerCase() === 'true'
718
+ || platform.options.card_actions === true
719
+ || platform.options.enable_card_actions === true
720
+ || this.defaultCardActionsEnabled,
636
721
  enabled: Boolean(String(platform.options.app_id || '').trim() && String(platform.options.app_secret || '').trim()),
637
722
  project,
638
723
  }));
@@ -656,12 +741,12 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
656
741
  tag: 'button',
657
742
  text: {
658
743
  tag: 'plain_text',
659
- content: button.text,
744
+ content: this.formatPermissionButtonLabel(button),
660
745
  },
661
- type: index === 0 ? 'primary' : 'default',
746
+ type: this.resolveLarkButtonType(button, index),
662
747
  value: {
663
748
  action: 'permission_response',
664
- response: button.data,
749
+ response: (0, desktop_js_1.normalizePermissionResponse)(button.data) || button.data,
665
750
  session_key: sessionKey || '',
666
751
  thread_id: threadId || '',
667
752
  },
@@ -741,11 +826,13 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
741
826
  processing: false,
742
827
  previewText: '',
743
828
  finalText: '',
829
+ progressMessageIds: {},
744
830
  thinkingSteps: [],
745
831
  toolCalls: [],
746
832
  statusLines: [],
747
833
  buttonRows: [],
748
834
  lastPatchedAt: 0,
835
+ lastPatchedAtByMessageId: {},
749
836
  };
750
837
  this.outboundTurns.set(sessionKey, turn);
751
838
  return turn;
@@ -755,12 +842,14 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
755
842
  if (existing) {
756
843
  if (messageId && !existing.messageId) {
757
844
  existing.messageId = messageId;
845
+ existing.finalMessageId = messageId;
758
846
  }
759
847
  return existing;
760
848
  }
761
849
  const turn = this.createTurnState(sessionKey);
762
850
  if (messageId) {
763
851
  turn.messageId = messageId;
852
+ turn.finalMessageId = messageId;
764
853
  }
765
854
  return turn;
766
855
  }
@@ -770,6 +859,8 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
770
859
  // Always start a fresh card for each generation phase.
771
860
  // This keeps Feishu message order aligned with user-visible timeline.
772
861
  turn.messageId = undefined;
862
+ turn.finalMessageId = undefined;
863
+ turn.progressMessageIds = {};
773
864
  turn.processing = true;
774
865
  turn.permissionMessageId = undefined;
775
866
  turn.previewText = '';
@@ -778,6 +869,7 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
778
869
  turn.toolCalls = [];
779
870
  turn.buttonRows = [];
780
871
  turn.statusLines = [];
872
+ turn.lastPatchedAtByMessageId = {};
781
873
  return;
782
874
  }
783
875
  if (event.type === 'typing_stop') {
@@ -796,15 +888,7 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
796
888
  }
797
889
  if (event.type === 'buttons') {
798
890
  turn.awaitingPermission = true;
799
- turn.buttonRows = this.enableCardActions && Array.isArray(event.buttonRows)
800
- ? event.buttonRows
801
- .map((row) => Array.isArray(row)
802
- ? row
803
- .filter((button) => Boolean(button?.text && button?.data))
804
- .map((button) => ({ text: button.text, data: button.data }))
805
- : [])
806
- .filter((row) => row.length > 0)
807
- : [];
891
+ turn.buttonRows = [];
808
892
  if (this.mirrorPermissionStateInMainCard && content) {
809
893
  this.pushUnique(turn.statusLines, `等待确认: ${content}`);
810
894
  }
@@ -828,42 +912,101 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
828
912
  turn.finalText = content;
829
913
  turn.previewText = content;
830
914
  }
831
- renderTurnCard(turn) {
832
- const sections = [];
833
- if (turn.thinkingSteps.length > 0) {
834
- sections.push(`**思考过程**\n${turn.thinkingSteps.map((step) => `• ${step.replace(/\s+/g, ' ').trim()}`).join('\n')}`);
835
- }
836
- if (turn.finalText) {
837
- sections.push(turn.finalText);
915
+ renderBridgeEventMessage(turn, event) {
916
+ const content = String(event.content || '').trim();
917
+ if (event.type === 'preview_start' || event.type === 'update_message') {
918
+ if (content.startsWith('💭 ')) {
919
+ return this.renderProgressMessage(event.previewHandle || 'thinking-preview', content);
920
+ }
921
+ if (content.startsWith('🔧 ')) {
922
+ return this.renderProgressMessage(this.progressKey('tool', event), content);
923
+ }
924
+ return {
925
+ key: 'final',
926
+ text: content,
927
+ buttonRows: turn.buttonRows,
928
+ isFinal: true,
929
+ };
838
930
  }
839
- else if (turn.previewText) {
840
- sections.push(turn.previewText);
931
+ if (event.type === 'status') {
932
+ return this.renderProgressMessage(this.progressKey('status', event), content.startsWith('⏳ ') ? content : `⏳ ${content}`);
841
933
  }
842
- else if (turn.processing && turn.toolCalls.length > 0) {
843
- sections.push(`**处理中**\n${this.formatCompactToolLines(turn.toolCalls).join('\n')}`);
934
+ if (event.type === 'reply') {
935
+ if (content.startsWith('💭 ')) {
936
+ return this.renderProgressMessage(this.progressKey('thinking', event), content);
937
+ }
938
+ if (content.startsWith('🔧 ')) {
939
+ return this.renderProgressMessage(this.progressKey('tool', event), content);
940
+ }
941
+ if (content.startsWith('⏳ ') || content.startsWith('📤 ')) {
942
+ return this.renderProgressMessage(this.progressKey('status', event), content);
943
+ }
944
+ return {
945
+ key: 'final',
946
+ text: content,
947
+ buttonRows: turn.buttonRows,
948
+ isFinal: true,
949
+ };
844
950
  }
845
- else if (turn.processing) {
846
- sections.push('**处理中**\n正在思考...');
951
+ if (event.type === 'typing_start' || event.type === 'typing_stop') {
952
+ return { key: 'noop', text: '', buttonRows: [], isFinal: false };
847
953
  }
848
954
  return {
849
- text: sections.join('\n\n').trim(),
850
- buttonRows: turn.buttonRows,
955
+ key: 'noop',
956
+ text: '',
957
+ buttonRows: [],
958
+ isFinal: false,
959
+ };
960
+ }
961
+ renderProgressMessage(key, text) {
962
+ return {
963
+ key,
964
+ text,
965
+ buttonRows: [],
966
+ isFinal: false,
851
967
  };
852
968
  }
853
- renderPermissionCard(turn, event) {
969
+ progressKey(prefix, event) {
970
+ const stableId = String(event.messageId || event.previewHandle || '').trim();
971
+ if (stableId) {
972
+ return `${prefix}:${stableId}`;
973
+ }
974
+ const content = String(event.content || '').trim().replace(/\s+/g, ' ');
975
+ return `${prefix}:${content.slice(0, 120)}`;
976
+ }
977
+ getRenderedMessageId(turn, rendered) {
978
+ if (rendered.isFinal) {
979
+ return turn.finalMessageId || turn.messageId;
980
+ }
981
+ return turn.progressMessageIds[rendered.key];
982
+ }
983
+ setRenderedMessageId(turn, rendered, messageId) {
984
+ if (rendered.isFinal) {
985
+ turn.finalMessageId = messageId;
986
+ turn.messageId = messageId;
987
+ return;
988
+ }
989
+ turn.progressMessageIds[rendered.key] = messageId;
990
+ }
991
+ renderPermissionCard(turn, event, cardActionsEnabled) {
854
992
  const summary = this.buildPermissionSummary(turn, String(event.content || '').trim());
855
993
  const sections = [
856
994
  '**需要工具确认**',
857
995
  summary.command ? `\`${summary.command}\`` : '',
858
996
  summary.reason || '',
859
- '回复:`allow` / `allow all` / `deny`',
997
+ cardActionsEnabled
998
+ ? '也可以直接回复:`allow` / `allow all` / `deny`'
999
+ : '请直接回复:`allow` / `allow all` / `deny`',
860
1000
  ].filter(Boolean);
861
- const buttonRows = this.enableCardActions && Array.isArray(event.buttonRows)
1001
+ const buttonRows = cardActionsEnabled && Array.isArray(event.buttonRows)
862
1002
  ? event.buttonRows
863
1003
  .map((row) => Array.isArray(row)
864
1004
  ? row
865
1005
  .filter((button) => Boolean(button?.text && button?.data))
866
- .map((button) => ({ text: button.text, data: button.data }))
1006
+ .map((button) => ({
1007
+ text: this.formatPermissionButtonLabel(button),
1008
+ data: (0, desktop_js_1.normalizePermissionResponse)(button.data) || button.data,
1009
+ }))
867
1010
  : [])
868
1011
  .filter((row) => row.length > 0)
869
1012
  : [];
@@ -892,7 +1035,7 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
892
1035
  if (!value || value.action !== 'permission_response') {
893
1036
  return;
894
1037
  }
895
- const response = String(value.response || '').trim();
1038
+ const response = (0, desktop_js_1.normalizePermissionResponse)(String(value.response || '').trim()) || String(value.response || '').trim();
896
1039
  const threadId = String(value.thread_id || '').trim();
897
1040
  if (!response || !threadId) {
898
1041
  return;
@@ -918,6 +1061,29 @@ class LocalCoreLarkGateway extends node_events_1.EventEmitter {
918
1061
  target.splice(0, target.length - 6);
919
1062
  }
920
1063
  }
1064
+ formatPermissionButtonLabel(button) {
1065
+ const response = (0, desktop_js_1.normalizePermissionResponse)(button.data) || (0, desktop_js_1.normalizePermissionResponse)(button.text);
1066
+ switch (response) {
1067
+ case 'allow':
1068
+ return '允许一次';
1069
+ case 'allow all':
1070
+ return '始终允许';
1071
+ case 'deny':
1072
+ return '拒绝';
1073
+ default:
1074
+ return button.text;
1075
+ }
1076
+ }
1077
+ resolveLarkButtonType(button, index) {
1078
+ const response = (0, desktop_js_1.normalizePermissionResponse)(button.data) || (0, desktop_js_1.normalizePermissionResponse)(button.text);
1079
+ if (response === 'deny') {
1080
+ return 'danger';
1081
+ }
1082
+ if (response === 'allow') {
1083
+ return 'primary';
1084
+ }
1085
+ return index === 0 ? 'primary' : 'default';
1086
+ }
921
1087
  buildPermissionSummary(turn, rawContent) {
922
1088
  const lastTool = turn.toolCalls[turn.toolCalls.length - 1] || '';
923
1089
  const [commandPart = '', reasonPart = ''] = lastTool.split(/\s+-\s+/, 2);
@@ -17,6 +17,8 @@ class LocalCoreController extends node_events_1.EventEmitter {
17
17
  runtime;
18
18
  runtimeDetection;
19
19
  busUnsubscribers = [];
20
+ pendingLogs = [];
21
+ handlingLog = false;
20
22
  constructor(userDataPath, runtime) {
21
23
  super();
22
24
  this.userDataPath = userDataPath;
@@ -38,6 +40,7 @@ class LocalCoreController extends node_events_1.EventEmitter {
38
40
  log: (message) => this.handleLog(message),
39
41
  emit: (event) => this.handleRuntimeDetectionEvent(event),
40
42
  });
43
+ this.flushPendingLogs();
41
44
  this.busUnsubscribers.push(this.kernel.context.bus.on('platform.bridge.updated', (event) => {
42
45
  this.emit('bridge', event);
43
46
  }), this.kernel.context.bus.on('scheduler.job.updated', (job) => {
@@ -382,8 +385,39 @@ class LocalCoreController extends node_events_1.EventEmitter {
382
385
  this.emit('runtime', await this.getRuntimeStatus());
383
386
  }
384
387
  handleLog(message) {
388
+ if (this.handlingLog) {
389
+ return;
390
+ }
391
+ const state = this.state;
392
+ if (!state) {
393
+ this.pendingLogs.push(message);
394
+ this.emit('logs', message);
395
+ return;
396
+ }
397
+ this.handlingLog = true;
398
+ try {
399
+ state.pushLog?.(message);
400
+ }
401
+ finally {
402
+ this.handlingLog = false;
403
+ }
385
404
  this.emit('logs', message);
386
405
  }
406
+ flushPendingLogs() {
407
+ if (this.pendingLogs.length === 0) {
408
+ return;
409
+ }
410
+ const logs = this.pendingLogs.splice(0);
411
+ this.handlingLog = true;
412
+ try {
413
+ for (const message of logs) {
414
+ this.state.pushLog?.(message);
415
+ }
416
+ }
417
+ finally {
418
+ this.handlingLog = false;
419
+ }
420
+ }
387
421
  resolveChannelRuntime(platform) {
388
422
  if (platform === this.channelRuntime.platform) {
389
423
  return this.channelRuntime;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kafca/agentdock",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "type": "module",
5
5
  "main": "dist-electron/electron/main.js",
6
6
  "bin": {
@@ -1,2 +0,0 @@
1
- import{u as ze,t as Me,r,j as s,C as ye,I as pe,M as K,B as C,w as ee,R as De,T as Le}from"./index-bNsLuLje.js";import{M as je}from"./Modal-DRn2QI8p.js";import{E as U}from"./EmptyState-DrCleE-v.js";import{C as Ae,S as Be}from"./ChatMarkdown-C-BlK1yX.js";import{l as Ke}from"./projects-DC4slOOB.js";import{l as Ue,g as $e,s as qe,a as be,r as Fe,d as Oe}from"./sessions-BIMMb8YC.js";import{s as He,a as ve,b as v,L as $,t as Ve}from"./session-utils-7J-omgrc.js";import{S as We}from"./search-bWm0yagY.js";import{P as Ge}from"./pencil-DrFGidyy.js";import{T as Je}from"./trash-2-CEov4VLH.js";import{B as Qe,U as Xe}from"./user-B93Dje9X.js";import"./index-HCsfse2M.js";import"./check-CIzPQt9l.js";const Ye=1500,Ze=9e4;function es(a){return String(a||"").startsWith("web:")}function ss(a){return(a||[]).map((c,p)=>({id:`${c.timestamp||p}-${c.role}-${p}`,role:c.role==="user"?"user":"assistant",content:c.content,timestamp:c.timestamp,kind:c.kind}))}function ts(a){return a.map(c=>`${c.role}:${c.kind||"final"}:${c.timestamp||""}:${c.content}`).join(`
2
- `)}function as(a){return a.filter(c=>c.role!=="user").length}function ns(a,c){var p;return(p=a.last_message)!=null&&p.content?a.last_message.content.replace(/\n/g," "):c("sessions.noMessages")}function we(a){return(a instanceof Error?a.message:String(a||"")).trim().toLowerCase().includes("no active session found")}function rs(a,c){return(a instanceof Error?a.message:String(a||"")).trim().toLowerCase()?we(a)?c("sessions.sessionUnavailable"):a instanceof Error?a.message:c("sessions.sendFailed"):c("sessions.sendFailed")}function js(){const{t:a}=ze(),[c,p]=Me(),[se,ke]=r.useState([]),[te,ae]=r.useState({}),[i,G]=r.useState(c.get("project")||""),[j,_]=r.useState([]),[n,h]=r.useState(null),[R,w]=r.useState([]),[J,ne]=r.useState(""),[I,Q]=r.useState(""),[Se,re]=r.useState(!0),[Ne,ie]=r.useState(!1),[Ce,le]=r.useState(!1),[X,oe]=r.useState(!1),[E,P]=r.useState("idle"),[ce,k]=r.useState(""),[T,q]=r.useState(null),[F,Y]=r.useState(""),[z,O]=r.useState(null),[de,H]=r.useState(null),me=r.useRef(null),x=r.useRef({project:"",sessionId:""}),V=r.useRef(null),W=r.useRef(null),b=c.get("project")||"",M=c.get("session")||"",ue=r.useMemo(()=>new Set(Object.values(te)),[te]),S=r.useMemo(()=>{const e=J.trim().toLowerCase();return e?j.filter(t=>He(t,e)):j},[J,j]),D=!!(n&&(n.live||ue.has(n.id))),Ee=!!(i&&D&&I.trim()&&!X&&E!=="sending"&&E!=="activating"&&E!=="polling"),f=r.useCallback((e,t)=>{const l=new URLSearchParams;e&&l.set("project",e),t&&l.set("session",t),p(l,{replace:!0})},[p]),d=r.useCallback((e="idle")=>{V.current&&(window.clearTimeout(V.current),V.current=null),W.current=null,P(e)},[]),xe=r.useCallback(e=>{_(t=>{const l=t.some(o=>o.id===e.id)?t.map(o=>o.id===e.id?{...o,...e}:o):[...t,e];return ve(l)})},[]),fe=r.useCallback(async()=>{re(!0);try{const{projects:e}=await Ke();ke(e||[])}finally{re(!1)}},[]),u=r.useCallback(async e=>{if(!e)return _([]),ae({}),[];ie(!0);try{const{sessions:t,active_keys:l}=await Ue(e),o=ve((t||[]).filter(m=>!es(m.session_key)));return ae(l||{}),x.current.project===e&&_(o),o}finally{ie(!1)}},[]),N=r.useCallback(async(e,t,l)=>{if(!e||!t)return h(null),w([]),null;l!=null&&l.silent||le(!0);try{const o=await $e(e,t,200);return x.current.project!==e||x.current.sessionId!==t||(xe(o),h({id:o.id,project:e,sessionKey:o.session_key,name:o.name||v(o),live:o.live,isDraft:!1,detail:o}),w(ss(o.history)),k("")),o}catch(o){return x.current.project===e&&x.current.sessionId===t&&k(o instanceof Error?o.message:a("sessions.loadFailed")),null}finally{l!=null&&l.silent||le(!1)}},[xe,a]),Z=r.useCallback(e=>{W.current=e,V.current=window.setTimeout(async()=>{const t=W.current;if(!t)return;if(Date.now()-t.startedAt>=Ze){d("timed_out"),k(a("sessions.pollTimeout")),await u(t.project);return}const l=await N(t.project,t.sessionId,{silent:!0});if(!l||!W.current){d(),await u(t.project);return}if(x.current.project!==t.project||x.current.sessionId!==t.sessionId){d();return}const o=ts(l.history),m=as(l.history)>t.assistantCountBefore,g=m&&o===t.lastSignature?t.stableCount+1:0,B={...t,lastSignature:o,stableCount:g};if(m&&g>=2){d(),await u(t.project);return}P("polling"),Z(B)},Ye)},[N,u,d,a]),he=r.useCallback((e,t,l)=>{d(),P("polling"),Z({project:e,sessionId:t,assistantCountBefore:l,stableCount:0,lastSignature:"",startedAt:Date.now()})},[Z,d]),Pe=r.useCallback(async e=>{if(d(),G(e),ne(""),k(""),Q(""),!e){x.current={project:"",sessionId:""},h(null),w([]),_([]),f("");return}x.current={project:e,sessionId:""},h(null),w([]),f(e),await u(e)},[u,d,f]),L=r.useCallback(async e=>{if(e.isDraft||e.live||!e.id)return e;P("activating");try{await qe(e.project,{session_key:e.sessionKey,session_id:e.id}),await u(e.project);const t=await N(e.project,e.id,{silent:!0}),l={...e,live:(t==null?void 0:t.live)??!0,isDraft:!1,detail:t??e.detail,name:(t==null?void 0:t.name)||e.name};return h(l),l}finally{P("idle")}},[N,u]),A=r.useCallback(async(e,t)=>{d(),k(""),x.current={project:e,sessionId:t.id},G(e),f(e,t.id),h({id:t.id,project:e,sessionKey:t.session_key,name:v(t),live:t.live,isDraft:!1,detail:null});const l=await N(e,t.id);l&&!l.live&&await L({id:l.id,project:e,sessionKey:l.session_key,name:l.name||v(l),live:l.live,isDraft:!1,detail:l})},[L,N,d,f]),_e=r.useCallback(async()=>{d(),i&&(await u(i),n!=null&&n.id&&(x.current={project:i,sessionId:n.id},await N(i,n.id)))},[n==null?void 0:n.id,N,u,i,d]),ge=r.useCallback(async()=>{if(!I.trim()||!i)return;if(!D){k(a("sessions.sessionUnavailable"));return}const e=I.trim(),t=`${crypto.randomUUID()}-user`,l={id:t,role:"user",content:e,timestamp:new Date().toISOString()};Q(""),oe(!0),P("sending"),k("");try{const o=n||S[0];if(!o)throw new Error(a("sessions.noAvailableChatTarget"));let m=n&&n.id===o.id?n:{id:o.id,project:i,sessionKey:"session_key"in o?o.session_key:o.sessionKey,name:"session_key"in o?v(o):o.name,live:o.live,isDraft:!1,detail:null};(!n||n.id!==m.id)&&(x.current={project:i,sessionId:m.id},f(i,m.id),h(m)),m=await L(m),w(y=>[...y,l]);try{await be(i,{session_key:m.sessionKey,message:e})}catch(y){if(!we(y)||!m.id)throw y;m=await L({...m,live:!1}),await be(i,{session_key:m.sessionKey,message:e})}const g=m.id;if(!g)throw new Error(a("sessions.createFailed"));const B=R.filter(y=>y.role!=="user").length;x.current={project:i,sessionId:g},h(y=>y&&{...y,id:g,isDraft:!1}),f(i,g),he(i,g,B),await u(i)}catch(o){d(),w(g=>g.filter(B=>B.id!==t));const m=rs(o,a);k(m)}finally{oe(!1)}},[n,D,L,I,S,R,u,i,he,d,a,f]),Re=r.useCallback(async()=>{if(!(!T||!i||!F.trim())){H("rename");try{await Fe(i,T.id,{name:F.trim()});const t=(await u(i)).find(l=>l.id===T.id);t&&(n==null?void 0:n.id)===t.id&&h(l=>l&&{...l,name:v(t)}),q(null),Y("")}finally{H(null)}}},[n==null?void 0:n.id,u,F,T,i]),Ie=r.useCallback(async()=>{if(!(!z||!i)){H("delete");try{await Oe(i,z.id);const e=await u(i);(n==null?void 0:n.id)===z.id&&(d(),x.current={project:i,sessionId:""},h(null),w([]),f(i)),e.length===0&&w([]),O(null)}finally{H(null)}}},[n==null?void 0:n.id,z,u,i,d,f]);r.useEffect(()=>{fe()},[fe]),r.useEffect(()=>{b&&(i&&i===b||G(b))},[b,i]),r.useEffect(()=>{if(!i){_([]);return}x.current.project=i,u(i)},[u,i]),r.useEffect(()=>{!i||M||n!=null&&n.id||S.length===0||A(i,S[0])},[n==null?void 0:n.id,S,A,M,i]),r.useEffect(()=>{if(!b||!M||i!==b)return;const e=j.find(t=>t.id===M);if(!e){j.length>0&&f(b,j[0].id);return}(n==null?void 0:n.id)!==e.id&&A(b,e)},[n==null?void 0:n.id,A,b,M,i,j,f]),r.useEffect(()=>{var e;(e=me.current)==null||e.scrollIntoView({behavior:"smooth"})},[R]),r.useEffect(()=>()=>d(),[d]);const Te=(n==null?void 0:n.name)||a("sessions.activeChat");return s.jsxs("div",{className:"grid min-h-[calc(100vh-8rem)] grid-cols-1 gap-4 xl:grid-cols-[22rem_minmax(0,1fr)] animate-fade-in",children:[s.jsxs(ye,{className:"flex flex-col gap-4 p-4 xl:h-[calc(100vh-8rem)] xl:overflow-hidden",children:[s.jsx("div",{className:"flex items-center gap-3",children:s.jsxs("div",{className:"flex-1",children:[s.jsx("p",{className:"text-xs font-medium uppercase tracking-[0.2em] text-gray-400",children:a("sessions.selectProject")}),s.jsxs("select",{value:i,onChange:e=>void Pe(e.target.value),className:"mt-2 w-full rounded-xl border border-gray-300/90 bg-white/90 px-3 py-2 text-sm text-gray-900 transition-all focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/45 dark:border-white/[0.1] dark:bg-[rgba(0,0,0,0.45)] dark:text-white",children:[s.jsx("option",{value:"",children:a("sessions.allProjects")}),se.map(e=>s.jsx("option",{value:e.name,children:e.name},e.name))]})]})}),s.jsxs("div",{className:"relative",children:[s.jsx(pe,{value:J,onChange:e=>ne(e.target.value),placeholder:a("common.search"),className:"pl-9","aria-label":a("common.search")}),s.jsx(We,{size:16,className:"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"})]}),s.jsxs("div",{className:"flex min-h-0 flex-1 flex-col overflow-hidden",children:[s.jsxs("div",{className:"mb-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400",children:[s.jsx("span",{children:a("sessions.historyTitle")}),s.jsx("span",{children:S.length})]}),s.jsx("div",{className:"min-h-0 flex-1 overflow-y-auto space-y-2 pr-1",children:Se?s.jsx("div",{className:"flex h-32 items-center justify-center text-gray-400",children:s.jsx($,{className:"animate-spin",size:18})}):i?Ne&&j.length===0?s.jsx("div",{className:"flex h-32 items-center justify-center text-gray-400",children:s.jsx($,{className:"animate-spin",size:18})}):j.length===0?s.jsx(U,{message:a("sessions.noSessions"),icon:K}):S.length===0?s.jsx(U,{message:a("sessions.noSessions"),icon:K}):S.map(e=>{const t=e.id===(n==null?void 0:n.id);return s.jsxs("div",{className:ee("rounded-2xl border p-3 transition-all",t?"border-accent/45 bg-accent/10 shadow-[0_0_20px_-12px_rgba(139,92,246,0.55)]":"border-gray-200/80 bg-white/70 hover:border-accent/30 dark:border-white/[0.08] dark:bg-white/[0.03]"),children:[s.jsxs("button",{type:"button",onClick:()=>void A(i,e),className:"w-full text-left","data-testid":"web-chat-session-open",children:[s.jsxs("div",{className:"flex items-start justify-between gap-2",children:[s.jsxs("div",{className:"min-w-0",children:[s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("span",{className:"truncate text-sm font-medium text-gray-900 dark:text-white",children:v(e)}),e.live||ue.has(e.id)?s.jsx("span",{className:"rounded-full bg-violet-50 px-2 py-0.5 text-[10px] text-violet-600 dark:bg-violet-950/20 dark:text-violet-300",children:"live"}):s.jsx("span",{className:"rounded-full bg-gray-100 px-2 py-0.5 text-[10px] text-gray-400 dark:bg-white/[0.06] dark:text-gray-500",children:"history"})]}),s.jsx("p",{className:"mt-1 truncate text-xs text-gray-500 dark:text-gray-400",children:Ve(e.updated_at||e.created_at,a("sessions.justNow"))})]}),s.jsx("span",{className:"shrink-0 text-[10px] text-gray-400",children:e.history_count})]}),s.jsx("p",{className:"mt-2 line-clamp-2 text-xs text-gray-500 dark:text-gray-400",children:ns(e,a)})]}),s.jsxs("div",{className:"mt-3 flex items-center justify-end gap-1",children:[s.jsx(C,{size:"sm",variant:"ghost",onClick:()=>{q({id:e.id,name:v(e)}),Y(v(e))},"data-testid":"web-chat-session-rename",children:s.jsx(Ge,{size:14})}),s.jsx(C,{size:"sm",variant:"ghost",onClick:()=>O({id:e.id,name:v(e)}),"data-testid":"web-chat-session-delete",children:s.jsx(Je,{size:14})})]})]},e.id)}):s.jsx(U,{message:a("sessions.projectRequired"),icon:K})})]})]}),s.jsxs(ye,{className:"flex min-h-[32rem] flex-col p-0 xl:h-[calc(100vh-8rem)]",children:[s.jsxs("div",{className:"flex items-center justify-between gap-3 border-b border-gray-200/80 px-5 py-4 dark:border-white/[0.08]",children:[s.jsxs("div",{className:"min-w-0",children:[s.jsx("div",{className:"flex items-center gap-2",children:s.jsx("h2",{className:"truncate text-lg font-semibold text-gray-900 dark:text-white",children:Te})}),s.jsx("p",{className:"mt-1 truncate text-sm text-gray-500 dark:text-gray-400",children:n?n.project:a("sessions.sendHint")})]}),s.jsxs(C,{size:"sm",variant:"secondary",onClick:()=>void _e(),"data-testid":"web-chat-refresh",children:[s.jsx(De,{size:14})," ",a("common.refresh")]})]}),s.jsx("div",{className:"flex-1 overflow-y-auto px-5 py-5",children:i?Ce?s.jsx("div",{className:"flex h-full items-center justify-center text-gray-400",children:s.jsx($,{className:"animate-spin",size:22})}):R.length===0?s.jsx("div",{className:"flex h-full items-center justify-center",children:s.jsx(U,{message:a(n?"sessions.noMessages":"sessions.sendHint"),icon:K})}):s.jsxs("div",{className:"space-y-5",children:[R.map(e=>{const t=e.role==="user";return s.jsxs("div",{className:ee("flex gap-3",t?"justify-end":"justify-start"),children:[!t&&s.jsx("div",{className:"mt-1 flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-accent/10",children:s.jsx(Qe,{size:16,className:"text-accent"})}),s.jsx("div",{className:ee("max-w-[85%] rounded-2xl px-5 py-3.5 text-sm",t?"bg-accent text-white rounded-br-md":"border border-gray-200 bg-white text-gray-900 shadow-sm dark:border-white/[0.08] dark:bg-[rgba(0,0,0,0.42)] dark:text-gray-100 rounded-bl-md"),children:s.jsx(Ae,{content:e.content,isUser:t})}),t&&s.jsx("div",{className:"mt-1 flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gray-200 dark:bg-white/[0.08]",children:s.jsx(Xe,{size:16,className:"text-gray-500 dark:text-gray-300"})})]},e.id)}),s.jsx("div",{ref:me})]}):s.jsx("div",{className:"flex h-full items-center justify-center",children:s.jsx(U,{message:a("sessions.emptySelection"),icon:K})})}),s.jsxs("div",{className:"border-t border-gray-200/80 px-5 py-4 dark:border-white/[0.08]",children:[ce&&s.jsx("div",{className:"mb-3 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-600 dark:border-red-900/40 dark:bg-red-950/20 dark:text-red-300",children:ce}),E==="polling"&&s.jsxs("div",{className:"mb-3 flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400",children:[s.jsx($,{size:14,className:"animate-spin"}),s.jsx("span",{children:a("sessions.waitingForReply")})]}),E==="activating"&&s.jsxs("div",{className:"mb-3 flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400",children:[s.jsx($,{size:14,className:"animate-spin"}),s.jsx("span",{children:a("sessions.preparingChat")})]}),s.jsxs("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-end",children:[s.jsx("div",{className:"flex-1",children:s.jsx(Le,{value:I,onChange:e=>Q(e.target.value),onKeyDown:e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),ge())},rows:3,placeholder:a(i?"sessions.messageInput":"sessions.projectRequired"),disabled:!i||!D||X||E==="polling"||E==="activating","data-testid":"web-chat-input"})}),s.jsxs(C,{onClick:()=>void ge(),disabled:!Ee,loading:X,size:"lg",className:"sm:self-stretch","data-testid":"web-chat-send",children:[s.jsx(Be,{size:16})," ",a("sessions.send")]})]}),!D&&n&&s.jsx("div",{className:"mt-3 rounded-xl border border-gray-200 bg-gray-50 px-4 py-3 text-sm text-gray-500 dark:border-white/[0.08] dark:bg-white/[0.03] dark:text-gray-400",children:a("sessions.notLiveHint")})]})]}),s.jsx(je,{open:!!T,onClose:()=>q(null),title:a("sessions.renameTitle"),children:s.jsxs("div",{className:"space-y-4",children:[s.jsx(pe,{value:F,onChange:e=>Y(e.target.value),placeholder:a("sessions.renamePlaceholder"),"data-testid":"web-chat-rename-input"}),s.jsxs("div",{className:"flex justify-end gap-2",children:[s.jsx(C,{variant:"secondary",onClick:()=>q(null),children:a("common.cancel")}),s.jsx(C,{onClick:()=>void Re(),loading:de==="rename","data-testid":"web-chat-rename-save",children:a("common.save")})]})]})}),s.jsx(je,{open:!!z,onClose:()=>O(null),title:a("sessions.deleteConfirmTitle"),children:s.jsxs("div",{className:"space-y-4",children:[s.jsx("p",{className:"text-sm text-gray-500 dark:text-gray-400",children:a("sessions.deleteConfirmBody")}),s.jsxs("div",{className:"flex justify-end gap-2",children:[s.jsx(C,{variant:"secondary",onClick:()=>O(null),children:a("common.cancel")}),s.jsx(C,{variant:"danger",onClick:()=>void Ie(),loading:de==="delete","data-testid":"web-chat-delete-confirm",children:a("common.delete")})]})]})})]})}export{js as default};
@@ -1 +0,0 @@
1
- import{u as C,z,r,j as e,L as S,B as j,w as g,I as B}from"./index-bNsLuLje.js";import{B as y}from"./Badge-BIuWuNsC.js";import{g as E,a as I}from"./sessions-BIMMb8YC.js";import{C as L,S as M}from"./ChatMarkdown-C-BlK1yX.js";import{A as R}from"./arrow-left-DLijH4Ma.js";import{C as D}from"./circle-C22V7KHC.js";import{W as v,R as K}from"./wifi-off-cCUI9WRl.js";import{B as U,U as W}from"./user-B93Dje9X.js";import"./check-CIzPQt9l.js";function G(){var h;const{t:a}=C(),{project:n,id:l}=z(),[s,N]=r.useState(null),[o,m]=r.useState(""),[d,x]=r.useState(!1),[b,f]=r.useState(!0),u=r.useRef(null),c=r.useCallback(async()=>{if(!(!n||!l))try{f(!0);const t=await E(n,l,200);N(t)}finally{f(!1)}},[n,l]);r.useEffect(()=>{c()},[c]),r.useEffect(()=>{var t;(t=u.current)==null||t.scrollIntoView({behavior:"smooth"})},[s==null?void 0:s.history]);const p=async()=>{if(!o.trim()||!n||!s)return;const t=o.trim();m(""),x(!0);try{await I(n,{session_key:s.session_key,message:t}),setTimeout(c,1500)}finally{x(!1)}},w=t=>{t.key==="Enter"&&!t.shiftKey&&(t.preventDefault(),p())};return b&&!s?e.jsx("div",{className:"flex items-center justify-center h-64 text-muted-foreground animate-pulse",children:"Loading..."}):e.jsxs("div",{className:"flex flex-col h-[calc(100vh-8rem)] animate-fade-in",children:[e.jsxs("div",{className:"flex items-center justify-between pb-4 border-b",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(S,{to:"/sessions",className:"p-2 rounded-md hover:bg-accent/10 transition-colors",children:e.jsx(R,{size:18,className:"text-muted-foreground"})}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("h2",{className:"text-lg font-semibold text-foreground",children:(s==null?void 0:s.name)||l}),s!=null&&s.live?e.jsxs("span",{className:"flex items-center gap-1 text-[10px] text-primary bg-primary/10 px-1.5 py-0.5 rounded-full",children:[e.jsx(D,{size:5,className:"fill-current"})," live"]}):e.jsxs("span",{className:"flex items-center gap-1 text-[10px] text-muted-foreground bg-muted px-1.5 py-0.5 rounded-full",children:[e.jsx(v,{size:9})," ",a("sessions.offline")]})]}),e.jsxs("div",{className:"flex items-center gap-2 mt-0.5",children:[e.jsx(y,{children:n}),(s==null?void 0:s.platform)&&e.jsx(y,{variant:"info",children:s.platform}),e.jsx("span",{className:"text-xs text-muted-foreground",children:s==null?void 0:s.session_key})]})]})]}),e.jsxs(j,{size:"sm",variant:"ghost",onClick:c,children:[e.jsx(K,{size:14})," ",a("common.refresh")]})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto py-6 space-y-5",children:[(!(s!=null&&s.history)||s.history.length===0)&&e.jsx("p",{className:"text-center text-sm text-muted-foreground py-12",children:a("sessions.noMessages")}),(h=s==null?void 0:s.history)==null?void 0:h.map((t,k)=>{const i=t.role==="user";return e.jsxs("div",{className:g("flex gap-3",i?"justify-end":"justify-start"),children:[!i&&e.jsx("div",{className:"w-8 h-8 rounded-md bg-primary/10 flex items-center justify-center shrink-0 mt-1",children:e.jsx(U,{size:16,className:"text-primary"})}),e.jsx("div",{className:g("rounded-lg px-5 py-3.5 text-sm",i?"max-w-[70%] bg-primary text-primary-foreground rounded-br-md":"max-w-[85%] bg-card border text-card-foreground rounded-bl-md shadow-sm"),children:e.jsx(L,{content:t.content,isUser:i})}),i&&e.jsx("div",{className:"w-8 h-8 rounded-md bg-muted flex items-center justify-center shrink-0 mt-1",children:e.jsx(W,{size:16,className:"text-muted-foreground"})})]},k)}),e.jsx("div",{ref:u})]}),e.jsx("div",{className:"border-t pt-4",children:s!=null&&s.live?e.jsxs("div",{className:"flex gap-3",children:[e.jsx(B,{value:o,onChange:t=>m(t.target.value),onKeyDown:w,placeholder:a("sessions.messageInput"),className:"flex-1",disabled:d}),e.jsx(j,{onClick:p,disabled:d||!o.trim(),size:"icon","aria-label":a("sessions.messageInput"),children:d?e.jsxs("svg",{className:"animate-spin h-4 w-4",viewBox:"0 0 24 24",children:[e.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4",fill:"none"}),e.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"})]}):e.jsx(M,{size:18})})]}):e.jsxs("div",{className:"flex items-center gap-2 px-4 py-3 text-sm text-muted-foreground bg-muted rounded-lg",children:[e.jsx(v,{size:14}),e.jsx("span",{children:a("sessions.notLiveHint")})]})})]})}export{G as default};