openclacky 0.9.7 → 0.9.9

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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/.clacky/skills/gem-release/SKILL.md +16 -1
  3. data/CHANGELOG.md +51 -0
  4. data/docs/browser-cdp-native-design.md +195 -0
  5. data/docs/session-management-redesign.md +202 -0
  6. data/docs/system-skill-authoring-guide.md +47 -0
  7. data/lib/clacky/agent/cost_tracker.rb +2 -1
  8. data/lib/clacky/agent/message_compressor_helper.rb +2 -2
  9. data/lib/clacky/agent/session_serializer.rb +36 -3
  10. data/lib/clacky/agent/skill_manager.rb +68 -5
  11. data/lib/clacky/agent/system_prompt_builder.rb +30 -13
  12. data/lib/clacky/agent/tool_executor.rb +8 -12
  13. data/lib/clacky/agent.rb +172 -30
  14. data/lib/clacky/brand_config.rb +90 -0
  15. data/lib/clacky/cli.rb +37 -8
  16. data/lib/clacky/default_parsers/doc_parser.rb +69 -0
  17. data/lib/clacky/default_parsers/docx_parser.rb +172 -0
  18. data/lib/clacky/default_parsers/pdf_parser.rb +79 -0
  19. data/lib/clacky/default_parsers/pptx_parser.rb +140 -0
  20. data/lib/clacky/default_parsers/xlsx_parser.rb +121 -0
  21. data/lib/clacky/default_skills/browser-setup/SKILL.md +158 -0
  22. data/lib/clacky/default_skills/channel-setup/SKILL.md +139 -42
  23. data/lib/clacky/default_skills/channel-setup/feishu_setup.rb +582 -0
  24. data/lib/clacky/default_skills/channel-setup/weixin_setup.rb +274 -0
  25. data/lib/clacky/default_skills/onboard/SKILL.md +132 -6
  26. data/lib/clacky/default_skills/personal-website/SKILL.md +113 -0
  27. data/lib/clacky/default_skills/personal-website/publish.rb +214 -0
  28. data/lib/clacky/default_skills/skill-add/SKILL.md +6 -7
  29. data/lib/clacky/default_skills/skill-add/scripts/install_from_zip.rb +1 -1
  30. data/lib/clacky/default_skills/skill-creator/SKILL.md +3 -3
  31. data/lib/clacky/json_ui_controller.rb +5 -0
  32. data/lib/clacky/message_format/open_ai.rb +7 -1
  33. data/lib/clacky/plain_ui_controller.rb +5 -0
  34. data/lib/clacky/server/browser_manager.rb +308 -0
  35. data/lib/clacky/server/channel/adapters/feishu/adapter.rb +80 -15
  36. data/lib/clacky/server/channel/adapters/feishu/bot.rb +216 -7
  37. data/lib/clacky/server/channel/adapters/feishu/message_parser.rb +12 -0
  38. data/lib/clacky/server/channel/adapters/wecom/adapter.rb +2 -8
  39. data/lib/clacky/server/channel/adapters/weixin/adapter.rb +391 -0
  40. data/lib/clacky/server/channel/adapters/weixin/api_client.rb +374 -0
  41. data/lib/clacky/server/channel/channel_config.rb +6 -0
  42. data/lib/clacky/server/channel/channel_manager.rb +50 -15
  43. data/lib/clacky/server/channel/channel_ui_controller.rb +18 -0
  44. data/lib/clacky/server/channel.rb +1 -0
  45. data/lib/clacky/server/http_server.rb +210 -45
  46. data/lib/clacky/server/scheduler.rb +1 -1
  47. data/lib/clacky/server/session_registry.rb +131 -40
  48. data/lib/clacky/server/web_ui_controller.rb +21 -2
  49. data/lib/clacky/session_manager.rb +43 -73
  50. data/lib/clacky/skill.rb +64 -42
  51. data/lib/clacky/tools/browser.rb +728 -183
  52. data/lib/clacky/tools/invoke_skill.rb +18 -11
  53. data/lib/clacky/tools/shell.rb +16 -7
  54. data/lib/clacky/tools/web_fetch.rb +3 -1
  55. data/lib/clacky/ui2/README.md +1 -1
  56. data/lib/clacky/ui2/components/input_area.rb +5 -2
  57. data/lib/clacky/ui2/components/welcome_banner.rb +23 -0
  58. data/lib/clacky/ui2/ui_controller.rb +16 -2
  59. data/lib/clacky/ui_interface.rb +7 -0
  60. data/lib/clacky/utils/file_processor.rb +116 -139
  61. data/lib/clacky/utils/model_pricing.rb +5 -4
  62. data/lib/clacky/utils/parser_manager.rb +93 -0
  63. data/lib/clacky/utils/workspace_rules.rb +46 -0
  64. data/lib/clacky/version.rb +1 -1
  65. data/lib/clacky/web/app.css +696 -62
  66. data/lib/clacky/web/app.js +181 -51
  67. data/lib/clacky/web/brand.js +14 -0
  68. data/lib/clacky/web/channels.js +10 -1
  69. data/lib/clacky/web/i18n.js +49 -7
  70. data/lib/clacky/web/index.html +67 -10
  71. data/lib/clacky/web/onboard.js +2 -1
  72. data/lib/clacky/web/sessions.js +255 -69
  73. data/lib/clacky/web/settings.js +113 -1
  74. data/lib/clacky/web/skills.js +16 -3
  75. data/lib/clacky/web/tasks.js +11 -6
  76. data/lib/clacky/web/weixin-qr.html +104 -0
  77. data/lib/clacky.rb +2 -0
  78. data/scripts/install.sh +41 -11
  79. metadata +48 -7
  80. data/lib/clacky/default_skills/pdf-reader/SKILL.md +0 -90
  81. data/lib/clacky/utils/file_parser/docx_parser.rb +0 -156
  82. data/lib/clacky/utils/file_parser/pptx_parser.rb +0 -116
  83. data/lib/clacky/utils/file_parser/xlsx_parser.rb +0 -95
  84. data/lib/clacky/utils/file_parser/zip_parser.rb +0 -60
  85. data/scripts/install_agent_browser.sh +0 -67
