@co0ontty/wand 1.10.0 → 1.14.3

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.
@@ -1080,6 +1080,46 @@
1080
1080
  padding-right: 64px;
1081
1081
  }
1082
1082
 
1083
+ .session-title-row {
1084
+ display: flex;
1085
+ align-items: flex-start;
1086
+ justify-content: space-between;
1087
+ gap: 8px;
1088
+ }
1089
+
1090
+ .session-title-row .session-title,
1091
+ .session-title-row .session-command {
1092
+ flex: 1;
1093
+ min-width: 0;
1094
+ }
1095
+
1096
+ .session-time {
1097
+ flex-shrink: 0;
1098
+ font-size: 0.6875rem;
1099
+ color: var(--text-muted);
1100
+ white-space: nowrap;
1101
+ line-height: 1.4;
1102
+ padding-top: 1px;
1103
+ }
1104
+
1105
+ .session-activity {
1106
+ font-size: 0.6875rem;
1107
+ color: var(--accent);
1108
+ white-space: nowrap;
1109
+ overflow: hidden;
1110
+ text-overflow: ellipsis;
1111
+ line-height: 1.3;
1112
+ opacity: 0.85;
1113
+ }
1114
+
1115
+ .session-recovery-hint {
1116
+ font-size: 0.625rem;
1117
+ color: var(--text-muted);
1118
+ background: rgba(150, 118, 85, 0.08);
1119
+ padding: 1px 5px;
1120
+ border-radius: 3px;
1121
+ }
1122
+
1083
1123
  /* ===== 会话操作按钮 ===== */
1084
1124
  .session-actions {
1085
1125
  position: absolute;
@@ -2102,6 +2142,7 @@
2102
2142
  padding: 10px;
2103
2143
  overflow: hidden;
2104
2144
  min-height: 0;
2145
+ min-width: 0;
2105
2146
  margin: 0 6px 6px;
2106
2147
  border-radius: var(--radius-md);
2107
2148
  border: 1px solid rgba(140, 110, 85, 0.35);
@@ -2118,22 +2159,16 @@
2118
2159
  align-items: stretch;
2119
2160
  }
2120
2161
 
2121
- .terminal-container > #output {
2122
- flex: 1;
2123
- min-height: 0;
2124
- position: relative;
2125
- }
2126
-
2127
- .terminal-container > #output .xterm {
2162
+ .terminal-container > .xterm {
2128
2163
  position: absolute;
2129
2164
  top: 0;
2130
2165
  left: 0;
2131
2166
  right: 0;
2132
2167
  bottom: 0;
2133
- }
2134
-
2135
- .terminal-container > #output .xterm-viewport {
2136
- background: transparent !important;
2168
+ width: 100%;
2169
+ height: 100%;
2170
+ padding: 0;
2171
+ overflow: hidden;
2137
2172
  }
2138
2173
 
2139
2174
  .terminal-container .xterm-viewport {
@@ -2144,8 +2179,9 @@
2144
2179
  display: none;
2145
2180
  }
2146
2181
 
2147
- .terminal-container .xterm {
2148
- padding: 0;
2182
+ .terminal-container .xterm-screen {
2183
+ max-width: 100%;
2184
+ overflow: hidden;
2149
2185
  }
2150
2186
 
2151
2187
  /* ===== 自定义终端滚动条 ===== */
@@ -2847,6 +2883,28 @@
2847
2883
  scrollbar-gutter: stable;
2848
2884
  }
2849
2885
 
2886
+ /* ===== 历史消息懒加载 ===== */
2887
+ .chat-load-more {
2888
+ display: flex;
2889
+ justify-content: center;
2890
+ padding: 8px 0;
2891
+ }
2892
+ .chat-load-more-btn {
2893
+ background: var(--bg-hover);
2894
+ border: 1px solid var(--border-subtle);
2895
+ border-radius: var(--radius-md);
2896
+ color: var(--text-secondary);
2897
+ font-size: 0.75rem;
2898
+ padding: 6px 16px;
2899
+ cursor: pointer;
2900
+ transition: all 0.2s ease;
2901
+ }
2902
+ .chat-load-more-btn:hover {
2903
+ background: var(--accent-muted);
2904
+ color: var(--accent);
2905
+ border-color: var(--accent);
2906
+ }
2907
+
2850
2908
  /* ===== 消息动画 ===== */
