openclacky 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.clacky/skills/gem-release/SKILL.md +99 -356
- data/.clacky/skills/gem-release/scripts/release.sh +304 -0
- data/CHANGELOG.md +42 -0
- data/docs/system-skill-authoring-guide.md +1 -1
- data/lib/clacky/agent/tool_executor.rb +3 -1
- data/lib/clacky/agent.rb +12 -7
- data/lib/clacky/agent_config.rb +9 -3
- data/lib/clacky/brand_config.rb +19 -4
- data/lib/clacky/cli.rb +1 -1
- data/lib/clacky/default_skills/{channel-setup → channel-manager}/SKILL.md +180 -18
- data/lib/clacky/default_skills/channel-manager/dingtalk_setup.rb +191 -0
- data/lib/clacky/default_skills/channel-manager/discord_setup.rb +199 -0
- data/lib/clacky/default_skills/channel-manager/install_feishu_skills.rb +105 -0
- data/lib/clacky/default_skills/onboard/SKILL.md +2 -2
- data/lib/clacky/default_skills/onboard/scripts/import_external_skills.rb +2 -4
- data/lib/clacky/default_skills/onboard/scripts/install_builtin_skills.rb +18 -96
- data/lib/clacky/default_skills/product-help/SKILL.md +10 -2
- data/lib/clacky/message_history.rb +26 -1
- data/lib/clacky/providers.rb +29 -4
- data/lib/clacky/server/channel/adapters/dingtalk/adapter.rb +177 -0
- data/lib/clacky/server/channel/adapters/dingtalk/api_client.rb +82 -0
- data/lib/clacky/server/channel/adapters/dingtalk/stream_client.rb +205 -0
- data/lib/clacky/server/channel/adapters/discord/adapter.rb +229 -0
- data/lib/clacky/server/channel/adapters/discord/api_client.rb +108 -0
- data/lib/clacky/server/channel/adapters/discord/gateway_client.rb +272 -0
- data/lib/clacky/server/channel/adapters/telegram/adapter.rb +375 -0
- data/lib/clacky/server/channel/adapters/telegram/api_client.rb +205 -0
- data/lib/clacky/server/channel/channel_config.rb +26 -0
- data/lib/clacky/server/channel.rb +3 -0
- data/lib/clacky/server/http_server.rb +75 -4
- data/lib/clacky/server/server_master.rb +35 -13
- data/lib/clacky/server/session_registry.rb +54 -3
- data/lib/clacky/server/web_ui_controller.rb +7 -1
- data/lib/clacky/telemetry.rb +1 -16
- data/lib/clacky/tools/browser.rb +8 -5
- data/lib/clacky/tools/glob.rb +11 -38
- data/lib/clacky/tools/grep.rb +7 -16
- data/lib/clacky/ui2/markdown_renderer.rb +1 -1
- data/lib/clacky/ui2/ui_controller.rb +2 -1
- data/lib/clacky/utils/file_ignore_helper.rb +49 -0
- data/lib/clacky/utils/gitignore_parser.rb +27 -0
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +248 -31
- data/lib/clacky/web/app.js +51 -1
- data/lib/clacky/web/channels.js +98 -28
- data/lib/clacky/web/datepicker.js +205 -0
- data/lib/clacky/web/i18n.js +48 -9
- data/lib/clacky/web/index.html +33 -6
- data/lib/clacky/web/onboard.js +46 -4
- data/lib/clacky/web/sessions.js +33 -72
- data/lib/clacky/web/settings.js +42 -4
- data/lib/clacky/web/version.js +52 -1
- metadata +21 -10
- data/docs/proposals/2026-05-11-system-prompt-alignment.md +0 -325
- data/docs/proposals/2026-05-12-memory-mechanism-optimization.md +0 -89
- /data/lib/clacky/default_skills/{channel-setup → channel-manager}/feishu_setup.rb +0 -0
- /data/lib/clacky/default_skills/{channel-setup → channel-manager}/import_lark_skills.rb +0 -0
- /data/lib/clacky/default_skills/{channel-setup → channel-manager}/weixin_setup.rb +0 -0
data/lib/clacky/web/app.css
CHANGED
|
@@ -687,11 +687,11 @@ body {
|
|
|
687
687
|
opacity: 0.5;
|
|
688
688
|
}
|
|
689
689
|
|
|
690
|
-
/* date
|
|
690
|
+
/* date trigger button */
|
|
691
691
|
.search-date {
|
|
692
692
|
flex: 1;
|
|
693
693
|
min-width: 0;
|
|
694
|
-
padding: 0 20px 0 4px;
|
|
694
|
+
padding: 0 20px 0 4px;
|
|
695
695
|
height: 100%;
|
|
696
696
|
font-size: 11px;
|
|
697
697
|
border: none;
|
|
@@ -699,22 +699,10 @@ body {
|
|
|
699
699
|
color: var(--color-text-secondary);
|
|
700
700
|
outline: none;
|
|
701
701
|
cursor: pointer;
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
}
|
|
707
|
-
.search-date::-webkit-calendar-picker-indicator {
|
|
708
|
-
position: absolute;
|
|
709
|
-
width: 100%;
|
|
710
|
-
height: 100%;
|
|
711
|
-
top: 0;
|
|
712
|
-
left: 0;
|
|
713
|
-
opacity: 0;
|
|
714
|
-
cursor: pointer;
|
|
715
|
-
}
|
|
716
|
-
[data-theme="dark"] .search-date {
|
|
717
|
-
color-scheme: dark;
|
|
702
|
+
text-align: left;
|
|
703
|
+
white-space: nowrap;
|
|
704
|
+
overflow: hidden;
|
|
705
|
+
text-overflow: ellipsis;
|
|
718
706
|
}
|
|
719
707
|
|
|
720
708
|
/* Active filter highlight */
|
|
@@ -724,15 +712,104 @@ body {
|
|
|
724
712
|
font-weight: 500;
|
|
725
713
|
}
|
|
726
714
|
|
|
715
|
+
/* ── DatePicker popup ─────────────────────────────────────────────────────── */
|
|
716
|
+
.dp-popup {
|
|
717
|
+
position: fixed;
|
|
718
|
+
z-index: 9999;
|
|
719
|
+
background: var(--color-bg-primary, #fff);
|
|
720
|
+
border: 1px solid var(--color-border-primary);
|
|
721
|
+
border-radius: 10px;
|
|
722
|
+
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
|
|
723
|
+
padding: 10px;
|
|
724
|
+
width: 230px;
|
|
725
|
+
font-size: 12px;
|
|
726
|
+
color: var(--color-text-primary);
|
|
727
|
+
user-select: none;
|
|
728
|
+
}
|
|
729
|
+
.dp-header {
|
|
730
|
+
display: flex;
|
|
731
|
+
align-items: center;
|
|
732
|
+
justify-content: space-between;
|
|
733
|
+
margin-bottom: 8px;
|
|
734
|
+
}
|
|
735
|
+
.dp-title {
|
|
736
|
+
font-weight: 600;
|
|
737
|
+
font-size: 13px;
|
|
738
|
+
}
|
|
739
|
+
.dp-nav {
|
|
740
|
+
background: none;
|
|
741
|
+
border: none;
|
|
742
|
+
cursor: pointer;
|
|
743
|
+
font-size: 16px;
|
|
744
|
+
color: var(--color-text-secondary);
|
|
745
|
+
padding: 2px 6px;
|
|
746
|
+
border-radius: 4px;
|
|
747
|
+
line-height: 1;
|
|
748
|
+
}
|
|
749
|
+
.dp-nav:hover { background: var(--color-bg-hover); }
|
|
750
|
+
.dp-grid {
|
|
751
|
+
display: grid;
|
|
752
|
+
grid-template-columns: repeat(7, 1fr);
|
|
753
|
+
gap: 2px;
|
|
754
|
+
}
|
|
755
|
+
.dp-dow {
|
|
756
|
+
text-align: center;
|
|
757
|
+
font-size: 10px;
|
|
758
|
+
color: var(--color-text-muted);
|
|
759
|
+
padding: 3px 0;
|
|
760
|
+
font-weight: 500;
|
|
761
|
+
}
|
|
762
|
+
.dp-day {
|
|
763
|
+
background: none;
|
|
764
|
+
border: none;
|
|
765
|
+
cursor: pointer;
|
|
766
|
+
border-radius: 6px;
|
|
767
|
+
padding: 4px 2px;
|
|
768
|
+
text-align: center;
|
|
769
|
+
color: var(--color-text-primary);
|
|
770
|
+
font-size: 12px;
|
|
771
|
+
line-height: 1.4;
|
|
772
|
+
}
|
|
773
|
+
.dp-day:hover { background: var(--color-bg-hover); }
|
|
774
|
+
.dp-day--today {
|
|
775
|
+
color: var(--color-accent-primary);
|
|
776
|
+
font-weight: 700;
|
|
777
|
+
}
|
|
778
|
+
.dp-day--selected {
|
|
779
|
+
background: var(--color-accent-primary);
|
|
780
|
+
color: var(--color-text-inverse) !important;
|
|
781
|
+
font-weight: 600;
|
|
782
|
+
}
|
|
783
|
+
.dp-day--selected:hover { background: var(--color-accent-primary); opacity: 0.88; }
|
|
784
|
+
.dp-footer {
|
|
785
|
+
display: flex;
|
|
786
|
+
justify-content: space-between;
|
|
787
|
+
margin-top: 8px;
|
|
788
|
+
padding-top: 8px;
|
|
789
|
+
border-top: 1px solid var(--color-border-primary);
|
|
790
|
+
}
|
|
791
|
+
.dp-btn-clear,
|
|
792
|
+
.dp-btn-today {
|
|
793
|
+
background: none;
|
|
794
|
+
border: none;
|
|
795
|
+
cursor: pointer;
|
|
796
|
+
font-size: 11px;
|
|
797
|
+
color: var(--color-accent-primary);
|
|
798
|
+
padding: 4px 8px;
|
|
799
|
+
border-radius: 4px;
|
|
800
|
+
}
|
|
801
|
+
.dp-btn-clear:hover,
|
|
802
|
+
.dp-btn-today:hover { background: var(--color-bg-hover); }
|
|
803
|
+
|
|
727
804
|
/* 清除筛选 — small ✕ icon button */
|
|
728
805
|
.btn-search-clear-all {
|
|
729
806
|
flex-shrink: 0;
|
|
730
807
|
margin-left: 4px;
|
|
731
|
-
width:
|
|
732
|
-
height:
|
|
808
|
+
width: 14px;
|
|
809
|
+
height: 14px;
|
|
733
810
|
padding: 0;
|
|
734
|
-
font-size:
|
|
735
|
-
line-height:
|
|
811
|
+
font-size: 9px;
|
|
812
|
+
line-height: 14px;
|
|
736
813
|
text-align: center;
|
|
737
814
|
border: none;
|
|
738
815
|
border-radius: 50%;
|
|
@@ -1049,7 +1126,7 @@ body {
|
|
|
1049
1126
|
border-radius: 6px;
|
|
1050
1127
|
border: 1px solid transparent;
|
|
1051
1128
|
transition: background .2s ease;
|
|
1052
|
-
margin
|
|
1129
|
+
margin: 0 8px 4px;
|
|
1053
1130
|
}
|
|
1054
1131
|
/* Default: secondary text color */
|
|
1055
1132
|
.task-item .task-name,
|
|
@@ -1086,15 +1163,15 @@ body {
|
|
|
1086
1163
|
.task-row {
|
|
1087
1164
|
display: flex;
|
|
1088
1165
|
align-items: center;
|
|
1089
|
-
gap:
|
|
1090
|
-
padding: 10px 12px 10px
|
|
1166
|
+
gap: 6px;
|
|
1167
|
+
padding: 10px 12px 10px 12px;
|
|
1091
1168
|
cursor: pointer;
|
|
1092
1169
|
border-radius: 6px;
|
|
1093
1170
|
transition: background 0.2s ease;
|
|
1094
1171
|
}
|
|
1095
1172
|
.task-icon {
|
|
1096
|
-
width:
|
|
1097
|
-
height:
|
|
1173
|
+
width: 17px;
|
|
1174
|
+
height: 17px;
|
|
1098
1175
|
flex-shrink: 0;
|
|
1099
1176
|
stroke-width: 2;
|
|
1100
1177
|
}
|
|
@@ -1726,6 +1803,7 @@ body {
|
|
|
1726
1803
|
border-top: 1px solid var(--color-border-primary);
|
|
1727
1804
|
margin: 0.8em 0;
|
|
1728
1805
|
}
|
|
1806
|
+
.msg-assistant img { max-width: 100%; height: auto; border-radius: 4px; }
|
|
1729
1807
|
.msg-assistant strong { font-weight: 600; color: var(--color-text-primary); }
|
|
1730
1808
|
.msg-assistant em { font-style: italic; color: var(--color-text-secondary); }
|
|
1731
1809
|
.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; }
|
|
@@ -3257,6 +3335,56 @@ body {
|
|
|
3257
3335
|
color: var(--color-accent-primary);
|
|
3258
3336
|
}
|
|
3259
3337
|
|
|
3338
|
+
/* Provider recommended badge (inside dropdown option) */
|
|
3339
|
+
.provider-badge-recommended {
|
|
3340
|
+
display: inline-block;
|
|
3341
|
+
font-size: 10px;
|
|
3342
|
+
font-weight: 600;
|
|
3343
|
+
padding: 1px 6px;
|
|
3344
|
+
margin-left: 6px;
|
|
3345
|
+
border-radius: 4px;
|
|
3346
|
+
background: var(--color-accent-primary, #4f46e5);
|
|
3347
|
+
color: #fff;
|
|
3348
|
+
vertical-align: middle;
|
|
3349
|
+
letter-spacing: 0.3px;
|
|
3350
|
+
line-height: 1.6;
|
|
3351
|
+
}
|
|
3352
|
+
|
|
3353
|
+
/* Provider promo hint (shown when openclacky is selected) */
|
|
3354
|
+
.provider-promo-hint {
|
|
3355
|
+
display: none;
|
|
3356
|
+
margin-top: 2px;
|
|
3357
|
+
padding: 8px 10px;
|
|
3358
|
+
border-radius: 6px;
|
|
3359
|
+
background: var(--color-bg-hover, #fafafa);
|
|
3360
|
+
}
|
|
3361
|
+
.provider-promo-hint.visible {
|
|
3362
|
+
display: block;
|
|
3363
|
+
}
|
|
3364
|
+
.provider-promo-hint .promo-inner {
|
|
3365
|
+
padding: 0;
|
|
3366
|
+
}
|
|
3367
|
+
.provider-promo-hint .promo-title {
|
|
3368
|
+
font-size: 11px;
|
|
3369
|
+
font-weight: 600;
|
|
3370
|
+
color: var(--color-text-secondary);
|
|
3371
|
+
margin-bottom: 3px;
|
|
3372
|
+
}
|
|
3373
|
+
.provider-promo-hint .promo-item {
|
|
3374
|
+
display: flex;
|
|
3375
|
+
align-items: baseline;
|
|
3376
|
+
gap: 4px;
|
|
3377
|
+
font-size: 11px;
|
|
3378
|
+
line-height: 1.6;
|
|
3379
|
+
color: var(--color-text-tertiary, #9ca3af);
|
|
3380
|
+
}
|
|
3381
|
+
.provider-promo-hint .promo-icon {
|
|
3382
|
+
flex-shrink: 0;
|
|
3383
|
+
font-size: 6px;
|
|
3384
|
+
opacity: 0.3;
|
|
3385
|
+
line-height: 2;
|
|
3386
|
+
}
|
|
3387
|
+
|
|
3260
3388
|
/* Model name combobox */
|
|
3261
3389
|
.model-name-combobox {
|
|
3262
3390
|
position: relative;
|
|
@@ -3449,6 +3577,13 @@ body {
|
|
|
3449
3577
|
.modal-box.sm { max-width: 480px; }
|
|
3450
3578
|
.modal-box.lg { max-width: 680px; }
|
|
3451
3579
|
.modal-title { font-size: 16px; font-weight: 600; margin-bottom: 16px; color: var(--color-text-primary); }
|
|
3580
|
+
.modal-confirm-message {
|
|
3581
|
+
font-size: 15px;
|
|
3582
|
+
line-height: 1.6;
|
|
3583
|
+
margin-bottom: 20px;
|
|
3584
|
+
white-space: pre-wrap;
|
|
3585
|
+
color: var(--color-text-primary);
|
|
3586
|
+
}
|
|
3452
3587
|
.modal-actions {
|
|
3453
3588
|
display: flex;
|
|
3454
3589
|
gap: 10px;
|
|
@@ -3523,6 +3658,10 @@ body {
|
|
|
3523
3658
|
font-weight: 500;
|
|
3524
3659
|
color: var(--color-text-primary);
|
|
3525
3660
|
}
|
|
3661
|
+
.label-required {
|
|
3662
|
+
color: var(--color-error);
|
|
3663
|
+
margin-left: 2px;
|
|
3664
|
+
}
|
|
3526
3665
|
.modal-input, .modal-select {
|
|
3527
3666
|
background: var(--color-bg-primary);
|
|
3528
3667
|
border: 1px solid var(--color-border-primary);
|
|
@@ -3537,6 +3676,9 @@ body {
|
|
|
3537
3676
|
outline: none;
|
|
3538
3677
|
border-color: var(--color-accent-primary);
|
|
3539
3678
|
}
|
|
3679
|
+
.modal-input.input-error {
|
|
3680
|
+
border-color: var(--color-error);
|
|
3681
|
+
}
|
|
3540
3682
|
.modal-select {
|
|
3541
3683
|
cursor: pointer;
|
|
3542
3684
|
appearance: none;
|
|
@@ -5744,14 +5886,22 @@ body.setup-mode[data-theme="dark"] {
|
|
|
5744
5886
|
display: flex;
|
|
5745
5887
|
align-items: center;
|
|
5746
5888
|
justify-content: center;
|
|
5747
|
-
font-size: 18px;
|
|
5748
|
-
font-weight: 700;
|
|
5749
5889
|
flex-shrink: 0;
|
|
5750
5890
|
}
|
|
5751
5891
|
|
|
5892
|
+
.channel-logo svg {
|
|
5893
|
+
width: 24px;
|
|
5894
|
+
height: 24px;
|
|
5895
|
+
display: block;
|
|
5896
|
+
}
|
|
5897
|
+
|
|
5752
5898
|
.channel-logo-feishu {
|
|
5753
|
-
background:
|
|
5754
|
-
|
|
5899
|
+
background: transparent;
|
|
5900
|
+
}
|
|
5901
|
+
|
|
5902
|
+
.channel-logo-feishu svg {
|
|
5903
|
+
width: 32px;
|
|
5904
|
+
height: 32px;
|
|
5755
5905
|
}
|
|
5756
5906
|
|
|
5757
5907
|
.channel-logo-wecom {
|
|
@@ -5764,6 +5914,21 @@ body.setup-mode[data-theme="dark"] {
|
|
|
5764
5914
|
color: #fff;
|
|
5765
5915
|
}
|
|
5766
5916
|
|
|
5917
|
+
.channel-logo-discord {
|
|
5918
|
+
background: linear-gradient(135deg, #5865f2, #4752c4);
|
|
5919
|
+
color: #fff;
|
|
5920
|
+
}
|
|
5921
|
+
|
|
5922
|
+
.channel-logo-telegram {
|
|
5923
|
+
background: linear-gradient(135deg, #2aabee, #229ed9);
|
|
5924
|
+
color: #fff;
|
|
5925
|
+
}
|
|
5926
|
+
|
|
5927
|
+
.channel-logo-dingtalk {
|
|
5928
|
+
background: linear-gradient(135deg, #3296fa, #1677ff);
|
|
5929
|
+
color: #fff;
|
|
5930
|
+
}
|
|
5931
|
+
|
|
5767
5932
|
.channel-card-name {
|
|
5768
5933
|
font-size: 15px;
|
|
5769
5934
|
font-weight: 600;
|
|
@@ -5777,6 +5942,13 @@ body.setup-mode[data-theme="dark"] {
|
|
|
5777
5942
|
}
|
|
5778
5943
|
|
|
5779
5944
|
/* Status badge */
|
|
5945
|
+
.channel-card-status {
|
|
5946
|
+
display: inline-flex;
|
|
5947
|
+
align-items: center;
|
|
5948
|
+
gap: 12px;
|
|
5949
|
+
flex-shrink: 0;
|
|
5950
|
+
}
|
|
5951
|
+
|
|
5780
5952
|
.channel-status-badge {
|
|
5781
5953
|
display: inline-flex;
|
|
5782
5954
|
align-items: center;
|
|
@@ -5846,6 +6018,17 @@ body.setup-mode[data-theme="dark"] {
|
|
|
5846
6018
|
transition: background 0.15s, opacity 0.15s;
|
|
5847
6019
|
}
|
|
5848
6020
|
|
|
6021
|
+
/* Test button uses an outline / ghost style so it does not compete with the primary action */
|
|
6022
|
+
.btn-channel-test {
|
|
6023
|
+
background: transparent;
|
|
6024
|
+
color: var(--color-text-secondary);
|
|
6025
|
+
border: 1px solid var(--color-border-primary);
|
|
6026
|
+
}
|
|
6027
|
+
.btn-channel-test:hover {
|
|
6028
|
+
background: var(--color-border-secondary);
|
|
6029
|
+
color: var(--color-text-primary);
|
|
6030
|
+
}
|
|
6031
|
+
|
|
5849
6032
|
.btn-channel-test:disabled,
|
|
5850
6033
|
.btn-channel-configure:disabled {
|
|
5851
6034
|
opacity: 0.5;
|
|
@@ -6210,6 +6393,40 @@ body.setup-mode[data-theme="dark"] {
|
|
|
6210
6393
|
line-height: 1.5;
|
|
6211
6394
|
}
|
|
6212
6395
|
|
|
6396
|
+
/* Restart-failed state — shows both recovery paths (tray + CLI) */
|
|
6397
|
+
.vup-restart-failed {
|
|
6398
|
+
padding: 4px 0 2px;
|
|
6399
|
+
}
|
|
6400
|
+
.vup-restart-failed-title {
|
|
6401
|
+
margin: 0 0 6px;
|
|
6402
|
+
font-size: 13px;
|
|
6403
|
+
font-weight: 600;
|
|
6404
|
+
color: var(--color-error, #ef4444);
|
|
6405
|
+
}
|
|
6406
|
+
.vup-restart-failed-desc {
|
|
6407
|
+
margin: 0 0 8px;
|
|
6408
|
+
font-size: 12px;
|
|
6409
|
+
color: var(--color-text-muted);
|
|
6410
|
+
line-height: 1.5;
|
|
6411
|
+
}
|
|
6412
|
+
.vup-restart-failed-options {
|
|
6413
|
+
margin: 0 0 10px;
|
|
6414
|
+
padding-left: 18px;
|
|
6415
|
+
font-size: 12px;
|
|
6416
|
+
color: var(--color-text-primary);
|
|
6417
|
+
line-height: 1.6;
|
|
6418
|
+
}
|
|
6419
|
+
.vup-restart-failed-options li + li {
|
|
6420
|
+
margin-top: 4px;
|
|
6421
|
+
}
|
|
6422
|
+
.vup-cmd {
|
|
6423
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
6424
|
+
font-size: 11.5px;
|
|
6425
|
+
background: var(--color-surface-muted, rgba(127,127,127,.12));
|
|
6426
|
+
padding: 1px 6px;
|
|
6427
|
+
border-radius: 4px;
|
|
6428
|
+
}
|
|
6429
|
+
|
|
6213
6430
|
|
|
6214
6431
|
|
|
6215
6432
|
/* ── Profile / My Data Panel ───────────────────────────────────────────────
|
data/lib/clacky/web/app.js
CHANGED
|
@@ -330,7 +330,57 @@ const Modal = (() => {
|
|
|
330
330
|
});
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
-
|
|
333
|
+
/** Show a rename dialog. Returns a Promise<string|null>. */
|
|
334
|
+
function rename(currentName = "") {
|
|
335
|
+
return new Promise(resolve => {
|
|
336
|
+
const input = $("rename-modal-input");
|
|
337
|
+
input.value = currentName;
|
|
338
|
+
input.classList.remove("input-error");
|
|
339
|
+
$("rename-modal-overlay").style.display = "flex";
|
|
340
|
+
|
|
341
|
+
setTimeout(() => {
|
|
342
|
+
input.focus();
|
|
343
|
+
input.select();
|
|
344
|
+
}, 50);
|
|
345
|
+
|
|
346
|
+
const cleanup = (result) => {
|
|
347
|
+
$("rename-modal-overlay").style.display = "none";
|
|
348
|
+
$("rename-modal-save").onclick = null;
|
|
349
|
+
$("rename-modal-cancel").onclick = null;
|
|
350
|
+
$("rename-modal-overlay").onclick = null;
|
|
351
|
+
input.onkeydown = null;
|
|
352
|
+
input.oninput = null;
|
|
353
|
+
resolve(result);
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const saveHandler = () => {
|
|
357
|
+
const newName = input.value.trim();
|
|
358
|
+
if (!newName) {
|
|
359
|
+
input.classList.add("input-error");
|
|
360
|
+
input.focus();
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
cleanup(newName === currentName ? null : newName);
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
input.oninput = () => input.classList.remove("input-error");
|
|
367
|
+
|
|
368
|
+
$("rename-modal-save").onclick = saveHandler;
|
|
369
|
+
$("rename-modal-cancel").onclick = () => cleanup(null);
|
|
370
|
+
|
|
371
|
+
input.onkeydown = (e) => {
|
|
372
|
+
if (e.key === "Enter") { e.preventDefault(); saveHandler(); }
|
|
373
|
+
if (e.key === "Escape") cleanup(null);
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
// Close on overlay click
|
|
377
|
+
$("rename-modal-overlay").onclick = (e) => {
|
|
378
|
+
if (e.target.id === "rename-modal-overlay") cleanup(null);
|
|
379
|
+
};
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return { confirm, prompt, rename };
|
|
334
384
|
})();
|
|
335
385
|
|
|
336
386
|
// ── Confirmation modal ────────────────────────────────────────────────────
|
data/lib/clacky/web/channels.js
CHANGED
|
@@ -2,38 +2,65 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Design principle: no configuration forms here.
|
|
4
4
|
// This page shows platform status only. All setup is done via Agent with browser automation.
|
|
5
|
-
// "Auto Setup" opens a chat session with /channel-
|
|
5
|
+
// "Auto Setup" opens a chat session with /channel-manager pre-filled — the Agent will use
|
|
6
6
|
// browser automation to complete the entire setup on the platform's web console.
|
|
7
|
-
// "Test" runs /channel-
|
|
7
|
+
// "Test" runs /channel-manager doctor via the Agent and streams results.
|
|
8
8
|
|
|
9
9
|
const Channels = (() => {
|
|
10
10
|
|
|
11
11
|
// Platform display metadata (use accessor to pick up runtime language)
|
|
12
|
+
// SVG sources: dashboard-icons (Lark, multi-color brand mark),
|
|
13
|
+
// TDesign icons (WeCom/WeChat, single-color), simpleicons (Discord/Telegram),
|
|
14
|
+
// ant-design/ant-design-icons outlined (DingTalk).
|
|
12
15
|
function PLATFORM_META() {
|
|
13
16
|
return {
|
|
14
17
|
feishu: {
|
|
15
|
-
logo: "
|
|
18
|
+
logo: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="62.16 94.5 407.87 324.19" aria-hidden="true"><path d="M274.18 264.785q.515-.517 1.03-1.027c.685-.688 1.372-1.258 2.056-1.945l1.37-1.372 4.118-4.113 5.598-5.601 4.8-4.797 4.575-4.457 4.796-4.688 4.344-4.344 6.059-6.054c1.14-1.145 2.285-2.29 3.543-3.317 2.168-2.054 4.457-4 6.855-5.828 2.172-1.715 4.344-3.312 6.516-4.914 3.082-2.172 6.398-4.344 9.71-6.285 3.204-1.941 6.63-3.656 10.06-5.371 3.199-1.602 6.515-2.973 9.827-4.23 1.829-.684 3.774-1.372 5.602-2.055.914-.344 1.941-.688 2.856-.914-8.57-33.715-24.227-64.575-45.258-90.86-4.114-5.14-10.399-8.113-17.028-8.113H130.754c-3.203 0-4.457 4-1.945 5.941 59.543 43.66 109.144 99.887 145.03 164.801 0-.226.227-.34.34-.457m0 0" fill="#00d6b9"/><path d="M204.79 418.691c90.288 0 169.03-49.828 210.058-123.543 1.488-2.628 2.859-5.257 4.23-7.882q-3.087 6-6.86 11.312l-2.741 3.77c-1.141 1.488-2.399 2.972-3.657 4.457-1.03 1.144-2.058 2.285-3.086 3.316-2.058 2.172-4.343 4.227-6.629 6.172a53 53 0 0 1-3.886 3.2c-1.598 1.144-3.086 2.284-4.684 3.429-1.031.683-2.058 1.371-3.086 1.941-1.144.684-2.172 1.258-3.316 1.942a131 131 0 0 1-6.969 3.543c-2.059.918-4.117 1.828-6.289 2.515-2.285.801-4.57 1.602-6.969 2.285-3.543.914-7.086 1.715-10.742 2.286-2.629.457-5.258.687-8 .914-2.86.23-5.601.23-8.457.23-3.086 0-6.289-.23-9.488-.57a83 83 0 0 1-7.086-1.031c-2.055-.34-4.113-.801-6.168-1.258-1.031-.227-2.176-.57-3.203-.797-2.973-.8-6.055-1.602-9.028-2.516-1.488-.457-2.972-.914-4.457-1.258-2.172-.683-4.457-1.37-6.629-2.058-1.828-.57-3.656-1.14-5.37-1.711q-2.573-.86-5.145-1.715c-1.14-.344-2.285-.8-3.543-1.144-1.371-.457-2.856-1.028-4.227-1.485-1.027-.344-2.058-.687-2.972-1.027-1.942-.688-4-1.488-5.942-2.172-1.144-.457-2.285-.914-3.43-1.258-1.484-.57-3.085-1.144-4.57-1.828-1.601-.687-3.203-1.258-4.8-1.945-1.028-.457-2.06-.797-3.087-1.258-1.257-.57-2.628-1.027-3.886-1.598-1.028-.457-1.942-.8-2.969-1.258l-3.086-1.37c-.914-.344-1.832-.801-2.746-1.145a44 44 0 0 1-2.512-1.14c-.8-.345-1.715-.802-2.515-1.145-.914-.344-1.715-.801-2.512-1.141-1.031-.457-2.172-1.031-3.203-1.484-1.14-.575-2.285-1.032-3.426-1.602-1.258-.574-2.402-1.144-3.66-1.715-1.027-.457-2.055-1.027-3.082-1.484-54.172-26.973-102.172-63.086-143.09-106.746-2.055-2.172-5.71-.684-5.71 2.289l.112 154.398v12.57c0 7.317 3.543 14.06 9.598 18.172 38.172 24.801 83.773 39.543 132.914 39.543m0 0" fill="#3370ff"/><path d="M414.84 295.188c0 .113-.113.113-.113.226zl.8-1.489c-.343.457-.574 1.028-.8 1.488m3.793-7.05.226-.457.114-.23q-.17.513-.34.687m0 0" fill="#133c9a"/><path d="M470.035 201.121c-18.285-9.031-38.86-14.059-60.687-14.059-12.914 0-25.485 1.829-37.371 5.141-1.372.344-2.743.8-4.114 1.258-.914.344-1.941.574-2.855.914-1.945.688-3.774 1.375-5.602 2.059-3.316 1.257-6.629 2.742-9.828 4.23-3.43 1.598-6.742 3.426-10.058 5.371a128 128 0 0 0-9.715 6.285c-2.285 1.602-4.457 3.2-6.512 4.914a154 154 0 0 0-6.86 5.828c-1.14 1.141-2.398 2.172-3.542 3.313l-6.055 6.059-4.344 4.343-4.8 4.684-4.57 4.46-4.802 4.798-11.086 11.086c-.687.687-1.37 1.37-2.058 1.945l-1.028 1.027c-.457.457-1.027 1.028-1.601 1.485-.57.57-1.14 1.031-1.711 1.601a244.4 244.4 0 0 1-49.828 35.313c1.027.457 2.168 1.027 3.199 1.488.8.34 1.715.797 2.512 1.14.8.344 1.715.801 2.515 1.145.801.344 1.602.684 2.516 1.14.914.345 1.828.802 2.742 1.145l3.086 1.371c1.027.457 1.942.801 2.969 1.258 1.258.57 2.629 1.028 3.887 1.598 1.03.46 2.058.8 3.086 1.258 1.601.687 3.199 1.258 4.8 1.945 1.485.57 3.086 1.14 4.57 1.828 1.145.457 2.286.914 3.43 1.258 1.946.684 4 1.484 5.946 2.172a81 81 0 0 1 2.968 1.027c1.371.457 2.856 1.028 4.23 1.485 1.141.343 2.286.8 3.544 1.14q2.567.86 5.14 1.719c1.829.57 3.657 1.14 5.372 1.71 2.171.688 4.457 1.376 6.628 2.06 1.489.457 2.973.914 4.457 1.257 2.973.914 5.942 1.715 9.032 2.512 1.027.344 2.168.574 3.199.8 2.055.458 4.113.915 6.172 1.259 2.398.457 4.683.8 7.082 1.03 3.203.34 6.402.571 9.488.571 2.856 0 5.715 0 8.457-.23 2.63-.227 5.371-.457 8-.914 3.656-.57 7.2-1.371 10.742-2.286 2.399-.683 4.688-1.37 6.973-2.285 2.172-.8 4.227-1.601 6.285-2.515 2.399-1.028 4.684-2.285 6.973-3.543 1.14-.57 2.168-1.258 3.312-1.942 1.028-.687 2.059-1.257 3.086-1.945 1.602-1.027 3.2-2.168 4.684-3.426a52 52 0 0 0 3.887-3.203c2.289-1.941 4.457-4 6.628-6.168 1.032-1.031 2.06-2.172 3.086-3.316 1.258-1.485 2.516-2.969 3.657-4.457.918-1.258 1.828-2.512 2.742-3.77 2.515-3.543 4.8-7.316 6.86-11.199l2.284-4.688 21.145-42.171v.113c6.742-14.742 16.226-28.113 27.656-39.426m0 0" fill="#133c9a"/></svg>`,
|
|
16
19
|
logoClass: "channel-logo-feishu",
|
|
17
20
|
name: "Feishu / Lark",
|
|
18
21
|
desc: I18n.t("channels.feishu.desc"),
|
|
19
|
-
setupCmd: "/channel-
|
|
20
|
-
testCmd: "/channel-
|
|
22
|
+
setupCmd: "/channel-manager setup feishu",
|
|
23
|
+
testCmd: "/channel-manager doctor",
|
|
21
24
|
},
|
|
22
25
|
wecom: {
|
|
23
|
-
logo: "
|
|
26
|
+
logo: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path fill="#fff" d="m17.326 8.158l-.003-.007a6.6 6.6 0 0 0-1.178-1.674c-1.266-1.307-3.067-2.19-5.102-2.417a9.3 9.3 0 0 0-2.124 0h-.001c-2.061.228-3.882 1.107-5.14 2.405a6.7 6.7 0 0 0-1.194 1.682A5.7 5.7 0 0 0 2 10.657c0 1.106.332 2.218.988 3.201l.006.01c.391.594 1.092 1.39 1.637 1.83l.983.793l-.208.875l.527-.267l.708-.358l.761.225c.467.137.955.227 1.517.29h.005q.515.06 1.026.059c.355 0 .724-.02 1.095-.06a9 9 0 0 0 1.346-.258c.095.7.43 1.337.932 1.81c-.658.208-1.352.358-2.061.436c-.442.048-.883.072-1.312.072q-.627 0-1.253-.072a10.7 10.7 0 0 1-1.861-.36l-2.84 1.438s-.29.131-.44.131c-.418 0-.702-.285-.702-.704c0-.252.067-.598.128-.84l.394-1.653c-.728-.586-1.563-1.544-2.052-2.287A7.76 7.76 0 0 1 0 10.658a7.7 7.7 0 0 1 .787-3.39a8.7 8.7 0 0 1 1.551-2.19c1.61-1.665 3.878-2.73 6.359-3.006a11.3 11.3 0 0 1 2.565 0c2.47.275 4.712 1.353 6.323 3.017a8.6 8.6 0 0 1 1.539 2.192c.466.945.769 1.937.769 2.978a3.06 3.06 0 0 0-2-.005c-.001-.644-.189-1.329-.564-2.09zm4.125 6.977l-.024-.024l-.024-.018l-.024-.018l-.096-.095a4.24 4.24 0 0 1-1.169-2.192q0-.038-.006-.075l-.006-.056l-.035-.144a1.3 1.3 0 0 0-.358-.61a1.386 1.386 0 0 0-1.957 0a1.4 1.4 0 0 0 0 1.963c.191.191.418.311.668.371c.024.012.06.012.084.012q.019 0 .041.006q.023.005.042.006a4.24 4.24 0 0 1 2.231 1.186c.048.048.096.095.131.143a.323.323 0 0 0 .466 0a.35.35 0 0 0 .036-.455m-1.05 4.37l-.025.025c-.119.096-.31.096-.453-.036a.326.326 0 0 1 0-.467c.047-.036.094-.083.141-.13l.002-.002a4.27 4.27 0 0 0 1.187-2.28q.005-.024.006-.043c0-.024 0-.06.012-.084a1.386 1.386 0 0 1 2.326-.67a1.4 1.4 0 0 1 0 1.964c-.167.18-.382.299-.608.359l-.143.036l-.057.005q-.035.006-.075.007a4.2 4.2 0 0 0-2.183 1.173l-.095.096q-.009.01-.018.024t-.018.024m-4.392-1.053l.024.024l.024.018q.015.009.024.018l.096.096a4.25 4.25 0 0 1 1.169 2.19q0 .04.006.076q.005.03.006.057l.035.143c.06.228.18.443.358.611c.537.539 1.42.539 1.957 0a1.4 1.4 0 0 0 0-1.964a1.4 1.4 0 0 0-.668-.371c-.024-.012-.06-.012-.084-.012q-.018 0-.041-.006l-.042-.006a4.25 4.25 0 0 1-2.231-1.185a1.4 1.4 0 0 1-.131-.144a.323.323 0 0 0-.466 0a.325.325 0 0 0-.036.455m1.039-4.358l.024-.024a.32.32 0 0 1 .453.035a.326.326 0 0 1 0 .467c-.047.036-.094.083-.141.13l-.002.002a4.27 4.27 0 0 0-1.187 2.281l-.006.042c0 .024 0 .06-.012.084a1.386 1.386 0 0 1-2.326.67a1.4 1.4 0 0 1 0-1.963c.166-.18.381-.3.608-.36l.143-.035q.026 0 .056-.006q.037-.005.075-.006a4.2 4.2 0 0 0 2.183-1.174l.096-.095l.018-.025z"/></svg>`,
|
|
24
27
|
logoClass: "channel-logo-wecom",
|
|
25
|
-
name: "WeCom
|
|
28
|
+
name: "WeCom",
|
|
26
29
|
desc: I18n.t("channels.wecom.desc"),
|
|
27
|
-
setupCmd: "/channel-
|
|
28
|
-
testCmd: "/channel-
|
|
30
|
+
setupCmd: "/channel-manager setup wecom",
|
|
31
|
+
testCmd: "/channel-manager doctor",
|
|
29
32
|
},
|
|
30
33
|
weixin: {
|
|
31
|
-
logo: "
|
|
34
|
+
logo: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path fill="#fff" d="M8.796 17.027H8.75c-1.153 0-2.254-.188-3.262-.53L2.65 17.92l.352-2.712C1.162 13.855 0 11.861 0 9.64c0-4.083 3.918-7.39 8.75-7.39c4.174 0 7.665 2.468 8.54 5.77a9 9 0 0 0-.6-.02c-4.364 0-8.19 3.037-8.19 7.11c0 .67.104 1.312.296 1.917M6 8a1 1 0 1 0 0-2a1 1 0 0 0 0 2m5.5.007a1 1 0 1 0 0-2a1 1 0 0 0 0 2"/><path fill="#fff" d="M21.874 19.52C23.187 18.405 24 16.863 24 15.16C24 11.758 20.754 9 16.75 9S9.5 11.758 9.5 15.161s3.246 6.161 7.25 6.161c.95 0 1.856-.155 2.686-.437l2.438 1.407zm-7.564-5.362a1 1 0 1 1 0-2a1 1 0 0 1 0 2m4.88 0a1 1 0 1 1 0-2a1 1 0 0 1 0 2"/></svg>`,
|
|
32
35
|
logoClass: "channel-logo-weixin",
|
|
33
|
-
name: "Weixin
|
|
36
|
+
name: "Weixin",
|
|
34
37
|
desc: I18n.t("channels.weixin.desc"),
|
|
35
|
-
setupCmd: "/channel-
|
|
36
|
-
testCmd: "/channel-
|
|
38
|
+
setupCmd: "/channel-manager setup weixin",
|
|
39
|
+
testCmd: "/channel-manager doctor",
|
|
40
|
+
},
|
|
41
|
+
dingtalk: {
|
|
42
|
+
logo: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" aria-hidden="true"><path fill="#fff" d="M573.7 252.5C422.5 197.4 201.3 96.7 201.3 96.7c-15.7-4.1-17.9 11.1-17.9 11.1c-5 61.1 33.6 160.5 53.6 182.8c19.9 22.3 319.1 113.7 319.1 113.7S326 357.9 270.5 341.9c-55.6-16-37.9 17.8-37.9 17.8c11.4 61.7 64.9 131.8 107.2 138.4c42.2 6.6 220.1 4 220.1 4s-35.5 4.1-93.2 11.9c-42.7 5.8-97 12.5-111.1 17.8c-33.1 12.5 24 62.6 24 62.6c84.7 76.8 129.7 50.5 129.7 50.5c33.3-10.7 61.4-18.5 85.2-24.2L565 743.1h84.6L603 928l205.3-271.9H700.8l22.3-38.7c.3.5.4.8.4.8S799.8 496.1 829 433.8l.6-1h-.1c5-10.8 8.6-19.7 10-25.8c17-71.3-114.5-99.4-265.8-154.5"/></svg>`,
|
|
43
|
+
logoClass: "channel-logo-dingtalk",
|
|
44
|
+
name: "DingTalk",
|
|
45
|
+
desc: I18n.t("channels.dingtalk.desc"),
|
|
46
|
+
setupCmd: "/channel-manager setup dingtalk",
|
|
47
|
+
testCmd: "/channel-manager doctor",
|
|
48
|
+
},
|
|
49
|
+
discord: {
|
|
50
|
+
logo: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path fill="#fff" d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/></svg>`,
|
|
51
|
+
logoClass: "channel-logo-discord",
|
|
52
|
+
name: "Discord",
|
|
53
|
+
desc: I18n.t("channels.discord.desc"),
|
|
54
|
+
setupCmd: "/channel-manager setup discord",
|
|
55
|
+
testCmd: "/channel-manager doctor",
|
|
56
|
+
},
|
|
57
|
+
telegram: {
|
|
58
|
+
logo: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path fill="#fff" d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/></svg>`,
|
|
59
|
+
logoClass: "channel-logo-telegram",
|
|
60
|
+
name: "Telegram",
|
|
61
|
+
desc: I18n.t("channels.telegram.desc"),
|
|
62
|
+
setupCmd: "/channel-manager setup telegram",
|
|
63
|
+
testCmd: "/channel-manager doctor",
|
|
37
64
|
},
|
|
38
65
|
};
|
|
39
66
|
}
|
|
@@ -46,10 +73,12 @@ const Channels = (() => {
|
|
|
46
73
|
|
|
47
74
|
// ── Data Loading ─────────────────────────────────────────────────────────────
|
|
48
75
|
|
|
49
|
-
async function _load() {
|
|
76
|
+
async function _load({ silent = false } = {}) {
|
|
50
77
|
const container = $("channels-list");
|
|
51
78
|
if (!container) return;
|
|
52
|
-
|
|
79
|
+
if (!silent) {
|
|
80
|
+
container.innerHTML = `<div class="channel-loading">${I18n.t("channels.loading")}</div>`;
|
|
81
|
+
}
|
|
53
82
|
|
|
54
83
|
try {
|
|
55
84
|
const res = await fetch("/api/channels");
|
|
@@ -77,8 +106,9 @@ const Channels = (() => {
|
|
|
77
106
|
}
|
|
78
107
|
|
|
79
108
|
function _renderCard(platform, data, meta) {
|
|
80
|
-
const enabled
|
|
81
|
-
const running
|
|
109
|
+
const enabled = !!data.enabled;
|
|
110
|
+
const running = !!data.running;
|
|
111
|
+
const hasConfig = !!data.has_config;
|
|
82
112
|
|
|
83
113
|
const card = document.createElement("div");
|
|
84
114
|
card.className = "channel-card";
|
|
@@ -87,17 +117,20 @@ const Channels = (() => {
|
|
|
87
117
|
card.innerHTML = `
|
|
88
118
|
<div class="channel-card-header">
|
|
89
119
|
<div class="channel-card-identity">
|
|
90
|
-
<span class="channel-logo ${_esc(meta.logoClass)}">${
|
|
120
|
+
<span class="channel-logo ${_esc(meta.logoClass)}">${meta.logo}</span>
|
|
91
121
|
<div>
|
|
92
122
|
<div class="channel-card-name">${_esc(meta.name)}</div>
|
|
93
123
|
<div class="channel-card-desc">${_esc(meta.desc)}</div>
|
|
94
124
|
</div>
|
|
95
125
|
</div>
|
|
96
|
-
<
|
|
126
|
+
<div class="channel-card-status">
|
|
127
|
+
${hasConfig ? _toggleHtml(platform, enabled) : ""}
|
|
128
|
+
<span class="channel-status-badge" id="channel-badge-${_esc(platform)}">${_badgeHtml(enabled, running, hasConfig)}</span>
|
|
129
|
+
</div>
|
|
97
130
|
</div>
|
|
98
131
|
|
|
99
132
|
<div class="channel-card-body">
|
|
100
|
-
${_statusHint(enabled, running)}
|
|
133
|
+
${_statusHint(enabled, running, hasConfig)}
|
|
101
134
|
</div>
|
|
102
135
|
|
|
103
136
|
<div class="channel-card-footer">
|
|
@@ -113,7 +146,7 @@ const Channels = (() => {
|
|
|
113
146
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
114
147
|
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
|
115
148
|
</svg>
|
|
116
|
-
${
|
|
149
|
+
${hasConfig ? I18n.t("channels.btn.reconfigure") : I18n.t("channels.btn.setup")}
|
|
117
150
|
</button>
|
|
118
151
|
</div>
|
|
119
152
|
</div>
|
|
@@ -122,37 +155,74 @@ const Channels = (() => {
|
|
|
122
155
|
// Bind events
|
|
123
156
|
card.querySelector(`#btn-test-${platform}`)?.addEventListener("click", () => _runTest(platform));
|
|
124
157
|
card.querySelector(`#btn-configure-${platform}`)?.addEventListener("click", () => _openSetup(platform));
|
|
158
|
+
card.querySelector(`#toggle-${platform}`)?.addEventListener("change", (ev) => _onToggle(platform, ev.target));
|
|
125
159
|
|
|
126
160
|
return card;
|
|
127
161
|
}
|
|
128
162
|
|
|
163
|
+
function _toggleHtml(platform, enabled) {
|
|
164
|
+
const aria = I18n.t(enabled ? "channels.toggle.on" : "channels.toggle.off");
|
|
165
|
+
return `
|
|
166
|
+
<label class="toggle-switch" title="${_esc(aria)}">
|
|
167
|
+
<input type="checkbox" id="toggle-${_esc(platform)}" ${enabled ? "checked" : ""} aria-label="${_esc(aria)}">
|
|
168
|
+
<span class="toggle-slider"></span>
|
|
169
|
+
</label>
|
|
170
|
+
`;
|
|
171
|
+
}
|
|
172
|
+
|
|
129
173
|
// ── Badge & status hint helpers ───────────────────────────────────────────────
|
|
130
174
|
|
|
131
|
-
function _badgeHtml(enabled, running) {
|
|
132
|
-
if (running)
|
|
133
|
-
if (enabled)
|
|
134
|
-
return
|
|
175
|
+
function _badgeHtml(enabled, running, hasConfig) {
|
|
176
|
+
if (running) return `<span class="badge-running">● ${I18n.t("channels.badge.running")}</span>`;
|
|
177
|
+
if (enabled) return `<span class="badge-enabled">● ${I18n.t("channels.badge.enabled")}</span>`;
|
|
178
|
+
if (hasConfig) return `<span class="badge-disabled">○ ${I18n.t("channels.badge.disabled")}</span>`;
|
|
179
|
+
return `<span class="badge-disabled">○ ${I18n.t("channels.badge.notConfigured")}</span>`;
|
|
135
180
|
}
|
|
136
181
|
|
|
137
|
-
function _statusHint(enabled, running) {
|
|
182
|
+
function _statusHint(enabled, running, hasConfig) {
|
|
138
183
|
if (running) {
|
|
139
184
|
return `<p class="channel-status-hint hint-ok">✓ ${I18n.t("channels.hint.running")}</p>`;
|
|
140
185
|
}
|
|
141
186
|
if (enabled) {
|
|
142
187
|
return `<p class="channel-status-hint hint-warn">⚠ ${I18n.t("channels.hint.enabledNotRunning")}</p>`;
|
|
143
188
|
}
|
|
189
|
+
if (hasConfig) {
|
|
190
|
+
return `<p class="channel-status-hint hint-idle">${I18n.t("channels.hint.disabled")}</p>`;
|
|
191
|
+
}
|
|
144
192
|
return `<p class="channel-status-hint hint-idle">${I18n.t("channels.hint.notConfigured")}</p>`;
|
|
145
193
|
}
|
|
146
194
|
|
|
195
|
+
// ── Toggle handler ───────────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
async function _onToggle(platform, checkbox) {
|
|
198
|
+
const desired = checkbox.checked;
|
|
199
|
+
checkbox.disabled = true;
|
|
200
|
+
try {
|
|
201
|
+
const res = await fetch(`/api/channels/${encodeURIComponent(platform)}/enabled`, {
|
|
202
|
+
method: "PATCH",
|
|
203
|
+
headers: { "Content-Type": "application/json" },
|
|
204
|
+
body: JSON.stringify({ enabled: desired }),
|
|
205
|
+
});
|
|
206
|
+
const data = await res.json();
|
|
207
|
+
if (!res.ok || !data.ok) throw new Error(data.error || "toggle failed");
|
|
208
|
+
await _load({ silent: true });
|
|
209
|
+
} catch (e) {
|
|
210
|
+
checkbox.checked = !desired;
|
|
211
|
+
alert("Error: " + e.message);
|
|
212
|
+
} finally {
|
|
213
|
+
checkbox.disabled = false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
147
217
|
// ── Actions ───────────────────────────────────────────────────────────────────
|
|
148
218
|
|
|
149
|
-
// Run E2E test: open a session and send /channel-
|
|
219
|
+
// Run E2E test: open a session and send /channel-manager doctor
|
|
150
220
|
async function _runTest(platform) {
|
|
151
221
|
const meta = PLATFORM_META()[platform];
|
|
152
222
|
await _sendToAgent(meta.testCmd, `Channel E2E Test — ${meta.name}`);
|
|
153
223
|
}
|
|
154
224
|
|
|
155
|
-
// Open setup: open a session and send /channel-
|
|
225
|
+
// Open setup: open a session and send /channel-manager setup <platform>
|
|
156
226
|
async function _openSetup(platform) {
|
|
157
227
|
const meta = PLATFORM_META()[platform];
|
|
158
228
|
await _sendToAgent(meta.setupCmd, `Channel Setup — ${meta.name}`);
|