@@ -295,7 +295,7 @@ body {
295
295
  flex-direction: column;
296
296
  min-height: 0;
297
297
  }
298
- #session-list { padding: 0 8px 8px; }
298
+ #session-list { padding: 4px 8px 8px; min-height: 108px; }
299
299
 
300
300
  /* ── Sidebar divider (Section Labels) ───────────────────────────────────── */
301
301
  .sidebar-divider {
@@ -303,13 +303,13 @@ body {
303
303
  align-items: center;
304
304
  justify-content: space-between;
305
305
  gap: 8px;
306
- padding: 12px 16px 6px 16px;
306
+ padding: 8px 14px 4px 14px;
307
307
  font-size: 10px;
308
308
  font-weight: 600;
309
309
  color: var(--color-text-tertiary);
310
310
  text-transform: uppercase;
311
311
  letter-spacing: 1px;
312
- margin-top: 8px;
312
+ margin-top: 4px;
313
313
  }
314
314
  .sidebar-divider:first-child {
315
315
  margin-top: 0;
@@ -318,36 +318,161 @@ body {
318
318
  flex: 1;
319
319
  white-space: nowrap;
320
320
  }
321
- /* Inline New Session button in Chat divider */
322
- .btn-new-inline {
323
- width: 20px;
324
- height: 20px;
321
+ /* ── Split button [+ ▾] ─────────────────────────────────────────────────── */
322
+ .btn-split-wrap {
323
+ position: relative;
324
+ display: flex;
325
+ align-items: center;
326
+ border-radius: 5px;
327
+ overflow: visible;
328
+ }
329
+ .btn-split-main {
330
+ height: 22px;
331
+ padding: 0 8px;
332
+ border: none;
333
+ border-radius: 5px 0 0 5px;
334
+ background: var(--color-bg-hover);
335
+ color: var(--color-text-secondary);
336
+ font-size: 11px;
337
+ font-weight: 500;
338
+ white-space: nowrap;
339
+ line-height: 1;
340
+ cursor: pointer;
341
+ transition: background 0.15s, color 0.15s;
342
+ }
343
+ .btn-split-main:hover {
344
+ background: var(--color-accent-primary);
345
+ color: #fff;
346
+ }
347
+ .btn-split-arrow {
348
+ height: 22px;
349
+ padding: 0 4px;
350
+ border: none;
351
+ border-left: 1px solid var(--color-border-primary);
352
+ border-radius: 0 5px 5px 0;
353
+ background: var(--color-bg-hover);
354
+ color: var(--color-text-tertiary);
355
+ font-size: 10px;
356
+ cursor: pointer;
357
+ transition: background 0.15s, color 0.15s;
358
+ }
359
+ .btn-split-arrow:hover {
360
+ background: var(--color-accent-primary);
361
+ color: #fff;
362
+ }
363
+
364
+ /* ── Dropdown menu ───────────────────────────────────────────────────────── */
365
+ .new-session-dropdown {
366
+ position: absolute;
367
+ top: calc(100% + 4px);
368
+ right: 0;
369
+ min-width: 150px;
370
+ background: var(--color-bg-card);
371
+ border: 1px solid var(--color-border-primary);
372
+ border-radius: 7px;
373
+ box-shadow: var(--shadow-md);
374
+ z-index: 200;
375
+ padding: 4px 0;
376
+ overflow: hidden;
377
+ }
378
+ .new-session-dropdown[hidden] { display: none; }
379
+ .dropdown-item {
380
+ padding: 7px 14px;
381
+ font-size: 13px;
382
+ color: var(--color-text-primary);
383
+ cursor: pointer;
384
+ white-space: nowrap;
385
+ transition: background 0.12s;
386
+ }
387
+ .dropdown-item:hover {
388
+ background: var(--color-bg-hover);
389
+ color: var(--color-accent-primary);
390
+ }
391
+
392
+ /* ── Session source tabs ─────────────────────────────────────────────────── */
393
+ .session-tabs {
394
+ display: flex;
395
+ gap: 0;
396
+ padding: 4px 8px 3px;
397
+ }
398
+ .session-tab {
399
+ flex: 1;
325
400
  border: none;
326
- border-radius: 4px;
327
401
  background: transparent;
402
+ color: var(--color-text-muted);
403
+ font-size: 10px;
404
+ font-weight: 500;
405
+ padding: 3px 0;
406
+ cursor: pointer;
407
+ border-bottom: 1.5px solid transparent;
408
+ transition: color 0.15s, border-color 0.15s;
409
+ text-align: center;
410
+ letter-spacing: 0.2px;
411
+ }
412
+ .session-tab:hover {
328
413
  color: var(--color-text-tertiary);
414
+ }
415
+ .session-tab.active {
416
+ color: var(--color-accent-primary);
417
+ border-bottom-color: var(--color-accent-primary);
418
+ }
419
+
420
+ /* ── Coding agent fixed section ──────────────────────────────────────────── */
421
+ #coding-section {
422
+ max-height: 160px;
329
423
  display: flex;
330
- align-items: center;
331
- justify-content: center;
424
+ flex-direction: column;
425
+ flex-shrink: 0;
426
+ }
427
+ .coding-section-divider {
428
+ padding: 8px 16px 4px;
429
+ font-size: 10px;
430
+ font-weight: 600;
431
+ color: var(--color-text-tertiary);
432
+ text-transform: uppercase;
433
+ letter-spacing: 1px;
434
+ border-top: 1px solid var(--color-border-primary);
435
+ margin-top: 2px;
436
+ flex-shrink: 0;
437
+ }
438
+ #coding-session-list {
439
+ padding: 0 8px 6px;
440
+ overflow-y: auto;
441
+ flex: 1;
442
+ min-height: 0;
443
+ }
444
+
445
+ /* ── Load-more link ───────────────────────────────────────────────────────── */
446
+ .btn-load-more-sessions {
447
+ display: block;
448
+ width: 100%;
449
+ margin: 6px 0 4px;
450
+ padding: 3px 0;
451
+ background: transparent;
452
+ border: none;
453
+ color: var(--color-text-muted);
454
+ font-size: 11px;
332
455
  cursor: pointer;
333
- padding: 0;
334
- transition: background-color 0.2s ease, color 0.2s ease, transform 0.15s ease;
456
+ text-align: center;
457
+ transition: color 0.15s;
335
458
  }
336
- .btn-new-inline:hover {
337
- background: var(--color-bg-hover);
459
+ .btn-load-more-sessions:hover {
338
460
  color: var(--color-accent-primary);
339
- transform: scale(1.1);
340
461
  }
341
- .btn-new-inline:active {
342
- transform: scale(0.95);
462
+ .btn-load-more-sessions:disabled {
463
+ cursor: default;
464
+ opacity: 0.5;
465
+ }
466
+
467
+ /* Legacy .btn-new-inline — kept for compat, can be removed after cleanup */
468
+ .btn-new-inline {
469
+ display: none;
343
470
  }
344
471
 
345
472
  /* ── Session empty placeholder ───────────────────────────────────────────── */
346
473
  .session-empty {
347
- margin: 4px 8px;
348
- padding: 10px 12px;
349
- border-radius: 6px;
350
- border: 1px dashed var(--color-border-primary);
474
+ margin: 0;
475
+ padding: 32px 12px;
351
476
  font-size: 12px;
352
477
  color: var(--color-text-muted);
353
478
  text-align: center;
@@ -356,15 +481,29 @@ body {
356
481
  /* ── Session List ────────────────────────────────────────────────────────── */
357
482
  .session-item {
358
483
  position: relative;
359
- padding: 10px 12px 10px 15px;
484
+ display: flex;
485
+ align-items: center;
486
+ gap: 7px;
487
+ padding: 5px 10px 4px 13px;
360
488
  border-radius: 6px;
361
489
  cursor: pointer;
362
- margin-bottom: 4px;
490
+ margin-bottom: 1px;
363
491
  border: 1px solid transparent;
364
492
  transition: background .2s ease, border-color .2s ease;
365
493
  }
494
+ /* Two-row text block next to dot */
495
+ .session-body {
496
+ flex: 1;
497
+ min-width: 0;
498
+ }
499
+
366
500
  /* Default: secondary text color for icons/text */
367
501
  .session-item .session-name {
502
+ flex: 1;
503
+ min-width: 0;
504
+ overflow: hidden;
505
+ text-overflow: ellipsis;
506
+ white-space: nowrap;
368
507
  color: var(--color-text-secondary);
369
508
  font-weight: 500;
370
509
  transition: color .2s ease, font-weight .2s ease;
@@ -382,7 +521,7 @@ body {
382
521
  }
383
522
  .session-item.active .session-name {
384
523
  color: var(--color-accent-primary);
385
- font-weight: 700;
524
+ font-weight: 600;
386
525
  }
387
526
  /* Left indicator bar (3px) for active state */
388
527
  .session-item.active::before {
@@ -399,23 +538,21 @@ body {
399
538
 
400
539
  /* Delete button on session list items — visible only on hover */
401
540
  .session-delete-btn {
402
- position: absolute;
403
- top: 50%;
404
- right: 8px;
405
- transform: translateY(-50%);
541
+ flex-shrink: 0;
406
542
  display: none;
407
543
  align-items: center;
408
544
  justify-content: center;
409
- width: 18px;
410
- height: 18px;
545
+ width: 16px;
546
+ height: 16px;
411
547
  background: transparent;
412
548
  border: none;
413
- border-radius: 4px;
414
- color: var(--color-text-secondary);
415
- font-size: 16px;
549
+ border-radius: 3px;
550
+ color: var(--color-text-muted);
551
+ font-size: 14px;
416
552
  line-height: 1;
417
553
  cursor: pointer;
418
554
  padding: 0;
555
+ margin-top: -3px;
419
556
  transition: background .15s, color .15s;
420
557
  }
421
558
  .session-item:hover .session-delete-btn { display: flex; }
@@ -432,28 +569,37 @@ body {
432
569
  box-sizing: border-box;
433
570
  }
434
571
  .session-name {
435
- font-size: 13px;
572
+ font-size: 12px;
436
573
  white-space: nowrap;
437
574
  overflow: hidden;
438
575
  text-overflow: ellipsis;
439
576
  }
440
577
  /* While renaming, lift overflow so the input is fully visible */
441
- .session-name:has(input),
442
578
  .session-name.renaming {
443
579
  overflow: visible;
444
580
  white-space: normal;
581
+ flex: 1;
582
+ }
583
+ /* Meta: second row — tasks + cost */
584
+ .session-meta {
585
+ display: block;
586
+ font-size: 10px;
587
+ color: var(--color-text-muted);
588
+ white-space: nowrap;
589
+ opacity: 0.7;
590
+ margin-top: 1px;
445
591
  }
446
- .session-meta { font-size: 11px; color: var(--color-text-secondary); margin-top: 3px; }
447
592
  .session-dot {
448
- display: inline-block;
449
- width: 7px;
450
- height: 7px;
593
+ flex-shrink: 0;
594
+ display: block;
595
+ width: 5px;
596
+ height: 5px;
451
597
  border-radius: 50%;
452
- margin-right: 5px;
598
+ margin-top: -7px;
453
599
  }
454
- .dot-idle { background: var(--color-text-secondary); }
455
- .dot-running { background: var(--color-success); animation: pulse 1s infinite; }
456
- .dot-error { background: var(--color-error); }
600
+ .dot-idle { background: var(--color-text-secondary); opacity: 0.4; }
601
+ .dot-running { background: var(--color-success); opacity: 1; animation: pulse 1s infinite; }
602
+ .dot-error { background: var(--color-error); opacity: 1; }
457
603
 
458
604
  @keyframes pulse {
459
605
  0%, 100% { opacity: 1; }
@@ -704,6 +850,12 @@ body {
704
850
  display: flex;
705
851
  align-items: center;
706
852
  gap: 8px;
853
+ min-width: 0;
854
+ }
855
+ .task-name-info {
856
+ display: flex;
857
+ flex-direction: column;
858
+ min-width: 0;
707
859
  }
708
860
  .task-name-text {
709
861
  font-weight: 600;
@@ -713,6 +865,10 @@ body {
713
865
  overflow: hidden;
714
866
  text-overflow: ellipsis;
715
867
  }
868
+ /* Mobile-only schedule shown inside name col — hidden on desktop */
869
+ .task-name-sched {
870
+ display: none;
871
+ }
716
872
 
717
873
  .task-col-schedule {
718
874
  font-size: 12px;
@@ -868,6 +1024,24 @@ body {
868
1024
  }
869
1025
 
870
1026
  /* Delete session button in chat header */
1027
+ /* Back button in chat header — hidden on desktop, shown on mobile via media query */
1028
+ .chat-back-btn {
1029
+ display: none;
1030
+ align-items: center;
1031
+ justify-content: center;
1032
+ width: 32px;
1033
+ height: 32px;
1034
+ background: transparent;
1035
+ border: none;
1036
+ border-radius: 6px;
1037
+ color: var(--color-text-secondary);
1038
+ cursor: pointer;
1039
+ padding: 0;
1040
+ flex-shrink: 0;
1041
+ transition: background .15s, color .15s;
1042
+ }
1043
+ .chat-back-btn:hover { background: var(--color-bg-hover); }
1044
+
871
1045
  .btn-delete-session {
872
1046
  margin-left: auto;
873
1047
  display: flex;
@@ -991,6 +1165,8 @@ body {
991
1165
  .msg-assistant em { font-style: italic; color: var(--color-text-secondary); }
992
1166
  .msg-tool { background: var(--color-bg-primary); border: 1px solid var(--color-border-primary); font-family: monospace; font-size: 12px; color: var(--color-text-secondary); align-self: flex-start; }
993
1167
  .msg-info { color: var(--color-text-secondary); font-size: 12px; align-self: center; font-style: italic; }
1168
+ .msg-idle-status { color: var(--color-accent-primary); animation: pulse 1.2s infinite; }
1169
+ .msg-idle-status.msg-idle-done { color: var(--color-text-secondary); animation: none; opacity: 0.7; }
994
1170
  .msg-error { background: var(--color-error-bg); border: 1px solid var(--color-error); color: var(--color-error); align-self: flex-start; }
995
1171
  .msg-success { color: var(--color-success); align-self: flex-start; font-size: 13px; }
996
1172
  .tool-name { color: var(--color-warning); font-weight: 600; }
@@ -1143,14 +1319,41 @@ body {
1143
1319
  object-fit: contain;
1144
1320
  }
1145
1321
  .msg-pdf-badge {
1146
- display: inline-block;
1147
- padding: 2px 8px;
1148
- border-radius: 4px;
1322
+ display: inline-flex;
1323
+ align-items: center;
1324
+ gap: 6px;
1325
+ padding: 6px 10px;
1326
+ border-radius: 8px;
1149
1327
  background: var(--color-bg-tertiary);
1150
1328
  border: 1px solid var(--color-border-primary);
1329
+ margin-bottom: 4px;
1330
+ max-width: 240px;
1331
+ min-width: 100px;
1332
+ vertical-align: middle;
1333
+ }
1334
+ .msg-pdf-badge-icon {
1335
+ font-size: 20px;
1336
+ line-height: 1;
1337
+ flex-shrink: 0;
1338
+ }
1339
+ .msg-pdf-badge-info {
1340
+ display: flex;
1341
+ flex-direction: column;
1342
+ overflow: hidden;
1343
+ }
1344
+ .msg-pdf-badge-name {
1151
1345
  font-size: 12px;
1346
+ color: var(--color-text-primary);
1347
+ font-weight: 500;
1348
+ white-space: nowrap;
1349
+ overflow: hidden;
1350
+ text-overflow: ellipsis;
1351
+ }
1352
+ .msg-pdf-badge-type {
1353
+ font-size: 10px;
1152
1354
  color: var(--color-text-secondary);
1153
- margin-bottom: 4px;
1355
+ text-transform: uppercase;
1356
+ letter-spacing: 0.05em;
1154
1357
  }
1155
1358
 
1156
1359
  /* ── Session info bar (above input area, mirrors CLI bottom status bar) ─── */
@@ -1278,39 +1481,52 @@ body {
1278
1481
  }
1279
1482
  .img-preview-remove:hover { background: var(--color-error); }
1280
1483
 
1281
- /* PDF preview badge in image-preview-strip */
1484
+ /* File preview card in image-preview-strip (PDF, DOC, XLSX, etc.) */
1282
1485
  .pdf-preview-item {
1283
1486
  position: relative;
1284
1487
  display: flex;
1285
- flex-direction: column;
1488
+ flex-direction: row;
1286
1489
  align-items: center;
1287
- justify-content: center;
1288
- width: 64px;
1490
+ gap: 8px;
1289
1491
  height: 64px;
1290
- border-radius: 6px;
1492
+ padding: 0 28px 0 10px;
1493
+ border-radius: 8px;
1291
1494
  border: 1px solid var(--color-border-primary);
1292
1495
  background: var(--color-bg-tertiary);
1293
1496
  flex-shrink: 0;
1294
- gap: 4px;
1497
+ max-width: 180px;
1498
+ min-width: 120px;
1499
+ box-sizing: border-box;
1295
1500
  }
1296
1501
  .pdf-preview-icon {
1297
- font-size: 22px;
1502
+ font-size: 24px;
1298
1503
  line-height: 1;
1504
+ flex-shrink: 0;
1505
+ }
1506
+ .pdf-preview-info {
1507
+ display: flex;
1508
+ flex-direction: column;
1509
+ overflow: hidden;
1299
1510
  }
1300
1511
  .pdf-preview-name {
1301
- font-size: 9px;
1302
- color: var(--color-text-secondary);
1303
- text-align: center;
1512
+ font-size: 11px;
1513
+ font-weight: 500;
1514
+ color: var(--color-text-primary);
1304
1515
  overflow: hidden;
1305
1516
  text-overflow: ellipsis;
1306
1517
  white-space: nowrap;
1307
- max-width: 56px;
1308
- padding: 0 2px;
1518
+ }
1519
+ .pdf-preview-type {
1520
+ font-size: 9px;
1521
+ color: var(--color-text-secondary);
1522
+ text-transform: uppercase;
1523
+ letter-spacing: 0.05em;
1524
+ margin-top: 2px;
1309
1525
  }
1310
1526
  .pdf-preview-remove {
1311
1527
  position: absolute;
1312
- top: 2px;
1313
- right: 2px;
1528
+ top: 4px;
1529
+ right: 4px;
1314
1530
  width: 16px;
1315
1531
  height: 16px;
1316
1532
  border-radius: 50%;
@@ -1402,6 +1618,28 @@ body {
1402
1618
  margin-bottom: 2px;
1403
1619
  }
1404
1620
 
1621
+ /* meta block: encrypted badge + source directory */
1622
+ .skill-ac-meta {
1623
+ display: inline-flex;
1624
+ align-items: center;
1625
+ gap: 4px;
1626
+ flex-shrink: 0;
1627
+ }
1628
+
1629
+ .skill-ac-enc {
1630
+ font-size: 11px;
1631
+ opacity: 0.5;
1632
+ cursor: default;
1633
+ }
1634
+
1635
+ .skill-ac-src {
1636
+ font-size: 10px;
1637
+ color: var(--color-text-tertiary);
1638
+ opacity: 0.6;
1639
+ white-space: nowrap;
1640
+ cursor: default;
1641
+ }
1642
+
1405
1643
  /* ── Input bar ───────────────────────────────────────────────────────────── */
1406
1644
  #input-bar {
1407
1645
  padding: 13.5px 16px;
@@ -1597,6 +1835,51 @@ body {
1597
1835
  }
1598
1836
  .settings-error { color: var(--color-error); }
1599
1837
 
1838
+ /* ── Browser toggle switch ───────────────────────────────────────────────── */
1839
+ .settings-browser-actions {
1840
+ display: flex;
1841
+ align-items: center;
1842
+ gap: 10px;
1843
+ flex-shrink: 0;
1844
+ }
1845
+ .toggle-switch {
1846
+ position: relative;
1847
+ display: inline-block;
1848
+ width: 36px;
1849
+ height: 20px;
1850
+ cursor: pointer;
1851
+ }
1852
+ .toggle-switch input {
1853
+ opacity: 0;
1854
+ width: 0;
1855
+ height: 0;
1856
+ position: absolute;
1857
+ }
1858
+ .toggle-slider {
1859
+ position: absolute;
1860
+ inset: 0;
1861
+ background: var(--color-border-primary);
1862
+ border-radius: 20px;
1863
+ transition: background 0.2s;
1864
+ }
1865
+ .toggle-slider::before {
1866
+ content: "";
1867
+ position: absolute;
1868
+ width: 14px;
1869
+ height: 14px;
1870
+ left: 3px;
1871
+ top: 3px;
1872
+ background: #fff;
1873
+ border-radius: 50%;
1874
+ transition: transform 0.2s;
1875
+ }
1876
+ .toggle-switch input:checked + .toggle-slider {
1877
+ background: var(--color-accent-primary);
1878
+ }
1879
+ .toggle-switch input:checked + .toggle-slider::before {
1880
+ transform: translateX(16px);
1881
+ }
1882
+
1600
1883
  /* ── Model card ──────────────────────────────────────────────────────────── */
1601
1884
  .model-card {
1602
1885
  background: var(--color-bg-secondary);
@@ -3421,6 +3704,34 @@ body.setup-mode[data-theme="dark"] {
3421
3704
  .badge-inactive { color: var(--color-text-secondary); }
3422
3705
 
3423
3706
  /* Support QR code */
3707
+ .brand-support-contact {
3708
+ display: flex;
3709
+ align-items: center;
3710
+ gap: 8px;
3711
+ padding: 8px 12px;
3712
+ background: var(--color-bg-tertiary);
3713
+ border: 1px solid var(--color-border-primary);
3714
+ border-radius: 8px;
3715
+ font-size: 13px;
3716
+ }
3717
+
3718
+ .brand-support-contact-label {
3719
+ color: var(--color-text-secondary);
3720
+ white-space: nowrap;
3721
+ }
3722
+
3723
+ .brand-support-contact-value,
3724
+ .brand-status-link {
3725
+ color: var(--color-accent-primary);
3726
+ text-decoration: none;
3727
+ word-break: break-all;
3728
+ }
3729
+
3730
+ .brand-support-contact-value:hover,
3731
+ .brand-status-link:hover {
3732
+ text-decoration: underline;
3733
+ }
3734
+
3424
3735
  .brand-support-qr {
3425
3736
  display: flex;
3426
3737
  flex-direction: column;
@@ -3866,6 +4177,11 @@ body.setup-mode[data-theme="dark"] {
3866
4177
  color: #fff;
3867
4178
  }
3868
4179
 
4180
+ .channel-logo-weixin {
4181
+ background: linear-gradient(135deg, #2dc100, #1aad19);
4182
+ color: #fff;
4183
+ }
4184
+
3869
4185
  .channel-card-name {
3870
4186
  font-size: 15px;
3871
4187
  font-weight: 600;
@@ -3880,13 +4196,22 @@ body.setup-mode[data-theme="dark"] {
3880
4196
 
3881
4197
  /* Status badge */
3882
4198
  .channel-status-badge {
4199
+ display: inline-flex;
4200
+ align-items: center;
4201
+ flex-shrink: 0;
4202
+ }
4203
+
4204
+ .badge-running,
4205
+ .badge-enabled,
4206
+ .badge-disabled {
4207
+ display: inline-flex;
4208
+ align-items: center;
3883
4209
  font-size: 12px;
3884
4210
  font-weight: 500;
3885
4211
  padding: 3px 10px;
3886
4212
  border-radius: 20px;
3887
4213
  white-space: nowrap;
3888
4214
  }
3889
-
3890
4215
  .badge-running { background: var(--color-success-bg); color: var(--color-success); border: 1px solid var(--color-success-border); }
3891
4216
  .badge-enabled { background: #1a2f4e; color: var(--color-accent-primary); border: 1px solid var(--color-accent-primary); }
3892
4217
  .badge-disabled { background: var(--color-bg-tertiary); color: var(--color-text-muted); border: 1px solid var(--color-border-primary); }
@@ -4284,3 +4609,312 @@ body.setup-mode[data-theme="dark"] {
4284
4609
  }
4285
4610
 
4286
4611
 
4612
+
4613
+ /* ── Mobile responsive (≤768px) ─────────────────────────────────────────── */
4614
+ @media (max-width: 768px) {
4615
+ /* Sidebar becomes a fixed drawer on top of content */
4616
+ #sidebar {
4617
+ position: fixed;
4618
+ top: 0;
4619
+ left: 0;
4620
+ height: 100%;
4621
+ z-index: 200;
4622
+ box-shadow: 4px 0 24px rgba(0,0,0,0.18);
4623
+ transition: transform 0.3s ease, background-color 0.3s ease;
4624
+ transform: translateX(0);
4625
+ margin-left: 0 !important;
4626
+ }
4627
+ #sidebar.hidden {
4628
+ transform: translateX(-100%);
4629
+ margin-left: 0 !important;
4630
+ }
4631
+
4632
+ /* Overlay backdrop */
4633
+ #sidebar-overlay {
4634
+ display: none;
4635
+ position: fixed;
4636
+ inset: 0;
4637
+ z-index: 199;
4638
+ background: rgba(0,0,0,0.4);
4639
+ }
4640
+ #sidebar-overlay.active {
4641
+ display: block;
4642
+ }
4643
+
4644
+ /* Main takes full width since sidebar is no longer in flow */
4645
+ #main {
4646
+ width: 100%;
4647
+ min-width: 0;
4648
+ overflow: visible;
4649
+ }
4650
+
4651
+ /* Tighten header padding */
4652
+ #top-header {
4653
+ padding: 0 12px;
4654
+ }
4655
+
4656
+ /* Chat sub-header: tighten padding */
4657
+ #chat-header {
4658
+ padding: 8px 12px;
4659
+ gap: 8px;
4660
+ }
4661
+
4662
+ /* Truncate long session titles */
4663
+ #chat-title {
4664
+ font-size: 14px;
4665
+ overflow: hidden;
4666
+ text-overflow: ellipsis;
4667
+ white-space: nowrap;
4668
+ min-width: 0;
4669
+ flex: 1;
4670
+ }
4671
+
4672
+ /* Status badge: smaller on mobile */
4673
+ .status-idle,
4674
+ .status-running,
4675
+ .status-error {
4676
+ font-size: 10px;
4677
+ padding: 1px 6px;
4678
+ flex-shrink: 0;
4679
+ }
4680
+
4681
+ /* Session info bar: single-line, no hover-expand, font smaller */
4682
+ #session-info-bar {
4683
+ padding: 3px 12px;
4684
+ font-size: 10px;
4685
+ overflow: hidden;
4686
+ white-space: nowrap;
4687
+ }
4688
+ /* Always show detail fields on mobile (no hover) */
4689
+ #session-info-bar .sib-detail {
4690
+ display: contents;
4691
+ }
4692
+ /* Hide less-important fields and their orphaned separators */
4693
+ #sib-id,
4694
+ .sib-sep-before-id,
4695
+ #sib-dir,
4696
+ #sib-mode-wrap,
4697
+ .sib-sep-before-tasks {
4698
+ display: none;
4699
+ }
4700
+ .sib-sep {
4701
+ margin: 0 4px;
4702
+ }
4703
+
4704
+ /* Input bar: proportional spacing, touch-friendly */
4705
+ #input-bar {
4706
+ padding: 8px 10px;
4707
+ gap: 4px;
4708
+ }
4709
+
4710
+ /* Attach & slash buttons: slightly smaller touch target */
4711
+ #btn-attach {
4712
+ padding: 4px;
4713
+ }
4714
+ #btn-slash {
4715
+ padding: 4px 6px;
4716
+ font-size: 14px;
4717
+ }
4718
+
4719
+ /* Textarea: prevent iOS auto-zoom (must be ≥16px) */
4720
+ #user-input {
4721
+ font-size: 16px;
4722
+ padding: 7px 10px;
4723
+ }
4724
+
4725
+ /* Send button: bigger tap target */
4726
+ #btn-send, #btn-interrupt {
4727
+ padding: 7px 14px;
4728
+ font-size: 14px;
4729
+ }
4730
+
4731
+ /* Back button: only visible on mobile when inside a session */
4732
+ .chat-back-btn {
4733
+ display: flex;
4734
+ }
4735
+
4736
+ /* Welcome page: vertically centered but shifted up, add horizontal padding */
4737
+ #welcome {
4738
+ justify-content: center;
4739
+ padding-bottom: 30vh;
4740
+ padding-left: 24px;
4741
+ padding-right: 24px;
4742
+ }
4743
+
4744
+ /* ── Tasks page ── */
4745
+ #task-detail-panel {
4746
+ overflow: visible;
4747
+ height: auto;
4748
+ flex: 1;
4749
+ min-height: 0;
4750
+ }
4751
+ #task-detail-body {
4752
+ padding: 16px 16px 80px;
4753
+ gap: 12px;
4754
+ overflow-y: auto;
4755
+ -webkit-overflow-scrolling: touch;
4756
+ overscroll-behavior: contain;
4757
+ flex: 1;
4758
+ min-height: 0;
4759
+ }
4760
+ /* btn-create-task: move below title instead of absolute top-right */
4761
+ .task-page-header {
4762
+ flex-direction: column;
4763
+ padding-right: 0;
4764
+ }
4765
+ .btn-create-task {
4766
+ position: static;
4767
+ align-self: flex-start;
4768
+ margin-top: 4px;
4769
+ }
4770
+ /* Table: let it scroll horizontally, keep columns readable */
4771
+ #task-list-table {
4772
+ overflow-x: auto;
4773
+ -webkit-overflow-scrolling: touch;
4774
+ }
4775
+ /* ── Task table: mobile layout ── */
4776
+ .task-table-header {
4777
+ display: none;
4778
+ }
4779
+ .task-table-row {
4780
+ display: flex;
4781
+ align-items: center;
4782
+ min-width: 0;
4783
+ gap: 8px;
4784
+ padding: 8px 10px;
4785
+ }
4786
+ /* Name col takes all remaining space */
4787
+ .task-col-name {
4788
+ flex: 1;
4789
+ min-width: 0;
4790
+ align-items: center;
4791
+ }
4792
+ /* Show schedule as subtitle under the task name */
4793
+ .task-name-sched {
4794
+ display: block;
4795
+ font-size: 11px;
4796
+ font-family: monospace;
4797
+ color: var(--color-warning);
4798
+ white-space: nowrap;
4799
+ overflow: hidden;
4800
+ text-overflow: ellipsis;
4801
+ }
4802
+ /* Hide standalone schedule & content columns */
4803
+ .task-col-schedule,
4804
+ .task-col-content {
4805
+ display: none;
4806
+ }
4807
+ /* Actions: fixed width, icon-only buttons */
4808
+ .task-col-actions {
4809
+ flex-shrink: 0;
4810
+ width: 104px;
4811
+ gap: 4px;
4812
+ }
4813
+ .task-btn .btn-label {
4814
+ display: none;
4815
+ }
4816
+ .task-btn {
4817
+ padding: 6px;
4818
+ width: 30px;
4819
+ height: 30px;
4820
+ justify-content: center;
4821
+ }
4822
+
4823
+ /* ── Skills page ── */
4824
+ #skills-panel {
4825
+ overflow: visible;
4826
+ height: auto;
4827
+ flex: 1;
4828
+ min-height: 0;
4829
+ }
4830
+ #skills-body {
4831
+ padding: 16px 16px 80px;
4832
+ gap: 12px;
4833
+ overflow-y: auto;
4834
+ -webkit-overflow-scrolling: touch;
4835
+ overscroll-behavior: contain;
4836
+ flex: 1;
4837
+ min-height: 0;
4838
+ }
4839
+ /* action buttons: move below title, inline row */
4840
+ .skills-page-header {
4841
+ padding-right: 0;
4842
+ }
4843
+ .skill-action-btns {
4844
+ position: static;
4845
+ margin-top: 8px;
4846
+ }
4847
+ /* Show system skills toggle: give right margin */
4848
+ #label-show-system {
4849
+ margin-right: 8px;
4850
+ }
4851
+
4852
+ /* ── Settings page ── */
4853
+ #settings-header {
4854
+ padding: 12px 16px;
4855
+ font-size: 15px;
4856
+ }
4857
+ #settings-body {
4858
+ padding: 16px 16px 80px;
4859
+ gap: 24px;
4860
+ overflow-y: auto;
4861
+ -webkit-overflow-scrolling: touch;
4862
+ overscroll-behavior: contain;
4863
+ }
4864
+ #settings-panel {
4865
+ overflow: visible;
4866
+ height: auto;
4867
+ flex: 1;
4868
+ min-height: 0;
4869
+ }
4870
+ .settings-section-title {
4871
+ font-size: 15px;
4872
+ flex-wrap: wrap;
4873
+ gap: 8px;
4874
+ }
4875
+ /* Add Model button: full width on mobile */
4876
+ .btn-settings-add {
4877
+ width: 100%;
4878
+ justify-content: center;
4879
+ }
4880
+ /* Model card: tighter, stack actions */
4881
+ .model-card {
4882
+ padding: 12px 14px;
4883
+ gap: 10px;
4884
+ }
4885
+ .model-card-header {
4886
+ flex-wrap: wrap;
4887
+ gap: 8px;
4888
+ }
4889
+ .model-card-actions {
4890
+ flex-wrap: wrap;
4891
+ }
4892
+
4893
+ /* ── Channels page ── */
4894
+ #channels-panel {
4895
+ overflow: visible;
4896
+ height: auto;
4897
+ flex: 1;
4898
+ min-height: 0;
4899
+ }
4900
+ #channels-body {
4901
+ padding: 16px 16px 80px;
4902
+ gap: 16px;
4903
+ overflow-y: auto;
4904
+ -webkit-overflow-scrolling: touch;
4905
+ overscroll-behavior: contain;
4906
+ }
4907
+ /* Channel card: tighter padding, stack footer buttons */
4908
+ .channel-card {
4909
+ padding: 14px 16px;
4910
+ gap: 12px;
4911
+ }
4912
+ .channel-card-footer {
4913
+ flex-wrap: wrap;
4914
+ gap: 8px;
4915
+ }
4916
+ .channel-card-actions {
4917
+ flex-wrap: wrap;
4918
+ gap: 6px;
4919
+ }
4920
+ }