2851
2909
  @keyframes messageSlide {
2852
2910
  from {
@@ -3878,6 +3936,38 @@
3878
3936
  font-style: italic;
3879
3937
  }
3880
3938
 
3939
+ /* ── Tool content lazy-load states ── */
3940
+ .tool-content-loading {
3941
+ font-size: 0.75rem;
3942
+ color: var(--text-muted);
3943
+ font-style: italic;
3944
+ padding: 8px 0;
3945
+ }
3946
+ .tool-content-loading::before {
3947
+ content: "";
3948
+ display: inline-block;
3949
+ width: 12px;
3950
+ height: 12px;
3951
+ border: 2px solid var(--text-muted);
3952
+ border-top-color: transparent;
3953
+ border-radius: 50%;
3954
+ animation: tool-content-spin 0.6s linear infinite;
3955
+ vertical-align: middle;
3956
+ margin-right: 6px;
3957
+ }
3958
+ @keyframes tool-content-spin {
3959
+ to { transform: rotate(360deg); }
3960
+ }
3961
+ .tool-content-error {
3962
+ font-size: 0.75rem;
3963
+ color: var(--error);
3964
+ padding: 8px 0;
3965
+ cursor: pointer;
3966
+ }
3967
+ .tool-content-error:hover {
3968
+ text-decoration: underline;
3969
+ }
3970
+
3881
3971
  /* ── Inline Terminal Display (Bash) ── */
3882
3972
  .inline-terminal {
3883
3973
  margin: 3px 0;
@@ -5506,9 +5596,19 @@
5506
5596
  }
5507
5597
  .session-modal .modal-body {
5508
5598
  padding-top: 16px;
5599
+ padding-bottom: 8px;
5509
5600
  }
5510
5601
  .session-modal .field:last-of-type {
5511
- margin-bottom: 14px;
5602
+ margin-bottom: 6px;
5603
+ }
5604
+ .session-modal .modal-footer {
5605
+ flex-shrink: 0;
5606
+ padding: 12px 22px 18px;
5607
+ border-top: 1px solid var(--border-subtle);
5608
+ }
5609
+ .session-modal .modal-footer .error-message {
5610
+ margin-top: 8px;
5611
+ margin-bottom: 0;
5512
5612
  }
5513
5613
  .mode-cards {
5514
5614
  display: flex;
@@ -6068,8 +6168,8 @@
6068
6168
  gap: 1px;
6069
6169
  }
6070
6170
  .terminal-scale-overlay-btn {
6071
- min-width: 20px;
6072
- height: 20px;
6171
+ min-width: 26px;
6172
+ height: 26px;
6073
6173
  font-size: 0.75rem;
6074
6174
  }
6075
6175
  .terminal-scale-overlay-label {
@@ -6268,8 +6368,8 @@
6268
6368
  display: inline-flex;
6269
6369
  align-items: center;
6270
6370
  justify-content: center;
6271
- width: 22px;
6272
- height: 22px;
6371
+ width: 28px;
6372
+ height: 28px;
6273
6373
  border: none;
6274
6374
  border-radius: 50%;
6275
6375
  background: transparent;
@@ -6303,14 +6403,14 @@
6303
6403
  display: none;
6304
6404
  }
6305
6405
  .inline-shortcuts-expanded-row .shortcut-key {
6306
- height: 22px;
6307
- min-width: 22px;
6406
+ height: 28px;
6407
+ min-width: 28px;
6308
6408
  font-size: 0.5625rem;
6309
6409
  padding: 0 4px;
6310
6410
  flex: 0 0 auto;
6311
6411
  }
6312
6412
  .inline-shortcuts-expanded-row .shortcut-key.shortcut-dir {
6313
- min-width: 20px;
6413
+ min-width: 24px;
6314
6414
  }
6315
6415
 
6316
6416
  /* 回到底部按钮 - 紧凑 */
@@ -6342,7 +6442,7 @@
6342
6442
  .mini-keyboard-header { padding: 6px 10px 4px; }
6343
6443
  .mini-keyboard-body { padding: 6px; gap: 4px; }
6344
6444
  .mk-row { gap: 3px; }
6345
- .mk-key { min-width: 36px; height: 32px; font-size: 0.625rem; border-radius: 6px; }
6445
+ .mk-key { min-width: 38px; height: 34px; font-size: 0.625rem; border-radius: 6px; }
6346
6446
 
6347
6447
  .floating-pad {
6348
6448
  right: 0;
@@ -6482,6 +6582,7 @@
6482
6582
  .modal-header { padding: 10px 12px; min-height: 40px; }
6483
6583
  .modal-body { padding: 10px 12px; }
6484
6584
  .modal-footer { padding: 8px 12px; }
6585
+ .session-modal .modal-footer { padding: 10px 12px 14px; }
6485
6586
 
6486
6587
  .tool-card {
6487
6588
  padding: 10px;
@@ -6511,6 +6612,11 @@
6511
6612
  .blank-chat-hint { font-size: 0.6875rem; }
6512
6613
  .mode-btn-group { gap: 4px; margin-top: 8px; }
6513
6614
  .mode-btn { padding: 5px 10px; font-size: 0.6875rem; min-height: 36px; }
6615
+
6616
+ /* 防止小屏溢出 */
6617
+ .notification-bubble { max-width: calc(100vw - 24px); }
6618
+ .blank-chat-cwd-dropdown { min-width: 0; width: calc(100vw - 32px); max-width: 320px; }
6619
+ .toast-message { max-width: calc(100vw - 32px); }
6514
6620
  }
6515
6621
 
6516
6622
  /* iPhone 14/15 等标准屏幕 (390px - 420px) - 使用基础移动端样式,略微调整 */
@@ -6532,8 +6638,8 @@
6532
6638
  @media (max-width: 390px) {
6533
6639
  /* 继承 max-width: 640px 的样式,此处仅做小屏微调 */
6534
6640
  .floating-sidebar-toggle {
6535
- width: 26px;
6536
- height: 26px;
6641
+ width: 28px;
6642
+ height: 28px;
6537
6643
  }
6538
6644
  .floating-sidebar-toggle .hamburger-icon { width: 11px; height: 8px; }
6539
6645
 
@@ -6555,21 +6661,22 @@
6555
6661
  }
6556
6662
 
6557
6663
  .input-composer { border-radius: 8px; }
6558
- .input-textarea { min-height: 28px; padding: 5px 6px 2px; font-size: 15px; }
6664
+ .input-textarea { min-height: 28px; padding: 5px 6px 2px; font-size: 16px; }
6559
6665
 
6560
- .btn-circle { width: 24px; height: 24px; min-width: 24px; }
6666
+ .btn-circle { width: 28px; height: 28px; min-width: 28px; }
6561
6667
 
6562
6668
  .session-item {
6563
6669
  padding: 6px 8px;
6564
6670
  min-height: 40px;
6565
6671
  }
6566
- .session-action-btn { width: 28px; height: 28px; min-width: 28px; min-height: 28px; }
6672
+ .session-action-btn { width: 30px; height: 30px; min-width: 30px; min-height: 30px; }
6567
6673
 
6568
6674
  .sidebar { top: 0; width: min(240px, calc(100vw - 10px)); }
6569
6675
 
6570
6676
  .modal { max-height: 80vh; }
6571
6677
  .modal-header { padding: 8px 10px; min-height: 36px; }
6572
6678
  .modal-body { padding: 8px 10px; }
6679
+ .session-modal .modal-footer { padding: 8px 10px 12px; }
6573
6680
  .field-row { grid-template-columns: 1fr; gap: 0; }
6574
6681
 
6575
6682
  .btn { min-height: 36px; padding: 8px 12px; }
@@ -7031,6 +7138,11 @@
7031
7138
  color: var(--fg-secondary);
7032
7139
  line-height: 1.4;
7033
7140
  margin-bottom: 0;
7141
+ max-height: 4.2em;
7142
+ overflow: hidden;
7143
+ display: -webkit-box;
7144
+ -webkit-line-clamp: 3;
7145
+ -webkit-box-orient: vertical;
7034
7146
  }
7035
7147
  .notification-bubble-actions {
7036
7148
  margin-top: 6px;
@@ -7267,6 +7379,27 @@
7267
7379
  margin-top: 10px;
7268
7380
  }
7269
7381
 
7382
+ .settings-connect-url-box {
7383
+ display: flex;
7384
+ align-items: center;
7385
+ gap: 8px;
7386
+ background: var(--bg-primary);
7387
+ border: 1px solid var(--border-subtle);
7388
+ border-radius: 6px;
7389
+ padding: 6px 10px;
7390
+ margin-top: 6px;
7391
+ }
7392
+
7393
+ .settings-connect-url-text {
7394
+ flex: 1;
7395
+ font-family: var(--font-mono, monospace);
7396
+ font-size: 13px;
7397
+ color: var(--text-primary);
7398
+ word-break: break-all;
7399
+ user-select: all;
7400
+ -webkit-user-select: all;
7401
+ }
7402
+
7270
7403
  .settings-divider {
7271
7404
  border: none;
7272
7405
  border-top: 1px solid var(--border-subtle);
@@ -7340,6 +7473,93 @@
7340
7473
  cursor: pointer;
7341
7474
  }
7342
7475
 
7476
+ /* Switch card list */
7477
+ .switch-card-list {
7478
+ display: flex;
7479
+ flex-direction: column;
7480
+ gap: 8px;
7481
+ }
7482
+ .switch-card {
7483
+ display: block;
7484
+ background: var(--bg-secondary);
7485
+ border: 1px solid var(--border);
7486
+ border-radius: 10px;
7487
+ padding: 12px 14px;
7488
+ cursor: pointer;
7489
+ transition: border-color 0.2s, background 0.2s;
7490
+ user-select: none;
7491
+ }
7492
+ .switch-card:hover {
7493
+ border-color: var(--text-muted);
7494
+ }
7495
+ .switch-card-header {
7496
+ display: flex;
7497
+ align-items: center;
7498
+ gap: 10px;
7499
+ }
7500
+ .switch-card-title {
7501
+ flex: 1;
7502
+ font-size: 0.8125rem;
7503
+ font-weight: 600;
7504
+ color: var(--text-primary);
7505
+ }
7506
+ .switch-card-desc {
7507
+ font-size: 0.75rem;
7508
+ color: var(--text-muted);
7509
+ margin-top: 0;
7510
+ max-height: 0;
7511
+ overflow: hidden;
7512
+ opacity: 0;
7513
+ transition: max-height 0.25s ease, opacity 0.2s ease, margin-top 0.25s ease;
7514
+ }
7515
+ /* When switch is ON, expand the description */
7516
+ .switch-card:has(.switch-toggle:checked) .switch-card-desc {
7517
+ max-height: 40px;
7518
+ opacity: 1;
7519
+ margin-top: 8px;
7520
+ }
7521
+ .switch-card:has(.switch-toggle:checked) {
7522
+ border-color: var(--accent);
7523
+ background: color-mix(in srgb, var(--accent) 6%, var(--bg-secondary));
7524
+ }
7525
+
7526
+ /* Switch toggle (iOS style) */
7527
+ .switch-toggle {
7528
+ position: absolute;
7529
+ opacity: 0;
7530
+ width: 0;
7531
+ height: 0;
7532
+ pointer-events: none;
7533
+ }
7534
+ .switch-slider {
7535
+ position: relative;
7536
+ display: inline-block;
7537
+ width: 40px;
7538
+ height: 22px;
7539
+ min-width: 40px;
7540
+ background: #c4b8a8;
7541
+ border-radius: 11px;
7542
+ transition: background 0.25s;
7543
+ }
7544
+ .switch-slider::after {
7545
+ content: "";
7546
+ position: absolute;
7547
+ top: 3px;
7548
+ left: 3px;
7549
+ width: 16px;
7550
+ height: 16px;
7551
+ background: #fff;
7552
+ border-radius: 50%;
7553
+ transition: transform 0.25s;
7554
+ box-shadow: 0 1px 3px rgba(0,0,0,0.2);
7555
+ }
7556
+ .switch-toggle:checked + .switch-slider {
7557
+ background: var(--accent);
7558
+ }
7559
+ .switch-toggle:checked + .switch-slider::after {
7560
+ transform: translateX(18px);
7561
+ }
7562
+
7343
7563
  .field-file {
7344
7564
  font-size: 0.8rem;
7345
7565
  padding: 6px;
@@ -3,14 +3,15 @@
3
3
  * Handles debounced output events, backpressure control, and client subscriptions.
4
4
  */
5
5
  import { WebSocketServer } from "ws";
6
- import type { SessionSnapshot, ProcessEvent } from "./types.js";
6
+ import type { CardExpandDefaults, SessionSnapshot, ProcessEvent } from "./types.js";
7
7
  export type { ProcessEvent } from "./types.js";
8
8
  export declare class WsBroadcastManager {
9
9
  private wss;
10
10
  private clients;
11
11
  private outputDebounceCache;
12
12
  private eventEmitter;
13
- constructor(wss: WebSocketServer);
13
+ private getCardDefaults;
14
+ constructor(wss: WebSocketServer, getCardDefaults?: () => CardExpandDefaults);
14
15
  /** Set up connection handling. Should be called once during server startup. */
15
16
  setup(getSession: (id: string) => SessionSnapshot | null): void;
16
17
  /** Emit a process event to all subscribed WebSocket clients. */
@@ -5,6 +5,7 @@
5
5
  import { WebSocket } from "ws";
6
6
  import { EventEmitter } from "node:events";
7
7
  import { validateSession } from "./auth.js";
8
+ import { truncateMessagesForTransport } from "./message-truncator.js";
8
9
  // ── Constants ──
9
10
  const MAX_QUEUE_SIZE = 500;
10
11
  const OUTPUT_DEBOUNCE_MS = 16;
@@ -14,8 +15,10 @@ export class WsBroadcastManager {
14
15
  clients = new Set();
15
16
  outputDebounceCache = new Map();
16
17
  eventEmitter = new EventEmitter();
17
- constructor(wss) {
18
+ getCardDefaults;
19
+ constructor(wss, getCardDefaults) {
18
20
  this.wss = wss;
21
+ this.getCardDefaults = getCardDefaults ?? (() => ({}));
19
22
  }
20
23
  /** Set up connection handling. Should be called once during server startup. */
21
24
  setup(getSession) {
@@ -45,10 +48,13 @@ export class WsBroadcastManager {
45
48
  if (msg.type === "subscribe" && msg.sessionId) {
46
49
  const snapshot = getSession(msg.sessionId);
47
50
  if (snapshot) {
51
+ const truncatedMessages = snapshot.messages
52
+ ? truncateMessagesForTransport(snapshot.messages, this.getCardDefaults())
53
+ : undefined;
48
54
  ws.send(JSON.stringify({
49
55
  type: "init",
50
56
  sessionId: msg.sessionId,
51
- data: { ...snapshot, messages: snapshot.messages, output: snapshot.output },
57
+ data: { ...snapshot, messages: truncatedMessages, output: snapshot.output },
52
58
  }));
53
59
  }
54
60
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@co0ontty/wand",
3
- "version": "1.10.0",
3
+ "version": "1.14.3",
4
4
  "description": "A web terminal for local CLI tools like Claude.",
5
5
  "type": "module",
6
6
  "bin": {